14 |
14 |
15 from VirtualMailManager.constants import \ |
15 from VirtualMailManager.constants import \ |
16 ACCOUNT_AND_ALIAS_PRESENT, DOMAIN_ALIAS_EXISTS, DOMAIN_EXISTS, \ |
16 ACCOUNT_AND_ALIAS_PRESENT, DOMAIN_ALIAS_EXISTS, DOMAIN_EXISTS, \ |
17 DOMAIN_INVALID, DOMAIN_TOO_LONG, NO_SUCH_DOMAIN |
17 DOMAIN_INVALID, DOMAIN_TOO_LONG, NO_SUCH_DOMAIN |
18 from VirtualMailManager.errors import DomainError as DomErr |
18 from VirtualMailManager.errors import DomainError as DomErr |
19 from VirtualMailManager.pycompat import any |
19 from VirtualMailManager.pycompat import all, any |
|
20 from VirtualMailManager.quotalimit import QuotaLimit |
20 from VirtualMailManager.transport import Transport |
21 from VirtualMailManager.transport import Transport |
21 |
22 |
22 |
23 |
23 MAILDIR_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz' |
24 MAILDIR_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz' |
24 RE_DOMAIN = re.compile(r"^(?:[a-z0-9-]{1,63}\.){1,}[a-z]{2,6}$") |
25 RE_DOMAIN = re.compile(r"^(?:[a-z0-9-]{1,63}\.){1,}[a-z]{2,6}$") |
25 _ = lambda msg: msg |
26 _ = lambda msg: msg |
26 |
27 |
27 |
28 |
28 class Domain(object): |
29 class Domain(object): |
29 """Class to manage e-mail domains.""" |
30 """Class to manage e-mail domains.""" |
30 __slots__ = ('_directory', '_gid', '_name', '_transport', '_dbh', '_new') |
31 __slots__ = ('_directory', '_gid', '_name', '_qlimit', '_transport', |
|
32 '_dbh', '_new') |
31 |
33 |
32 def __init__(self, dbh, domainname): |
34 def __init__(self, dbh, domainname): |
33 """Creates a new Domain instance. |
35 """Creates a new Domain instance. |
34 |
36 |
35 Loads all relevant data from the database, if the domain could be |
37 Loads all relevant data from the database, if the domain could be |
60 |
63 |
61 Raises a DomainError if Domain._name isn't the primary name of the |
64 Raises a DomainError if Domain._name isn't the primary name of the |
62 domain. |
65 domain. |
63 """ |
66 """ |
64 dbc = self._dbh.cursor() |
67 dbc = self._dbh.cursor() |
65 dbc.execute('SELECT dd.gid, tid, domaindir, is_primary FROM ' |
68 dbc.execute('SELECT dd.gid, qid, tid, domaindir, is_primary FROM ' |
66 'domain_data dd, domain_name dn WHERE domainname = %s AND ' |
69 'domain_data dd, domain_name dn WHERE domainname = %s AND ' |
67 'dn.gid = dd.gid', (self._name,)) |
70 'dn.gid = dd.gid', (self._name,)) |
68 result = dbc.fetchone() |
71 result = dbc.fetchone() |
69 dbc.close() |
72 dbc.close() |
70 if result: |
73 if result: |
71 if not result[3]: |
74 if not result[4]: |
72 raise DomErr(_(u"The domain '%s' is an alias domain.") % |
75 raise DomErr(_(u"The domain '%s' is an alias domain.") % |
73 self._name, DOMAIN_ALIAS_EXISTS) |
76 self._name, DOMAIN_ALIAS_EXISTS) |
74 self._gid, self._directory = result[0], result[2] |
77 self._gid, self._directory = result[0], result[3] |
75 self._transport = Transport(self._dbh, tid=result[1]) |
78 self._qlimit = QuotaLimit(self._dbh, qid=result[1]) |
|
79 self._transport = Transport(self._dbh, tid=result[2]) |
76 self._new = False |
80 self._new = False |
77 |
81 |
78 def _set_gid(self): |
82 def _set_gid(self): |
79 """Sets the ID of the domain - if not set yet.""" |
83 """Sets the ID of the domain - if not set yet.""" |
80 assert self._gid == 0 |
84 assert self._gid == 0 |
107 database.""" |
111 database.""" |
108 if self._new: |
112 if self._new: |
109 raise DomErr(_(u"The domain '%s' doesn't exist.") % self._name, |
113 raise DomErr(_(u"The domain '%s' doesn't exist.") % self._name, |
110 NO_SUCH_DOMAIN) |
114 NO_SUCH_DOMAIN) |
111 |
115 |
|
116 def _update_tables(self, column, value, force=False): |
|
117 """Update various columns in the domain_data table. When *force* is |
|
118 `True` also the corresponding column in the users table will be |
|
119 updated. |
|
120 |
|
121 Arguments: |
|
122 |
|
123 `column` : basestring |
|
124 Name of the table column. Currently: qid and tid |
|
125 `value` : long |
|
126 The referenced key |
|
127 `force` : bool |
|
128 enforce the new setting also for existing users. Default: `False` |
|
129 """ |
|
130 if column not in ('qid', 'tid'): |
|
131 raise ValueError('Unknown column: %r' % column) |
|
132 dbc = self._dbh.cursor() |
|
133 dbc.execute('UPDATE domain_data SET %s = %%s WHERE gid = %%s' % column, |
|
134 (value, self._gid)) |
|
135 if dbc.rowcount > 0: |
|
136 self._dbh.commit() |
|
137 if force: |
|
138 dbc.execute('UPDATE users SET %s = %%s WHERE gid = %%s' % column, |
|
139 (value, self._gid)) |
|
140 if dbc.rowcount > 0: |
|
141 self._dbh.commit() |
|
142 dbc.close() |
|
143 |
112 @property |
144 @property |
113 def gid(self): |
145 def gid(self): |
114 """The GID of the Domain.""" |
146 """The GID of the Domain.""" |
115 return self._gid |
147 return self._gid |
116 |
148 |
121 |
153 |
122 @property |
154 @property |
123 def directory(self): |
155 def directory(self): |
124 """The Domain's directory.""" |
156 """The Domain's directory.""" |
125 return self._directory |
157 return self._directory |
|
158 |
|
159 @property |
|
160 def quotalimit(self): |
|
161 """The Domain's quota limit.""" |
|
162 return self._qlimit |
|
163 |
|
164 @property |
|
165 def transport(self): |
|
166 """The Domain's transport.""" |
|
167 return self._transport |
126 |
168 |
127 def set_directory(self, basedir): |
169 def set_directory(self, basedir): |
128 """Set the path value of the Domain's directory, inside *basedir*. |
170 """Set the path value of the Domain's directory, inside *basedir*. |
129 |
171 |
130 Argument: |
172 Argument: |
138 assert self._directory is None |
180 assert self._directory is None |
139 self._set_gid() |
181 self._set_gid() |
140 self._directory = os.path.join(basedir, choice(MAILDIR_CHARS), |
182 self._directory = os.path.join(basedir, choice(MAILDIR_CHARS), |
141 str(self._gid)) |
183 str(self._gid)) |
142 |
184 |
143 @property |
185 def set_quotalimit(self, quotalimit): |
144 def transport(self): |
186 """Set the quota limit for the new Domain. |
145 """The Domain's transport.""" |
187 |
146 return self._transport |
188 Argument: |
|
189 |
|
190 `quotalimit` : VirtualMailManager.quotalimit.QuotaLimit |
|
191 The quota limit of the new Domain. |
|
192 """ |
|
193 if not self._new: |
|
194 raise DomErr(_(u"The domain '%s' already exists.") % self._name, |
|
195 DOMAIN_EXISTS) |
|
196 assert isinstance(quotalimit, QuotaLimit) |
|
197 self._qlimit = quotalimit |
147 |
198 |
148 def set_transport(self, transport): |
199 def set_transport(self, transport): |
149 """Set the transport for the new Domain. |
200 """Set the transport for the new Domain. |
150 |
201 |
151 Argument: |
202 Argument: |
162 def save(self): |
213 def save(self): |
163 """Stores the new domain in the database.""" |
214 """Stores the new domain in the database.""" |
164 if not self._new: |
215 if not self._new: |
165 raise DomErr(_(u"The domain '%s' already exists.") % self._name, |
216 raise DomErr(_(u"The domain '%s' already exists.") % self._name, |
166 DOMAIN_EXISTS) |
217 DOMAIN_EXISTS) |
167 assert self._directory is not None and self._transport is not None |
218 assert all((self._directory, self._qlimit, self._transport)) |
168 dbc = self._dbh.cursor() |
219 dbc = self._dbh.cursor() |
169 dbc.execute('INSERT INTO domain_data (gid, tid, domaindir) VALUES ' |
220 dbc.execute('INSERT INTO domain_data (gid, qid, tid, domaindir) ' |
170 '(%s, %s, %s)', (self._gid, self._transport.tid, |
221 'VALUES (%s, %s, %s, %s)', (self._gid, self._qlimit.qid, |
171 self._directory)) |
222 self._transport.tid, self._directory)) |
172 dbc.execute('INSERT INTO domain_name (domainname, gid, is_primary) ' |
223 dbc.execute('INSERT INTO domain_name (domainname, gid, is_primary) ' |
173 'VALUES (%s, %s, TRUE)', (self._name, self._gid)) |
224 'VALUES (%s, %s, TRUE)', (self._name, self._gid)) |
174 self._dbh.commit() |
225 self._dbh.commit() |
175 dbc.close() |
226 dbc.close() |
176 self._new = False |
227 self._new = False |
196 'domain_data'): |
247 'domain_data'): |
197 dbc.execute("DELETE FROM %s WHERE gid = %u" % (tbl, self._gid)) |
248 dbc.execute("DELETE FROM %s WHERE gid = %u" % (tbl, self._gid)) |
198 self._dbh.commit() |
249 self._dbh.commit() |
199 dbc.close() |
250 dbc.close() |
200 self._gid = 0 |
251 self._gid = 0 |
201 self._directory = self._transport = None |
252 self._directory = self._qlimit = self._transport = None |
202 self._new = True |
253 self._new = True |
|
254 |
|
255 def update_quotalimit(self, quotalimit, force=False): |
|
256 """Update the quota limit of the Domain. |
|
257 |
|
258 If *force* is `True` the new *quotalimit* will be applied to |
|
259 all existing accounts of the domain. Otherwise the *quotalimit* |
|
260 will be only applied to accounts created from now on. |
|
261 |
|
262 Arguments: |
|
263 |
|
264 `quotalimit` : VirtualMailManager.quotalimit.QuotaLimit |
|
265 the new quota limit of the domain. |
|
266 `force` : bool |
|
267 enforce new quota limit for all accounts, default `False` |
|
268 """ |
|
269 self._chk_state() |
|
270 assert isinstance(quotalimit, QuotaLimit) |
|
271 if quotalimit == self._qlimit: |
|
272 return |
|
273 self._update_tables('qid', quotalimit.qid, force) |
|
274 self._qlimit = quotalimit |
203 |
275 |
204 def update_transport(self, transport, force=False): |
276 def update_transport(self, transport, force=False): |
205 """Sets a new transport for the Domain. |
277 """Sets a new transport for the Domain. |
206 |
278 |
207 If *force* is `True` the new *transport* will be assigned to all |
279 If *force* is `True` the new *transport* will be assigned to all |
217 """ |
289 """ |
218 self._chk_state() |
290 self._chk_state() |
219 assert isinstance(transport, Transport) |
291 assert isinstance(transport, Transport) |
220 if transport == self._transport: |
292 if transport == self._transport: |
221 return |
293 return |
222 dbc = self._dbh.cursor() |
294 self._update_tables('tid', transport.tid, force) |
223 dbc.execute("UPDATE domain_data SET tid = %s WHERE gid = %s", |
|
224 (transport.tid, self._gid)) |
|
225 if dbc.rowcount > 0: |
|
226 self._dbh.commit() |
|
227 if force: |
|
228 dbc.execute("UPDATE users SET tid = %s WHERE gid = %s", |
|
229 (transport.tid, self._gid)) |
|
230 if dbc.rowcount > 0: |
|
231 self._dbh.commit() |
|
232 dbc.close() |
|
233 self._transport = transport |
295 self._transport = transport |
234 |
296 |
235 def get_info(self): |
297 def get_info(self): |
236 """Returns a dictionary with information about the domain.""" |
298 """Returns a dictionary with information about the domain.""" |
237 self._chk_state() |
299 self._chk_state() |
238 dbc = self._dbh.cursor() |
300 dbc = self._dbh.cursor() |
239 dbc.execute('SELECT gid, domainname, transport, domaindir, ' |
301 dbc.execute('SELECT gid, domainname, transport, domaindir, ' |
240 'aliasdomains, accounts, aliases, relocated FROM ' |
302 'aliasdomains, accounts, aliases, relocated, bytes, ' |
241 'vmm_domain_info WHERE gid = %s', (self._gid,)) |
303 'messages FROM vmm_domain_info WHERE gid = %s', |
|
304 (self._gid,)) |
242 info = dbc.fetchone() |
305 info = dbc.fetchone() |
243 dbc.close() |
306 dbc.close() |
244 keys = ('gid', 'domainname', 'transport', 'domaindir', 'aliasdomains', |
307 keys = ('gid', 'domainname', 'transport', 'domaindir', 'aliasdomains', |
245 'accounts', 'aliases', 'relocated') |
308 'accounts', 'aliases', 'relocated', 'bytes', 'messages') |
246 return dict(zip(keys, info)) |
309 return dict(zip(keys, info)) |
247 |
310 |
248 def get_accounts(self): |
311 def get_accounts(self): |
249 """Returns a list with all accounts of the domain.""" |
312 """Returns a list with all accounts of the domain.""" |
250 self._chk_state() |
313 self._chk_state() |