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