34 |
34 |
35 |
35 |
36 from shutil import copy2 |
36 from shutil import copy2 |
37 from ConfigParser import (Error, MissingSectionHeaderError, NoOptionError, |
37 from ConfigParser import (Error, MissingSectionHeaderError, NoOptionError, |
38 NoSectionError, ParsingError, RawConfigParser) |
38 NoSectionError, ParsingError, RawConfigParser) |
39 from cStringIO import StringIO |
39 from cStringIO import StringIO# TODO: move interactive stff to cli |
40 |
40 |
41 from __main__ import os, ENCODING, ERR, get_unicode, w_std |
41 import VirtualMailManager.constants.ERROR as ERR |
42 from Exceptions import VMMConfigException |
42 |
|
43 from VirtualMailManager import ENCODING, exec_ok, get_unicode, is_dir |
|
44 from VirtualMailManager.cli import w_std# move to cli |
|
45 from VirtualMailManager.Exceptions import VMMConfigException |
43 |
46 |
44 |
47 |
45 class BadOptionError(Error): |
48 class BadOptionError(Error): |
46 """Raised when a option isn't in the format 'section.option'.""" |
49 """Raised when a option isn't in the format 'section.option'.""" |
47 pass |
50 pass |
60 option, section)) |
63 option, section)) |
61 |
64 |
62 |
65 |
63 class LazyConfig(RawConfigParser): |
66 class LazyConfig(RawConfigParser): |
64 """The **lazy** derivate of the `RawConfigParser`. |
67 """The **lazy** derivate of the `RawConfigParser`. |
65 |
68 |
66 There are two additional getters: |
69 There are two additional getters: |
67 |
70 |
68 `LazyConfig.pget()` |
71 `LazyConfig.pget()` |
69 The polymorphic getter, which returns a option's value with the |
72 The polymorphic getter, which returns a option's value with the |
70 appropriate type. |
73 appropriate type. |
71 `LazyConfig.dget()` |
74 `LazyConfig.dget()` |
72 Like `LazyConfig.pget()`, but returns the option's default, from |
75 Like `LazyConfig.pget()`, but returns the option's default, from |
125 Else one of the following exceptions will be thrown: |
128 Else one of the following exceptions will be thrown: |
126 |
129 |
127 * `BadOptionError` |
130 * `BadOptionError` |
128 * `NoSectionError` |
131 * `NoSectionError` |
129 * `NoOptionError` |
132 * `NoOptionError` |
130 """ |
133 """ |
131 sect_opt = section_option.lower().split('.') |
134 sect_opt = section_option.lower().split('.') |
132 if len(sect_opt) != 2:# do we need a regexp to check the format? |
135 if len(sect_opt) != 2:# do we need a regexp to check the format? |
133 raise BadOptionError( |
136 raise BadOptionError( |
134 _(u'Bad format: “%s” - expected: section.option') % \ |
137 _(u'Bad format: “%s” - expected: section.option') % \ |
135 get_unicode(section_option)) |
138 get_unicode(section_option)) |
156 if '__name__' in d: del d['__name__'] |
159 if '__name__' in d: del d['__name__'] |
157 return d.iteritems() |
160 return d.iteritems() |
158 |
161 |
159 def dget(self, option): |
162 def dget(self, option): |
160 """Returns the value of the `option`. |
163 """Returns the value of the `option`. |
161 |
164 |
162 If the option could not be found in the configuration file, the |
165 If the option could not be found in the configuration file, the |
163 configured default value, from ``LazyConfig._cfg`` will be |
166 configured default value, from ``LazyConfig._cfg`` will be |
164 returned. |
167 returned. |
165 |
168 |
166 Arguments: |
169 Arguments: |
167 |
170 |
168 `option` : string |
171 `option` : string |
169 the configuration option in the form |
172 the configuration option in the form |
170 "``section``\ **.**\ ``option``" |
173 "``section``\ **.**\ ``option``" |
171 |
174 |
172 Throws a `NoDefaultError`, if no default value was passed to |
175 Throws a `NoDefaultError`, if no default value was passed to |
186 section, option = self.__get_section_option(option) |
189 section, option = self.__get_section_option(option) |
187 return self._cfg[section][option].getter(section, option) |
190 return self._cfg[section][option].getter(section, option) |
188 |
191 |
189 def set(self, option, value): |
192 def set(self, option, value): |
190 """Set the value of an option. |
193 """Set the value of an option. |
191 |
194 |
192 Throws a ``ValueError`` if `value` couldn't be converted to |
195 Throws a ``ValueError`` if `value` couldn't be converted to |
193 ``LazyConfigOption.cls``""" |
196 ``LazyConfigOption.cls``""" |
194 section, option = self.__get_section_option(option) |
197 section, option = self.__get_section_option(option) |
195 val = self._cfg[section][option].cls(value) |
198 val = self._cfg[section][option].cls(value) |
196 if not self._cfg[section][option].validate is None: |
199 if not self._cfg[section][option].validate is None: |
200 RawConfigParser.set(self, section, option, val) |
203 RawConfigParser.set(self, section, option, val) |
201 self._modified = True |
204 self._modified = True |
202 |
205 |
203 def has_section(self, section): |
206 def has_section(self, section): |
204 """Checks if ``section`` is a known configuration section.""" |
207 """Checks if ``section`` is a known configuration section.""" |
205 return section.lower() in self._cfg |
208 return section.lower() in self._cfg |
206 |
209 |
207 def has_option(self, option): |
210 def has_option(self, option): |
208 """Checks if the option (section\ **.**\ option) is a known |
211 """Checks if the option (section\ **.**\ option) is a known |
209 configuration option.""" |
212 configuration option.""" |
210 try: |
213 try: |
211 self.__get_section_option(option) |
214 self.__get_section_option(option) |
212 return True |
215 return True |
213 except(BadOptionError, NoSectionError, NoOptionError): |
216 except(BadOptionError, NoSectionError, NoOptionError): |
214 return False |
217 return False |
215 |
|
216 |
218 |
217 |
219 |
218 class LazyConfigOption(object): |
220 class LazyConfigOption(object): |
219 """A simple container class for configuration settings. |
221 """A simple container class for configuration settings. |
220 |
222 |
264 |
266 |
265 def __init__(self, filename): |
267 def __init__(self, filename): |
266 """Creates a new Config instance |
268 """Creates a new Config instance |
267 |
269 |
268 Arguments: |
270 Arguments: |
269 |
271 |
270 ``filename`` |
272 ``filename`` |
271 path to the configuration file |
273 path to the configuration file |
272 """ |
274 """ |
273 LazyConfig.__init__(self) |
275 LazyConfig.__init__(self) |
274 self.__cfgFileName = filename |
276 self.__cfgFileName = filename |
288 'pop3' : LCO(bool_t, True, self.get_boolean), |
290 'pop3' : LCO(bool_t, True, self.get_boolean), |
289 'sieve': LCO(bool_t, True, self.get_boolean), |
291 'sieve': LCO(bool_t, True, self.get_boolean), |
290 'smtp' : LCO(bool_t, True, self.get_boolean), |
292 'smtp' : LCO(bool_t, True, self.get_boolean), |
291 }, |
293 }, |
292 'bin': { |
294 'bin': { |
293 'dovecotpw': LCO(str, '/usr/sbin/dovecotpw', self.get, |
295 'dovecotpw': LCO(str, '/usr/sbin/dovecotpw', self.get, exec_ok), |
294 self.exec_ok), |
296 'du': LCO(str, '/usr/bin/du', self.get, exec_ok), |
295 'du': LCO(str, '/usr/bin/du', self.get, self.exec_ok), |
297 'postconf': LCO(str, '/usr/sbin/postconf', self.get, exec_ok), |
296 'postconf': LCO(str, '/usr/sbin/postconf', self.get, |
|
297 self.exec_ok), |
|
298 }, |
298 }, |
299 'database': { |
299 'database': { |
300 'host': LCO(str, 'localhost', self.get), |
300 'host': LCO(str, 'localhost', self.get), |
301 'name': LCO(str, 'mailsys', self.get), |
301 'name': LCO(str, 'mailsys', self.get), |
302 'pass': LCO(str, None, self.get), |
302 'pass': LCO(str, None, self.get), |
311 'maildir': { |
311 'maildir': { |
312 'folders': LCO(str, 'Drafts:Sent:Templates:Trash', self.get), |
312 'folders': LCO(str, 'Drafts:Sent:Templates:Trash', self.get), |
313 'name': LCO(str, 'Maildir', self.get), |
313 'name': LCO(str, 'Maildir', self.get), |
314 }, |
314 }, |
315 'misc': { |
315 'misc': { |
316 'base_directory': LCO(str, '/srv/mail', self.get, self.is_dir), |
316 'base_directory': LCO(str, '/srv/mail', self.get, is_dir), |
317 'dovecot_version': LCO(int, 12, self.getint), |
317 'dovecot_version': LCO(int, 12, self.getint), |
318 'gid_mail': LCO(int, 8, self.getint), |
318 'gid_mail': LCO(int, 8, self.getint), |
319 'password_scheme': LCO(str, 'CRAM-MD5', self.get, |
319 'password_scheme': LCO(str, 'CRAM-MD5', self.get, |
320 self.known_scheme), |
320 self.known_scheme), |
321 'transport': LCO(str, 'dovecot:', self.get), |
321 'transport': LCO(str, 'dovecot:', self.get), |
339 def check(self): |
339 def check(self): |
340 """Performs a configuration check. |
340 """Performs a configuration check. |
341 |
341 |
342 Raises a VMMConfigException if the check fails. |
342 Raises a VMMConfigException if the check fails. |
343 """ |
343 """ |
|
344 # TODO: There are only two settings w/o defaults. |
|
345 # So there is no need for cStringIO |
344 if not self.__chkCfg(): |
346 if not self.__chkCfg(): |
345 errmsg = StringIO() |
347 errmsg = StringIO() |
346 errmsg.write(_(u'Missing options, which have no default value.\n')) |
348 errmsg.write(_(u'Missing options, which have no default value.\n')) |
347 errmsg.write(_(u'Using configuration file: %s\n') %\ |
349 errmsg.write(_(u'Using configuration file: %s\n') %\ |
348 self.__cfgFileName) |
350 self.__cfgFileName) |
354 |
356 |
355 def getsections(self): |
357 def getsections(self): |
356 """Returns an iterator object for all configuration sections.""" |
358 """Returns an iterator object for all configuration sections.""" |
357 return self._cfg.iterkeys() |
359 return self._cfg.iterkeys() |
358 |
360 |
359 def is_dir(self, path): |
|
360 """Checks if ``path`` is a directory. |
|
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): |
361 def known_scheme(self, scheme): |
386 """Converts ``scheme`` to upper case and checks if is known by |
362 """Converts ``scheme`` to upper case and checks if is known by |
387 Dovecot (listed in VirtualMailManager.SCHEMES). |
363 Dovecot (listed in VirtualMailManager.SCHEMES). |
388 |
364 |
389 Throws a `ConfigValueError` if the scheme is not listed in |
365 Throws a `ConfigValueError` if the scheme is not listed in |
390 VirtualMailManager.SCHEMES. |
366 VirtualMailManager.SCHEMES. |
391 """ |
367 """ |
392 scheme = scheme.upper() |
368 scheme = scheme.upper() |
393 # TODO: VMM.SCHEMES |
369 # TODO: VMM.SCHEMES |
402 """Interactive method for configuring all options in the given sections |
378 """Interactive method for configuring all options in the given sections |
403 |
379 |
404 Arguments: |
380 Arguments: |
405 sections -- list of strings with section names |
381 sections -- list of strings with section names |
406 """ |
382 """ |
|
383 # TODO: Derivate CliConfig from Config an move the interactive |
|
384 # stuff to CliConfig |
407 input_fmt = _(u'Enter new value for option %(option)s \ |
385 input_fmt = _(u'Enter new value for option %(option)s \ |
408 [%(current_value)s]: ') |
386 [%(current_value)s]: ') |
409 failures = 0 |
387 failures = 0 |
410 |
388 |
411 w_std(_(u'Using configuration file: %s\n') % self.__cfgFileName) |
389 w_std(_(u'Using configuration file: %s\n') % self.__cfgFileName) |
433 if self._modified: |
411 if self._modified: |
434 self.__saveChanges() |
412 self.__saveChanges() |
435 |
413 |
436 def __saveChanges(self): |
414 def __saveChanges(self): |
437 """Writes changes to the configuration file.""" |
415 """Writes changes to the configuration file.""" |
|
416 # TODO: Move interactive stuff to CliConfig |
438 copy2(self.__cfgFileName, self.__cfgFileName+'.bak') |
417 copy2(self.__cfgFileName, self.__cfgFileName+'.bak') |
439 self.__cfgFile = open(self.__cfgFileName, 'w') |
418 self.__cfgFile = open(self.__cfgFileName, 'w') |
440 self.write(self.__cfgFile) |
419 self.write(self.__cfgFile) |
441 self.__cfgFile.close() |
420 self.__cfgFile.close() |
442 |
421 |
443 def __chkCfg(self): |
422 def __chkCfg(self): |
444 """Checks all section's options for settings w/o default values. |
423 """Checks all section's options for settings w/o default values. |
445 |
424 |
446 Returns ``True`` if everything is fine, else ``False``.""" |
425 Returns ``True`` if everything is fine, else ``False``.""" |
447 errors = False |
426 errors = False |
448 for section in self._cfg.iterkeys(): |
427 for section in self._cfg.iterkeys(): |
449 missing = [] |
428 missing = [] |
450 for option, value in self._cfg[section].iteritems(): |
429 for option, value in self._cfg[section].iteritems(): |