[FIX] @charset in CSS files
[odoo/odoo.git] / openerp / tools / config.py
1 #openerp.loggers.handlers. -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #    Copyright (C) 2010-2014 OpenERP s.a. (<http://openerp.com>).
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU Affero General Public License as
10 #    published by the Free Software Foundation, either version 3 of the
11 #    License, or (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU Affero General Public License for more details.
17 #
18 #    You should have received a copy of the GNU Affero General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 import ConfigParser
24 import optparse
25 import os
26 import sys
27 import openerp
28 import openerp.conf
29 import openerp.loglevels as loglevels
30 import logging
31 import openerp.release as release
32 import appdirs
33
34 class MyOption (optparse.Option, object):
35     """ optparse Option with two additional attributes.
36
37     The list of command line options (getopt.Option) is used to create the
38     list of the configuration file options. When reading the file, and then
39     reading the command line arguments, we don't want optparse.parse results
40     to override the configuration file values. But if we provide default
41     values to optparse, optparse will return them and we can't know if they
42     were really provided by the user or not. A solution is to not use
43     optparse's default attribute, but use a custom one (that will be copied
44     to create the default values of the configuration file).
45
46     """
47     def __init__(self, *opts, **attrs):
48         self.my_default = attrs.pop('my_default', None)
49         super(MyOption, self).__init__(*opts, **attrs)
50
51
52 def check_ssl():
53     try:
54         from OpenSSL import SSL
55         import socket
56
57         return hasattr(socket, 'ssl') and hasattr(SSL, "Connection")
58     except:
59         return False
60
61 DEFAULT_LOG_HANDLER = [':INFO']
62
63 def _get_default_datadir():
64     return appdirs.user_data_dir(appname='OpenERP', appauthor=release.author)
65
66 class configmanager(object):
67     def __init__(self, fname=None):
68         # Options not exposed on the command line. Command line options will be added
69         # from optparse's parser.
70         self.options = {
71             'admin_passwd': 'admin',
72             'csv_internal_sep': ',',
73             'publisher_warranty_url': 'http://services.openerp.com/publisher-warranty/',
74             'reportgz': False,
75             'root_path': None,
76         }
77
78         # Not exposed in the configuration file.
79         self.blacklist_for_save = set([
80             'publisher_warranty_url', 'load_language', 'root_path',
81             'init', 'save', 'config', 'update', 'stop_after_init'
82         ])
83
84         # dictionary mapping option destination (keys in self.options) to MyOptions.
85         self.casts = {}
86
87         self.misc = {}
88         self.config_file = fname
89         self.has_ssl = check_ssl()
90
91         self._LOGLEVELS = dict([
92             (getattr(loglevels, 'LOG_%s' % x), getattr(logging, x)) 
93             for x in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET')
94         ])
95
96         version = "%s %s" % (release.description, release.version)
97         self.parser = parser = optparse.OptionParser(version=version, option_class=MyOption)
98
99         # Server startup config
100         group = optparse.OptionGroup(parser, "Common options")
101         group.add_option("-c", "--config", dest="config", help="specify alternate config file")
102         group.add_option("-s", "--save", action="store_true", dest="save", default=False,
103                           help="save configuration to ~/.openerp_serverrc")
104         group.add_option("-i", "--init", dest="init", help="install one or more modules (comma-separated list, use \"all\" for all modules), requires -d")
105         group.add_option("-u", "--update", dest="update",
106                           help="update one or more modules (comma-separated list, use \"all\" for all modules). Requires -d.")
107         group.add_option("--without-demo", dest="without_demo",
108                           help="disable loading demo data for modules to be installed (comma-separated, use \"all\" for all modules). Requires -d and -i. Default is %default",
109                           my_default=False)
110         group.add_option("-P", "--import-partial", dest="import_partial", my_default='',
111                         help="Use this for big data importation, if it crashes you will be able to continue at the current state. Provide a filename to store intermediate importation states.")
112         group.add_option("--pidfile", dest="pidfile", help="file where the server pid will be stored")
113         group.add_option("--addons-path", dest="addons_path",
114                          help="specify additional addons paths (separated by commas).",
115                          action="callback", callback=self._check_addons_path, nargs=1, type="string")
116         group.add_option("--load", dest="server_wide_modules", help="Comma-separated list of server-wide modules default=web")
117
118         group.add_option("-D", "--data-dir", dest="data_dir", my_default=_get_default_datadir(),
119                          help="Directory where to store OpenERP data")
120         parser.add_option_group(group)
121
122         # XML-RPC / HTTP
123         group = optparse.OptionGroup(parser, "XML-RPC Configuration")
124         group.add_option("--xmlrpc-interface", dest="xmlrpc_interface", my_default='',
125                          help="Specify the TCP IP address for the XML-RPC protocol. The empty string binds to all interfaces.")
126         group.add_option("--xmlrpc-port", dest="xmlrpc_port", my_default=8069,
127                          help="specify the TCP port for the XML-RPC protocol", type="int")
128         group.add_option("--no-xmlrpc", dest="xmlrpc", action="store_false", my_default=True,
129                          help="disable the XML-RPC protocol")
130         group.add_option("--proxy-mode", dest="proxy_mode", action="store_true", my_default=False,
131                          help="Enable correct behavior when behind a reverse proxy")
132         group.add_option("--longpolling-port", dest="longpolling_port", my_default=8072,
133                          help="specify the TCP port for longpolling requests", type="int")
134         parser.add_option_group(group)
135
136         # XML-RPC / HTTPS
137         title = "XML-RPC Secure Configuration"
138         if not self.has_ssl:
139             title += " (disabled as ssl is unavailable)"
140
141         group = optparse.OptionGroup(parser, title)
142         group.add_option("--xmlrpcs-interface", dest="xmlrpcs_interface", my_default='',
143                          help="Specify the TCP IP address for the XML-RPC Secure protocol. The empty string binds to all interfaces.")
144         group.add_option("--xmlrpcs-port", dest="xmlrpcs_port", my_default=8071,
145                          help="specify the TCP port for the XML-RPC Secure protocol", type="int")
146         group.add_option("--no-xmlrpcs", dest="xmlrpcs", action="store_false", my_default=True,
147                          help="disable the XML-RPC Secure protocol")
148         group.add_option("--cert-file", dest="secure_cert_file", my_default='server.cert',
149                          help="specify the certificate file for the SSL connection")
150         group.add_option("--pkey-file", dest="secure_pkey_file", my_default='server.pkey',
151                          help="specify the private key file for the SSL connection")
152         parser.add_option_group(group)
153
154         # WEB
155         group = optparse.OptionGroup(parser, "Web interface Configuration")
156         group.add_option("--db-filter", dest="dbfilter", default='.*',
157                          help="Filter listed database", metavar="REGEXP")
158         parser.add_option_group(group)
159
160         # Testing Group
161         group = optparse.OptionGroup(parser, "Testing Configuration")
162         group.add_option("--test-file", dest="test_file", my_default=False,
163                          help="Launch a python or YML test file.")
164         group.add_option("--test-report-directory", dest="test_report_directory", my_default=False,
165                          help="If set, will save sample of all reports in this directory.")
166         group.add_option("--test-enable", action="store_true", dest="test_enable",
167                          my_default=False, help="Enable YAML and unit tests.")
168         group.add_option("--test-commit", action="store_true", dest="test_commit",
169                          my_default=False, help="Commit database changes performed by YAML or XML tests.")
170         parser.add_option_group(group)
171
172         # Logging Group
173         group = optparse.OptionGroup(parser, "Logging Configuration")
174         group.add_option("--logfile", dest="logfile", help="file where the server log will be stored")
175         group.add_option("--logrotate", dest="logrotate", action="store_true", my_default=False, help="enable logfile rotation")
176         group.add_option("--syslog", action="store_true", dest="syslog", my_default=False, help="Send the log to the syslog server")
177         group.add_option('--log-handler', action="append", default=DEFAULT_LOG_HANDLER, my_default=DEFAULT_LOG_HANDLER, metavar="PREFIX:LEVEL", help='setup a handler at LEVEL for a given PREFIX. An empty PREFIX indicates the root logger. This option can be repeated. Example: "openerp.orm:DEBUG" or "werkzeug:CRITICAL" (default: ":INFO")')
178         group.add_option('--log-request', action="append_const", dest="log_handler", const="openerp.http.rpc.request:DEBUG", help='shortcut for --log-handler=openerp.http.rpc.request:DEBUG')
179         group.add_option('--log-response', action="append_const", dest="log_handler", const="openerp.http.rpc.response:DEBUG", help='shortcut for --log-handler=openerp.http.rpc.response:DEBUG')
180         group.add_option('--log-web', action="append_const", dest="log_handler", const="openerp.http:DEBUG", help='shortcut for --log-handler=openerp.http:DEBUG')
181         group.add_option('--log-sql', action="append_const", dest="log_handler", const="openerp.sql_db:DEBUG", help='shortcut for --log-handler=openerp.sql_db:DEBUG')
182         group.add_option('--log-db', dest='log_db', help="Logging database", my_default=False)
183         # For backward-compatibility, map the old log levels to something
184         # quite close.
185         levels = [
186             'info', 'debug_rpc', 'warn', 'test', 'critical',
187             'debug_sql', 'error', 'debug', 'debug_rpc_answer', 'notset'
188         ]
189         group.add_option('--log-level', dest='log_level', type='choice',
190                          choices=levels, my_default='info',
191                          help='specify the level of the logging. Accepted values: %s (deprecated option).' % (levels,))
192
193         parser.add_option_group(group)
194
195         # SMTP Group
196         group = optparse.OptionGroup(parser, "SMTP Configuration")
197         group.add_option('--email-from', dest='email_from', my_default=False,
198                          help='specify the SMTP email address for sending email')
199         group.add_option('--smtp', dest='smtp_server', my_default='localhost',
200                          help='specify the SMTP server for sending email')
201         group.add_option('--smtp-port', dest='smtp_port', my_default=25,
202                          help='specify the SMTP port', type="int")
203         group.add_option('--smtp-ssl', dest='smtp_ssl', action='store_true', my_default=False,
204                          help='if passed, SMTP connections will be encrypted with SSL (STARTTLS)')
205         group.add_option('--smtp-user', dest='smtp_user', my_default=False,
206                          help='specify the SMTP username for sending email')
207         group.add_option('--smtp-password', dest='smtp_password', my_default=False,
208                          help='specify the SMTP password for sending email')
209         parser.add_option_group(group)
210
211         group = optparse.OptionGroup(parser, "Database related options")
212         group.add_option("-d", "--database", dest="db_name", my_default=False,
213                          help="specify the database name")
214         group.add_option("-r", "--db_user", dest="db_user", my_default=False,
215                          help="specify the database user name")
216         group.add_option("-w", "--db_password", dest="db_password", my_default=False,
217                          help="specify the database password")
218         group.add_option("--pg_path", dest="pg_path", help="specify the pg executable path")
219         group.add_option("--db_host", dest="db_host", my_default=False,
220                          help="specify the database host")
221         group.add_option("--db_port", dest="db_port", my_default=False,
222                          help="specify the database port", type="int")
223         group.add_option("--db_maxconn", dest="db_maxconn", type='int', my_default=64,
224                          help="specify the the maximum number of physical connections to posgresql")
225         group.add_option("--db-template", dest="db_template", my_default="template1",
226                          help="specify a custom database template to create a new database")
227         parser.add_option_group(group)
228
229         group = optparse.OptionGroup(parser, "Internationalisation options",
230             "Use these options to translate OpenERP to another language."
231             "See i18n section of the user manual. Option '-d' is mandatory."
232             "Option '-l' is mandatory in case of importation"
233             )
234         group.add_option('--load-language', dest="load_language",
235                          help="specifies the languages for the translations you want to be loaded")
236         group.add_option('-l', "--language", dest="language",
237                          help="specify the language of the translation file. Use it with --i18n-export or --i18n-import")
238         group.add_option("--i18n-export", dest="translate_out",
239                          help="export all sentences to be translated to a CSV file, a PO file or a TGZ archive and exit")
240         group.add_option("--i18n-import", dest="translate_in",
241                          help="import a CSV or a PO file with translations and exit. The '-l' option is required.")
242         group.add_option("--i18n-overwrite", dest="overwrite_existing_translations", action="store_true", my_default=False,
243                          help="overwrites existing translation terms on updating a module or importing a CSV or a PO file.")
244         group.add_option("--modules", dest="translate_modules",
245                          help="specify modules to export. Use in combination with --i18n-export")
246         parser.add_option_group(group)
247
248         security = optparse.OptionGroup(parser, 'Security-related options')
249         security.add_option('--no-database-list', action="store_false", dest='list_db', my_default=True,
250                             help="disable the ability to return the list of databases")
251         parser.add_option_group(security)
252
253         # Advanced options
254         group = optparse.OptionGroup(parser, "Advanced options")
255         if os.name == 'posix':
256             group.add_option('--auto-reload', dest='auto_reload', action='store_true', my_default=False, help='enable auto reload')
257         group.add_option('--debug', dest='debug_mode', action='store_true', my_default=False, help='enable debug mode')
258         group.add_option("--stop-after-init", action="store_true", dest="stop_after_init", my_default=False,
259                           help="stop the server after its initialization")
260         group.add_option("-t", "--timezone", dest="timezone", my_default=False,
261                          help="specify reference timezone for the server (e.g. Europe/Brussels")
262         group.add_option("--osv-memory-count-limit", dest="osv_memory_count_limit", my_default=False,
263                          help="Force a limit on the maximum number of records kept in the virtual "
264                               "osv_memory tables. The default is False, which means no count-based limit.",
265                          type="int")
266         group.add_option("--osv-memory-age-limit", dest="osv_memory_age_limit", my_default=1.0,
267                          help="Force a limit on the maximum age of records kept in the virtual "
268                               "osv_memory tables. This is a decimal value expressed in hours, "
269                               "and the default is 1 hour.",
270                          type="float")
271         group.add_option("--max-cron-threads", dest="max_cron_threads", my_default=2,
272                          help="Maximum number of threads processing concurrently cron jobs (default 2).",
273                          type="int")
274         group.add_option("--unaccent", dest="unaccent", my_default=False, action="store_true",
275                          help="Use the unaccent function provided by the database when available.")
276         parser.add_option_group(group)
277
278         if os.name == 'posix':
279             group = optparse.OptionGroup(parser, "Multiprocessing options")
280             # TODO sensible default for the three following limits.
281             group.add_option("--workers", dest="workers", my_default=0,
282                              help="Specify the number of workers, 0 disable prefork mode.",
283                              type="int")
284             group.add_option("--limit-memory-soft", dest="limit_memory_soft", my_default=2048 * 1024 * 1024,
285                              help="Maximum allowed virtual memory per worker, when reached the worker be reset after the current request (default 671088640 aka 640MB).",
286                              type="int")
287             group.add_option("--limit-memory-hard", dest="limit_memory_hard", my_default=2560 * 1024 * 1024,
288                              help="Maximum allowed virtual memory per worker, when reached, any memory allocation will fail (default 805306368 aka 768MB).",
289                              type="int")
290             group.add_option("--limit-time-cpu", dest="limit_time_cpu", my_default=60,
291                              help="Maximum allowed CPU time per request (default 60).",
292                              type="int")
293             group.add_option("--limit-time-real", dest="limit_time_real", my_default=120,
294                              help="Maximum allowed Real time per request (default 120).",
295                              type="int")
296             group.add_option("--limit-request", dest="limit_request", my_default=8192,
297                              help="Maximum number of request to be processed per worker (default 8192).",
298                              type="int")
299             parser.add_option_group(group)
300
301         # Copy all optparse options (i.e. MyOption) into self.options.
302         for group in parser.option_groups:
303             for option in group.option_list:
304                 if option.dest not in self.options:
305                     self.options[option.dest] = option.my_default
306                     self.casts[option.dest] = option
307
308         # generate default config
309         self._parse_config()
310
311     def parse_config(self, args=None):
312         """ Parse the configuration file (if any) and the command-line
313         arguments.
314
315         This method initializes openerp.tools.config and openerp.conf (the
316         former should be removed in the furture) with library-wide
317         configuration values.
318
319         This method must be called before proper usage of this library can be
320         made.
321
322         Typical usage of this method:
323
324             openerp.tools.config.parse_config(sys.argv[1:])
325         """
326         self._parse_config(args)
327         openerp.netsvc.init_logger()
328         openerp.modules.module.initialize_sys_path()
329
330     def _parse_config(self, args=None):
331         if args is None:
332             args = []
333         opt, args = self.parser.parse_args(args)
334
335         def die(cond, msg):
336             if cond:
337                 self.parser.error(msg)
338
339         # Ensures no illegitimate argument is silently discarded (avoids insidious "hyphen to dash" problem)
340         die(args, "unrecognized parameters: '%s'" % " ".join(args))
341
342         die(bool(opt.syslog) and bool(opt.logfile),
343             "the syslog and logfile options are exclusive")
344
345         die(opt.translate_in and (not opt.language or not opt.db_name),
346             "the i18n-import option cannot be used without the language (-l) and the database (-d) options")
347
348         die(opt.overwrite_existing_translations and not (opt.translate_in or opt.update),
349             "the i18n-overwrite option cannot be used without the i18n-import option or without the update option")
350
351         die(opt.translate_out and (not opt.db_name),
352             "the i18n-export option cannot be used without the database (-d) option")
353
354         # Check if the config file exists (-c used, but not -s)
355         die(not opt.save and opt.config and not os.path.exists(opt.config),
356             "The config file '%s' selected with -c/--config doesn't exist, "\
357             "use -s/--save if you want to generate it"% opt.config)
358
359         # place/search the config file on Win32 near the server installation
360         # (../etc from the server)
361         # if the server is run by an unprivileged user, he has to specify location of a config file where he has the rights to write,
362         # else he won't be able to save the configurations, or even to start the server...
363         # TODO use appdirs
364         if os.name == 'nt':
365             rcfilepath = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'openerp-server.conf')
366         else:
367             rcfilepath = os.path.expanduser('~/.openerp_serverrc')
368
369         self.rcfile = os.path.abspath(
370             self.config_file or opt.config \
371                 or os.environ.get('OPENERP_SERVER') or rcfilepath)
372         self.load()
373
374         # Verify that we want to log or not, if not the output will go to stdout
375         if self.options['logfile'] in ('None', 'False'):
376             self.options['logfile'] = False
377         # the same for the pidfile
378         if self.options['pidfile'] in ('None', 'False'):
379             self.options['pidfile'] = False
380
381         # if defined dont take the configfile value even if the defined value is None
382         keys = ['xmlrpc_interface', 'xmlrpc_port', 'longpolling_port',
383                 'db_name', 'db_user', 'db_password', 'db_host',
384                 'db_port', 'db_template', 'logfile', 'pidfile', 'smtp_port',
385                 'email_from', 'smtp_server', 'smtp_user', 'smtp_password',
386                 'db_maxconn', 'import_partial', 'addons_path',
387                 'xmlrpc', 'syslog', 'without_demo', 'timezone',
388                 'xmlrpcs_interface', 'xmlrpcs_port', 'xmlrpcs',
389                 'secure_cert_file', 'secure_pkey_file', 'dbfilter', 'log_handler', 'log_level', 'log_db'
390                 ]
391
392         for arg in keys:
393             # Copy the command-line argument (except the special case for log_handler, due to
394             # action=append requiring a real default, so we cannot use the my_default workaround)
395             if getattr(opt, arg) and getattr(opt, arg) != DEFAULT_LOG_HANDLER:
396                 self.options[arg] = getattr(opt, arg)
397             # ... or keep, but cast, the config file value.
398             elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
399                 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
400
401         if isinstance(self.options['log_handler'], basestring):
402             self.options['log_handler'] = self.options['log_handler'].split(',')
403
404         # if defined but None take the configfile value
405         keys = [
406             'language', 'translate_out', 'translate_in', 'overwrite_existing_translations',
407             'debug_mode', 'smtp_ssl', 'load_language',
408             'stop_after_init', 'logrotate', 'without_demo', 'xmlrpc', 'syslog',
409             'list_db', 'xmlrpcs', 'proxy_mode',
410             'test_file', 'test_enable', 'test_commit', 'test_report_directory',
411             'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads', 'unaccent',
412             'data_dir',
413         ]
414
415         posix_keys = [
416             'auto_reload', 'workers',
417             'limit_memory_hard', 'limit_memory_soft',
418             'limit_time_cpu', 'limit_time_real', 'limit_request',
419         ]
420
421         if os.name == 'posix':
422             keys += posix_keys
423         else:
424             self.options.update(dict.fromkeys(posix_keys, None))
425
426         # Copy the command-line arguments...
427         for arg in keys:
428             if getattr(opt, arg) is not None:
429                 self.options[arg] = getattr(opt, arg)
430             # ... or keep, but cast, the config file value.
431             elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
432                 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
433
434         self.options['root_path'] = os.path.abspath(os.path.expanduser(os.path.expandvars(os.path.dirname(openerp.__file__))))
435         if not self.options['addons_path'] or self.options['addons_path']=='None':
436             self.options['addons_path'] = os.path.join(self.options['root_path'], 'addons')
437         else:
438             self.options['addons_path'] = ",".join(
439                     os.path.abspath(os.path.expanduser(os.path.expandvars(x)))
440                       for x in self.options['addons_path'].split(','))
441
442         self.options['init'] = opt.init and dict.fromkeys(opt.init.split(','), 1) or {}
443         self.options["demo"] = not opt.without_demo and self.options['init'] or {}
444         self.options['update'] = opt.update and dict.fromkeys(opt.update.split(','), 1) or {}
445         self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all']
446         self.options['translate_modules'].sort()
447
448         # TODO checking the type of the parameters should be done for every
449         # parameters, not just the timezone.
450         # The call to get_server_timezone() sets the timezone; this should
451         # probably done here.
452         if self.options['timezone']:
453             # Prevent the timezone to be True. (The config file parsing changes
454             # the string 'True' to the boolean value True. It would be probably
455             # be better to remove that conversion.)
456             die(not isinstance(self.options['timezone'], basestring),
457                 "Invalid timezone value in configuration or environment: %r.\n"
458                 "Please fix this in your configuration." %(self.options['timezone']))
459
460             # If an explicit TZ was provided in the config, make sure it is known
461             try:
462                 import pytz
463                 pytz.timezone(self.options['timezone'])
464             except pytz.UnknownTimeZoneError:
465                 die(True, "The specified timezone (%s) is invalid" % self.options['timezone'])
466             except:
467                 # If pytz is missing, don't check the provided TZ, it will be ignored anyway.
468                 pass
469
470         if opt.pg_path:
471             self.options['pg_path'] = opt.pg_path
472
473         if self.options.get('language', False):
474             if len(self.options['language']) > 5:
475                 raise Exception('ERROR: The Lang name must take max 5 chars, Eg: -lfr_BE')
476
477         if not self.options['db_user']:
478             try:
479                 import getpass
480                 self.options['db_user'] = getpass.getuser()
481             except:
482                 self.options['db_user'] = None
483
484         die(not self.options['db_user'], 'ERROR: No user specified for the connection to the database')
485
486         if self.options['db_password']:
487             if sys.platform == 'win32' and not self.options['db_host']:
488                 self.options['db_host'] = 'localhost'
489             #if self.options['db_host']:
490             #    self._generate_pgpassfile()
491
492         if opt.save:
493             self.save()
494
495         openerp.conf.addons_paths = self.options['addons_path'].split(',')
496         if opt.server_wide_modules:
497             openerp.conf.server_wide_modules = map(lambda m: m.strip(), opt.server_wide_modules.split(','))
498         else:
499             openerp.conf.server_wide_modules = ['web','web_kanban']
500
501     def _generate_pgpassfile(self):
502         """
503         Generate the pgpass file with the parameters from the command line (db_host, db_user,
504         db_password)
505
506         Used because pg_dump and pg_restore can not accept the password on the command line.
507         """
508         is_win32 = sys.platform == 'win32'
509         if is_win32:
510             filename = os.path.join(os.environ['APPDATA'], 'pgpass.conf')
511         else:
512             filename = os.path.join(os.environ['HOME'], '.pgpass')
513
514         text_to_add = "%(db_host)s:*:*:%(db_user)s:%(db_password)s" % self.options
515
516         if os.path.exists(filename):
517             content = [x.strip() for x in file(filename, 'r').readlines()]
518             if text_to_add in content:
519                 return
520
521         fp = file(filename, 'a+')
522         fp.write(text_to_add + "\n")
523         fp.close()
524
525         if is_win32:
526             try:
527                 import _winreg
528             except ImportError:
529                 _winreg = None
530             x=_winreg.ConnectRegistry(None,_winreg.HKEY_LOCAL_MACHINE)
531             y = _winreg.OpenKey(x, r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", 0,_winreg.KEY_ALL_ACCESS)
532             _winreg.SetValueEx(y,"PGPASSFILE", 0, _winreg.REG_EXPAND_SZ, filename )
533             _winreg.CloseKey(y)
534             _winreg.CloseKey(x)
535         else:
536             import stat
537             os.chmod(filename, stat.S_IRUSR + stat.S_IWUSR)
538
539     def _is_addons_path(self, path):
540         for f in os.listdir(path):
541             modpath = os.path.join(path, f)
542             if os.path.isdir(modpath):
543                 def hasfile(filename):
544                     return os.path.isfile(os.path.join(modpath, filename))
545                 if hasfile('__init__.py') and (hasfile('__openerp__.py') or hasfile('__terp__.py')):
546                     return True
547         return False
548
549     def _check_addons_path(self, option, opt, value, parser):
550         ad_paths = []
551         for path in value.split(','):
552             path = path.strip()
553             res = os.path.abspath(os.path.expanduser(path))
554             if not os.path.isdir(res):
555                 raise optparse.OptionValueError("option %s: no such directory: %r" % (opt, path))
556             if not self._is_addons_path(res):
557                 raise optparse.OptionValueError("option %s: The addons-path %r does not seem to a be a valid Addons Directory!" % (opt, path))
558             ad_paths.append(res)
559
560         setattr(parser.values, option.dest, ",".join(ad_paths))
561
562     def load(self):
563         p = ConfigParser.ConfigParser()
564         try:
565             p.read([self.rcfile])
566             for (name,value) in p.items('options'):
567                 if value=='True' or value=='true':
568                     value = True
569                 if value=='False' or value=='false':
570                     value = False
571                 self.options[name] = value
572             #parse the other sections, as well
573             for sec in p.sections():
574                 if sec == 'options':
575                     continue
576                 if not self.misc.has_key(sec):
577                     self.misc[sec]= {}
578                 for (name, value) in p.items(sec):
579                     if value=='True' or value=='true':
580                         value = True
581                     if value=='False' or value=='false':
582                         value = False
583                     self.misc[sec][name] = value
584         except IOError:
585             pass
586         except ConfigParser.NoSectionError:
587             pass
588
589     def save(self):
590         p = ConfigParser.ConfigParser()
591         loglevelnames = dict(zip(self._LOGLEVELS.values(), self._LOGLEVELS.keys()))
592         p.add_section('options')
593         for opt in sorted(self.options.keys()):
594             if opt in ('version', 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations', 'init', 'update'):
595                 continue
596             if opt in self.blacklist_for_save:
597                 continue
598             if opt in ('log_level',):
599                 p.set('options', opt, loglevelnames.get(self.options[opt], self.options[opt]))
600             else:
601                 p.set('options', opt, self.options[opt])
602
603         for sec in sorted(self.misc.keys()):
604             p.add_section(sec)
605             for opt in sorted(self.misc[sec].keys()):
606                 p.set(sec,opt,self.misc[sec][opt])
607
608         # try to create the directories and write the file
609         try:
610             rc_exists = os.path.exists(self.rcfile)
611             if not rc_exists and not os.path.exists(os.path.dirname(self.rcfile)):
612                 os.makedirs(os.path.dirname(self.rcfile))
613             try:
614                 p.write(file(self.rcfile, 'w'))
615                 if not rc_exists:
616                     os.chmod(self.rcfile, 0600)
617             except IOError:
618                 sys.stderr.write("ERROR: couldn't write the config file\n")
619
620         except OSError:
621             # what to do if impossible?
622             sys.stderr.write("ERROR: couldn't create the config directory\n")
623
624     def get(self, key, default=None):
625         return self.options.get(key, default)
626
627     def get_misc(self, sect, key, default=None):
628         return self.misc.get(sect,{}).get(key, default)
629
630     def __setitem__(self, key, value):
631         self.options[key] = value
632         if key in self.options and isinstance(self.options[key], basestring) and \
633                 key in self.casts and self.casts[key].type in optparse.Option.TYPE_CHECKER:
634             self.options[key] = optparse.Option.TYPE_CHECKER[self.casts[key].type](self.casts[key], key, self.options[key])
635
636     def __getitem__(self, key):
637         return self.options[key]
638
639     @property
640     def addons_data_dir(self):
641         d = os.path.join(self['data_dir'], 'addons', release.series)
642         if not os.path.exists(d):
643             os.makedirs(d, 0700)
644         else:
645             os.chmod(d, 0700)
646         return d
647
648     @property
649     def session_dir(self):
650         d = os.path.join(self['data_dir'], 'sessions', release.series)
651         if not os.path.exists(d):
652             os.makedirs(d, 0700)
653         else:
654             os.chmod(d, 0700)
655         return d
656
657     def filestore(self, dbname):
658         return os.path.join(self['data_dir'], 'filestore', dbname)
659
660 config = configmanager()
661
662
663 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: