/usr/share/pyshared/yapsy/AutoInstallPluginManager.py is in python-yapsy 1.10.2-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | #!/usr/bin/python
# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t; python-indent: 4 -*-
"""
Role
====
Defines plugin managers that can handle the installation of plugin
files into the right place. Then the end-user does not have to browse
to the plugin directory to install them.
API
===
"""
import sys
import os
import shutil
import zipfile
import StringIO
from yapsy.IPlugin import IPlugin
from yapsy.PluginManagerDecorator import PluginManagerDecorator
from yapsy import log
class AutoInstallPluginManager(PluginManagerDecorator):
"""
A plugin manager that also manages the installation of the plugin
files into the appropriate directory.
"""
def __init__(self,
plugin_install_dir=None,
decorated_manager=None,
# The following args will only be used if we need to
# create a default PluginManager
categories_filter={"Default":IPlugin},
directories_list=None,
plugin_info_ext="yapsy-plugin"):
"""
Create the plugin manager and set up the directory where to
install new plugins.
Arguments
``plugin_install_dir``
The directory where new plugins to be installed will be copied.
.. warning:: If ``plugin_install_dir`` does not correspond to
an element of the ``directories_list``, it is
appended to the later.
"""
# Create the base decorator class
PluginManagerDecorator.__init__(self,
decorated_manager,
categories_filter,
directories_list,
plugin_info_ext)
# set the directory for new plugins
self.plugins_places=[]
self.setInstallDir(plugin_install_dir)
def setInstallDir(self,plugin_install_dir):
"""
Set the directory where to install new plugins.
"""
if not (plugin_install_dir in self.plugins_places):
self.plugins_places.append(plugin_install_dir)
self.install_dir = plugin_install_dir
def getInstallDir(self):
"""
Return the directory where new plugins should be installed.
"""
return self.install_dir
def install(self, directory, plugin_info_filename):
"""
Giving the plugin's info file (e.g. ``myplugin.yapsy-plugin``),
and the directory where it is located, get all the files that
define the plugin and copy them into the correct directory.
Return ``True`` if the installation is a success, ``False`` if
it is a failure.
"""
# start collecting essential info about the new plugin
plugin_info, config_parser = self._gatherCorePluginInfo(directory, plugin_info_filename)
# now determine the path of the file to execute,
# depending on wether the path indicated is a
# directory or a file
if not (os.path.exists(plugin_info.path) or os.path.exists(plugin_info.path+".py") ):
log.warning("Could not find the plugin's implementation for %s." % plugin_info.name)
return False
if os.path.isdir(plugin_info.path):
try:
shutil.copytree(plugin_info.path,
os.path.join(self.install_dir,os.path.basename(plugin_info.path)))
shutil.copy(os.path.join(directory, plugin_info_filename),
self.install_dir)
except:
log.error("Could not install plugin: %s." % plugin_info.name)
return False
else:
return True
elif os.path.isfile(plugin_info.path+".py"):
try:
shutil.copy(plugin_info.path+".py",
self.install_dir)
shutil.copy(os.path.join(directory, plugin_info_filename),
self.install_dir)
except:
log.error("Could not install plugin: %s." % plugin_info.name)
return False
else:
return True
else:
return False
def installFromZIP(self, plugin_ZIP_filename):
"""
Giving the plugin's zip file (e.g. ``myplugin.zip``), check
that their is a valid info file in it and correct all the
plugin files into the correct directory.
.. warning:: Only available for python 2.6 and later.
Return ``True`` if the installation is a success, ``False`` if
it is a failure.
"""
if sys.version_info < (2, 6):
raise NotImplementedError("Installing fom a ZIP file is only supported for Python2.6 and later.")
if not os.path.isfile(plugin_ZIP_filename):
log.warning("Could not find the plugin's zip file at '%s'." % plugin_ZIP_filename)
return False
candidateZipFile = zipfile.ZipFile(plugin_ZIP_filename)
if candidateZipFile.testzip() is not None:
log.warning("Corruption detected in Zip file '%s'." % plugin_ZIP_filename)
return False
zipContent = candidateZipFile.namelist()
log.info("Investigating the content of a zip file containing: '%s'" % zipContent)
log.info("Sanity checks on zip's contained files (looking for hazardous path symbols).")
# check absence of root path and ".." shortcut that would
# send the file oustide the desired directory
for containedFileName in zipContent:
# WARNING: the sanity checks below are certainly not
# exhaustive (maybe we could do something a bit smarter by
# using os.path.expanduser, os.path.expandvars and
# os.path.normpath)
if containedFileName.startswith("/"):
log.warning("Unsecure zip file, rejected because one of its file paths ('%s') starts with '/'" % containedFileName)
return False
if containedFileName.startswith(r"\\") or containedFileName.startswith("//"):
log.warning(r"Unsecure zip file, rejected because one of its file paths ('%s') starts with '\\'" % containedFileName)
return False
if os.path.splitdrive(containedFileName)[0]:
log.warning("Unsecure zip file, rejected because one of its file paths ('%s') starts with a drive letter" % containedFileName)
return False
if os.path.isabs(containedFileName):
log.warning("Unsecure zip file, rejected because one of its file paths ('%s') is absolute" % containedFileName)
return False
pathComponent = os.path.split(containedFileName)
if ".." in pathComponent:
log.warning("Unsecure zip file, rejected because one of its file paths ('%s') contains '..'" % containedFileName)
return False
if "~" in pathComponent:
log.warning("Unsecure zip file, rejected because one of its file paths ('%s') contains '~'" % containedFileName)
return False
infoFileCandidates = [filename for filename in zipContent if os.path.dirname(filename)==""]
if not infoFileCandidates:
log.warning("Zip file structure seems wrong in '%s', no info file found." % plugin_ZIP_filename)
return False
isValid = False
log.info("Looking for the zipped plugin's info file among '%s'" % infoFileCandidates)
for infoFileName in infoFileCandidates:
infoFile = candidateZipFile.read(infoFileName)
log.info("Assuming the zipped plugin info file to be '%s'" % infoFileName)
pluginName,moduleName,_ = self._getPluginNameAndModuleFromStream(StringIO.StringIO(infoFile))
if moduleName is None:
continue
log.info("Checking existence of the expected module '%s' in the zip file" % moduleName)
if moduleName in zipContent or os.path.join(moduleName,"__init__.py") in zipContent:
isValid = True
break
if not isValid:
log.warning("Zip file structure seems wrong in '%s', "
"could not match info file with the implementation of plugin '%s'." % (plugin_ZIP_filename,pluginName))
return False
else:
try:
candidateZipFile.extractall(self.install_dir)
return True
except Exception,e:
log.error("Could not install plugin '%s' from zip file '%s' (exception: '%s')." % (pluginName,plugin_ZIP_filename,e))
return False
|