#!/usr/bin/env python # Michael Cohen # David Collett # # ****************************************************** # Version: FLAG $Version: 0.87-pre1 Date: Thu Jun 12 00:48:38 EST 2008$ # ****************************************************** # # * This program is free software; you can redistribute it and/or # * modify it under the terms of the GNU General Public License # * as published by the Free Software Foundation; either version 2 # * of the License, or (at your option) any later version. # * # * This program is distributed in the hope that it will be useful, # * but WITHOUT ANY WARRANTY; without even the implied warranty of # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # * GNU General Public License for more details. # * # * You should have received a copy of the GNU General Public License # * along with this program; if not, write to the Free Software # * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ***************************************************** """ This module implements a class registry. We scan the plugins directory for all python files and add those classes which should be registered into their own lookup tables. These are then ordered as required. The rest of Flag will then call onto the registered classes when needed. This mechanism allows us to reorgenise the code according to functionality. For example we may include a Scanner, Report and File classes in the same plugin and have them all automatically loaded. """ import pyflag.conf config=pyflag.conf.ConfObject() import os,sys,imp import pyflag.pyflaglog as pyflaglog ## Define the parameters we need. The default plugins directory is ## taken from the path of the current module because the installer ## will put the plugins directory within this directory. config.add_option("PLUGINS", default=os.path.dirname(__file__) + "/plugins", help="Plugin directories to use") class Registry: """ Main class to register classes derived from a given parent class. """ modules = [] module_desc = [] module_paths = [] classes = [] order = [] filenames = {} ## These are the modules which have been disabled loaded_modules = [] ## Excluded dirs are not descended into excluded_dirs = [] def __init__(self,ParentClass): """ Search the plugins directory for all classes extending ParentClass. These will be considered as implementations and added to our internal registry. """ ## Create instance variables self.classes = [] self.order = [] ## Recurse over all the plugin directories recursively for path in config.PLUGINS.split(':'): for dirpath, dirnames, filenames in os.walk(path): sys.path.append(dirpath) excluded = False for x in self.excluded_dirs: if dirpath.startswith(x): excluded = True break if excluded: continue for filename in filenames: #Lose the extension for the module name module_name = filename[:-3] if filename.lower().startswith("__dont_descend__"): for d in dirnames: excluded_path = os.path.join(dirpath, d) if excluded_path not in self.excluded_dirs: self.excluded_dirs.append(excluded_path) if filename.endswith(".py"): path = dirpath+'/'+filename try: if path not in self.loaded_modules: self.loaded_modules.append(path) pyflaglog.log(pyflaglog.VERBOSE_DEBUG,"Will attempt to load plugin '%s/%s'" % (dirpath,filename)) try: #open the plugin file fd = open(path ,"r") except Exception,e: pyflaglog.log(pyflaglog.DEBUG, "Unable to open plugin file '%s': %s" % (filename,e)) continue #load the module into our namespace try: module = imp.load_source(module_name,dirpath+'/'+filename,fd) except Exception,e: pyflaglog.log(pyflaglog.ERRORS, "*** Unable to load module %s: %s" % (module_name,e)) continue fd.close() #Is this module active? try: if module.hidden: pyflaglog.log(pyflaglog.VERBOSE_DEBUG, "*** Will not load Module %s: Module Hidden"% (module_name)) continue except AttributeError: pass try: if not module.active: pyflaglog.log(pyflaglog.VERBOSE_DEBUG, "*** Will not load Module %s: Module not active" % (module_name)) continue except AttributeError: pass #find the module description try: module_desc = module.description except AttributeError: module_desc = module_name ## Store information about this module here. self.modules.append(module) self.module_desc.append(module_desc) self.module_paths.append(path) else: try: ## We already have the module in the cache: module = self.modules[self.module_paths.index(path)] module_desc = self.module_desc[self.module_paths.index(path)] except (ValueError, IndexError),e: ## If not the module has been loaded, but disabled continue #Now we enumerate all the classes in the #module to see which one is a ParentClass: for cls in dir(module): try: Class = module.__dict__[cls] if issubclass(Class,ParentClass) and Class!=ParentClass: ## Check the class for consitancy try: self.check_class(Class) except AttributeError,e: err = "Failed to load %s '%s': %s" % (ParentClass,cls,e) pyflaglog.log(pyflaglog.WARNINGS, err) continue ## Add the class to ourselves: self.add_class(ParentClass, module_desc, cls, Class, filename) # Oops: it isnt a class... except (TypeError, NameError) , e: continue except TypeError, e: pyflaglog.log(pyflaglog.ERRORS, "Could not compile module %s: %s" % (module_name,e)) continue def add_class(self, ParentClass, module_desc, cls, Class, filename): """ Adds the class provided to our self. This is here to be possibly over ridden by derived classes. """ if Class not in self.classes and Class != ParentClass: self.classes.append(Class) self.filenames[self.get_name(Class)] = filename try: self.order.append(Class.order) except: self.order.append(10) pyflaglog.log(pyflaglog.VERBOSE_DEBUG, "Added %s '%s:%s'" % (ParentClass,module_desc,cls)) def check_class(self,Class): """ Run a set of tests on the class to ensure its ok to use. If there is any problem, we chuck an exception. """ def import_module(self,name=None,load_as=None): """ Loads the named module into the system module name space. After calling this it is possible to do: import load_as in all other modules. Note that to avoid race conditions its best to only attempt to use the module after the registry is initialised (i.e. at run time not load time). @arg load_as: name to use in the systems namespace. @arg name: module name to import @note: If there are several modules of the same name (which should be avoided) the last one encountered during registring should persist. This may lead to indereminate behaviour. """ if not load_as: load_as=name for module in self.modules: if name==module.__name__: sys.modules[load_as] = module return raise ImportError("No module by name %s" % name) def get_name(self, cls): try: name=cls.name cls.clsname = name return name except AttributeError: name = ("%s" % cls).split(".")[-1] cls.clsname = name return name def filename(self, cls_name): return self.filenames.get(cls_name, "Unknown") class ReportRegistry(Registry): """ A class to register reports. We have the extra task of resolving reports into families (Or report groups). This is done by examining the family property of the report. """ family = {} def __init__(self,ParentClass): Registry.__init__(self,ParentClass) for cls in self.classes: try: self.family[cls.family].append(cls) except KeyError: self.family[cls.family]=[cls] ## Sort all reports in all families: def sort_function(x,y): a=x.order b=y.order if a