[FIX] do not expose posix only config options on windows
[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-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         # dictionary mapping option destination (keys in self.options) to MyOptions.
84         self.casts = {}
85
86         self.misc = {}
87         self.config_file = fname
88         self.has_ssl = check_ssl()
89
90         self._LOGLEVELS = dict([(getattr(loglevels, 'LOG_%s' % x), getattr(logging, x)) for x in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET')])
91
92         version = "%s %s" % (release.description, release.version)
93         self.parser = parser = optparse.OptionParser(version=version, option_class=MyOption)
94
95         # Server startup config
96         group = optparse.OptionGroup(parser, "Common options")
97         group.add_option("-c", "--config", dest="config", help="specify alternate config file")
98         group.add_option("-s", "--save", action="store_true", dest="save", default=False,
99                           help="save configuration to ~/.openerp_serverrc")
100         group.add_option("-i", "--init", dest="init", help="install one or more modules (comma-separated list, use \"all\" for all modules), requires -d")
101         group.add_option("-u", "--update", dest="update",
102                           help="update one or more modules (comma-separated list, use \"all\" for all modules). Requires -d.")
103         group.add_option("--without-demo", dest="without_demo",
104                           help="disable loading demo data for modules to be installed (comma-separated, use \"all\" for all modules). Requires -d and -i. Default is %default",
105                           my_default=False)
106         group.add_option("-P", "--import-partial", dest="import_partial", my_default='',
107                         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.")
108         group.add_option("--pidfile", dest="pidfile", help="file where the server pid will be stored")
109         group.add_option("--addons-path", dest="addons_path",
110                          help="specify additional addons paths (separated by commas).",
111                          action="callback", callback=self._check_addons_path, nargs=1, type="string")
112         group.add_option("--load", dest="server_wide_modules", help="Comma-separated list of server-wide modules default=web")
113
114         group.add_option("-D", "--data-dir", dest="data_dir", my_default=_get_default_datadir(),
115                          help="Directory where to store OpenERP data")
116         parser.add_option_group(group)
117
118         # XML-RPC / HTTP
119         group = optparse.OptionGroup(parser, "XML-RPC Configuration")
120         group.add_option("--xmlrpc-interface", dest="xmlrpc_interface", my_default='',
121                          help="Specify the TCP IP address for the XML-RPC protocol. The empty string binds to all interfaces.")
122         group.add_option("--xmlrpc-port", dest="xmlrpc_port", my_default=8069,
123                          help="specify the TCP port for the XML-RPC protocol", type="int")
124         group.add_option("--no-xmlrpc", dest="xmlrpc", action="store_false", my_default=True,
125                          help="disable the XML-RPC protocol")
126         group.add_option("--proxy-mode", dest="proxy_mode", action="store_true", my_default=False,
127                          help="Enable correct behavior when behind a reverse proxy")
128         group.add_option("--longpolling-port", dest="longpolling_port", my_default=8072,
129                          help="specify the TCP port for longpolling requests", type="int")
130         parser.add_option_group(group)
131
132         # XML-RPC / HTTPS
133         title = "XML-RPC Secure Configuration"
134         if not self.has_ssl:
135             title += " (disabled as ssl is unavailable)"
136
137         group = optparse.OptionGroup(parser, title)
138         group.add_option("--xmlrpcs-interface", dest="xmlrpcs_interface", my_default='',
139                          help="Specify the TCP IP address for the XML-RPC Secure protocol. The empty string binds to all interfaces.")
140         group.add_option("--xmlrpcs-port", dest="xmlrpcs_port", my_default=8071,
141                          help="specify the TCP port for the XML-RPC Secure protocol", type="int")
142         group.add_option("--no-xmlrpcs", dest="xmlrpcs", action="store_false", my_default=True,
143                          help="disable the XML-RPC Secure protocol")
144         group.add_option("--cert-file", dest="secure_cert_file", my_default='server.cert',
145                          help="specify the certificate file for the SSL connection")
146         group.add_option("--pkey-file", dest="secure_pkey_file", my_default='server.pkey',
147                          help="specify the private key file for the SSL connection")
148         parser.add_option_group(group)
149
150         # WEB
151         # TODO move to web addons after MetaOption merge
152         group = optparse.OptionGroup(parser, "Web interface Configuration")
153         group.add_option("--db-filter", dest="dbfilter", default='.*',
154                          help="Filter listed database", metavar="REGEXP")
155         parser.add_option_group(group)
156
157         # Static HTTP
158         group = optparse.OptionGroup(parser, "Static HTTP service")
159         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")
160         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/')")
161         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 '/')")
162         parser.add_option_group(group)
163
164         # Testing Group
165         group = optparse.OptionGroup(parser, "Testing Configuration")
166         group.add_option("--test-file", dest="test_file", my_default=False,
167                          help="Launch a python or YML test file.")
168         group.add_option("--test-report-directory", dest="test_report_directory", my_default=False,
169                          help="If set, will save sample of all reports in this directory.")
170         group.add_option("--test-enable", action="store_true", dest="test_enable",
171                          my_default=False, help="Enable YAML and unit tests.")
172         group.add_option("--test-commit", action="store_true", dest="test_commit",
173                          my_default=False, help="Commit database changes performed by YAML or XML tests.")
174         parser.add_option_group(group)
175
176         # Logging Group
177         group = optparse.OptionGroup(parser, "Logging Configuration")
178         group.add_option("--logfile", dest="logfile", help="file where the server log will be stored")
179         group.add_option("--logrotate", dest="logrotate", action="store_true", my_default=False, help="enable logfile rotation")
180         group.add_option("--syslog", action="store_true", dest="syslog", my_default=False, help="Send the log to the syslog server")
181         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")')
182         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')
183         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')
184         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')
185         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')
186         # For backward-compatibility, map the old log levels to something
187         # quite close.
188         levels = ['info', 'debug_rpc', 'warn', 'test', 'critical',
189             'debug_sql', 'error', 'debug', 'debug_rpc_answer', 'notset']
190         group.add_option('--log-level', dest='log_level', type='choice', choices=levels,
191             my_default='info', help='specify the level of the logging. Accepted values: ' + str(levels) + ' (deprecated option).')
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         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         # TODO use appdirs
361         if os.name == 'nt':
362             rcfilepath = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'openerp-server.conf')
363         else:
364             rcfilepath = os.path.expanduser('~/.openerp_serverrc')
365
366         self.rcfile = os.path.abspath(
367             self.config_file or opt.config \
368                 or os.environ.get('OPENERP_SERVER') or rcfilepath)
369         self.load()
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', 'longpolling_port',
380                 'db_name', 'db_user', 'db_password', 'db_host',
381                 'db_port', 'db_template', 'logfile', 'pidfile', 'smtp_port',
382                 'email_from', 'smtp_server', 'smtp_user', 'smtp_password',
383                 'db_maxconn', 'import_partial', 'addons_path',
384                 'xmlrpc', 'syslog', 'without_demo', 'timezone',
385                 'xmlrpcs_interface', 'xmlrpcs_port', 'xmlrpcs',
386                 'static_http_enable', 'static_http_document_root', 'static_http_url_prefix',
387                 'secure_cert_file', 'secure_pkey_file', 'dbfilter', 'log_handler', 'log_level'
388                 ]
389
390         for arg in keys:
391             # Copy the command-line argument (except the special case for log_handler, due to
392             # action=append requiring a real default, so we cannot use the my_default workaround)
393             if getattr(opt, arg) and getattr(opt, arg) != DEFAULT_LOG_HANDLER:
394                 self.options[arg] = getattr(opt, arg)
395             # ... or keep, but cast, the config file value.
396             elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
397                 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
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', '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             'data_dir',
411         ]
412
413         posix_keys = [
414             'auto_reload', 'workers',
415             'limit_memory_hard', 'limit_memory_soft',
416             'limit_time_cpu', 'limit_time_real', 'limit_request',
417         ]
418
419         if os.name == 'posix':
420             keys += posix_keys
421         else:
422             self.options.update(dict.fromkeys(posix_keys, None))
423
424         # Copy the command-line arguments...
425         for arg in keys:
426             if getattr(opt, arg) is not None:
427                 self.options[arg] = getattr(opt, arg)
428             # ... or keep, but cast, the config file value.
429             elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
430                 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
431
432         self.options['root_path'] = os.path.abspath(os.path.expanduser(os.path.expandvars(os.path.dirname(openerp.__file__))))
433         if not self.options['addons_path'] or self.options['addons_path']=='None':
434             self.options['addons_path'] = os.path.join(self.options['root_path'], 'addons')
435         else:
436             self.options['addons_path'] = ",".join(
437                     os.path.abspath(os.path.expanduser(os.path.expandvars(x)))
438                       for x in self.options['addons_path'].split(','))
439
440         self.options['init'] = opt.init and dict.fromkeys(opt.init.split(','), 1) or {}
441         self.options["demo"] = not opt.without_demo and self.options['init'] or {}
442         self.options['update'] = opt.update and dict.fromkeys(opt.update.split(','), 1) or {}
443         self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all']
444         self.options['translate_modules'].sort()
445
446         # TODO checking the type of the parameters should be done for every
447         # parameters, not just the timezone.
448         # The call to get_server_timezone() sets the timezone; this should
449         # probably done here.
450         if self.options['timezone']:
451             # Prevent the timezone to be True. (The config file parsing changes
452             # the string 'True' to the boolean value True. It would be probably
453             # be better to remove that conversion.)
454             die(not isinstance(self.options['timezone'], basestring),
455                 "Invalid timezone value in configuration or environment: %r.\n"
456                 "Please fix this in your configuration." %(self.options['timezone']))
457
458             # If an explicit TZ was provided in the config, make sure it is known
459             try:
460                 import pytz
461                 pytz.timezone(self.options['timezone'])
462             except pytz.UnknownTimeZoneError:
463                 die(True, "The specified timezone (%s) is invalid" % self.options['timezone'])
464             except:
465                 # If pytz is missing, don't check the provided TZ, it will be ignored anyway.
466                 pass
467
468         if opt.pg_path:
469             self.options['pg_path'] = opt.pg_path
470
471         if self.options.get('language', False):
472             if len(self.options['language']) > 5:
473                 raise Exception('ERROR: The Lang name must take max 5 chars, Eg: -lfr_BE')
474
475         if not self.options['db_user']:
476             try:
477                 import getpass
478                 self.options['db_user'] = getpass.getuser()
479             except:
480                 self.options['db_user'] = None
481
482         die(not self.options['db_user'], 'ERROR: No user specified for the connection to the database')
483
484         if self.options['db_password']:
485             if sys.platform == 'win32' and not self.options['db_host']:
486                 self.options['db_host'] = 'localhost'
487             #if self.options['db_host']:
488             #    self._generate_pgpassfile()
489
490         if opt.save:
491             self.save()
492
493         openerp.conf.addons_paths = self.options['addons_path'].split(',')
494         if opt.server_wide_modules:
495             openerp.conf.server_wide_modules = map(lambda m: m.strip(), opt.server_wide_modules.split(','))
496         else:
497             openerp.conf.server_wide_modules = ['web','web_kanban']
498         if complete:
499             openerp.modules.module.initialize_sys_path()
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 config = configmanager()
658
659
660 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: