6 VirtualMailManager.Config |
6 VirtualMailManager.Config |
7 |
7 |
8 VMM's configuration module for simplified configuration access. |
8 VMM's configuration module for simplified configuration access. |
9 """ |
9 """ |
10 |
10 |
|
11 import re |
11 |
12 |
12 from ConfigParser import \ |
13 from ConfigParser import \ |
13 Error, MissingSectionHeaderError, NoOptionError, NoSectionError, \ |
14 Error, MissingSectionHeaderError, NoOptionError, NoSectionError, \ |
14 ParsingError, RawConfigParser |
15 ParsingError, RawConfigParser |
15 from cStringIO import StringIO# TODO: move interactive stff to cli |
16 from cStringIO import StringIO# TODO: move interactive stff to cli |
16 |
17 |
17 from VirtualMailManager.common import exec_ok, get_unicode, is_dir |
18 from VirtualMailManager.common import exec_ok, get_unicode, is_dir, version_hex |
18 from VirtualMailManager.constants.ERROR import CONF_ERROR |
19 from VirtualMailManager.constants.ERROR import CONF_ERROR |
19 from VirtualMailManager.errors import ConfigError |
20 from VirtualMailManager.errors import ConfigError |
20 |
21 |
21 |
22 |
22 _ = lambda msg: msg |
23 _ = lambda msg: msg |
119 * `NoOptionError` |
120 * `NoOptionError` |
120 |
121 |
121 """ |
122 """ |
122 sect_opt = section_option.lower().split('.') |
123 sect_opt = section_option.lower().split('.') |
123 # TODO: cache it |
124 # TODO: cache it |
124 if len(sect_opt) != 2:# do we need a regexp to check the format? |
125 if len(sect_opt) != 2: # do we need a regexp to check the format? |
125 raise BadOptionError( |
126 raise BadOptionError( |
126 _(u"Bad format: '%s' - expected: section.option") % |
127 _(u"Bad format: '%s' - expected: section.option") % |
127 get_unicode(section_option)) |
128 get_unicode(section_option)) |
128 if not sect_opt[0] in self._cfg: |
129 if not sect_opt[0] in self._cfg: |
129 raise NoSectionError(sect_opt[0]) |
130 raise NoSectionError(sect_opt[0]) |
134 def items(self, section): |
135 def items(self, section): |
135 """returns an iterable that returns key, value ``tuples`` from |
136 """returns an iterable that returns key, value ``tuples`` from |
136 the given ``section``. |
137 the given ``section``. |
137 |
138 |
138 """ |
139 """ |
139 if section in self._sections:# check if the section was parsed |
140 if section in self._sections: # check if the section was parsed |
140 sect = self._sections[section] |
141 sect = self._sections[section] |
141 elif not section in self._cfg: |
142 elif not section in self._cfg: |
142 raise NoSectionError(section) |
143 raise NoSectionError(section) |
143 else: |
144 else: |
144 return ((k, self._cfg[section][k].default) \ |
145 return ((k, self._cfg[section][k].default) \ |
169 """ |
170 """ |
170 section, option = self._get_section_option(option) |
171 section, option = self._get_section_option(option) |
171 try: |
172 try: |
172 return self._cfg[section][option].getter(section, option) |
173 return self._cfg[section][option].getter(section, option) |
173 except (NoSectionError, NoOptionError): |
174 except (NoSectionError, NoOptionError): |
174 if not self._cfg[section][option].default is None:# may be False |
175 if not self._cfg[section][option].default is None: # may be False |
175 return self._cfg[section][option].default |
176 return self._cfg[section][option].default |
176 else: |
177 else: |
177 raise NoDefaultError(section, option) |
178 raise NoDefaultError(section, option) |
178 |
179 |
179 def pget(self, option): |
180 def pget(self, option): |
248 None or any method, that takes one argument, in order to |
249 None or any method, that takes one argument, in order to |
249 check the value, when `LazyConfig.set()` is called. |
250 check the value, when `LazyConfig.set()` is called. |
250 |
251 |
251 """ |
252 """ |
252 self.__cls = cls |
253 self.__cls = cls |
253 if not default is None:# enforce the type of the default value |
254 if not default is None: # enforce the type of the default value |
254 self.__default = self.__cls(default) |
255 self.__default = self.__cls(default) |
255 else: |
256 else: |
256 self.__default = default |
257 self.__default = default |
257 if not callable(getter): |
258 if not callable(getter): |
258 raise TypeError('getter has to be a callable, got a %r' % |
259 raise TypeError('getter has to be a callable, got a %r' % |
340 'folders': LCO(str, 'Drafts:Sent:Templates:Trash', self.get), |
341 'folders': LCO(str, 'Drafts:Sent:Templates:Trash', self.get), |
341 'format': LCO(str, 'maildir', self.get), |
342 'format': LCO(str, 'maildir', self.get), |
342 }, |
343 }, |
343 'misc': { |
344 'misc': { |
344 'base_directory': LCO(str, '/srv/mail', self.get, is_dir), |
345 'base_directory': LCO(str, '/srv/mail', self.get, is_dir), |
345 'dovecot_version': LCO(int, 12, self.getint), |
346 'dovecot_version': LCO(str, '1.2.11', self.hexversion, |
|
347 check_version_format), |
346 'gid_mail': LCO(int, 8, self.getint), |
348 'gid_mail': LCO(int, 8, self.getint), |
347 'password_scheme': LCO(str, 'CRAM-MD5', self.get, |
349 'password_scheme': LCO(str, 'CRAM-MD5', self.get, |
348 self.known_scheme), |
350 self.known_scheme), |
349 'transport': LCO(str, 'dovecot:', self.get), |
351 'transport': LCO(str, 'dovecot:', self.get), |
350 }, |
352 }, |
367 self._cfg_file.close() |
369 self._cfg_file.close() |
368 |
370 |
369 def check(self): |
371 def check(self): |
370 """Performs a configuration check. |
372 """Performs a configuration check. |
371 |
373 |
372 Raises a ConfigError if the check fails. |
374 Raises a ConfigError if settings w/o a default value are missed. |
373 |
375 Or a ConfigValueError if 'misc.dovecot_version' has the wrong |
|
376 format. |
374 """ |
377 """ |
375 # TODO: There are only two settings w/o defaults. |
378 # TODO: There are only two settings w/o defaults. |
376 # So there is no need for cStringIO |
379 # So there is no need for cStringIO |
377 if not self.__chk_cfg(): |
380 if not self.__chk_cfg(): |
378 errmsg = StringIO() |
381 errmsg = StringIO() |
382 for section, options in self.__missing.iteritems(): |
385 for section, options in self.__missing.iteritems(): |
383 errmsg.write(_(u'* Section: %s\n') % section) |
386 errmsg.write(_(u'* Section: %s\n') % section) |
384 for option in options: |
387 for option in options: |
385 errmsg.write((u' %s\n') % option) |
388 errmsg.write((u' %s\n') % option) |
386 raise ConfigError(errmsg.getvalue(), CONF_ERROR) |
389 raise ConfigError(errmsg.getvalue(), CONF_ERROR) |
|
390 check_version_format(self.get('misc', 'dovecot_version')) |
|
391 |
|
392 def hexversion(self, section, option): |
|
393 return version_hex(self.get(section, option)) |
387 |
394 |
388 def known_scheme(self, scheme): |
395 def known_scheme(self, scheme): |
389 """Converts `scheme` to upper case and checks if is known by |
396 """Converts `scheme` to upper case and checks if is known by |
390 Dovecot (listed in VirtualMailManager.SCHEMES). |
397 Dovecot (listed in VirtualMailManager.SCHEMES). |
391 |
398 |
421 if missing: |
428 if missing: |
422 self.__missing[section] = missing |
429 self.__missing[section] = missing |
423 return not errors |
430 return not errors |
424 |
431 |
425 |
432 |
|
433 def check_version_format(version_string): |
|
434 """Check if the *version_string* has the proper format, e.g.: '1.2.3'. |
|
435 Returns the validated version string if it has the expected format. |
|
436 Otherwise a `ConfigValueError` will be raised. |
|
437 """ |
|
438 version_re = r'^\d+\.\d+\.(?:\d+|(?:alpha|beta|rc)\d+)$' |
|
439 if not re.match(version_re, version_string): |
|
440 raise ConfigValueError(_(u"Not a valid Dovecot version: '%s'") % |
|
441 get_unicode(version_string)) |
|
442 return version_string |
|
443 |
426 del _ |
444 del _ |