1 # -*- coding: UTF-8 -*- |
|
2 # Copyright (c) 2007 - 2010, Pascal Volk |
|
3 # See COPYING for distribution information. |
|
4 |
|
5 """Configuration class for read, modify and write the |
|
6 configuration from Virtual Mail Manager. |
|
7 |
|
8 """ |
|
9 |
|
10 from shutil import copy2 |
|
11 from ConfigParser import ConfigParser, MissingSectionHeaderError, ParsingError |
|
12 from cStringIO import StringIO |
|
13 |
|
14 from __main__ import ENCODING, ERR, w_std |
|
15 from Exceptions import VMMConfigException |
|
16 |
|
17 class Config(ConfigParser): |
|
18 """This class is for reading and modifying vmm's configuration file.""" |
|
19 |
|
20 def __init__(self, filename): |
|
21 """Creates a new Config instance |
|
22 |
|
23 Arguments: |
|
24 filename -- path to the configuration file |
|
25 """ |
|
26 ConfigParser.__init__(self) |
|
27 self.__cfgFileName = filename |
|
28 self.__cfgFile = None |
|
29 self.__VMMsections = ['database', 'maildir', 'services', 'domdir', |
|
30 'bin', 'misc', 'config'] |
|
31 self.__changes = False |
|
32 self.__missing = {} |
|
33 self.__dbopts = [ |
|
34 ['host', 'localhot'], |
|
35 ['user', 'vmm'], |
|
36 ['pass', 'your secret password'], |
|
37 ['name', 'mailsys'] |
|
38 ] |
|
39 self.__mdopts = [ |
|
40 ['name', 'Maildir'], |
|
41 ['folders', 'Drafts:Sent:Templates:Trash'], |
|
42 ['mode', 448], |
|
43 ['diskusage', 'false'], |
|
44 ['delete', 'false'] |
|
45 ] |
|
46 self.__serviceopts = [ |
|
47 ['smtp', 'true'], |
|
48 ['pop3', 'true'], |
|
49 ['imap', 'true'], |
|
50 ['sieve', 'true'] |
|
51 ] |
|
52 self.__domdopts = [ |
|
53 ['base', '/srv/mail'], |
|
54 ['mode', 504], |
|
55 ['delete', 'false'] |
|
56 ] |
|
57 self.__binopts = [ |
|
58 ['dovecotpw', '/usr/sbin/dovecotpw'], |
|
59 ['du', '/usr/bin/du'], |
|
60 ['postconf', '/usr/sbin/postconf'] |
|
61 ] |
|
62 self.__miscopts = [ |
|
63 ['passwdscheme', 'PLAIN'], |
|
64 ['gid_mail', 8], |
|
65 ['forcedel', 'false'], |
|
66 ['transport', 'dovecot:'], |
|
67 ['dovecotvers', '11'] |
|
68 ] |
|
69 |
|
70 def load(self): |
|
71 """Loads the configuration, read only. |
|
72 |
|
73 Raises a VMMConfigException if the configuration syntax is invalid. |
|
74 """ |
|
75 try: |
|
76 self.__cfgFile = file(self.__cfgFileName, 'r') |
|
77 self.readfp(self.__cfgFile) |
|
78 except (MissingSectionHeaderError, ParsingError), e: |
|
79 self.__cfgFile.close() |
|
80 raise VMMConfigException(str(e), ERR.CONF_ERROR) |
|
81 self.__cfgFile.close() |
|
82 |
|
83 def check(self): |
|
84 """Performs a configuration check. |
|
85 |
|
86 Raises a VMMConfigException if the check fails. |
|
87 """ |
|
88 if not self.__chkSections(): |
|
89 errmsg = StringIO() |
|
90 errmsg.write(_("Using configuration file: %s\n") %\ |
|
91 self.__cfgFileName) |
|
92 for k,v in self.__missing.items(): |
|
93 if v[0] is True: |
|
94 errmsg.write(_(u"missing section: %s\n") % k) |
|
95 else: |
|
96 errmsg.write(_(u"missing options in section %s:\n") % k) |
|
97 for o in v: |
|
98 errmsg.write(" * %s\n" % o) |
|
99 raise VMMConfigException(errmsg.getvalue(), ERR.CONF_ERROR) |
|
100 |
|
101 def getsections(self): |
|
102 """Return a list with all configurable sections.""" |
|
103 return self.__VMMsections[:-1] |
|
104 |
|
105 def get(self, section, option, raw=False, vars=None): |
|
106 return unicode(ConfigParser.get(self, section, option, raw, vars), |
|
107 ENCODING, 'replace') |
|
108 |
|
109 def configure(self, sections): |
|
110 """Interactive method for configuring all options in the given sections |
|
111 |
|
112 Arguments: |
|
113 sections -- list of strings with section names |
|
114 """ |
|
115 if not isinstance(sections, list): |
|
116 raise TypeError("Argument 'sections' is not a list.") |
|
117 # if [config] done = false (default at 1st run), |
|
118 # then set changes true |
|
119 try: |
|
120 if not self.getboolean('config', 'done'): |
|
121 self.__changes = True |
|
122 except ValueError: |
|
123 self.set('config', 'done', 'False') |
|
124 self.__changes = True |
|
125 w_std(_(u'Using configuration file: %s\n') % self.__cfgFileName) |
|
126 for s in sections: |
|
127 if s != 'config': |
|
128 w_std(_(u'* Config section: ā%sā') % s ) |
|
129 for opt, val in self.items(s): |
|
130 newval = raw_input( |
|
131 _('Enter new value for option %(opt)s [%(val)s]: ').encode( |
|
132 ENCODING, 'replace') % {'opt': opt, 'val': val}) |
|
133 if newval and newval != val: |
|
134 self.set(s, opt, newval) |
|
135 self.__changes = True |
|
136 print |
|
137 if self.__changes: |
|
138 self.__saveChanges() |
|
139 |
|
140 def __saveChanges(self): |
|
141 """Writes changes to the configuration file.""" |
|
142 self.set('config', 'done', 'true') |
|
143 copy2(self.__cfgFileName, self.__cfgFileName+'.bak') |
|
144 self.__cfgFile = file(self.__cfgFileName, 'w') |
|
145 self.write(self.__cfgFile) |
|
146 self.__cfgFile.close() |
|
147 |
|
148 def __chkSections(self): |
|
149 """Checks if all configuration sections are existing.""" |
|
150 errors = False |
|
151 for s in self.__VMMsections: |
|
152 if not self.has_section(s): |
|
153 self.__missing[s] = [True] |
|
154 errors = True |
|
155 elif not self.__chkOptions(s): |
|
156 errors = True |
|
157 return not errors |
|
158 |
|
159 def __chkOptions(self, section): |
|
160 """Checks if all configuration options in section are existing. |
|
161 |
|
162 Arguments: |
|
163 section -- the section to be checked |
|
164 """ |
|
165 retval = True |
|
166 missing = [] |
|
167 if section == 'database': |
|
168 opts = self.__dbopts |
|
169 elif section == 'maildir': |
|
170 opts = self.__mdopts |
|
171 elif section == 'services': |
|
172 opts = self.__serviceopts |
|
173 elif section == 'domdir': |
|
174 opts = self.__domdopts |
|
175 elif section == 'bin': |
|
176 opts = self.__binopts |
|
177 elif section == 'misc': |
|
178 opts = self.__miscopts |
|
179 elif section == 'config': |
|
180 opts = [['done', 'false']] |
|
181 for o, v in opts: |
|
182 if not self.has_option(section, o): |
|
183 missing.append(o) |
|
184 retval = False |
|
185 if len(missing): |
|
186 self.__missing[section] = missing |
|
187 return retval |
|