#!/usr/bin/env python # -*- coding: utf-8 -*- """ creole lint main module """ import types import sys import re from glob import glob from os.path import join, basename, isfile, abspath, normpath, isabs from lxml.etree import parse from creole import config, eosfunc from pyeole.process import system_code from creole.lint.parsetemplate import parse_templates from creole.lint.normalize import is_correct from creole.var_loader import parse_dtd from creole.lxml_parser import parse_xml_file from creole.config import FLATTENED_CREOLE_DIR, dtdfilename from creole.loader import PopulateTiramisuObjects # variables internes aux dictionnaires DICO_TEST_VARS = ['test_', 'tmp_'] # variables de conteneur calculées dynamiquement CONTAINER_VARS = ['container_path_', 'container_ip_', 'container_name_', 'adresse_ip_ftp', 'adresse_ip_mysql', 'adresse_ip_dhcp', 'adresse_ip_internet', 'adresse_ip_interbase', 'adresse_ip_postgresql'] # faux-positifs sur les variables d'activation EXCLUDE_ACTIVATION_VARS = ['activer_cntlm_eth0', 'activer_cntlm_eth1', 'activer_cntlm_eth2', 'activer_cntlm_eth3', 'activer_cntlm_eth4', 'activer_supp_proxy_eth0', 'activer_bash_completion', 'activer_log_martian', 'activer_dns_eth0', 'activer_ctrl_alt_suppr', 'activer_ipv6', 'activer_courier_commun', 'activer_web_valider_ca', 'activer_admin_passfile', 'activer_regles_filtrage_port_source', 'activer_ftp_anonymous_access', 'activer_ftp_access', 'activer_pydio_local', 'activer_courier_imap_sso', 'activer_pydio_ftp', 'activer_client_ldap', ] # templates à ne pas tester par défaut EXCLUDE_TMPL = ['/usr/share/eole/creole/distrib/named.conf', '/usr/share/eole/creole/distrib/common-squid1.conf', '/usr/share/eole/creole/distrib/zstats.cfg', '/usr/share/eole/creole/distrib/hosts', '/usr/share/eole/creole/distrib/active_tags', ] # dictionnaires conservés pour compatibilité 2.3 OLD_DICOS = ['/usr/share/eole/creole/dicos/51_gepi.xml', '/usr/share/eole/creole/dicos/51_taskfreak.xml', '/usr/share/eole/creole/dicos/51_wordpress.xml', '/usr/share/eole/creole/dicos/60_roundcube.xml', '/usr/share/eole/creole/dicos/61_ajaxplorer.xml', '/usr/share/eole/creole/dicos/61_dokuwiki.xml', '/usr/share/eole/creole/dicos/61_fluxbb.xml', '/usr/share/eole/creole/dicos/61_piwigo.xml', '/usr/share/eole/creole/dicos/51_grr.xml', '/usr/share/eole/creole/dicos/51_cdt.xml', '/usr/share/eole/creole/dicos/51_piwik.xml', '/usr/share/eole/creole/dicos/51_spip.xml', ] starttoken = '%' varstarttoken = '%%' builts = [u'ArithmeticError', u'AssertionError', u'AttributeError', u'BaseException', u'DeprecationWarning', u'EOFError', u'Ellipsis', u'EnvironmentError', u'Exception', u'False', u'FloatingPointError', u'FutureWarning', u'GeneratorExit', u'IOError', u'ImportError', u'ImportWarning', u'IndentationError', u'IndexError', u'KeyError', u'KeyboardInterrupt', u'LookupError', u'MemoryError', u'NameError', u'None', u'NotImplemented', u'NotImplementedError', u'OSError', u'OverflowError', u'PendingDeprecationWarning', u'ReferenceError', u'RuntimeError', u'RuntimeWarning', u'StandardError', u'StopIteration', u'SyntaxError', u'SyntaxWarning', u'SystemError', u'SystemExit', u'TabError', u'True', u'TypeError', u'UnboundLocalError', u'UnicodeDecodeError', u'UnicodeEncodeError', u'UnicodeError', u'UnicodeTranslateError', u'UnicodeWarning', u'UserWarning', u'ValueError', u'Warning', u'ZeroDivisionError', u'_', u'__debug__', u'__doc__', u'__import__', u'__name__', u'abs', u'all', u'any', u'apply', u'basestring', u'bool', u'buffer', u'callable', u'chr', u'classmethod', u'cmp', u'coerce', u'compile', u'complex', u'copyright', u'credits', u'delattr', u'dict', u'dir', u'divmod', u'enumerate', u'eval', u'execfile', u'exit', u'file', u'filter', u'float', u'frozenset', u'getattr', u'globals', u'hasattr', u'hash', u'help', u'hex', u'id', u'input', u'int', u'intern', u'isinstance', u'issubclass', u'iter', u'len', u'license', u'list', u'locals', u'long', u'map', u'max', u'min', u'object', u'oct', u'open', u'ord', u'pow', u'property', u'quit', u'range', u'raw_input', u'reduce', u'reload', u'repr', u'reversed', u'round', u'set', u'setattr', u'slice', u'sorted', u'staticmethod', u'str', u'sum', u'super', u'tuple', u'type', u'unichr', u'unicode', u'vars', u'xrange', u'zip'] builts.append(u'is_defined') builts.append(u'split') builts.append(u'lower') cmd_client = (u'creole_client', ('get', 'get_containers')) for func in dir(eosfunc): if not func.startswith('_'): builts.append(unicode(func, 'utf-8')) # FIXME: je sais pas où la mettre def is_container_var(varname): """ variables de conteneur calculées dynamiquement """ for var in CONTAINER_VARS: if varname.startswith(var): return True return False class TmplVar(): def __init__(self, name, fd, line): self.name = name self.location = [] self.add_location(fd, line) def add_location(self, fd, line): fd = basename(fd) self.location.append((fd, line+1)) def set_location(self, location): self.location = location def get_location(self): return self.location class Var(): def __init__(self, name, description, separator, help, defaultvalue, is_slave): self.name = name self.description = description self.separator = separator self.help = help self.defaultvalue = defaultvalue self.is_slave = is_slave class CreoleLinter: """Base class for linters, collects creole vars and templates **has to be launched once and only once** """ display = True class __impl: warnno = 1 warncomment = "Undefined comment" """ Implementation of the singleton interface """ def set_config(self, tmpl_dir_or_file=None): if tmpl_dir_or_file != None and type(tmpl_dir_or_file) != str: raise TypeError('tmpl_dir_or_file doit être une string') if self.tmpl_dir_or_file != None: sys.stderr('Tentative de redefinition de tmpl_dir_or_file') if tmpl_dir_or_file == None: self.tmpl_dir_or_file = config.distrib_dir else: if not isabs(tmpl_dir_or_file): tmpl_dir_or_file = normpath(join(config.distrib_dir, tmpl_dir_or_file)) if not isfile(tmpl_dir_or_file): raise Exception("template doit etre le nom d'un template valide") self.tmpl_dir_or_file = tmpl_dir_or_file self.eoledirs = config.eoledirs self.dtddir = config.dtddir self.exclude_var = [] self.pkgname = None def load_dics(self): if self.config is None: self._collect_vars_in_dicos() # def load_tmpls(self): # if self.tmplvars == None: # self._collect_set_vars() # self._collect_def_vars() # self._collect_for_vars() # self._collect_define_vars() # self._collect_vars_in_tmplfiles() # def get_dicos_name(self): if self.config is None: raise Exception('Dictionnaire non chargé') dic = self.variables.keys() dic.sort() return dic # def get_dicos_files(self): # if self.creoledic == None: # raise Exception('Dictionnaire non chargé') # dic = [] # for name in self.creoledic.generic['files']: # dic.append(basename(name['source'])) # dic.sort() # return dic # # def get_tmplvars_name(self): # if self.tmplvars == None: # raise Exception('Template non chargé') # tmpl = self.tmplvars.keys() # tmpl.sort() # return tmpl # # def get_defvars_name(self): # if self.defvars == None: # raise Exception('Template non chargé') # defv = self.defvars.keys() # defv.sort() # return defv # # def get_setvars_name(self): # if self.setvars == None: # raise Exception('Fonction non chargé') # var = self.setvars.keys() # var.sort() # return var # # def get_forvars_name(self): # if self.forvars == None: # raise Exception('Fonction non chargé') # var = self.forvars.keys() # var.sort() # return var # # def get_tmplvar(self, name): # return self.tmplvars[name] # # def get_separators(self): # return self.creoledic.get_separators() # def get_dico_file_names(self): if self.eoledirs == None or self.tmpl_dir_or_file == None: raise Exception('Utiliser la methode set_config avant') ret = [] for eoledir in self.eoledirs: eoledir = abspath(eoledir) if isfile(eoledir): ret.append(eoledir) else: ret.extend(glob(join(eoledir, '*.xml'))) return ret def get_dtd(self): if self.dtddir == None: raise Exception('Utiliser la methode set_config avant') return join(self.dtddir, 'creole.dtd') def _collect_vars_in_dicos(self): if self.eoledirs == None or self.tmpl_dir_or_file == None: raise Exception('Utiliser la methode set_config avant') flattened = join(FLATTENED_CREOLE_DIR, 'flattened_creole.xml') with file(flattened, 'r') as fhd: xmlroot = parse(fhd).getroot() tiramisu_objects = PopulateTiramisuObjects() tiramisu_objects.parse_dtd(dtdfilename) tiramisu_objects.make_tiramisu_objects(xmlroot) self.config = tiramisu_objects.build() self.config.read_write() self.config.cfgimpl_get_settings().remove('hidden') self.config.cfgimpl_get_settings().remove('validator') self.config.cfgimpl_get_settings().remove('disabled') for path in self.config.creole.make_dict(): spath = path.split('.') vname = spath[-1] fname = spath[0] is_slave = False if len(spath) == 3: master = spath[1] if master != vname: is_slave = True option = self.config.unwrap_from_path('creole.' + path) self.variables[vname] = Var(vname, option.impl_get_information('doc', None), option.impl_get_information('separator', ''), option.impl_get_information('help', None), option.impl_getdefault(), is_slave) if fname not in self.families: self.families.append(fname) def _parse_tabs_in_dicos(self): if self.eoledirs == None or self.tmpl_dir_or_file == None: raise Exception('Utiliser la methode set_config avant') tabs_in_dics = [] fnames = [] for directory in self.eoledirs: if isfile(directory): fnames.append(directory) else: fnames.extend(glob(join(directory, "*.xml"))) for fname in fnames: fh = file(fname, 'r') content = fh.read() if '\t' in content: tabs_in_dics.append(fname) fh.close() return tabs_in_dics def _list_tmpl_files(self): if isfile(self.tmpl_dir_or_file): return [self.tmpl_dir_or_file] ret = [] for filename in glob(join(self.tmpl_dir_or_file, '*')): if filename.startswith('.') or filename.endswith('~'): continue if filename in EXCLUDE_TMPL: print " \\-- template desactivé : {0}".format(filename) continue ret.append(filename) return ret # def _add_collected_var(self, dvar, var, fd, linenb): # if dvar.has_key(var): # dvar[var].add_location(fd=fd, line=linenb) # else: # dvar[var] = TmplVar(name=var, fd=fd, line=linenb) # # def _collect_var_in(self, dvar, var, fd, linenb, exists=False): # not_added=True # if exists == True: # if self.forvars.has_key(var): # #si deja en memoire # if (basename(fd), linenb+1) in self.forvars[var].location: # return # self._add_collected_var(self.forvars, var, fd, linenb) # not_added=False # if self.setvars.has_key(var): # self._add_collected_var(self.setvars, var, fd, linenb) # not_added=False # if self.defvars.has_key(var): # self._add_collected_var(self.defvars, var, fd, linenb) # not_added=False # #test les builtsin seulement si variable # if not_added == True and unicode(var, 'utf-8') in builts: # #self.builtsvar.append(var) # return # if not_added == True: # self._add_collected_var(dvar, var, fd, linenb) # # def _collect_vars_in(self, expr, dvar, fd, linenb, tvarstarttoken, # exists=False): # if self.unknown_client == None: # self.unknown_client = [] # varcreole = re.compile(tvarstarttoken+'([a-zA-Z0-9_\.{}]+)') # varcreole2 = re.compile(tvarstarttoken+'(\w+)') # varcreolebr = re.compile(tvarstarttoken+'{(\w+)}') # varmulti = re.compile('(\w+)\.(\w+)') # for var in varcreole.findall(expr): # ret = varmulti.match(var) # if ret != None: # if ret.group(1) == cmd_client[0]: # if ret.group(2) not in cmd_client[1]: # self.unknown_client.append(TmplVar(name=ret.group(2), fd=fd, line=linenb)) # else: # #%%var.sousvar # self._collect_var_in(dvar, ret.group(1), fd, linenb, exists) # self._collect_var_in(dvar, ret.group(2), fd, linenb, exists) # else: # #%%var # for var2 in varcreole2.findall(tvarstarttoken+var): # self._collect_var_in(dvar, var2, fd, linenb, exists) # #%%{var} # for var2 in varcreolebr.findall(tvarstarttoken+var): # self._collect_var_in(dvar, var2, fd, linenb, exists) # # def _collect_vars(self, tvars, tpattern, all_char=False, with_var=False, with_vars=True, broken=None): # """ # collect vars in template for a specified pattern # # :tvars: all collected var are store in this variable # :tpattern: re pattern # :broken: if set, store broken variable # """ # if tvars == None: # tvars = {} # tstarttoken = '' # tvarstarttoken = '' # for tmplfd in self._list_tmpl_files(): # fh = open(tmplfd, 'r') # lines = fh.readlines() # length = len(lines) # settings = False # if tstarttoken != starttoken or \ # tvarstarttoken != varstarttoken: # if all_char: # char = '(.*)' # else: # char = '( *)' # pattern = re.compile(char+starttoken+tpattern) # tstarttoken = starttoken # tvarstarttoken = varstarttoken # for linenb in range(length): # line = lines[linenb] # if line.strip() == '%compiler-settings': # settings = True # if settings and line.strip() == \ # '%end compiler-settings'.strip(): # settings = False # if not settings: # ret = pattern.match(line.strip()) # if ret != None: # expr = ret.group(2).strip() # if with_var: # self._collect_var_in(tvars, expr, tmplfd, linenb) # if with_vars: # self._collect_vars_in(expr, # tvars, tmplfd, linenb, # tvarstarttoken) # len_token = len(varstarttoken) # if broken is not None and expr.strip()[:len_token] != tvarstarttoken: # broken.append(TmplVar( # name=line.strip(), # fd=tmplfd, line=linenb)) # else: # tline = line.split('=') # tkey = tline[0].strip() # if tkey == 'cheetahVarStartToken': # tvarstarttoken = tline[1].strip() # elif tkey == 'directiveStartToken': # tstarttoken = tline[1].strip() # pattern = re.compile(tstarttoken+tpattern) # fh.close() # return tvars # # def _collect_for_vars(self): # """ # collect all vars generate in 'for' # """ # self.brokenfor = [] # self.forvars = self._collect_vars(self.forvars, 'for (.+) in (.*)', broken=self.brokenfor) # # def _collect_def_vars(self): # """ # collect all vars generate in 'def' # """ # self.defvars = self._collect_vars(self.defvars, 'def (.*)\((.*)\)', with_var=True) # # def _collect_set_vars(self): # """ # collect all vars generate in 'set' # """ # self.setvars = self._collect_vars(self.setvars, 'set (.*)=.*') # # def _collect_define_vars(self): # """ # collect all vars generate in 'def' # """ # self.var_with_is_defined = self._collect_vars( # self.var_with_is_defined, # "is_defined\(\'(\w+)\'\)", # all_char=True, # with_var=True, with_vars=False) # ##FIXME pas de support de cheetahVarStartToken, ... # #if self.var_with_is_defined == None: # # self.var_with_is_defined = {} # # pattern = re.compile('(.*) %sis_defined\(\'(\w+)\'\)'%varstarttoken) # # for tmplfd in self._list_tmpl_files(): # # fh = open(tmplfd, 'r') # # lines = fh.readlines() # # length = len(lines) # # for linenb in range(length): # # line = lines[linenb] # # ret = pattern.match(line) # # if ret != None: # # self._collect_var_in(self.var_with_is_defined, ret.group(2), tmplfd, linenb) # # fh.close() # # def _collect_vars_in_tmplfiles(self): # if self.eoledirs == None or self.tmpl_dir_or_file == None: # raise Exception('Utiliser la methode set_config avant') # # XXX ".eolvars" is a good placeholder for var names to be kept in touch # if self.tmplvars == None: # self.tmplvars = {} # for tmplfd in self._list_tmpl_files(): # fh = open(tmplfd, 'r') # lines = fh.readlines() # length = len(lines) # settings = False # tvarstarttoken = varstarttoken # for linenb in range(length): # line = lines[linenb] # if line.strip() == '%compiler-settings': # settings = True # if settings and line.strip() == \ # '%end compiler-settings'.strip(): # settings = False # if not settings: # self._collect_vars_in(line, self.tmplvars, tmplfd, # linenb, tvarstarttoken, True) # else: # tline = line.split('=') # tkey = tline[0].strip() # if tkey == 'cheetahVarStartToken': # tvarstarttoken = tline[1].strip() # # fh.close() # # storage for the instance reference __instance = None def __init__(self): """ Create singleton instance """ # Check whether we already have an instance if CreoleLinter.__instance is None: # Create and remember instance CreoleLinter.__instance = CreoleLinter.__impl() self.tmpl_dir_or_file = None self.eoledirs = None self.config = None self.variables = {} self.families = [] self.tmplvars = None self.forvars = None self.defvars = None self.setvars = None self.unknown_client = None self.brokenfor = None self.var_with_is_defined = None self.exclude_var = None self.skip_var = {} self.conflevel = 'eole' # Store instance reference as the only member in the handle self.__dict__['_CreoleLinter__instance'] = CreoleLinter.__instance def __getattr__(self, attr): """ Delegate access to implementation """ return getattr(self.__instance, attr) def __setattr__(self, attr, value): """ Delegate access to implementation """ return setattr(self.__instance, attr, value) #class SaveItem(CreoleLinter): # """ # eolvars not present in the dicos # """ # name = 'save' # warnno = 1 # warncomment = "Ne pas utiliser la fonction SaveItem comme un test" # # def process(self): # self.load_dics() # if self.pkgname == None: # raise Exception('fichier creolelint.conf incomplet (name)') # filename = join(expanduser('~/.creolelint'), self.pkgname+'.conf') # fh = open(filename, 'w') # fh.write('vardico = '+str(self.get_dicos_name())+'\n') # fh.close() # print('fichier sauvegardé') # # def check(self): # return [] # #class OrphansInDicosItem(CreoleLinter): # """ # eolvars not present in the dicos # """ # name = 'orphans_in_dicos' # warnno = 8 # warncomment = "Variable dictionnaire non utilisée dans un template" # # def check(self): # self.load_dics() # self.load_tmpls() # # vars_name = set(self.get_dicos_name()) # tmplvars_name = set(self.get_tmplvars_name()) # only_in_dicos = vars_name - tmplvars_name # ret = [] # skip_var = self.skip_var.get(self.name, {}) # for var in only_in_dicos: # if skip_var.has_key(var): # continue # test_start = False # for start in DICO_TEST_VARS: # if var.startswith(start): # test_start = True # break # if not test_start: # ret.append(self.variables[var]) # return ret # #class OrphansInTmplItem(CreoleLinter): # """ # eolvars not present in the templates # """ # name = 'orphans_in_tmpl' # warnno = 8 # warncomment = "Variable template non présente dans le dictionnaire" # # def check(self): # self.load_dics() # self.load_tmpls() # # vars_name = self.get_dicos_name() # if self.exclude_var != None: # vars_name.extend(self.exclude_var) # vars_name=set(vars_name) # tmplvars_name = set(self.get_tmplvars_name()) # only_in_tmpl = tmplvars_name - vars_name # #remove is_defined # is_defined_name = set(self.var_with_is_defined.keys()) # only_in_tmpl = only_in_tmpl - is_defined_name # set_var = set(self.get_setvars_name()) # only_in_tmpl = only_in_tmpl - set_var # ret = [] # for var in only_in_tmpl: # skipped_location = [] # for location in self.tmplvars[var].location: # if self.skip_var.has_key(self.name) and self.skip_var[self.name].has_key(var): # if not location in self.skip_var[self.name][var]: # skipped_location.append(location) # else: # skipped_location.append(location) # if skipped_location != []: # tmplvar = TmplVar(var, '', 0) # tmplvar.set_location(skipped_location) # ret.append(tmplvar) # #results.append(self.tmplvars[var]) # return ret # #class OrphansDefItem(CreoleLinter): # name = 'orphans_def' # warnno = 7 # warncomment = "Fonction définie mais non utilisée" # # def check(self): # self.load_tmpls() # results = [] # for defvar in self.get_defvars_name(): # defname = {} # for filedesc, linenb in self.defvars[defvar].location: # if not defname.has_key((defvar, filedesc)): # defname[(defvar, filedesc)]=linenb # else: # defname[(defvar, filedesc)]="exists" # for defvar, filedesc in defname.keys(): # if defname[(defvar, filedesc)] != "exists": # results.append(TmplVar(name=defvar, fd=filedesc, line=defname[(defvar, filedesc)])) # # return results # #class OrphansSetItem(CreoleLinter): # name = 'orphans_set' # warnno = 7 # warncomment = "Variable définie dans le template mais non utilisée" # # def check(self): # self.load_tmpls() # results = [] # tmpl_vars = set(self.get_tmplvars_name()) # for setvar in self.get_setvars_name(): # setname = {} # for filedesc, linenb in self.setvars[setvar].location: # if setname.has_key((setvar, filedesc)) == False: # setname[(setvar, filedesc)]=linenb # else: # setname[(setvar, filedesc)]="exists" # # for setvar, filedesc in setname.keys(): # if setname[(setvar, filedesc)] != "exists": # results.append(TmplVar(name=setvar, fd=filedesc, line=setname[(setvar, filedesc)])) # # return results # #class OrphansForItem(CreoleLinter): # name = 'orphans_for' # warnno = 7 # warncomment = "Variable définie dans une boucle mais non utilisée" # # def check(self): # self.load_tmpls() # results = [] # for forvar in self.get_forvars_name(): # forname = {} # for filedesc, linenb in self.forvars[forvar].location: # if forname.has_key((forvar, filedesc)) == False: # forname[(forvar, filedesc)]=linenb # else: # forname[(forvar, filedesc)]="exists" # # for forvar, filedesc in forname.keys(): # if forname[(forvar, filedesc)] != "exists": # results.append(TmplVar(name=forvar, fd=filedesc, line=forname[(forvar, filedesc)])) # return results # #class OrphansDicosFilesItem(CreoleLinter): # """ # """ # name = 'orphans_dicos_files' # warnno = 1 # warncomment = "Template déclaré dans le dicos inexistant" # # def check(self): # self.load_dics() # # dicos_files = [] # for filen in self.get_dicos_files(): # dicos_files.append(basename(filen)) # dicos_files = set(dicos_files) # tmpl_files = [] # for filen in self._list_tmpl_files(): # tmpl_files.append(unicode(basename(filen), 'utf-8')) # tmpl_files=set(tmpl_files) # orphans = dicos_files - tmpl_files # ret = [] # for var in orphans: # if self.skip_var.has_key(self.name) and not self.skip_var[self.name].has_key(var): # ret.append(var) # else: # ret.append(var) # # return ret # #class OrphansTmplFilesItem(CreoleLinter): # """ # """ # name = 'orphans_tmpl_files' # warnno = 1 # warncomment = "Template non déclaré dans le dicos" # # def check(self): # self.load_dics() # # dicos_files = [] # for filen in self.get_dicos_files(): # dicos_files.append(basename(filen)) # dicos_files = set(dicos_files) # tmpl_files = [] # for filen in self._list_tmpl_files(): # tmpl_files.append(unicode(basename(filen), 'utf-8')) # tmpl_files=set(tmpl_files) # return tmpl_files - dicos_files # class WrongDicosNameItem(CreoleLinter): name = 'wrong_dicos_name' warnno = 6 warncomment = "Dictionnaire avec un nom invalide" def check(self): if self.conflevel == 'common': pattern = '(0' elif self.conflevel == 'conf': pattern = '(1' elif self.conflevel == 'eole': pattern = '(2' else: pattern = '([3-9]' pattern += '[0-9]_[a-z0-9_]+)' cpattern = re.compile(pattern) ret = [] for filename in self.get_dico_file_names(): name = basename(filename) if cpattern.match(name) == None: ret.append(name) return ret class HiddenIfInDicosItem(CreoleLinter): name = 'hidden_if_in_dicos' warnno = 5 warncomment = "Dictionnaire contenant un hidden_if_*" def check(self): ret = [] dtd = parse_dtd(self.get_dtd()) for filename in self.get_dico_file_names(): if filename in OLD_DICOS: continue parse = parse_xml_file(filename, dtd, parse_all=False) for cond in parse['conditions'].values(): if cond[0]['name'].startswith('hidden_if_'): ret.append(filename) break return ret class ConditionWithoutTarget(CreoleLinter): name = 'condition_without_target' warnno = 5 warncomment = "Dictionnaire contenant une condition sans target" def check(self): ret = [] dtd = parse_dtd(self.get_dtd()) for filename in self.get_dico_file_names(): if filename in OLD_DICOS: continue parse = parse_xml_file(filename, dtd, parse_all=False) for cond in parse['conditions'].values(): for con in cond: if con['family'] == con['list'] == con['variable'] == []: ret.append(filename) break return ret class ObligatoireInDicosItem(CreoleLinter): name = 'obligatoire_in_dicos' warnno = 5 warncomment = "Dictionnaire contenant un check \"obligatoire\"" def check(self): ret = [] dtd = parse_dtd(self.get_dtd()) for filename in self.get_dico_file_names(): if filename in OLD_DICOS: continue parse = parse_xml_file(filename, dtd, parse_all=False) for cond in parse['checks'].values(): if cond[0][0] == 'obligatoire': ret.append(filename) break return ret class FamilyWithoutHelp(CreoleLinter): name = 'family_without_help' warnno = 5 warncomment = "Famille sans balise d'aide" def check(self): self.load_dics() ret = [] for grp in self.config.creole.iter_groups(): doc = grp[1].cfgimpl_get_description().impl_get_information('help', None) if doc is None: ret.append(grp[0]) return ret class FamilyWithoutIcon(CreoleLinter): name = 'family_without_icon' warnno = 5 warncomment = "Famille sans icône spécifique" def check(self): self.load_dics() ret = [] for grp in self.config.creole.iter_groups(): if grp[1].cfgimpl_get_description().impl_get_information('icon') is None: ret.append(grp[0]) return ret #class DefineItem(CreoleLinter): # """ # check for syntaxes # """ # name = 'define' # warnno = 4 # warncomment = "Redéfinition d'un variable d'un dictionnaire" # # def check(self): # """ # verifie si une variable définie est une variable du dictionnaire # """ # self.load_dics() # self.load_tmpls() # dicos = set(self.get_dicos_name()) # defv = set(self.get_defvars_name()) # ret=[] # for var in defv & dicos: # ret.append(self.defvars[var]) # return ret # class BuiltinsItem(CreoleLinter): """ verifier si une variable de dico n'est pas dans l'espace de nommage """ name = 'builtins' warnno = 4 warncomment = "Variable identitique à une fonction python" def check(self): self.load_dics() # self.load_tmpls() ret = [] #dans le dictionnaire for var in set(builts) & set(self.get_dicos_name()): ret.append(self.variables[var]) # #dans les variables de template # for var in set(builts) & set(self.get_tmplvars_name()): # ret.append(self.tmplvars[var]) # #dans les boucles for # for var in set(builts) & set(self.get_forvars_name()): # ret.append(self.forvars[var]) # #dans la definition de variable dans un template # for var in set(builts) & set(self.get_setvars_name()): # ret.append(self.setvars[var]) # #dans les noms de fonction # for var in set(builts) & set(self.get_defvars_name()): # ret.append(self.defvars[var]) return ret #class SyntaxForItem(CreoleLinter): # """ # verifie la syntaxe de la ligne for # """ # name = 'syntax_for' # warnno = 1 # warncomment = "Syntaxe de la ligne for incorrect" # def check(self): # self.load_tmpls() # return self.brokenfor # #class SyntaxVarItem(CreoleLinter): # """ # verifie les variables suivant la syntaxe de pattern # """ # name = 'syntax_var' # pattern = '([a-z0-9][a-z0-9]+_[a-z0-9_]+)' # warnno = 6 # warncomment = "La variable ne respecte pas la regexp %s" % pattern # # def check(self): # cpattern = re.compile(self.pattern) # self.load_dics() # self.load_tmpls() # ret=[] # #dans le dictionnaire # for var in self.get_dicos_name(): # if cpattern.match(var) == None: # ret.append(self.variables[var]) # #dans les variables de template # for var in self.get_tmplvars_name(): # if cpattern.match(var) == None: # skipped_location = [] # for location in self.tmplvars[var].location: # if self.skip_var.has_key(self.name) and self.skip_var[self.name].has_key(var): # if not location in self.skip_var[self.name][var]: # skipped_location.append(location) # else: # skipped_location.append(location) # if skipped_location != []: # tmplvar = TmplVar(var, '', 0) # tmplvar.set_location(skipped_location) # ret.append(tmplvar) # #ret.append(self.tmplvars[var]) # #dans les boucles for # for var in self.get_forvars_name(): # if cpattern.match(var) == None: # ret.append(self.forvars[var]) # #dans la definition de variable dans un template # for var in self.get_setvars_name(): # if cpattern.match(var) == None: # ret.append(self.setvars[var]) # return ret # #class ForbiddenTemplateVarItem(CreoleLinter): # """ # vérifie la présence des noms de variable interdits dans les templates # """ # name = 'syntax_var2' # warnno = 6 # warncomment = "Nom de variable interdit dans un template" # # def check(self): # #self.load_dics() # self.load_tmpls() # ret=[] # #dans les variables de template # for var in self.get_tmplvars_name(): # for start in DICO_TEST_VARS: # if var.startswith(start): # ret.append(var) # break # return ret # #class SyntaxFunctionItem(CreoleLinter): # """ # verifie les fonctions suivant la syntaxe de pattern # """ # name = 'syntax_function' # pattern = '([a-z0-9][a-z0-9]+_[a-z0-9_]+)' # warnno = 6 # warncomment = "La fonction ne respecte pas la regexp %s" % pattern # # def check(self): # cpattern = re.compile(self.pattern) # self.load_tmpls() # ret=[] # #dans les noms de fonction # for var in self.get_defvars_name(): # if cpattern.match(var) == None: # ret.append(self.defvars[var]) # return ret # #class OrphansVarHelpItem(CreoleLinter): # name = 'orphans_var_help' # warnno = 3 # warncomment = "Aide définie dans le dictionnaire pour une variable inexistante" # # def check(self): # self.load_dics() # vars_name = set(self.get_dicos_name()) # vars_help = set(self.creoledic.get_helps()['variables'].keys()) # #print vars_help # only_in_help = vars_help - vars_name # results = [] # for tmpl in only_in_help: # results.append(tmpl) # return results #class OrphansFamHelpItem(CreoleLinter): # name = 'orphans_fam_help' # warnno = 3 # warncomment = "Aide définie dans le dictionnaire pour une famille inexistante" # # def check(self): # self.load_dics() # #FIXME # vars_name = set(self.families) # vars_help = set(self.creoledic.get_helps()['families'].keys()) # only_in_help = vars_help - vars_name # results = [] # for tmpl in only_in_help: # results.append(tmpl) # return results # class ValidVarLabelItem(CreoleLinter): name = 'valid_var_label' warnno = 5 warncomment = "Libellé de variable non valide dans un dictionnaire" def check(self): self.load_dics() ret = [] for var in self.variables.values(): if not is_container_var(var.name): ret.extend(is_correct(var.description, var.name)) return ret class ActivationVarWithoutHelp(CreoleLinter): name = 'activation_var_without_help' warnno = 5 warncomment = "Variable d'activation sans balise d'aide" def check(self): self.load_dics() ret = [] for var, var_obj in self.variables.items(): if var.startswith('activer_') and var not in EXCLUDE_ACTIVATION_VARS: if var_obj.help is None: ret.append(var) return ret class ValidSeparatorLabelItem(CreoleLinter): name = 'valid_separator_label' warnno = 5 warncomment = "Libellé de séparateur non valide dans un dictionnaire" def check(self): self.load_dics() ret = [] for var, var_obj in self.variables.items(): if var_obj.separator == '': #FIXME: variables de conteneur dynamiques continue ret.extend(is_correct(var_obj.separator[0], var)) return ret class ValidHelpLabelItem(CreoleLinter): name = 'valid_help_label' warnno = 5 warncomment = "Libellé d'aide non valide dans un dictionnaire" def check(self): self.load_dics() ret = [] for var, var_obj in self.variables.items(): # help/variable ret.extend(is_correct(var_obj.help, var)) for grp in self.config.creole.iter_groups(): # help/family ret.extend(is_correct(grp[1].cfgimpl_get_description().impl_get_information('help', ''), grp[0], family=True)) return ret class ValidSlaveValue(CreoleLinter): name = 'valid_slave_value' warnno = 5 warncomment = "Variable esclave avec une liste en valeur défaut" def check(self): self.load_dics() ret = [] for var, var_obj in self.variables.items(): if var_obj.is_slave: if len(var_obj.defaultvalue) > 1: ret.append(var) return ret ##class ValidCheckEnumOuiNon(CreoleLinter): ## name = 'valid_check_enum_ouinon' ## warnno = 6 ## warncomment = "Variable avec un valid_enum à oui/non au lieu du type oui/non" ## ## def check(self): ## ret = [] ## for var, content in self.creoledic.variables.items(): ## print content ## if str(type(content)) != "": ## for check in content.choices: ## if check[0] == u'valid_enum': ## for valid_enum in check[1]: ## if valid_enum['value'].startswith('['): ## eval_valid_enum = eval(valid_enum['value']) ## if set(eval_valid_enum) == set(['non', 'oui']): ## ret.append(var) ## return ret # ##class ValidCheckEnumOnOff(CreoleLinter): ## name = 'valid_check_enum_onoff' ## warnno = 6 ## warncomment = "Variable avec un valid_enum à on/off au lieu du type on/off" ## ## def check(self): ## ret = [] ## for var, content in self.creoledic.variables.items(): ## if str(type(content)) != "": ## for check in content.checks: ## if check[0] == u'valid_enum': ## for valid_enum in check[1]: ## if valid_enum['value'].startswith('['): ## eval_valid_enum = eval(valid_enum['value']) ## if set(eval_valid_enum) == set(['on', 'off']): ## ret.append(var) ## return ret # class TabsInDicosItem(CreoleLinter): name = 'tabs_in_dicos' warnno = 5 warncomment = "Tabulation dans le dictionnaire au lieu de 4 espaces" def check(self): return self._parse_tabs_in_dicos() #class ValidClientOption(CreoleLinter): # name = 'valid_client_option' # warnno = 6 # warncomment = "Option inconnu pour %s" % cmd_client[0] # # def check(self): # self.load_tmpls() # return self.unknown_client class ValidParseTmpl(CreoleLinter): name = 'valid_parse_tmpl' warnno = 1 warncomment = "Template Non valide" display = False def check(self): parse_templates(self._list_tmpl_files()) return [] class ValidDTDItem(CreoleLinter): name = 'valid_dtd' warnno = 1 warncomment = "DTD Non valide" display = False def check(self): dtd = self.get_dtd() for filename in self.get_dico_file_names(): system_code(['xmllint', '--noout', '--dtdvalid', dtd, filename]) return [] class OldFwFile(CreoleLinter): name = 'old_fw_file' warnno = 5 warncomment = "Ancien fichier eole-firewall présent sur le serveur" def check(self): fw_templates = '/usr/share/eole/creole/distrib/*.fw' fw_files = '/usr/share/eole/firewall/*.fw' return glob(fw_files) + glob(fw_templates) def validate(keyword, ansi, tmpl): globs = globals() classitem = None for cls in globs: if cls.startswith('_') and type(globs[cls])!=types.ClassType and cls == 'CreoleLinter' and cls == 'TmplVar': continue if hasattr(globs[cls], 'name'): if globs[cls].name == keyword: classitem = globs[cls] break if classitem == None: raise Exception('test %s inconnu'%keyword) cl = classitem() if cl.eoledirs == None: cl.set_config(tmpl_dir_or_file=tmpl) ansi.process(cl)