Пример #1
0
	def locatePlugins(self):
		"""
		Walk through the plugins' places and look for plugins.

		Return the number of plugins found.
		"""
# 		print "%s.locatePlugins" % self.__class__
		self._candidates = []
		for directory in map(os.path.abspath,self.plugins_places):
			# first of all, is it a directory :)
			if not os.path.isdir(directory):
				log.debug("%s skips %s (not a directory)" % (self.__class__.__name__,directory))
				continue
			# iteratively walks through the directory
			log.debug("%s walks into directory: %s" % (self.__class__.__name__,directory))
			for item in os.walk(directory):
				dirpath = item[0]
				for filename in item[2]:
					# eliminate the obvious non plugin files
					if not filename.endswith(".%s" % self.plugin_info_ext):
						continue
					candidate_infofile = os.path.join(dirpath,filename)
					log.debug("""%s found a candidate: 
	%s""" % (self.__class__.__name__, candidate_infofile))
#					print candidate_infofile
					plugin_info = self.gatherBasicPluginInfo(dirpath,filename)
					if plugin_info is None:
						log.info("Plugin candidate rejected: '%s'" % candidate_infofile)
						continue
					# now determine the path of the file to execute,
					# depending on wether the path indicated is a
					# directory or a file
#					print plugin_info.path
					if os.path.isdir(plugin_info.path):
						candidate_filepath = os.path.join(plugin_info.path,"__init__")
					elif os.path.isfile(plugin_info.path+".py"):
						candidate_filepath = plugin_info.path
					else:
						log.info("Plugin candidate rejected: '%s'" % candidate_infofile) 
						continue
#					print candidate_filepath
					self._candidates.append((candidate_infofile, candidate_filepath, plugin_info))
		return len(self._candidates)
	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 not os.path.isfile(plugin_ZIP_filename):
			log.warning("Could not find the plugin's zip file at '%s'." % plugin_ZIP_filename)
			return False
		try:
			candidateZipFile = zipfile.ZipFile(plugin_ZIP_filename)
			first_bad_file = candidateZipFile.testzip()
			if first_bad_file:
				raise Exception("Corrupted ZIP with first bad file '%s'" % first_bad_file)
		except Exception as e:
			log.warning("Invalid zip file '%s' (error: %s)." % (plugin_ZIP_filename,e))
			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(str(infoFile,encoding="utf-8")))
			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 as e:
				log.error("Could not install plugin '%s' from zip file '%s' (exception: '%s')." % (pluginName,plugin_ZIP_filename,e))
				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
Пример #4
0
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
		try:
			candidateZipFile = zipfile.ZipFile(plugin_ZIP_filename)
			first_bad_file = candidateZipFile.testzip()
			if first_bad_file:
				raise Exception("Corrupted ZIP with first bad file '%s'" % first_bad_file)
		except Exception,e:
			log.warning("Invalid zip file '%s' (error: %s)." % (plugin_ZIP_filename,e))
			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