def test__extractCorePluginInfo_with_unicode_filename(self): """Note: this test is redundant with its 'builtin_str' counterpart on Python3 but not on Python2""" # Note: compat.py redefines str as unicode for Python2 plugin_desc_content = str("simpleplugin.yapsy-plugin") analyzer = PluginFileAnalyzerWithInfoFile("mouf", ("yapsy-plugin")) infos, parser = analyzer._extractCorePluginInfo(self.plugin_directory, plugin_desc_content) self.assertEqual("Simple Plugin", infos["name"]) self.assertEqual(os.path.join(self.plugin_directory, "SimplePlugin"), infos["path"])
def NormalizePluginNameForModuleName(pluginName): """ Normalize a plugin name into a safer name for a module name. .. note:: may do a little more modifications than strictly necessary and is not optimized for speed. """ if is_py2: pluginName = str(pluginName, 'utf-8') if len(pluginName) == 0: return "_" if pluginName[0].isdigit(): pluginName = "_" + pluginName ret = RE_NON_ALPHANUM.sub("_", pluginName) if is_py2: ret = ret.encode('utf-8') return ret
def NormalizePluginNameForModuleName(pluginName): """ Normalize a plugin name into a safer name for a module name. .. note:: may do a little more modifications than strictly necessary and is not optimized for speed. """ if is_py2: pluginName = str(pluginName, 'utf-8') if len(pluginName)==0: return "_" if pluginName[0].isdigit(): pluginName = "_" + pluginName ret = RE_NON_ALPHANUM.sub("_",pluginName) if is_py2: ret = ret.encode('utf-8') return ret
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 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) candidate_module_paths = [ moduleName, # Try path consistent with the platform specific one os.path.join(moduleName,"__init__.py"), # Try typical paths (unix and windows) "%s/__init__.py" % moduleName, "%s\\__init__.py" % moduleName ] for candidate in candidate_module_paths: if candidate in zipContent: isValid = True break if isValid: 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