VirtualMailManager/account.py
branchv0.7.x
changeset 643 df1e3b67882a
parent 638 0de0b9e75c9f
child 662 9ec7770193ad
equal deleted inserted replaced
642:4cd9d0a9f42f 643:df1e3b67882a
    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)
   157 
   157 
   158     def _chk_state(self):
   158     def _chk_state(self):
   159         """Raise an AccountError if the Account is new - not yet saved in the
   159         """Raise an AccountError if the Account is new - not yet saved in the
   160         database."""
   160         database."""
   161         if self._new:
   161         if self._new:
   162             raise AErr(_(u"The account '%s' does not exist.") % self._addr,
   162             raise AErr(_("The account '%s' does not exist.") % self._addr,
   163                        NO_SUCH_ACCOUNT)
   163                        NO_SUCH_ACCOUNT)
   164 
   164 
   165     @property
   165     @property
   166     def address(self):
   166     def address(self):
   167         """The Account's EmailAddress instance."""
   167         """The Account's EmailAddress instance."""
   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