55 self._domain = Domain(self._dbh, self._addr.domainname) |
55 self._domain = Domain(self._dbh, self._addr.domainname) |
56 if not self._domain.gid: |
56 if not self._domain.gid: |
57 # TP: Hm, what “quotation marks” should be used? |
57 # TP: Hm, what “quotation marks” should be used? |
58 # If you are unsure have a look at: |
58 # If you are unsure have a look at: |
59 # http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage |
59 # http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage |
60 raise AErr(_(u"The domain '%s' does not exist.") % |
60 raise AErr(_("The domain '%s' does not exist.") % |
61 self._addr.domainname, NO_SUCH_DOMAIN) |
61 self._addr.domainname, NO_SUCH_DOMAIN) |
62 self._uid = 0 |
62 self._uid = 0 |
63 self._mail = None |
63 self._mail = None |
64 self._qlimit = None |
64 self._qlimit = None |
65 self._services = None |
65 self._services = None |
67 self._note = None |
67 self._note = None |
68 self._passwd = None |
68 self._passwd = None |
69 self._new = True |
69 self._new = True |
70 self._load() |
70 self._load() |
71 |
71 |
72 def __nonzero__(self): |
72 def __bool__(self): |
73 """Returns `True` if the Account is known, `False` if it's new.""" |
73 """Returns `True` if the Account is known, `False` if it's new.""" |
74 return not self._new |
74 return not self._new |
75 |
75 |
76 def _load(self): |
76 def _load(self): |
77 """Load 'uid', 'mid', 'qid', 'ssid', 'tid' and 'note' from the |
77 """Load 'uid', 'mid', 'qid', 'ssid', 'tid' and 'note' from the |
114 def _prepare(self, maillocation): |
114 def _prepare(self, maillocation): |
115 """Check and set different attributes - before we store the |
115 """Check and set different attributes - before we store the |
116 information in the database. |
116 information in the database. |
117 """ |
117 """ |
118 if maillocation.dovecot_version > cfg_dget('misc.dovecot_version'): |
118 if maillocation.dovecot_version > cfg_dget('misc.dovecot_version'): |
119 raise AErr(_(u"The mailbox format '%(mbfmt)s' requires Dovecot " |
119 raise AErr(_("The mailbox format '%(mbfmt)s' requires Dovecot " |
120 u">= v%(version)s.") % { |
120 ">= v%(version)s.") % { |
121 'mbfmt': maillocation.mbformat, |
121 'mbfmt': maillocation.mbformat, |
122 'version': version_str(maillocation.dovecot_version)}, |
122 'version': version_str(maillocation.dovecot_version)}, |
123 INVALID_MAIL_LOCATION) |
123 INVALID_MAIL_LOCATION) |
124 transport = self._transport or self._domain.transport |
124 transport = self._transport or self._domain.transport |
125 validate_transport(transport, maillocation) |
125 validate_transport(transport, maillocation) |
213 |
213 |
214 `password` : basestring |
214 `password` : basestring |
215 The password for the new Account. |
215 The password for the new Account. |
216 """ |
216 """ |
217 if not self._new: |
217 if not self._new: |
218 raise AErr(_(u"The account '%s' already exists.") % self._addr, |
218 raise AErr(_("The account '%s' already exists.") % self._addr, |
219 ACCOUNT_EXISTS) |
219 ACCOUNT_EXISTS) |
220 if not isinstance(password, basestring) or not password: |
220 if not isinstance(password, str) or not password: |
221 raise AErr(_(u"Could not accept password: '%s'") % password, |
221 raise AErr(_("Could not accept password: '%s'") % password, |
222 ACCOUNT_MISSING_PASSWORD) |
222 ACCOUNT_MISSING_PASSWORD) |
223 self._passwd = password |
223 self._passwd = password |
224 |
224 |
225 def set_note(self, note): |
225 def set_note(self, note): |
226 """Set the account's (optional) note. |
226 """Set the account's (optional) note. |
228 Argument: |
228 Argument: |
229 |
229 |
230 `note` : basestring or None |
230 `note` : basestring or None |
231 The note, or None to remove |
231 The note, or None to remove |
232 """ |
232 """ |
233 assert note is None or isinstance(note, basestring) |
233 assert note is None or isinstance(note, str) |
234 self._note = note |
234 self._note = note |
235 |
235 |
236 def save(self): |
236 def save(self): |
237 """Save the new Account in the database.""" |
237 """Save the new Account in the database.""" |
238 if not self._new: |
238 if not self._new: |
239 raise AErr(_(u"The account '%s' already exists.") % self._addr, |
239 raise AErr(_("The account '%s' already exists.") % self._addr, |
240 ACCOUNT_EXISTS) |
240 ACCOUNT_EXISTS) |
241 if not self._passwd: |
241 if not self._passwd: |
242 raise AErr(_(u"No password set for account: '%s'") % self._addr, |
242 raise AErr(_("No password set for account: '%s'") % self._addr, |
243 ACCOUNT_MISSING_PASSWORD) |
243 ACCOUNT_MISSING_PASSWORD) |
244 self._prepare(MailLocation(self._dbh, mbfmt=cfg_dget('mailbox.format'), |
244 self._prepare(MailLocation(self._dbh, mbfmt=cfg_dget('mailbox.format'), |
245 directory=cfg_dget('mailbox.root'))) |
245 directory=cfg_dget('mailbox.root'))) |
246 dbc = self._dbh.cursor() |
246 dbc = self._dbh.cursor() |
247 dbc.execute('INSERT INTO users (local_part, passwd, uid, gid, mid, ' |
247 dbc.execute('INSERT INTO users (local_part, passwd, uid, gid, mid, ' |
269 The attribute name: 'name', 'password' or 'note' |
269 The attribute name: 'name', 'password' or 'note' |
270 `value` : basestring |
270 `value` : basestring |
271 The new value of the attribute. |
271 The new value of the attribute. |
272 """ |
272 """ |
273 if field not in ('name', 'password', 'note'): |
273 if field not in ('name', 'password', 'note'): |
274 raise AErr(_(u"Unknown field: '%s'") % field, INVALID_ARGUMENT) |
274 raise AErr(_("Unknown field: '%s'") % field, INVALID_ARGUMENT) |
275 self._chk_state() |
275 self._chk_state() |
276 dbc = self._dbh.cursor() |
276 dbc = self._dbh.cursor() |
277 if field == 'password': |
277 if field == 'password': |
278 dbc.execute('UPDATE users SET passwd = %s WHERE uid = %s', |
278 dbc.execute('UPDATE users SET passwd = %s WHERE uid = %s', |
279 (pwhash(value, user=self._addr), self._uid)) |
279 (pwhash(value, user=self._addr), self._uid)) |
291 |
291 |
292 `quotalimit` : VirtualMailManager.quotalimit.QuotaLimit |
292 `quotalimit` : VirtualMailManager.quotalimit.QuotaLimit |
293 the new quota limit of the domain. |
293 the new quota limit of the domain. |
294 """ |
294 """ |
295 if cfg_dget('misc.dovecot_version') < 0x10102f00: |
295 if cfg_dget('misc.dovecot_version') < 0x10102f00: |
296 raise VMMError(_(u'PostgreSQL-based dictionary quota requires ' |
296 raise VMMError(_('PostgreSQL-based dictionary quota requires ' |
297 u'Dovecot >= v1.1.2.'), VMM_ERROR) |
297 'Dovecot >= v1.1.2.'), VMM_ERROR) |
298 self._chk_state() |
298 self._chk_state() |
299 if quotalimit == self._qlimit: |
299 if quotalimit == self._qlimit: |
300 return |
300 return |
301 self._qlimit = quotalimit |
301 self._qlimit = quotalimit |
302 if quotalimit is not None: |
302 if quotalimit is not None: |
351 else: |
351 else: |
352 services = self._domain.serviceset.services |
352 services = self._domain.serviceset.services |
353 fmt = format_domain_default |
353 fmt = format_domain_default |
354 |
354 |
355 ret = {} |
355 ret = {} |
356 for service, state in services.iteritems(): |
356 for service, state in services.items(): |
357 # TP: A service (e.g. pop3 or imap) may be enabled/usable or |
357 # TP: A service (e.g. pop3 or imap) may be enabled/usable or |
358 # disabled/unusable for a user. |
358 # disabled/unusable for a user. |
359 ret[service] = fmt((_('disabled'), _('enabled'))[state]) |
359 ret[service] = fmt((_('disabled'), _('enabled'))[state]) |
360 return ret |
360 return ret |
361 |
361 |
374 'FROM users LEFT JOIN userquota USING (uid) WHERE ' |
374 'FROM users LEFT JOIN userquota USING (uid) WHERE ' |
375 'users.uid = %s', (self._uid,)) |
375 'users.uid = %s', (self._uid,)) |
376 info = dbc.fetchone() |
376 info = dbc.fetchone() |
377 dbc.close() |
377 dbc.close() |
378 if info: |
378 if info: |
379 info = dict(zip(('name', 'uq_bytes', 'uq_messages'), info)) |
379 info = dict(list(zip(('name', 'uq_bytes', 'uq_messages'), info))) |
380 info.update(self._get_info_serviceset()) |
380 info.update(self._get_info_serviceset()) |
381 info['address'] = self._addr |
381 info['address'] = self._addr |
382 info['gid'] = self._domain.gid |
382 info['gid'] = self._domain.gid |
383 info['home'] = '%s/%s' % (self._domain.directory, self._uid) |
383 info['home'] = '%s/%s' % (self._domain.directory, self._uid) |
384 info['mail_location'] = self._mail.mail_location |
384 info['mail_location'] = self._mail.mail_location |
393 info['transport'] = self._get_info_transport() |
393 info['transport'] = self._get_info_transport() |
394 info['note'] = self._note |
394 info['note'] = self._note |
395 info['uid'] = self._uid |
395 info['uid'] = self._uid |
396 return info |
396 return info |
397 # nearly impossible‽ |
397 # nearly impossible‽ |
398 raise AErr(_(u"Could not fetch information for account: '%s'") % |
398 raise AErr(_("Could not fetch information for account: '%s'") % |
399 self._addr, NO_SUCH_ACCOUNT) |
399 self._addr, NO_SUCH_ACCOUNT) |
400 |
400 |
401 def get_aliases(self): |
401 def get_aliases(self): |
402 """Return a list with all alias e-mail addresses, whose destination |
402 """Return a list with all alias e-mail addresses, whose destination |
403 is the address of the Account.""" |
403 is the address of the Account.""" |
437 self._dbh.commit() |
437 self._dbh.commit() |
438 else: # check first for aliases |
438 else: # check first for aliases |
439 a_count = self._count_aliases() |
439 a_count = self._count_aliases() |
440 if a_count > 0: |
440 if a_count > 0: |
441 dbc.close() |
441 dbc.close() |
442 raise AErr(_(u"There are %(count)d aliases with the " |
442 raise AErr(_("There are %(count)d aliases with the " |
443 u"destination address '%(address)s'.") % |
443 "destination address '%(address)s'.") % |
444 {'count': a_count, 'address': self._addr}, |
444 {'count': a_count, 'address': self._addr}, |
445 ALIAS_PRESENT) |
445 ALIAS_PRESENT) |
446 dbc.execute('DELETE FROM users WHERE uid = %s', (self._uid,)) |
446 dbc.execute('DELETE FROM users WHERE uid = %s', (self._uid,)) |
447 self._dbh.commit() |
447 self._dbh.commit() |
448 dbc.close() |
448 dbc.close() |
464 The Account unique ID. |
464 The Account unique ID. |
465 `dbh` : pyPgSQL.PgSQL.Connection |
465 `dbh` : pyPgSQL.PgSQL.Connection |
466 a database connection for the database access. |
466 a database connection for the database access. |
467 """ |
467 """ |
468 try: |
468 try: |
469 uid = long(uid) |
469 uid = int(uid) |
470 except ValueError: |
470 except ValueError: |
471 raise AErr(_(u'UID must be an int/long.'), INVALID_ARGUMENT) |
471 raise AErr(_('UID must be an int/long.'), INVALID_ARGUMENT) |
472 if uid < 1: |
472 if uid < 1: |
473 raise AErr(_(u'UID must be greater than 0.'), INVALID_ARGUMENT) |
473 raise AErr(_('UID must be greater than 0.'), INVALID_ARGUMENT) |
474 dbc = dbh.cursor() |
474 dbc = dbh.cursor() |
475 dbc.execute("SELECT local_part||'@'|| domain_name.domainname AS address, " |
475 dbc.execute("SELECT local_part||'@'|| domain_name.domainname AS address, " |
476 "uid, users.gid, note FROM users LEFT JOIN domain_name ON " |
476 "uid, users.gid, note FROM users LEFT JOIN domain_name ON " |
477 "(domain_name.gid = users.gid AND is_primary) WHERE uid = %s", |
477 "(domain_name.gid = users.gid AND is_primary) WHERE uid = %s", |
478 (uid,)) |
478 (uid,)) |
479 info = dbc.fetchone() |
479 info = dbc.fetchone() |
480 dbc.close() |
480 dbc.close() |
481 if not info: |
481 if not info: |
482 raise AErr(_(u"There is no account with the UID: '%d'") % uid, |
482 raise AErr(_("There is no account with the UID: '%d'") % uid, |
483 NO_SUCH_ACCOUNT) |
483 NO_SUCH_ACCOUNT) |
484 info = dict(zip(('address', 'uid', 'gid', 'note'), info)) |
484 info = dict(list(zip(('address', 'uid', 'gid', 'note'), info))) |
485 return info |
485 return info |
486 |
486 |
487 del _, cfg_dget |
487 del _, cfg_dget |