6 ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
6 ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
7 |
7 |
8 Virtual Mail Manager's Account class to manage e-mail accounts. |
8 Virtual Mail Manager's Account class to manage e-mail accounts. |
9 """ |
9 """ |
10 |
10 |
11 from VirtualMailManager.common import version_str, \ |
11 from VirtualMailManager.common import version_str |
12 format_domain_default |
|
13 from VirtualMailManager.constants import \ |
12 from VirtualMailManager.constants import \ |
14 ACCOUNT_EXISTS, ACCOUNT_MISSING_PASSWORD, ALIAS_PRESENT, \ |
13 ACCOUNT_EXISTS, ACCOUNT_MISSING_PASSWORD, ALIAS_PRESENT, \ |
15 INVALID_ARGUMENT, INVALID_MAIL_LOCATION, NO_SUCH_ACCOUNT, \ |
14 INVALID_ARGUMENT, INVALID_MAIL_LOCATION, NO_SUCH_ACCOUNT, \ |
16 NO_SUCH_DOMAIN, VMM_ERROR |
15 NO_SUCH_DOMAIN, VMM_ERROR |
17 from VirtualMailManager.domain import Domain |
16 from VirtualMailManager.domain import Domain |
80 self._addr.localpart)) |
79 self._addr.localpart)) |
81 result = dbc.fetchone() |
80 result = dbc.fetchone() |
82 dbc.close() |
81 dbc.close() |
83 if result: |
82 if result: |
84 self._uid, _mid, _qid, _ssid, _tid = result |
83 self._uid, _mid, _qid, _ssid, _tid = result |
85 |
84 if _qid != self._qlimit.qid: |
86 def load_helper(ctor, own, field, dbresult): |
85 self._qlimit = QuotaLimit(self._dbh, qid=_qid) |
87 cur = None if own is None else getattr(own, field) |
86 if _ssid != self._services.ssid: |
88 if cur != dbresult: |
87 self._services = ServiceSet(self._dbh, ssid=_ssid) |
89 kwargs = { field : dbresult } |
88 if _tid != self._transport.tid: |
90 return None if dbresult is None \ |
89 self._transport = Transport(self._dbh, tid=_tid) |
91 else ctor(self._dbh, **kwargs) |
|
92 |
|
93 self._qlimit = load_helper(QuotaLimit, self._qlimit, 'qid', _qid) |
|
94 self._services = load_helper(ServiceSet, self._services, 'ssid', |
|
95 _ssid) |
|
96 self._transport = load_helper(Transport, self._transport, 'tid', |
|
97 _tid) |
|
98 self._mail = MailLocation(self._dbh, mid=_mid) |
90 self._mail = MailLocation(self._dbh, mid=_mid) |
99 self._new = False |
91 self._new = False |
100 |
92 |
101 def _set_uid(self): |
93 def _set_uid(self): |
102 """Set the unique ID for the new Account.""" |
94 """Set the unique ID for the new Account.""" |
229 dbc = self._dbh.cursor() |
221 dbc = self._dbh.cursor() |
230 dbc.execute('INSERT INTO users (local_part, passwd, uid, gid, mid, ' |
222 dbc.execute('INSERT INTO users (local_part, passwd, uid, gid, mid, ' |
231 'qid, ssid, tid) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)', |
223 'qid, ssid, tid) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)', |
232 (self._addr.localpart, |
224 (self._addr.localpart, |
233 pwhash(self._passwd, user=self._addr), self._uid, |
225 pwhash(self._passwd, user=self._addr), self._uid, |
234 self._domain.gid, self._mail.mid, |
226 self._domain.gid, self._mail.mid, self._qlimit.qid, |
235 self._qlimit.qid if self._qlimit else None, |
227 self._services.ssid, self._transport.tid)) |
236 self._services.ssid if self._services else None, |
|
237 self._transport.tid if self._transport else None)) |
|
238 self._dbh.commit() |
228 self._dbh.commit() |
239 dbc.close() |
229 dbc.close() |
240 self._new = False |
230 self._new = False |
241 |
231 |
242 def modify(self, field, value): |
232 def modify(self, field, value): |
275 """ |
265 """ |
276 if cfg_dget('misc.dovecot_version') < 0x10102f00: |
266 if cfg_dget('misc.dovecot_version') < 0x10102f00: |
277 raise VMMError(_(u'PostgreSQL-based dictionary quota requires ' |
267 raise VMMError(_(u'PostgreSQL-based dictionary quota requires ' |
278 u'Dovecot >= v1.1.2.'), VMM_ERROR) |
268 u'Dovecot >= v1.1.2.'), VMM_ERROR) |
279 self._chk_state() |
269 self._chk_state() |
|
270 assert isinstance(quotalimit, QuotaLimit) |
280 if quotalimit == self._qlimit: |
271 if quotalimit == self._qlimit: |
281 return |
272 return |
|
273 self._update_tables('qid', quotalimit.qid) |
282 self._qlimit = quotalimit |
274 self._qlimit = quotalimit |
283 if quotalimit is not None: |
|
284 assert isinstance(quotalimit, QuotaLimit) |
|
285 quotalimit = quotalimit.qid |
|
286 self._update_tables('qid', quotalimit) |
|
287 |
275 |
288 def update_serviceset(self, serviceset): |
276 def update_serviceset(self, serviceset): |
289 """Assign a different set of services to the Account. |
277 """Assign a different set of services to the Account. |
290 |
278 |
291 Argument: |
279 Argument: |
292 |
280 |
293 `serviceset` : VirtualMailManager.serviceset.ServiceSet |
281 `serviceset` : VirtualMailManager.serviceset.ServiceSet |
294 the new service set. |
282 the new service set. |
295 """ |
283 """ |
296 self._chk_state() |
284 self._chk_state() |
|
285 assert isinstance(serviceset, ServiceSet) |
297 if serviceset == self._services: |
286 if serviceset == self._services: |
298 return |
287 return |
|
288 self._update_tables('ssid', serviceset.ssid) |
299 self._services = serviceset |
289 self._services = serviceset |
300 if serviceset is not None: |
|
301 assert isinstance(serviceset, ServiceSet) |
|
302 serviceset = serviceset.ssid |
|
303 self._update_tables('ssid', serviceset) |
|
304 |
290 |
305 def update_transport(self, transport): |
291 def update_transport(self, transport): |
306 """Sets a new transport for the Account. |
292 """Sets a new transport for the Account. |
307 |
293 |
308 Arguments: |
294 Arguments: |
309 |
295 |
310 `transport` : VirtualMailManager.transport.Transport |
296 `transport` : VirtualMailManager.transport.Transport |
311 the new transport |
297 the new transport |
312 """ |
298 """ |
313 self._chk_state() |
299 self._chk_state() |
|
300 assert isinstance(transport, Transport) |
314 if transport == self._transport: |
301 if transport == self._transport: |
315 return |
302 return |
|
303 if transport.transport.lower() in ('virtual', 'virtual:') and \ |
|
304 not self._mail.postfix: |
|
305 raise AErr(_(u"Invalid transport '%(transport)s' for mailbox " |
|
306 u"format '%(mbfmt)s'.") % |
|
307 {'transport': transport, 'mbfmt': self._mail.mbformat}, |
|
308 INVALID_MAIL_LOCATION) |
|
309 self._update_tables('tid', transport.tid) |
316 self._transport = transport |
310 self._transport = transport |
317 if transport is not None: |
|
318 assert isinstance(transport, Transport) |
|
319 if transport.transport.lower() in ('virtual', 'virtual:') and \ |
|
320 not self._mail.postfix: |
|
321 raise AErr(_(u"Invalid transport '%(transport)s' for mailbox " |
|
322 u"format '%(mbfmt)s'.") % |
|
323 {'transport': transport, 'mbfmt': self._mail.mbformat}, |
|
324 INVALID_MAIL_LOCATION) |
|
325 transport = transport.tid |
|
326 self._update_tables('tid', transport) |
|
327 |
|
328 def _get_info_transport(self): |
|
329 if self._transport: |
|
330 return self._transport.transport |
|
331 return format_domain_default(self._domain.transport.transport) |
|
332 |
|
333 def _get_info_serviceset(self): |
|
334 if self._services: |
|
335 services = self._services.services |
|
336 fmt = lambda s: s |
|
337 else: |
|
338 services = self._domain.serviceset.services |
|
339 fmt = format_domain_default |
|
340 |
|
341 ret = {} |
|
342 for service, state in services.iteritems(): |
|
343 # TP: A service (e.g. pop3 or imap) may be enabled/usable or |
|
344 # disabled/unusable for a user. |
|
345 ret[service] = fmt((_('disabled'), _('enabled'))[state]) |
|
346 return ret |
|
347 |
311 |
348 def get_info(self): |
312 def get_info(self): |
349 """Returns a dict with some information about the Account. |
313 """Returns a dict with some information about the Account. |
350 |
314 |
351 The keys of the dict are: 'address', 'gid', 'home', 'imap' |
315 The keys of the dict are: 'address', 'gid', 'home', 'imap' |
352 'mail_location', 'name', 'pop3', 'sieve', 'smtp', transport', 'uid', |
316 'mail_location', 'name', 'pop3', 'sieve', 'smtp', transport', 'uid', |
353 'uq_bytes', 'uq_messages', 'ql_bytes', 'ql_messages', and |
317 'uq_bytes', 'uq_messages', 'ql_bytes', and 'ql_messages'. |
354 'ql_domaindefault'. |
|
355 """ |
318 """ |
356 self._chk_state() |
319 self._chk_state() |
357 dbc = self._dbh.cursor() |
320 dbc = self._dbh.cursor() |
358 dbc.execute('SELECT name, CASE WHEN bytes IS NULL THEN 0 ELSE bytes ' |
321 dbc.execute('SELECT name, CASE WHEN bytes IS NULL THEN 0 ELSE bytes ' |
359 'END, CASE WHEN messages IS NULL THEN 0 ELSE messages END ' |
322 'END, CASE WHEN messages IS NULL THEN 0 ELSE messages END ' |
361 'users.uid = %s', (self._uid,)) |
324 'users.uid = %s', (self._uid,)) |
362 info = dbc.fetchone() |
325 info = dbc.fetchone() |
363 dbc.close() |
326 dbc.close() |
364 if info: |
327 if info: |
365 info = dict(zip(('name', 'uq_bytes', 'uq_messages'), info)) |
328 info = dict(zip(('name', 'uq_bytes', 'uq_messages'), info)) |
366 info.update(self._get_info_serviceset()) |
329 for service, state in self._services.services.iteritems(): |
|
330 # TP: A service (e.g. pop3 or imap) may be enabled/usable or |
|
331 # disabled/unusable for a user. |
|
332 info[service] = (_('disabled'), _('enabled'))[state] |
367 info['address'] = self._addr |
333 info['address'] = self._addr |
368 info['gid'] = self._domain.gid |
334 info['gid'] = self._domain.gid |
369 info['home'] = '%s/%s' % (self._domain.directory, self._uid) |
335 info['home'] = '%s/%s' % (self._domain.directory, self._uid) |
370 info['mail_location'] = self._mail.mail_location |
336 info['mail_location'] = self._mail.mail_location |
371 if self._qlimit: |
337 info['ql_bytes'] = self._qlimit.bytes |
372 info['ql_bytes'] = self._qlimit.bytes |
338 info['ql_messages'] = self._qlimit.messages |
373 info['ql_messages'] = self._qlimit.messages |
339 info['transport'] = self._transport.transport |
374 info['ql_domaindefault'] = False |
|
375 else: |
|
376 info['ql_bytes'] = self._domain.quotalimit.bytes |
|
377 info['ql_messages'] = self._domain.quotalimit.messages |
|
378 info['ql_domaindefault'] = True |
|
379 info['transport'] = self._get_info_transport() |
|
380 info['uid'] = self._uid |
340 info['uid'] = self._uid |
381 return info |
341 return info |
382 # nearly impossibleā½ |
342 # nearly impossibleā½ |
383 raise AErr(_(u"Could not fetch information for account: '%s'") % |
343 raise AErr(_(u"Could not fetch information for account: '%s'") % |
384 self._addr, NO_SUCH_ACCOUNT) |
344 self._addr, NO_SUCH_ACCOUNT) |