[MERGE] fixes in account_followup
[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         # Needed a few day for runbot and saas
150         group.add_option("--no-netrpc", dest="netrpc", action="store_false", my_default=False, help="disable the NETRPC protocol")
151         group.add_option("--netrpc", dest="netrpc", action="store_true", my_default=False, help="enable the NETRPC protocol")
152         parser.add_option_group(group)
153
154         # WEB
155         # TODO move to web addons after MetaOption merge
156         group = optparse.OptionGroup(parser, "Web interface Configuration")
157         group.add_option("--db-filter", dest="dbfilter", default='.*',
158                          help="Filter listed database", metavar="REGEXP")
159         parser.add_option_group(group)
160
161         # Static HTTP
162         group = optparse.OptionGroup(parser, "Static HTTP service")
163         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")
164         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/')")
165         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 '/')")
166         parser.add_option_group(group)
167
168         # Testing Group
169         group = optparse.OptionGroup(parser, "Testing Configuration")
170         group.add_option("--test-file", dest="test_file", my_default=False,
171                          help="Launch a YML test file.")
172         group.add_option("--test-report-directory", dest="test_report_directory", my_default=False,
173                          help="If set, will save sample of all reports in this directory.")
174         group.add_option("--test-enable", action="store_true", dest="test_enable",
175                          my_default=False, help="Enable YAML and unit tests.")
176         group.add_option("--test-commit", action="store_true", dest="test_commit",
177                          my_default=False, help="Commit database changes performed by YAML or XML tests.")
178         parser.add_option_group(group)
179
180         # Logging Group
181         group = optparse.OptionGroup(parser, "Logging Configuration")
182         group.add_option("--logfile", dest="logfile", help="file where the server log will be stored")
183         group.add_option("--no-logrotate", dest="logrotate", action="store_false", my_default=True, help="do not rotate the logfile")
184         group.add_option("--syslog", action="store_true", dest="syslog", my_default=False, help="Send the log to the syslog server")
185         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")')
186         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')
187         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')
188         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')
189         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')
190         # For backward-compatibility, map the old log levels to something
191         # quite close.
192         levels = ['info', 'debug_rpc', 'warn', 'test', 'critical',
193             'debug_sql', 'error', 'debug', 'debug_rpc_answer', 'notset']
194         group.add_option('--log-level', dest='log_level', type='choice', choices=levels,
195             my_default='info', help='specify the level of the logging. Accepted values: ' + str(levels) + ' (deprecated option).')
196
197         parser.add_option_group(group)
198
199         # SMTP Group
200         group = optparse.OptionGroup(parser, "SMTP Configuration")
201         group.add_option('--email-from', dest='email_from', my_default=False,
202                          help='specify the SMTP email address for sending email')
203         group.add_option('--smtp', dest='smtp_server', my_default='localhost',
204                          help='specify the SMTP server for sending email')
205         group.add_option('--smtp-port', dest='smtp_port', my_default=25,
206                          help='specify the SMTP port', type="int")
207         group.add_option('--smtp-ssl', dest='smtp_ssl', action='store_true', my_default=False,
208                          help='specify the SMTP server support SSL or not')
209         group.add_option('--smtp-user', dest='smtp_user', my_default=False,
210                          help='specify the SMTP username for sending email')
211         group.add_option('--smtp-password', dest='smtp_password', my_default=False,
212                          help='specify the SMTP password for sending email')
213         parser.add_option_group(group)
214
215         group = optparse.OptionGroup(parser, "Database related options")
216         group.add_option("-d", "--database", dest="db_name", my_default=False,
217                          help="specify the database name")
218         group.add_option("-r", "--db_user", dest="db_user", my_default=False,
219                          help="specify the database user name")
220         group.add_option("-w", "--db_password", dest="db_password", my_default=False,
221                          help="specify the database password")
222         group.add_option("--pg_path", dest="pg_path", help="specify the pg executable path")
223         group.add_option("--db_host", dest="db_host", my_default=False,
224                          help="specify the database host")
225         group.add_option("--db_port", dest="db_port", my_default=False,
226                          help="specify the database port", type="int")
227         group.add_option("--db_maxconn", dest="db_maxconn", type='int', my_default=64,
228                          help="specify the the maximum number of physical connections to posgresql")
229         group.add_option("--db-template", dest="db_template", my_default="template1",
230                          help="specify a custom database template to create a new database")
231         parser.add_option_group(group)
232
233         group = optparse.OptionGroup(parser, "Internationalisation options",
234             "Use these options to translate OpenERP to another language."
235             "See i18n section of the user manual. Option '-d' is mandatory."
236             "Option '-l' is mandatory in case of importation"
237             )
238         group.add_option('--load-language', dest="load_language",
239                          help="specifies the languages for the translations you want to be loaded")
240         group.add_option('-l', "--language", dest="language",
241                          help="specify the language of the translation file. Use it with --i18n-export or --i18n-import")
242         group.add_option("--i18n-export", dest="translate_out",
243                          help="export all sentences to be translated to a CSV file, a PO file or a TGZ archive and exit")
244         group.add_option("--i18n-import", dest="translate_in",
245                          help="import a CSV or a PO file with translations and exit. The '-l' option is required.")
246         group.add_option("--i18n-overwrite", dest="overwrite_existing_translations", action="store_true", my_default=False,
247                          help="overwrites existing translation terms on updating a module or importing a CSV or a PO file.")
248         group.add_option("--modules", dest="translate_modules",
249                          help="specify modules to export. Use in combination with --i18n-export")
250         parser.add_option_group(group)
251
252         security = optparse.OptionGroup(parser, 'Security-related options')
253         security.add_option('--no-database-list', action="store_false", dest='list_db', my_default=True,
254                             help="disable the ability to return the list of databases")
255         parser.add_option_group(security)
256
257         # Advanced options
258         group = optparse.OptionGroup(parser, "Advanced options")
259         group.add_option('--debug', dest='debug_mode', action='store_true', my_default=False, help='enable debug mode')
260         group.add_option("--stop-after-init", action="store_true", dest="stop_after_init", my_default=False,
261                           help="stop the server after its initialization")
262         group.add_option("-t", "--timezone", dest="timezone", my_default=False,
263                          help="specify reference timezone for the server (e.g. Europe/Brussels")
264         group.add_option("--osv-memory-count-limit", dest="osv_memory_count_limit", my_default=False,
265                          help="Force a limit on the maximum number of records kept in the virtual "
266                               "osv_memory tables. The default is False, which means no count-based limit.",
267                          type="int")
268         group.add_option("--osv-memory-age-limit", dest="osv_memory_age_limit", my_default=1.0,
269                          help="Force a limit on the maximum age of records kept in the virtual "
270                               "osv_memory tables. This is a decimal value expressed in hours, "
271                               "and the default is 1 hour.",
272                          type="float")
273         group.add_option("--max-cron-threads", dest="max_cron_threads", my_default=2,
274                          help="Maximum number of threads processing concurrently cron jobs (default 2).",
275                          type="int")
276         group.add_option("--unaccent", dest="unaccent", my_default=False, action="store_true",
277                          help="Use the unaccent function provided by the database when available.")
278         parser.add_option_group(group)
279
280         group = optparse.OptionGroup(parser, "Multiprocessing options")
281         # TODO sensible default for the three following limits.
282         group.add_option("--workers", dest="workers", my_default=0,
283                          help="Specify the number of workers, 0 disable prefork mode.",
284                          type="int")
285         group.add_option("--limit-memory-soft", dest="limit_memory_soft", my_default=640 * 1024 * 1024,
286                          help="Maximum allowed virtual memory per worker, when reached the worker be reset after the current request (default 671088640 aka 640MB).",
287                          type="int")
288         group.add_option("--limit-memory-hard", dest="limit_memory_hard", my_default=768 * 1024 * 1024,
289                          help="Maximum allowed virtual memory per worker, when reached, any memory allocation will fail (default 805306368 aka 768MB).",
290                          type="int")
291         group.add_option("--limit-time-cpu", dest="limit_time_cpu", my_default=60,
292                          help="Maximum allowed CPU time per request (default 60).",
293                          type="int")
294         group.add_option("--limit-time-real", dest="limit_time_real", my_default=120,
295                          help="Maximum allowed Real time per request (default 120).",
296                          type="int")
297         group.add_option("--limit-request", dest="limit_request", my_default=8192,
298                          help="Maximum number of request to be processed per worker (default 8192).",
299                          type="int")
300         parser.add_option_group(group)
301
302         # Copy all optparse options (i.e. MyOption) into self.options.
303         for group in parser.option_groups:
304             for option in group.option_list:
305                 if option.dest not in self.options:
306                     self.options[option.dest] = option.my_default
307                     self.casts[option.dest] = option
308
309         self.parse_config(None, False)
310
311     def parse_config(self, args=None, complete=True):
312         """ Parse the configuration file (if any) and the command-line
313         arguments.
314
315         This method initializes openerp.tools.config and openerp.conf (the
316         former should be removed in the furture) with library-wide
317         configuration values.
318
319         This method must be called before proper usage of this library can be
320         made.
321
322         Typical usage of this method:
323
324             openerp.tools.config.parse_config(sys.argv[1:])
325
326         :param complete: this is a hack used in __init__(), leave it to True.
327
328         """
329         if args is None:
330             args = []
331         opt, args = self.parser.parse_args(args)
332
333         def die(cond, msg):
334             if cond:
335                 self.parser.error(msg)
336
337         # Ensures no illegitimate argument is silently discarded (avoids insidious "hyphen to dash" problem)
338         die(args, "unrecognized parameters: '%s'" % " ".join(args))
339
340         die(bool(opt.syslog) and bool(opt.logfile),
341             "the syslog and logfile options are exclusive")
342
343         die(opt.translate_in and (not opt.language or not opt.db_name),
344             "the i18n-import option cannot be used without the language (-l) and the database (-d) options")
345
346         die(opt.overwrite_existing_translations and not (opt.translate_in or opt.update),
347             "the i18n-overwrite option cannot be used without the i18n-import option or without the update option")
348
349         die(opt.translate_out and (not opt.db_name),
350             "the i18n-export option cannot be used without the database (-d) option")
351
352         # Check if the config file exists (-c used, but not -s)
353         die(not opt.save and opt.config and not os.path.exists(opt.config),
354             "The config file '%s' selected with -c/--config doesn't exist, "\
355             "use -s/--save if you want to generate it"% opt.config)
356
357         # place/search the config file on Win32 near the server installation
358         # (../etc from the server)
359         # 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,
360         # else he won't be able to save the configurations, or even to start the server...
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
372         # Verify that we want to log or not, if not the output will go to stdout
373         if self.options['logfile'] in ('None', 'False'):
374             self.options['logfile'] = False
375         # the same for the pidfile
376         if self.options['pidfile'] in ('None', 'False'):
377             self.options['pidfile'] = False
378
379         # if defined dont take the configfile value even if the defined value is None
380         keys = ['xmlrpc_interface', 'xmlrpc_port', '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                 'netrpc_interface', 'netrpc_port', 'db_maxconn', 'import_partial', 'addons_path',
384                 'netrpc', '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
400         if isinstance(self.options['log_handler'], basestring):
401             self.options['log_handler'] = self.options['log_handler'].split(',')
402
403         # if defined but None take the configfile value
404         keys = [
405             'language', 'translate_out', 'translate_in', 'overwrite_existing_translations',
406             'debug_mode', 'smtp_ssl', 'load_language',
407             'stop_after_init', 'logrotate', 'without_demo', 'netrpc', 'xmlrpc', 'syslog',
408             'list_db', 'xmlrpcs', 'proxy_mode',
409             'test_file', 'test_enable', 'test_commit', 'test_report_directory',
410             'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads', 'unaccent',
411             'workers', 'limit_memory_hard', 'limit_memory_soft', 'limit_time_cpu', 'limit_time_real', 'limit_request'
412         ]
413
414         for arg in keys:
415             # Copy the command-line argument...
416             if getattr(opt, arg) is not None:
417                 self.options[arg] = getattr(opt, arg)
418             # ... or keep, but cast, the config file value.
419             elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
420                 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
421
422         self.options['root_path'] = os.path.abspath(os.path.expanduser(os.path.expandvars(os.path.dirname(openerp.__file__))))
423         if not self.options['addons_path'] or self.options['addons_path']=='None':
424             self.options['addons_path'] = os.path.join(self.options['root_path'], 'addons')
425         else:
426             self.options['addons_path'] = ",".join(
427                     os.path.abspath(os.path.expanduser(os.path.expandvars(x)))
428                       for x in self.options['addons_path'].split(','))
429
430         self.options['init'] = opt.init and dict.fromkeys(opt.init.split(','), 1) or {}
431         self.options["demo"] = not opt.without_demo and self.options['init'] or {}
432         self.options['update'] = opt.update and dict.fromkeys(opt.update.split(','), 1) or {}
433         self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all']
434         self.options['translate_modules'].sort()
435
436         # TODO checking the type of the parameters should be done for every
437         # parameters, not just the timezone.
438         # The call to get_server_timezone() sets the timezone; this should
439         # probably done here.
440         if self.options['timezone']:
441             # Prevent the timezone to be True. (The config file parsing changes
442             # the string 'True' to the boolean value True. It would be probably
443             # be better to remove that conversion.)
444             die(not isinstance(self.options['timezone'], basestring),
445                 "Invalid timezone value in configuration or environment: %r.\n"
446                 "Please fix this in your configuration." %(self.options['timezone']))
447
448             # If an explicit TZ was provided in the config, make sure it is known
449             try:
450                 import pytz
451                 pytz.timezone(self.options['timezone'])
452             except pytz.UnknownTimeZoneError:
453                 die(True, "The specified timezone (%s) is invalid" % self.options['timezone'])
454             except:
455                 # If pytz is missing, don't check the provided TZ, it will be ignored anyway.
456                 pass
457
458         if opt.pg_path:
459             self.options['pg_path'] = opt.pg_path
460
461         if self.options.get('language', False):
462             if len(self.options['language']) > 5:
463                 raise Exception('ERROR: The Lang name must take max 5 chars, Eg: -lfr_BE')
464
465         if not self.options['db_user']:
466             try:
467                 import getpass
468                 self.options['db_user'] = getpass.getuser()
469             except:
470                 self.options['db_user'] = None
471
472         die(not self.options['db_user'], 'ERROR: No user specified for the connection to the database')
473
474         if self.options['db_password']:
475             if sys.platform == 'win32' and not self.options['db_host']:
476                 self.options['db_host'] = 'localhost'
477             #if self.options['db_host']:
478             #    self._generate_pgpassfile()
479
480         if opt.save:
481             self.save()
482
483         openerp.conf.addons_paths = self.options['addons_path'].split(',')
484         if opt.server_wide_modules:
485             openerp.conf.server_wide_modules = map(lambda m: m.strip(), opt.server_wide_modules.split(','))
486         else:
487             openerp.conf.server_wide_modules = ['web','web_kanban']
488         if complete:
489             openerp.modules.module.initialize_sys_path()
490             openerp.modules.loading.open_openerp_namespace()
491
492     def _generate_pgpassfile(self):
493         """
494         Generate the pgpass file with the parameters from the command line (db_host, db_user,
495         db_password)
496
497         Used because pg_dump and pg_restore can not accept the password on the command line.
498         """
499         is_win32 = sys.platform == 'win32'
500         if is_win32:
501             filename = os.path.join(os.environ['APPDATA'], 'pgpass.conf')
502         else:
503             filename = os.path.join(os.environ['HOME'], '.pgpass')
504
505         text_to_add = "%(db_host)s:*:*:%(db_user)s:%(db_password)s" % self.options
506
507         if os.path.exists(filename):
508             content = [x.strip() for x in file(filename, 'r').readlines()]
509             if text_to_add in content:
510                 return
511
512         fp = file(filename, 'a+')
513         fp.write(text_to_add + "\n")
514         fp.close()
515
516         if is_win32:
517             try:
518                 import _winreg
519             except ImportError:
520                 _winreg = None
521             x=_winreg.ConnectRegistry(None,_winreg.HKEY_LOCAL_MACHINE)
522             y = _winreg.OpenKey(x, r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", 0,_winreg.KEY_ALL_ACCESS)
523             _winreg.SetValueEx(y,"PGPASSFILE", 0, _winreg.REG_EXPAND_SZ, filename )
524             _winreg.CloseKey(y)
525             _winreg.CloseKey(x)
526         else:
527             import stat
528             os.chmod(filename, stat.S_IRUSR + stat.S_IWUSR)
529
530     def _is_addons_path(self, path):
531         for f in os.listdir(path):
532             modpath = os.path.join(path, f)
533             if os.path.isdir(modpath):
534                 def hasfile(filename):
535                     return os.path.isfile(os.path.join(modpath, filename))
536                 if hasfile('__init__.py') and (hasfile('__openerp__.py') or hasfile('__terp__.py')):
537                     return True
538         return False
539
540     def _check_addons_path(self, option, opt, value, parser):
541         ad_paths = []
542         for path in value.split(','):
543             path = path.strip()
544             res = os.path.abspath(os.path.expanduser(path))
545             if not os.path.isdir(res):
546                 raise optparse.OptionValueError("option %s: no such directory: %r" % (opt, path))
547             if not self._is_addons_path(res):
548                 raise optparse.OptionValueError("option %s: The addons-path %r does not seem to a be a valid Addons Directory!" % (opt, path))
549             ad_paths.append(res)
550
551         setattr(parser.values, option.dest, ",".join(ad_paths))
552
553     def load(self):
554         p = ConfigParser.ConfigParser()
555         try:
556             p.read([self.rcfile])
557             for (name,value) in p.items('options'):
558                 if value=='True' or value=='true':
559                     value = True
560                 if value=='False' or value=='false':
561                     value = False
562                 self.options[name] = value
563             #parse the other sections, as well
564             for sec in p.sections():
565                 if sec == 'options':
566                     continue
567                 if not self.misc.has_key(sec):
568                     self.misc[sec]= {}
569                 for (name, value) in p.items(sec):
570                     if value=='True' or value=='true':
571                         value = True
572                     if value=='False' or value=='false':
573                         value = False
574                     self.misc[sec][name] = value
575         except IOError:
576             pass
577         except ConfigParser.NoSectionError:
578             pass
579
580     def save(self):
581         p = ConfigParser.ConfigParser()
582         loglevelnames = dict(zip(self._LOGLEVELS.values(), self._LOGLEVELS.keys()))
583         p.add_section('options')
584         for opt in sorted(self.options.keys()):
585             if opt in ('version', 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations', 'init', 'update'):
586                 continue
587             if opt in self.blacklist_for_save:
588                 continue
589             if opt in ('log_level',):
590                 p.set('options', opt, loglevelnames.get(self.options[opt], self.options[opt]))
591             else:
592                 p.set('options', opt, self.options[opt])
593
594         for sec in sorted(self.misc.keys()):
595             p.add_section(sec)
596             for opt in sorted(self.misc[sec].keys()):
597                 p.set(sec,opt,self.misc[sec][opt])
598
599         # try to create the directories and write the file
600         try:
601             rc_exists = os.path.exists(self.rcfile)
602             if not rc_exists and not os.path.exists(os.path.dirname(self.rcfile)):
603                 os.makedirs(os.path.dirname(self.rcfile))
604             try:
605                 p.write(file(self.rcfile, 'w'))
606                 if not rc_exists:
607                     os.chmod(self.rcfile, 0600)
608             except IOError:
609                 sys.stderr.write("ERROR: couldn't write the config file\n")
610
611         except OSError:
612             # what to do if impossible?
613             sys.stderr.write("ERROR: couldn't create the config directory\n")
614
615     def get(self, key, default=None):
616         return self.options.get(key, default)
617
618     def get_misc(self, sect, key, default=None):
619         return self.misc.get(sect,{}).get(key, default)
620
621     def __setitem__(self, key, value):
622         self.options[key] = value
623         if key in self.options and isinstance(self.options[key], basestring) and \
624                 key in self.casts and self.casts[key].type in optparse.Option.TYPE_CHECKER:
625             self.options[key] = optparse.Option.TYPE_CHECKER[self.casts[key].type](self.casts[key], key, self.options[key])
626
627     def __getitem__(self, key):
628         return self.options[key]
629
630 config = configmanager()
631
632
633 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: