"Fossies" - the Fresh Open Source Software Archive

Member "viewvc-1.1.27/lib/config.py" (6 Jun 2019, 18814 Bytes) of package /linux/misc/viewvc-1.1.27.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "config.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.1.26_vs_1.1.27.

    1 # -*-python-*-
    2 #
    3 # Copyright (C) 1999-2019 The ViewCVS Group. All Rights Reserved.
    4 #
    5 # By using this file, you agree to the terms and conditions set forth in
    6 # the LICENSE.html file which can be found at the top level of the ViewVC
    7 # distribution or at http://viewvc.org/license-1.html.
    8 #
    9 # For more information, visit http://viewvc.org/
   10 #
   11 # -----------------------------------------------------------------------
   12 #
   13 # config.py: configuration utilities
   14 #
   15 # -----------------------------------------------------------------------
   16 
   17 import sys
   18 import os
   19 import string
   20 import ConfigParser
   21 import fnmatch
   22 
   23 
   24 #########################################################################
   25 #
   26 # CONFIGURATION
   27 # -------------
   28 #
   29 # There are three forms of configuration:
   30 #
   31 #    1. edit the viewvc.conf created by the viewvc-install(er)
   32 #    2. as (1), but delete all unchanged entries from viewvc.conf
   33 #    3. do not use viewvc.conf and just edit the defaults in this file
   34 #
   35 # Most users will want to use (1), but there are slight speed advantages
   36 # to the other two options. Note that viewvc.conf values are a bit easier
   37 # to work with since it is raw text, rather than python literal values.
   38 #
   39 #
   40 # A WORD ABOUT OPTION LAYERING/OVERRIDES
   41 # --------------------------------------
   42 #
   43 # ViewVC has three "layers" of configuration options:
   44 #
   45 #    1. base configuration options - very basic configuration bits
   46 #       found in sections like 'general', 'options', etc.
   47 #    2. vhost overrides - these options overlay/override the base
   48 #       configuration on a per-vhost basis.
   49 #    3. root overrides - these options overlay/override the base
   50 #       configuration and vhost overrides on a per-root basis.
   51 #
   52 # Here's a diagram of the valid overlays/overrides:
   53 #
   54 #         PER-ROOT          PER-VHOST            BASE
   55 #       
   56 #                         ,-----------.     ,-----------.
   57 #                         | vhost-*/  |     |           |
   58 #                         |  general  | --> |  general  |
   59 #                         |           |     |           |
   60 #                         `-----------'     `-----------'
   61 #       ,-----------.     ,-----------.     ,-----------.
   62 #       |  root-*/  |     | vhost-*/  |     |           |
   63 #       |  options  | --> |  options  | --> |  options  |
   64 #       |           |     |           |     |           |
   65 #       `-----------'     `-----------'     `-----------'
   66 #       ,-----------.     ,-----------.     ,-----------.
   67 #       |  root-*/  |     | vhost-*/  |     |           |
   68 #       | templates | --> | templates | --> | templates |
   69 #       |           |     |           |     |           |
   70 #       `-----------'     `-----------'     `-----------'
   71 #       ,-----------.     ,-----------.     ,-----------.
   72 #       |  root-*/  |     | vhost-*/  |     |           |
   73 #       | utilities | --> | utilities | --> | utilities |
   74 #       |           |     |           |     |           |
   75 #       `-----------'     `-----------'     `-----------'
   76 #                         ,-----------.     ,-----------.
   77 #                         | vhost-*/  |     |           |
   78 #                         |   cvsdb   | --> |   cvsdb   |
   79 #                         |           |     |           |
   80 #                         `-----------'     `-----------'
   81 #       ,-----------.     ,-----------.     ,-----------.
   82 #       |  root-*/  |     | vhost-*/  |     |           |
   83 #       |  authz-*  | --> |  authz-*  | --> |  authz-*  |
   84 #       |           |     |           |     |           |
   85 #       `-----------'     `-----------'     `-----------'
   86 #                                           ,-----------.
   87 #                                           |           |
   88 #                                           |  vhosts   |
   89 #                                           |           |
   90 #                                           `-----------'
   91 #                                           ,-----------.
   92 #                                           |           |
   93 #                                           |   query   |
   94 #                                           |           |
   95 #                                           `-----------'
   96 #
   97 # ### TODO:  Figure out what this all means for the 'kv' stuff.
   98 #
   99 #########################################################################
  100 
  101 class Config:
  102   _base_sections = (
  103     # Base configuration sections.
  104     'authz-*',
  105     'cvsdb',
  106     'general',
  107     'options',
  108     'query',
  109     'templates',
  110     'utilities',
  111     )
  112   _force_multi_value = (
  113     # Configuration values with multiple, comma-separated values.
  114     'allowed_views',
  115     'binary_mime_types',
  116     'custom_log_formatting',
  117     'cvs_roots',
  118     'kv_files',
  119     'languages',
  120     'mime_types_files',
  121     'root_parents',
  122     'svn_roots',
  123     'renamed_roots',
  124     )
  125   _allowed_overrides = {
  126     # Mapping of override types to allowed overridable sections.
  127     'vhost' : ('authz-*',
  128                'cvsdb',
  129                'general',
  130                'options',
  131                'templates',
  132                'utilities',
  133                ),
  134     'root'  : ('authz-*',
  135                'options',
  136                'templates',
  137                'utilities',
  138                )
  139     }
  140 
  141   def __init__(self):
  142     self.root_options_overlayed = 0
  143     for section in self._base_sections:
  144       if section[-1] == '*':
  145         continue
  146       setattr(self, section, _sub_config())
  147 
  148   def load_config(self, pathname, vhost=None):
  149     """Load the configuration file at PATHNAME, applying configuration
  150     settings there as overrides to the built-in default values.  If
  151     VHOST is provided, also process the configuration overrides
  152     specific to that virtual host."""
  153     
  154     self.conf_path = os.path.isfile(pathname) and pathname or None
  155     self.base = os.path.dirname(pathname)
  156     self.parser = ConfigParser.ConfigParser()
  157     self.parser.optionxform = lambda x: x # don't case-normalize option names.
  158     self.parser.read(self.conf_path or [])
  159     
  160     for section in self.parser.sections():
  161       if self._is_allowed_section(section, self._base_sections):
  162         self._process_section(self.parser, section, section)
  163 
  164     if vhost and self.parser.has_section('vhosts'):
  165       self._process_vhost(self.parser, vhost)
  166 
  167   def load_kv_files(self, language):
  168     """Process the key/value (kv) files specified in the
  169     configuration, merging their values into the configuration as
  170     dotted heirarchical items."""
  171     
  172     kv = _sub_config()
  173 
  174     for fname in self.general.kv_files:
  175       if fname[0] == '[':
  176         idx = string.index(fname, ']')
  177         parts = string.split(fname[1:idx], '.')
  178         fname = string.strip(fname[idx+1:])
  179       else:
  180         parts = [ ]
  181       fname = string.replace(fname, '%lang%', language)
  182 
  183       parser = ConfigParser.ConfigParser()
  184       parser.optionxform = lambda x: x # don't case-normalize option names.
  185       parser.read(os.path.join(self.base, fname))
  186       for section in parser.sections():
  187         for option in parser.options(section):
  188           full_name = parts + [section]
  189           ob = kv
  190           for name in full_name:
  191             try:
  192               ob = getattr(ob, name)
  193             except AttributeError:
  194               c = _sub_config()
  195               setattr(ob, name, c)
  196               ob = c
  197           setattr(ob, option, parser.get(section, option))
  198 
  199     return kv
  200 
  201   def path(self, path):
  202     """Return PATH relative to the config file directory."""
  203     return os.path.join(self.base, path)
  204 
  205   def _process_section(self, parser, section, subcfg_name):
  206     if not hasattr(self, subcfg_name):
  207       setattr(self, subcfg_name, _sub_config())
  208     sc = getattr(self, subcfg_name)
  209 
  210     for opt in parser.options(section):
  211       value = parser.get(section, opt)
  212       if opt in self._force_multi_value:
  213         value = map(string.strip, filter(None, string.split(value, ',')))
  214       else:
  215         try:
  216           value = int(value)
  217         except ValueError:
  218           pass
  219 
  220       ### FIXME: This feels like unnecessary depth of knowledge for a
  221       ### semi-generic configuration object.
  222       if opt == 'cvs_roots' or opt == 'svn_roots' or opt == 'renamed_roots':
  223         value = _parse_roots(opt, value)
  224 
  225       setattr(sc, opt, value)
  226 
  227   def _is_allowed_section(self, section, allowed_sections):
  228     """Return 1 iff SECTION is an allowed section, defined as being
  229     explicitly present in the ALLOWED_SECTIONS list or present in the
  230     form 'someprefix-*' in that list."""
  231     
  232     for allowed_section in allowed_sections:
  233       if allowed_section[-1] == '*':
  234         if _startswith(section, allowed_section[:-1]):
  235           return 1
  236       elif allowed_section == section:
  237         return 1
  238     return 0
  239 
  240   def _is_allowed_override(self, sectype, secspec, section):
  241     """Test if SECTION is an allowed override section for sections of
  242     type SECTYPE ('vhosts' or 'root', currently) and type-specifier
  243     SECSPEC (a rootname or vhostname, currently).  If it is, return
  244     the overridden base section name.  If it's not an override section
  245     at all, return None.  And if it's an override section but not an
  246     allowed one, raise IllegalOverrideSection."""
  247 
  248     cv = '%s-%s/' % (sectype, secspec)
  249     lcv = len(cv)
  250     if section[:lcv] != cv:
  251       return None
  252     base_section = section[lcv:]
  253     if self._is_allowed_section(base_section,
  254                                 self._allowed_overrides[sectype]):
  255       return base_section
  256     raise IllegalOverrideSection(sectype, section)
  257 
  258   def _process_vhost(self, parser, vhost):
  259     # Find a vhost name for this VHOST, if any (else, we've nothing to do).
  260     canon_vhost = self._find_canon_vhost(parser, vhost)
  261     if not canon_vhost:
  262       return
  263 
  264     # Overlay any option sections associated with this vhost name.
  265     for section in parser.sections():
  266       base_section = self._is_allowed_override('vhost', canon_vhost, section)
  267       if base_section:
  268         self._process_section(parser, section, base_section)
  269 
  270   def _find_canon_vhost(self, parser, vhost):
  271     vhost = string.split(string.lower(vhost), ':')[0]  # lower-case, no port
  272     for canon_vhost in parser.options('vhosts'):
  273       value = parser.get('vhosts', canon_vhost)
  274       patterns = map(string.lower, map(string.strip,
  275                                        filter(None, string.split(value, ','))))
  276       for pat in patterns:
  277         if fnmatch.fnmatchcase(vhost, pat):
  278           return canon_vhost
  279 
  280     return None
  281 
  282   def overlay_root_options(self, rootname):
  283     """Overlay per-root options for ROOTNAME atop the existing option
  284     set.  This is a destructive change to the configuration."""
  285 
  286     did_overlay = 0
  287     
  288     if not self.conf_path:
  289       return
  290 
  291     for section in self.parser.sections():
  292       base_section = self._is_allowed_override('root', rootname, section)
  293       if base_section:
  294         # We can currently only deal with root overlays happening
  295         # once, so check that we've not yet done any overlaying of
  296         # per-root options.
  297         assert(self.root_options_overlayed == 0)
  298         self._process_section(self.parser, section, base_section)
  299         did_overlay = 1
  300 
  301     # If we actually did any overlaying, remember this fact so we
  302     # don't do it again later.
  303     if did_overlay:
  304       self.root_options_overlayed = 1
  305 
  306   def _get_parser_items(self, parser, section):
  307     """Basically implement ConfigParser.items() for pre-Python-2.3 versions."""
  308     try:
  309       return self.parser.items(section)
  310     except AttributeError:
  311       d = {}
  312       for option in parser.options(section):
  313         d[option] = parser.get(section, option)
  314       return d.items()
  315 
  316   def get_authorizer_and_params_hack(self, rootname):
  317     """Return a 2-tuple containing the name and parameters of the
  318     authorizer configured for use with ROOTNAME.
  319 
  320     ### FIXME: This whole thing is a hack caused by our not being able
  321     ### to non-destructively overlay root options when trying to do
  322     ### something like a root listing (which might need to get
  323     ### different authorizer bits for each and every root in the list).
  324     ### Until we have a good way to do that, we expose this function,
  325     ### which assumes that base and per-vhost configuration has been
  326     ### absorbed into this object and that per-root options have *not*
  327     ### been overlayed.  See issue #371."""
  328 
  329     # We assume that per-root options have *not* been overlayed.
  330     assert(self.root_options_overlayed == 0)
  331 
  332     if not self.conf_path:
  333       return None, {}
  334 
  335     # Figure out the authorizer by searching first for a per-root
  336     # override, then falling back to the base/vhost configuration.
  337     authorizer = None
  338     root_options_section = 'root-%s/options' % (rootname)
  339     if self.parser.has_section(root_options_section) \
  340        and self.parser.has_option(root_options_section, 'authorizer'):
  341       authorizer = self.parser.get(root_options_section, 'authorizer')
  342     if not authorizer:
  343       authorizer = self.options.authorizer
  344 
  345     # No authorizer?  Get outta here.
  346     if not authorizer:
  347       return None, {}
  348 
  349     # Dig up the parameters for the authorizer, starting with the
  350     # base/vhost items, then overlaying any root-specific ones we find.
  351     params = {}
  352     authz_section = 'authz-%s' % (authorizer)
  353     if hasattr(self, authz_section):
  354       sub_config = getattr(self, authz_section)
  355       for attr in dir(sub_config):
  356         params[attr] = getattr(sub_config, attr)
  357     root_authz_section = 'root-%s/authz-%s' % (rootname, authorizer)
  358     for section in self.parser.sections():
  359       if section == root_authz_section:
  360         for key, value in self._get_parser_items(self.parser, section):
  361           params[key] = value
  362     return authorizer, params
  363 
  364   def get_authorizer_params(self, authorizer=None):
  365     """Return a dictionary of parameter names and values which belong
  366     to the configured authorizer (or AUTHORIZER, if provided)."""
  367     params = {}
  368     if authorizer is None:
  369       authorizer = self.options.authorizer
  370     if authorizer:
  371       authz_section = 'authz-%s' % (self.options.authorizer)
  372       if hasattr(self, authz_section):
  373         sub_config = getattr(self, authz_section)
  374         for attr in dir(sub_config):
  375           params[attr] = getattr(sub_config, attr)
  376     return params
  377   
  378   def set_defaults(self):
  379     "Set some default values in the configuration."
  380 
  381     self.general.cvs_roots = { }
  382     self.general.svn_roots = { }
  383     self.general.renamed_roots = { }
  384     self.general.root_parents = []
  385     self.general.default_root = ''
  386     self.general.mime_types_files = ["mimetypes.conf"]
  387     self.general.address = ''
  388     self.general.kv_files = [ ]
  389     self.general.languages = ['en-us']
  390 
  391     self.utilities.rcs_dir = ''
  392     if sys.platform == "win32":
  393       self.utilities.cvsnt = 'cvs'
  394     else:
  395       self.utilities.cvsnt = None
  396     self.utilities.svn = ''
  397     self.utilities.diff = ''
  398     self.utilities.cvsgraph = ''
  399 
  400     self.options.root_as_url_component = 1
  401     self.options.checkout_magic = 0
  402     self.options.allowed_views = ['annotate', 'diff', 'markup', 'roots']
  403     self.options.authorizer = None
  404     self.options.mangle_email_addresses = 0
  405     self.options.custom_log_formatting = []
  406     self.options.default_file_view = "log"
  407     self.options.binary_mime_types = []
  408     self.options.http_expiration_time = 600
  409     self.options.generate_etags = 1
  410     self.options.svn_ignore_mimetype = 0
  411     self.options.svn_config_dir = None
  412     self.options.max_filesize_kbytes = 512
  413     self.options.use_rcsparse = 0
  414     self.options.sort_by = 'file'
  415     self.options.sort_group_dirs = 1
  416     self.options.hide_attic = 1
  417     self.options.hide_errorful_entries = 0
  418     self.options.log_sort = 'date'
  419     self.options.diff_format = 'h'
  420     self.options.hide_cvsroot = 1
  421     self.options.hr_breakable = 1
  422     self.options.hr_funout = 1
  423     self.options.hr_ignore_white = 0
  424     self.options.hr_ignore_keyword_subst = 1
  425     self.options.hr_intraline = 0
  426     self.options.allow_compress = 0
  427     self.options.template_dir = "templates"
  428     self.options.docroot = None
  429     self.options.show_subdir_lastmod = 0
  430     self.options.show_roots_lastmod = 0
  431     self.options.show_logs = 1
  432     self.options.show_log_in_markup = 1
  433     self.options.cross_copies = 1
  434     self.options.use_localtime = 0
  435     self.options.iso8601_timestamps = 0
  436     self.options.short_log_len = 80
  437     self.options.enable_syntax_coloration = 1
  438     self.options.tabsize = 8
  439     self.options.detect_encoding = 0
  440     self.options.use_cvsgraph = 0
  441     self.options.cvsgraph_conf = "cvsgraph.conf"
  442     self.options.use_re_search = 0
  443     self.options.dir_pagesize = 0
  444     self.options.log_pagesize = 0
  445     self.options.log_pagesextra = 3
  446     self.options.limit_changes = 100
  447     self.options.stacktraces = 0
  448 
  449     self.templates.diff = None
  450     self.templates.directory = None
  451     self.templates.error = None
  452     self.templates.file = None
  453     self.templates.graph = None
  454     self.templates.log = None
  455     self.templates.query = None
  456     self.templates.query_form = None
  457     self.templates.query_results = None
  458     self.templates.roots = None
  459 
  460     self.cvsdb.enabled = 0
  461     self.cvsdb.host = ''
  462     self.cvsdb.port = 3306
  463     self.cvsdb.database_name = ''
  464     self.cvsdb.user = ''
  465     self.cvsdb.passwd = ''
  466     self.cvsdb.readonly_user = ''
  467     self.cvsdb.readonly_passwd = '' 
  468     self.cvsdb.row_limit = 1000
  469     self.cvsdb.rss_row_limit = 100
  470     self.cvsdb.check_database_for_root = 0
  471 
  472     self.query.viewvc_base_url = None
  473     
  474 def _startswith(somestr, substr):
  475   return somestr[:len(substr)] == substr
  476 
  477 def _parse_roots(config_name, config_value):
  478   roots = { }
  479   for root in config_value:
  480     pos = string.find(root, ':')
  481     if pos < 0:
  482       raise MalformedRoot(config_name, root)
  483     name, path = map(string.strip, (root[:pos], root[pos+1:]))
  484     roots[name] = path
  485   return roots
  486 
  487 class ViewVCConfigurationError(Exception):
  488   pass
  489 
  490 class IllegalOverrideSection(ViewVCConfigurationError):
  491   def __init__(self, override_type, section_name):
  492     self.section_name = section_name
  493     self.override_type = override_type
  494   def __str__(self):
  495     return "malformed configuration: illegal %s override section: %s" \
  496            % (self.override_type, self.section_name)
  497   
  498 class MalformedRoot(ViewVCConfigurationError):
  499   def __init__(self, config_name, value_given):
  500     Exception.__init__(self, config_name, value_given)
  501     self.config_name = config_name
  502     self.value_given = value_given
  503   def __str__(self):
  504     return "malformed configuration: '%s' uses invalid syntax: %s" \
  505            % (self.config_name, self.value_given)
  506 
  507 
  508 class _sub_config:
  509   pass
  510 
  511 if not hasattr(sys, 'hexversion'):
  512   # Python 1.5 or 1.5.1. fix the syntax for ConfigParser options.
  513   import regex
  514   ConfigParser.option_cre = regex.compile('^\([-A-Za-z0-9._]+\)\(:\|['
  515                                           + string.whitespace
  516                                           + ']*=\)\(.*\)$')