1 # -*- coding: utf-8 ; test-case-name: bridgedb.test.test_configure -*-
3 # This file is part of BridgeDB, a Tor bridge distribution system.
5 # :authors: please see the AUTHORS file for attributions
6 # :copyright: (c) 2013-2015, Isis Lovecruft
7 # (c) 2013-2015, Matthew Finkel
8 # (c) 2007-2015, Nick Mathewson
9 # (c) 2007-2015, The Tor Project, Inc.
10 # :license: see LICENSE for licensing information
12 """Utilities for dealing with configuration files for BridgeDB."""
17 # Used to set the SUPPORTED_TRANSPORTS:
18 from bridgedb
import strings
21 def loadConfig(configFile
=None, configCls
=None):
22 """Load configuration settings on top of the current settings.
24 All pathnames and filenames within settings in the ``configFile`` will be
25 expanded, and their expanded values will be stored in the returned
26 :class:`configuration <bridgedb.configure.Conf>` object.
30 On the strange-looking use of::
32 exec compile(open(configFile).read(), '<string>', 'exec') in dict()
36 The contents of the config file should be compiled first, and then passed
37 to ``exec()`` -- not ``execfile()`` ! -- in order to get the contents of
38 the config file to exist within the scope of the configuration dictionary.
39 Otherwise, Python *will* default_ to executing the config file directly
40 within the ``globals()`` scope.
42 Additionally, it's roughly 20-30 times faster_ to use the ``compile()``
43 builtin on a string (the contents of the file) before passing it to
44 ``exec()``, than using ``execfile()`` directly on the file.
46 .. _default: http://stackoverflow.com/q/17470193
47 .. _faster: http://lucumr.pocoo.org/2011/2/1/exec-in-python/
49 :ivar bool itsSafeToUseLogging: This is called in
50 :func:`bridgedb.main.run` before
51 :func:`bridgedb.safelog.configureLogging`. When called from
52 :func:`~bridgedb.main.run`, the **configCls** parameter is not given,
53 because that is the first time that a
54 :class:`config <bridgedb.configure.Conf>` has been created. If a
55 :class:`logging.Logger` is created in this function, then logging will
56 not be correctly configured, therefore, if the **configCls** parameter
57 is not given, then it's the first time this function has been called
58 and it is therefore *not* safe to make calls to the logging module.
59 :type configFile: :any:`str` or ``None``
60 :param configFile: If given, the filename of the config file to load.
61 :type configCls: :class:`bridgedb.configure.Conf` or ``None``
62 :param configCls: The current configuration instance, if one already
64 :returns: A new :class:`configuration <bridgedb.configure.Conf>`, with the
65 old settings as defaults, and the settings from the **configFile** (if
66 given) overriding those defaults.
68 itsSafeToUseLogging
= False
72 itsSafeToUseLogging
= True
73 oldConfig
= configCls
.__dict
__
74 configuration
.update(**oldConfig
) # Load current settings
75 logging
.info("Reloading over in-memory configurations...")
78 if (configFile
is None) and ('CONFIG_FILE' in configuration
):
79 conffile
= configuration
['CONFIG_FILE']
81 if conffile
is not None:
82 if itsSafeToUseLogging
:
83 logging
.info("Loading settings from config file: '%s'" % conffile
)
84 compiled
= compile(open(conffile
).read(), '<string>', 'exec')
85 exec(compiled
, configuration
)
87 if itsSafeToUseLogging
:
88 logging
.debug("New configuration settings:")
89 logging
.debug("\n".join(["{0} = {1}".format(key
, value
)
90 for key
, value
in configuration
.items()
91 if not key
.startswith('_')]))
93 # Create a :class:`Conf` from the settings stored within the local scope
94 # of the ``configuration`` dictionary:
95 config
= Conf(**configuration
)
97 # We want to set the updated/expanded paths for files on the ``config``,
98 # because the copy of this config, `state.config` is used later to compare
99 # with a new :class:`Conf` instance, to see if there were any changes.
101 # See :meth:`bridgedb.persistent.State.useUpdatedSettings`.
103 for attr
in ["PROXY_LIST_FILES"]:
104 setting
= getattr(config
, attr
, None)
105 if setting
is None: # pragma: no cover
106 setattr(config
, attr
, []) # If they weren't set, make them lists
108 setattr(config
, attr
, # If they were set, expand the paths:
109 [os
.path
.abspath(os
.path
.expanduser(f
)) for f
in setting
])
111 for attr
in ["DB_FILE", "DB_LOG_FILE", "MASTER_KEY_FILE", "PIDFILE",
112 "ASSIGNMENTS_FILE", "HTTPS_CERT_FILE", "HTTPS_KEY_FILE",
113 "MOAT_CERT_FILE", "MOAT_KEY_FILE",
114 "LOG_FILE", "COUNTRY_BLOCK_FILE",
115 "GIMP_CAPTCHA_DIR", "GIMP_CAPTCHA_HMAC_KEYFILE",
116 "GIMP_CAPTCHA_RSA_KEYFILE", "NO_DISTRIBUTION_FILE"]:
117 setting
= getattr(config
, attr
, None)
119 setattr(config
, attr
, setting
)
121 setattr(config
, attr
, os
.path
.abspath(os
.path
.expanduser(setting
)))
123 for attr
in ["MOAT_ROTATION_PERIOD",
124 "HTTPS_ROTATION_PERIOD",
125 "EMAIL_ROTATION_PERIOD"]:
126 setting
= getattr(config
, attr
, None) # Default to None
127 setattr(config
, attr
, setting
)
129 for attr
in ["IGNORE_NETWORKSTATUS",
131 "MOAT_CSP_REPORT_ONLY",
132 "MOAT_CSP_INCLUDE_SELF",
136 setting
= getattr(config
, attr
, True) # Default to True
137 setattr(config
, attr
, setting
)
139 for attr
in ["FORCE_PORTS", "FORCE_FLAGS", "NO_DISTRIBUTION_COUNTRIES"]:
140 setting
= getattr(config
, attr
, []) # Default to empty lists
141 setattr(config
, attr
, setting
)
143 for attr
in ["SUPPORTED_TRANSPORTS"]:
144 setting
= getattr(config
, attr
, {}) # Default to empty dicts
145 setattr(config
, attr
, setting
)
147 # Set the SUPPORTED_TRANSPORTS to populate the webserver and email options:
148 strings
._setSupportedTransports
(getattr(config
, "SUPPORTED_TRANSPORTS", {}))
149 strings
._setDefaultTransport
(getattr(config
, "DEFAULT_TRANSPORT", ""))
150 logging
.info("Currently supported transports: %s" %
151 " ".join(strings
._getSupportedTransports
()))
152 logging
.info("Default transport: %s" % strings
._getDefaultTransport
())
154 for domain
in config
.EMAIL_DOMAINS
:
155 config
.EMAIL_DOMAIN_MAP
[domain
] = domain
157 if conffile
: # Store the pathname of the config file, if one was used
158 config
.CONFIG_FILE
= os
.path
.abspath(os
.path
.expanduser(conffile
))
164 """A configuration object. Holds unvalidated attributes."""
165 def __init__(self
, **attrs
):
166 for key
, value
in attrs
.items():
167 if key
== key
.upper():
168 if not key
.startswith('__'):
169 self
.__dict
__[key
] = value