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