1 # -*- coding: UTF-8 -*- |
1 # -*- coding: UTF-8 -*- |
2 # Copyright (c) 2007 - 2010, Pascal Volk |
2 # Copyright (c) 2007 - 2010, Pascal Volk |
3 # See COPYING for distribution information. |
3 # See COPYING for distribution information. |
4 |
4 |
5 """Configuration class for read, modify and write the |
5 """vmm's configuration module for simplified configuration access. |
6 configuration from Virtual Mail Manager. |
6 |
7 |
7 This module defines a few classes: |
|
8 |
|
9 ``LazyConfig`` |
|
10 This class provides the following additonal methods |
|
11 |
|
12 * `LazyConfig.pget()` |
|
13 polymorphic getter which returns the value with the appropriate |
|
14 type. |
|
15 * `LazyConfig.dget()` |
|
16 like *pget()*, but checks additonal for default values in |
|
17 `LazyConfig._cfg`. |
|
18 * `LazyConfig.set()` |
|
19 like `RawConfigParser.set()`, but converts the new value to the |
|
20 appropriate type/class and optional validates the new value. |
|
21 * `LazyConfig.bool_new()` |
|
22 converts data from raw_input into boolean values. |
|
23 * `LazyConfig.get_boolean()` |
|
24 like `RawConfigParser.getboolean()`, but doesn't fail on real |
|
25 `bool` values. |
|
26 |
|
27 ``Config`` |
|
28 The Config class used by vmm. |
|
29 |
|
30 ``LazyConfigOption`` |
|
31 The class for the configuration objects in the ``Config`` class' |
|
32 ``_cfg`` dictionary. |
8 """ |
33 """ |
9 |
34 |
|
35 |
10 from shutil import copy2 |
36 from shutil import copy2 |
11 from ConfigParser import ConfigParser, MissingSectionHeaderError, ParsingError |
37 from ConfigParser import (Error, MissingSectionHeaderError, NoOptionError, |
|
38 NoSectionError, ParsingError, RawConfigParser) |
12 from cStringIO import StringIO |
39 from cStringIO import StringIO |
13 |
40 |
14 from __main__ import ENCODING, ERR, w_std |
41 from __main__ import os, ENCODING, ERR, get_unicode, w_std |
15 from Exceptions import VMMConfigException |
42 from Exceptions import VMMConfigException |
16 |
43 |
17 class Config(ConfigParser): |
44 |
|
45 class BadOptionError(Error): |
|
46 """Raised when a option isn't in the format 'section.option'.""" |
|
47 pass |
|
48 |
|
49 |
|
50 class ConfigValueError(Error): |
|
51 """Raised when creating or validating of new values fails.""" |
|
52 pass |
|
53 |
|
54 |
|
55 class NoDefaultError(Error): |
|
56 """Raised when the requested option has no default value.""" |
|
57 |
|
58 def __init__(self, section, option): |
|
59 Error.__init__(self, 'Option %r in section %r has no default value' %( |
|
60 option, section)) |
|
61 |
|
62 |
|
63 class LazyConfig(RawConfigParser): |
|
64 """The **lazy** derivate of the `RawConfigParser`. |
|
65 |
|
66 There are two additional getters: |
|
67 |
|
68 `LazyConfig.pget()` |
|
69 The polymorphic getter, which returns a option's value with the |
|
70 appropriate type. |
|
71 `LazyConfig.dget()` |
|
72 Like `LazyConfig.pget()`, but returns the option's default, from |
|
73 `LazyConfig._cfg['sectionname']['optionname'].default`, if the |
|
74 option is not configured in a ini-like configuration file. |
|
75 |
|
76 |
|
77 `LazyConfig.set()` differs from ``RawConfigParser``'s ``set()`` method. |
|
78 ``LazyConfig.set()`` takes the ``section`` and ``option`` arguments |
|
79 combined to a single string in the form |
|
80 "``section``\ **.**\ ``option``". |
|
81 """ |
|
82 def __init__(self): |
|
83 RawConfigParser.__init__(self) |
|
84 self._modified = False |
|
85 self._cfg = { |
|
86 'sectionname': { |
|
87 'optionname': LazyConfigOption(int, 1, self.getint) |
|
88 } |
|
89 } |
|
90 """sample _cfg dictionary. Create your own in your derived class.""" |
|
91 |
|
92 def bool_new(self, value): |
|
93 """Converts the string `value` into a `bool` and returns it. |
|
94 |
|
95 | '1', 'on', 'yes' and 'true' will become ``True`` |
|
96 | '0', 'off', 'no' and 'false' will become ``False`` |
|
97 |
|
98 Throws a `ConfigValueError` for all other values, except ``bool``\ s. |
|
99 """ |
|
100 if isinstance(value, bool): |
|
101 return value |
|
102 if value.lower() in self._boolean_states: |
|
103 return self._boolean_states[value.lower()] |
|
104 else: |
|
105 raise ConfigValueError(_(u'Not a boolean: “%s”') % \ |
|
106 get_unicode(value)) |
|
107 |
|
108 def get_boolean(self, section, option): |
|
109 # if the setting was not written to the configuration file, it may |
|
110 # be still a boolean value - lets see |
|
111 if self._modified: |
|
112 tmp = self.get(section, option) |
|
113 if isinstance(tmp, bool): |
|
114 return tmp |
|
115 return self.getboolean(section, option) |
|
116 |
|
117 def __get_section_option(self, section_option): |
|
118 """splits ``section_option`` (section\ **.**\ option) in two parts |
|
119 and returns them as list ``[section, option]``, if: |
|
120 |
|
121 * it likes the format of ``section_option`` |
|
122 * the ``section`` is known |
|
123 * the ``option`` is known |
|
124 |
|
125 Else one of the following exceptions will be thrown: |
|
126 |
|
127 * `BadOptionError` |
|
128 * `NoSectionError` |
|
129 * `NoOptionError` |
|
130 """ |
|
131 sect_opt = section_option.lower().split('.') |
|
132 if len(sect_opt) != 2:# do we need a regexp to check the format? |
|
133 raise BadOptionError( |
|
134 _(u'Bad format: “%s” - expected: section.option') % \ |
|
135 get_unicode(section_option)) |
|
136 if not sect_opt[0] in self._cfg: |
|
137 raise NoSectionError(sect_opt[0]) |
|
138 if not sect_opt[1] in self._cfg[sect_opt[0]]: |
|
139 raise NoOptionError(sect_opt[1], sect_opt[0]) |
|
140 return sect_opt |
|
141 |
|
142 def items(self, section): |
|
143 """returns a ``list`` with key, value ``tuples`` from the given |
|
144 ``section``: ``[(key, value), …]``""" |
|
145 if section in self._sections:# check if the section was parsed |
|
146 d2 = self._sections[section] |
|
147 elif not section in self._cfg: |
|
148 raise NoSectionError(section) |
|
149 else: |
|
150 return ((k, self._cfg[section][k].default) \ |
|
151 for k in self._cfg[section].iterkeys()) |
|
152 # still here? Get defaults and merge defaults with configured setting |
|
153 d = dict((k, self._cfg[section][k].default) \ |
|
154 for k in self._cfg[section].iterkeys()) |
|
155 d.update(d2) |
|
156 if '__name__' in d: del d['__name__'] |
|
157 return d.iteritems() |
|
158 |
|
159 def dget(self, option): |
|
160 """Returns the value of the `option`. |
|
161 |
|
162 If the option could not be found in the configuration file, the |
|
163 configured default value, from ``LazyConfig._cfg`` will be |
|
164 returned. |
|
165 |
|
166 Arguments: |
|
167 |
|
168 `option` : string |
|
169 the configuration option in the form |
|
170 "``section``\ **.**\ ``option``" |
|
171 |
|
172 Throws a `NoDefaultError`, if no default value was passed to |
|
173 `LazyConfigOption.__init__()` for the `option`. |
|
174 """ |
|
175 section, option = self.__get_section_option(option) |
|
176 try: |
|
177 return self._cfg[section][option].getter(section, option) |
|
178 except (NoSectionError, NoOptionError): |
|
179 if not self._cfg[section][option].default is None: |
|
180 return self._cfg[section][option].default |
|
181 else: |
|
182 raise NoDefaultError(section, option) |
|
183 |
|
184 def pget(self, option): |
|
185 """Returns the value of the `option`.""" |
|
186 section, option = self.__get_section_option(option) |
|
187 return self._cfg[section][option].getter(section, option) |
|
188 |
|
189 def set(self, option, value): |
|
190 """Set the value of an option. |
|
191 |
|
192 Throws a ``ValueError`` if `value` couldn't be converted to |
|
193 ``LazyConfigOption.cls``""" |
|
194 section, option = self.__get_section_option(option) |
|
195 val = self._cfg[section][option].cls(value) |
|
196 if not self._cfg[section][option].validate is None: |
|
197 val = self._cfg[section][option].validate(val) |
|
198 if not RawConfigParser.has_section(self, section): |
|
199 self.add_section(section) |
|
200 RawConfigParser.set(self, section, option, val) |
|
201 self._modified = True |
|
202 |
|
203 def has_section(self, section): |
|
204 """Checks if ``section`` is a known configuration section.""" |
|
205 return section.lower() in self._cfg |
|
206 |
|
207 def has_option(self, option): |
|
208 """Checks if the option (section\ **.**\ option) is a known |
|
209 configuration option.""" |
|
210 try: |
|
211 self.__get_section_option(option) |
|
212 return True |
|
213 except(BadOptionError, NoSectionError, NoOptionError): |
|
214 return False |
|
215 |
|
216 |
|
217 |
|
218 class LazyConfigOption(object): |
|
219 """A simple container class for configuration settings. |
|
220 |
|
221 ``LazyConfigOption`` instances are required by `LazyConfig` instances, |
|
222 and instances of classes derived from ``LazyConfig``, like the |
|
223 `Config` class. |
|
224 """ |
|
225 __slots__ = ('cls', 'default', 'getter', 'validate') |
|
226 |
|
227 def __init__(self, cls, default, getter, validate=None): |
|
228 """Creates a new ``LazyConfigOption`` instance. |
|
229 |
|
230 Arguments: |
|
231 |
|
232 ``cls`` : type |
|
233 The class/type of the option's value |
|
234 ``default`` |
|
235 Default value of the option. Use ``None`` if the option should |
|
236 not have a default value. |
|
237 ``getter`` : callable |
|
238 A method's name of `RawConfigParser` and derived classes, to |
|
239 get a option's value, e.g. `self.getint`. |
|
240 ``validate`` : NoneType or a callable |
|
241 None or any method, that takes one argument, in order to check |
|
242 the value, when `LazyConfig.set()` is called. |
|
243 """ |
|
244 self.cls = cls |
|
245 """The class of the option's value e.g. `str`, `unicode` or `bool`""" |
|
246 self.default = default |
|
247 """The option's default value, may be ``None``""" |
|
248 if callable(getter): |
|
249 self.getter = getter |
|
250 """The getter method to get the option's value""" |
|
251 else: |
|
252 raise TypeError('getter has to be a callable, got a %r'\ |
|
253 % getter.__class__.__name__) |
|
254 if validate is None or callable(validate): |
|
255 self.validate = validate |
|
256 """A method to validate the value""" |
|
257 else: |
|
258 raise TypeError('validate has to be callable or None, got a %r'\ |
|
259 % validate.__class__.__name__) |
|
260 |
|
261 |
|
262 class Config(LazyConfig): |
18 """This class is for reading and modifying vmm's configuration file.""" |
263 """This class is for reading and modifying vmm's configuration file.""" |
19 |
264 |
20 def __init__(self, filename): |
265 def __init__(self, filename): |
21 """Creates a new Config instance |
266 """Creates a new Config instance |
22 |
267 |
23 Arguments: |
268 Arguments: |
24 filename -- path to the configuration file |
269 |
25 """ |
270 ``filename`` |
26 ConfigParser.__init__(self) |
271 path to the configuration file |
|
272 """ |
|
273 LazyConfig.__init__(self) |
27 self.__cfgFileName = filename |
274 self.__cfgFileName = filename |
28 self.__cfgFile = None |
275 self.__cfgFile = None |
29 self.__VMMsections = ('account', 'bin', 'database', 'domain', |
|
30 'maildir', 'misc', 'config') |
|
31 self.__changes = False |
|
32 self.__missing = {} |
276 self.__missing = {} |
33 self.__dbopts = [ |
277 |
34 ['host', 'localhot'], |
278 LCO = LazyConfigOption |
35 ['user', 'vmm'], |
279 bool_t = self.bool_new |
36 ['pass', 'your secret password'], |
280 self._cfg = { |
37 ['name', 'mailsys'] |
281 'account': { |
38 ] |
282 'delete_directory': LCO(bool_t, False, self.get_boolean), |
39 self.__mdopts = [ |
283 'directory_mode': LCO(int, 448, self.getint), |
40 ['name', 'Maildir'], |
284 'disk_usage': LCO(bool_t, False, self.get_boolean), |
41 ['folders', 'Drafts:Sent:Templates:Trash'], |
285 'password_length': LCO(int, 8, self.getint), |
42 ] |
286 'random_password': LCO(bool_t, False, self.get_boolean), |
43 self.__accountopts = [ |
287 'imap' : LCO(bool_t, True, self.get_boolean), |
44 ['delete_directory', 'false'], |
288 'pop3' : LCO(bool_t, True, self.get_boolean), |
45 ['directory_mode', 448], |
289 'sieve': LCO(bool_t, True, self.get_boolean), |
46 ['disk_usage', 'false'], |
290 'smtp' : LCO(bool_t, True, self.get_boolean), |
47 ['password_length', 8], |
291 }, |
48 ['random_password', 'false'], |
292 'bin': { |
49 ['smtp', 'true'], |
293 'dovecotpw': LCO(str, '/usr/sbin/dovecotpw', self.get, |
50 ['pop3', 'true'], |
294 self.exec_ok), |
51 ['imap', 'true'], |
295 'du': LCO(str, '/usr/bin/du', self.get, self.exec_ok), |
52 ['sieve', 'true'] |
296 'postconf': LCO(str, '/usr/sbin/postconf', self.get, |
53 ] |
297 self.exec_ok), |
54 self.__domdopts = [ |
298 }, |
55 ['auto_postmaster', 'true'], |
299 'database': { |
56 ['delete_directory', 'false'], |
300 'host': LCO(str, 'localhost', self.get), |
57 ['directory_mode', 504], |
301 'name': LCO(str, 'mailsys', self.get), |
58 ['force_deletion', 'false'], |
302 'pass': LCO(str, None, self.get), |
59 ] |
303 'user': LCO(str, None, self.get), |
60 self.__binopts = [ |
304 }, |
61 ['dovecotpw', '/usr/sbin/dovecotpw'], |
305 'domain': { |
62 ['du', '/usr/bin/du'], |
306 'auto_postmaster': LCO(bool_t, True, self.get_boolean), |
63 ['postconf', '/usr/sbin/postconf'] |
307 'delete_directory': LCO(bool_t, False, self.get_boolean), |
64 ] |
308 'directory_mode': LCO(int, 504, self.getint), |
65 self.__miscopts = [ |
309 'force_deletion': LCO(bool_t, False, self.get_boolean), |
66 ['base_directory', '/srv/mail'], |
310 }, |
67 ['dovecot_version', '11'], |
311 'maildir': { |
68 ['gid_mail', 8], |
312 'folders': LCO(str, 'Drafts:Sent:Templates:Trash', self.get), |
69 ['password_scheme', 'PLAIN'], |
313 'name': LCO(str, 'Maildir', self.get), |
70 ['transport', 'dovecot:'], |
314 }, |
71 ] |
315 'misc': { |
|
316 'base_directory': LCO(str, '/srv/mail', self.get, self.is_dir), |
|
317 'dovecot_version': LCO(int, 12, self.getint), |
|
318 'gid_mail': LCO(int, 8, self.getint), |
|
319 'password_scheme': LCO(str, 'CRAM-MD5', self.get, |
|
320 self.known_scheme), |
|
321 'transport': LCO(str, 'dovecot:', self.get), |
|
322 }, |
|
323 'config': {'done': LCO(bool_t, False, self.get_boolean)} |
|
324 } |
72 |
325 |
73 def load(self): |
326 def load(self): |
74 """Loads the configuration, read only. |
327 """Loads the configuration, read only. |
75 |
328 |
76 Raises a VMMConfigException if the configuration syntax is invalid. |
329 Raises a VMMConfigException if the configuration syntax is invalid. |
77 """ |
330 """ |
78 try: |
331 try: |
79 self.__cfgFile = file(self.__cfgFileName, 'r') |
332 self.__cfgFile = open(self.__cfgFileName, 'r') |
80 self.readfp(self.__cfgFile) |
333 self.readfp(self.__cfgFile) |
81 except (MissingSectionHeaderError, ParsingError), e: |
334 except (MissingSectionHeaderError, ParsingError), e: |
82 self.__cfgFile.close() |
335 self.__cfgFile.close() |
83 raise VMMConfigException(str(e), ERR.CONF_ERROR) |
336 raise VMMConfigException(str(e), ERR.CONF_ERROR) |
84 self.__cfgFile.close() |
337 self.__cfgFile.close() |
86 def check(self): |
339 def check(self): |
87 """Performs a configuration check. |
340 """Performs a configuration check. |
88 |
341 |
89 Raises a VMMConfigException if the check fails. |
342 Raises a VMMConfigException if the check fails. |
90 """ |
343 """ |
91 if not self.__chkSections(): |
344 if not self.__chkCfg(): |
92 errmsg = StringIO() |
345 errmsg = StringIO() |
93 errmsg.write(_("Using configuration file: %s\n") %\ |
346 errmsg.write(_(u'Missing options, which have no default value.\n')) |
94 self.__cfgFileName) |
347 errmsg.write(_(u'Using configuration file: %s\n') %\ |
95 for k,v in self.__missing.items(): |
348 self.__cfgFileName) |
96 if v[0] is True: |
349 for section, options in self.__missing.iteritems(): |
97 errmsg.write(_(u"missing section: %s\n") % k) |
350 errmsg.write(_(u'* Section: %s\n') % section) |
98 else: |
351 for option in options: |
99 errmsg.write(_(u"missing options in section %s:\n") % k) |
352 errmsg.write((u' %s\n') % option) |
100 for o in v: |
|
101 errmsg.write(" * %s\n" % o) |
|
102 raise VMMConfigException(errmsg.getvalue(), ERR.CONF_ERROR) |
353 raise VMMConfigException(errmsg.getvalue(), ERR.CONF_ERROR) |
103 |
354 |
104 def getsections(self): |
355 def getsections(self): |
105 """Return a list with all configurable sections.""" |
356 """Returns a generator object for all configurable sections.""" |
106 return self.__VMMsections[:-1] |
357 return (s for s in self._cfg.iterkeys() if s != 'config') |
107 |
358 |
108 def get(self, section, option, raw=False, vars=None): |
359 def is_dir(self, path): |
109 return unicode(ConfigParser.get(self, section, option, raw, vars), |
360 """Checks if ``path`` is a directory. |
110 ENCODING, 'replace') |
361 |
|
362 Throws a `ConfigValueError` if ``path`` is not a directory. |
|
363 """ |
|
364 path = self.__expand_path(path) |
|
365 if not os.path.isdir(path): |
|
366 raise ConfigValueError(_(u'“%s” is not a directory') % \ |
|
367 get_unicode(path)) |
|
368 return path |
|
369 |
|
370 def exec_ok(self, binary): |
|
371 """Checks if the ``binary`` exists and if it is executable. |
|
372 |
|
373 Throws a `ConfigValueError` if the ``binary`` isn't a file or is |
|
374 not executable. |
|
375 """ |
|
376 binary = self.__expand_path(binary) |
|
377 if not os.path.isfile(binary): |
|
378 raise ConfigValueError(_(u'“%s” is not a file') % \ |
|
379 get_unicode(binary)) |
|
380 if not os.access(binary, os.X_OK): |
|
381 raise ConfigValueError(_(u'File is not executable: “%s”') % \ |
|
382 get_unicode(binary)) |
|
383 return binary |
|
384 |
|
385 def known_scheme(self, scheme): |
|
386 """Converts ``scheme`` to upper case and checks if is known by |
|
387 Dovecot (listed in VirtualMailManager.SCHEMES). |
|
388 |
|
389 Throws a `ConfigValueError` if the scheme is not listed in |
|
390 VirtualMailManager.SCHEMES. |
|
391 """ |
|
392 scheme = scheme.upper() |
|
393 # TODO: VMM.SCHEMES |
|
394 |
|
395 def unicode(self, section, option): |
|
396 """Returns the value of the ``option`` from ``section``, converted |
|
397 to Unicode. |
|
398 """ |
|
399 return get_unicode(self.get(section, option)) |
111 |
400 |
112 def configure(self, sections): |
401 def configure(self, sections): |
113 """Interactive method for configuring all options in the given sections |
402 """Interactive method for configuring all options in the given sections |
114 |
403 |
115 Arguments: |
404 Arguments: |
116 sections -- list of strings with section names |
405 sections -- list of strings with section names |
117 """ |
406 """ |
118 if not isinstance(sections, list): |
407 input_fmt = _(u'Enter new value for option %(option)s \ |
119 raise TypeError("Argument 'sections' is not a list.") |
408 [%(current_value)s]: ') |
120 # if [config] done = false (default at 1st run), |
409 failures = 0 |
|
410 |
|
411 # if config.done == false (default at 1st run), |
121 # then set changes true |
412 # then set changes true |
122 try: |
413 if not self.dget('config.done'): |
123 if not self.getboolean('config', 'done'): |
414 self._modified = True |
124 self.__changes = True |
|
125 except ValueError: |
|
126 self.set('config', 'done', 'False') |
|
127 self.__changes = True |
|
128 w_std(_(u'Using configuration file: %s\n') % self.__cfgFileName) |
415 w_std(_(u'Using configuration file: %s\n') % self.__cfgFileName) |
129 for s in sections: |
416 for s in sections: |
130 if s != 'config': |
417 w_std(_(u'* Config section: “%s”') % s ) |
131 w_std(_(u'* Config section: “%s”') % s ) |
|
132 for opt, val in self.items(s): |
418 for opt, val in self.items(s): |
133 newval = raw_input( |
419 failures = 0 |
134 _('Enter new value for option %(opt)s [%(val)s]: ').encode( |
420 while True: |
135 ENCODING, 'replace') % {'opt': opt, 'val': val}) |
421 newval = raw_input(input_fmt.encode(ENCODING,'replace') %{ |
136 if newval and newval != val: |
422 'option': opt, 'current_value': val}) |
137 self.set(s, opt, newval) |
423 if newval and newval != val: |
138 self.__changes = True |
424 try: |
|
425 self.set('%s.%s' % (s, opt), newval) |
|
426 break |
|
427 except (ValueError, ConfigValueError), e: |
|
428 w_std(_(u'Warning: %s') % e) |
|
429 failures += 1 |
|
430 if failures > 2: |
|
431 raise VMMConfigException( |
|
432 _(u'Too many failures - try again later.'), |
|
433 ERR.VMM_TOO_MANY_FAILURES) |
|
434 else: |
|
435 break |
139 print |
436 print |
140 if self.__changes: |
437 if self._modified: |
141 self.__saveChanges() |
438 self.__saveChanges() |
142 |
439 |
143 def __saveChanges(self): |
440 def __saveChanges(self): |
144 """Writes changes to the configuration file.""" |
441 """Writes changes to the configuration file.""" |
145 self.set('config', 'done', 'true') |
442 self.set('config.done', True) |
146 copy2(self.__cfgFileName, self.__cfgFileName+'.bak') |
443 copy2(self.__cfgFileName, self.__cfgFileName+'.bak') |
147 self.__cfgFile = file(self.__cfgFileName, 'w') |
444 self.__cfgFile = open(self.__cfgFileName, 'w') |
148 self.write(self.__cfgFile) |
445 self.write(self.__cfgFile) |
149 self.__cfgFile.close() |
446 self.__cfgFile.close() |
150 |
447 |
151 def __chkSections(self): |
448 def __chkCfg(self): |
152 """Checks if all configuration sections are existing.""" |
449 """Checks all section's options for settings w/o default values. |
|
450 |
|
451 Returns ``True`` if everything is fine, else ``False``.""" |
153 errors = False |
452 errors = False |
154 for s in self.__VMMsections: |
453 for section in self._cfg.iterkeys(): |
155 if not self.has_section(s): |
454 missing = [] |
156 self.__missing[s] = [True] |
455 for option, value in self._cfg[section].iteritems(): |
157 errors = True |
456 if (value.default is None |
158 elif not self.__chkOptions(s): |
457 and not RawConfigParser.has_option(self, section, option)): |
159 errors = True |
458 missing.append(option) |
|
459 errors = True |
|
460 if len(missing): |
|
461 self.__missing[section] = missing |
160 return not errors |
462 return not errors |
161 |
463 |
162 def __chkOptions(self, section): |
464 def __expand_path(self, path): |
163 """Checks if all configuration options in section are existing. |
465 """Expands paths, starting with ``.`` or ``~``, to an absolute path.""" |
164 |
466 if path.startswith('.'): |
165 Arguments: |
467 return os.path.abspath(path) |
166 section -- the section to be checked |
468 if path.startswith('~'): |
167 """ |
469 return os.path.expanduser(path) |
168 retval = True |
470 return path |
169 missing = [] |
|
170 if section == 'database': |
|
171 opts = self.__dbopts |
|
172 elif section == 'maildir': |
|
173 opts = self.__mdopts |
|
174 elif section == 'account': |
|
175 opts = self.__accountopts |
|
176 elif section == 'domain': |
|
177 opts = self.__domdopts |
|
178 elif section == 'bin': |
|
179 opts = self.__binopts |
|
180 elif section == 'misc': |
|
181 opts = self.__miscopts |
|
182 elif section == 'config': |
|
183 opts = [['done', 'false']] |
|
184 for o, v in opts: |
|
185 if not self.has_option(section, o): |
|
186 missing.append(o) |
|
187 retval = False |
|
188 if len(missing): |
|
189 self.__missing[section] = missing |
|
190 return retval |
|