VirtualMailManager/account.py
branchv0.6.x
changeset 390 660b42391c8e
parent 389 5f7e9f778b29
child 404 0c52094447b0
--- a/VirtualMailManager/account.py	Wed Feb 09 22:09:35 2011 +0000
+++ b/VirtualMailManager/account.py	Thu Feb 10 20:10:28 2011 +0000
@@ -10,6 +10,7 @@
 
 from VirtualMailManager.domain import Domain
 from VirtualMailManager.emailaddress import EmailAddress
+from VirtualMailManager.quotalimit import QuotaLimit
 from VirtualMailManager.transport import Transport
 from VirtualMailManager.common import version_str
 from VirtualMailManager.constants import \
@@ -31,7 +32,7 @@
 class Account(object):
     """Class to manage e-mail accounts."""
     __slots__ = ('_addr', '_dbh', '_domain', '_mail', '_new', '_passwd',
-                 '_transport', '_uid')
+                 '_qlimit', '_transport', '_uid')
 
     def __init__(self, dbh, address):
         """Creates a new Account instance.
@@ -59,6 +60,7 @@
                        self._addr.domainname, NO_SUCH_DOMAIN)
         self._uid = 0
         self._mail = None
+        self._qlimit = self._domain.quotalimit
         self._transport = self._domain.transport
         self._passwd = None
         self._new = True
@@ -69,15 +71,17 @@
         return not self._new
 
     def _load(self):
-        """Load 'uid', 'mid' and 'tid' from the database and set _new to
-        `False` - if the user could be found. """
+        """Load 'uid', 'mid', 'qid' and 'tid' from the database and set
+        _new to `False` - if the user could be found. """
         dbc = self._dbh.cursor()
-        dbc.execute('SELECT uid, mid, tid FROM users WHERE gid = %s AND '
+        dbc.execute('SELECT uid, mid, qid, tid FROM users WHERE gid = %s AND '
                     'local_part=%s', (self._domain.gid, self._addr.localpart))
         result = dbc.fetchone()
         dbc.close()
         if result:
-            self._uid, _mid, _tid = result
+            self._uid, _mid, _qid, _tid = result
+            if _qid != self._qlimit.qid:
+                self._qlimit = QuotaLimit(self._dbh, qid=_qid)
             if _tid != self._transport.tid:
                 self._transport = Transport(self._dbh, tid=_tid)
             self._mail = MailLocation(self._dbh, mid=_mid)
@@ -142,6 +146,25 @@
             self._dbh.commit()
         dbc.close()
 
+    def _update_tables(self, column, value):
+        """Update various columns in the users table.
+
+        Arguments:
+
+        `column` : basestring
+          Name of the table column. Currently: qid and tid
+        `value` : long
+          The referenced key
+        """
+        if column not in ('qid', 'tid'):
+            raise ValueError('Unknown column: %r' % column)
+        dbc = self._dbh.cursor()
+        dbc.execute('UPDATE users SET %s = %%s WHERE uid = %%s' % column,
+                    (value, self._uid))
+        if dbc.rowcount > 0:
+            self._dbh.commit()
+        dbc.close()
+
     def _count_aliases(self):
         """Count all alias addresses where the destination address is the
         address of the Account."""
@@ -206,6 +229,9 @@
         `password` : basestring
           The password for the new Account.
         """
+        if not self._new:
+            raise AErr(_(u"The account '%s' already exists.") % self._addr,
+                       ACCOUNT_EXISTS)
         if not isinstance(password, basestring) or not password:
             raise AErr(_(u"Could not accept password: '%s'") % password,
                        ACCOUNT_MISSING_PASSWORD)
@@ -247,13 +273,14 @@
                                    directory=cfg_dget('mailbox.root')))
         dbc = self._dbh.cursor()
         dbc.execute('INSERT INTO users (local_part, passwd, uid, gid, mid, '
-                    'tid, smtp, pop3, imap, %s) VALUES' % (sieve_col,) + \
-                    '(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)',
+                    'qid, tid, smtp, pop3, imap, %s) VALUES' % (sieve_col,) + \
+                    '(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)',
                     (self._addr.localpart,
                      pwhash(self._passwd, user=self._addr), self._uid,
-                     self._domain.gid, self._mail.mid, self._transport.tid,
-                     cfg_dget('account.smtp'), cfg_dget('account.pop3'),
-                     cfg_dget('account.imap'), cfg_dget('account.sieve')))
+                     self._domain.gid, self._mail.mid, self._qlimit.qid,
+                     self._transport.tid, cfg_dget('account.smtp'),
+                     cfg_dget('account.pop3'), cfg_dget('account.imap'),
+                     cfg_dget('account.sieve')))
         self._dbh.commit()
         dbc.close()
         self._new = False
@@ -261,28 +288,22 @@
     def modify(self, field, value):
         """Update the Account's *field* to the new *value*.
 
-        Possible values for *field* are: 'name', 'password' and
-        'transport'.  *value* is the *field*'s new value.
+        Possible values for *field* are: 'name', 'password'.
 
         Arguments:
 
         `field` : basestring
-          The attribute name: 'name', 'password' or 'transport'
+          The attribute name: 'name' or 'password'
         `value` : basestring
           The new value of the attribute.
         """
-        if field not in ('name', 'password', 'transport'):
+        if field not in ('name', 'password'):
             raise AErr(_(u"Unknown field: '%s'") % field, INVALID_ARGUMENT)
         self._chk_state()
         dbc = self._dbh.cursor()
         if field == 'password':
             dbc.execute('UPDATE users SET passwd = %s WHERE uid = %s',
                         (pwhash(value, user=self._addr), self._uid))
-        elif field == 'transport':
-            if value != self._transport.transport:
-                self._transport = Transport(self._dbh, transport=value)
-                dbc.execute('UPDATE users SET tid = %s WHERE uid = %s',
-                            (self._transport.tid, self._uid))
         else:
             dbc.execute('UPDATE users SET name = %s WHERE uid = %s',
                         (value, self._uid))
@@ -290,28 +311,67 @@
             self._dbh.commit()
         dbc.close()
 
+    def update_quotalimit(self, quotalimit):
+        """Update the user's quota limit.
+
+        Arguments:
+
+        `quotalimit` : VirtualMailManager.quotalimit.QuotaLimit
+          the new quota limit of the domain.
+        """
+        self._chk_state()
+        assert isinstance(quotalimit, QuotaLimit)
+        if quotalimit == self._qlimit:
+            return
+        self._update_tables('qid', quotalimit.qid)
+        self._qlimit = quotalimit
+
+    def update_transport(self, transport):
+        """Sets a new transport for the Account.
+
+        Arguments:
+
+        `transport` : VirtualMailManager.transport.Transport
+          the new transport
+        """
+        self._chk_state()
+        assert isinstance(transport, Transport)
+        if transport == self._transport:
+            return
+        if transport.transport.lower() in ('virtual', 'virtual:') and \
+           not self._mail.postfix:
+            raise AErr(_(u"Invalid transport '%(transport)s' for mailbox "
+                         u"format '%(mbfmt)s'") %
+                       {'transport': transport, 'mbfmt': self._mail.mbformat},
+                       INVALID_MAIL_LOCATION)
+        self._update_tables('tid', transport.tid)
+        self._transport = transport
+
     def get_info(self):
         """Returns a dict with some information about the Account.
 
         The keys of the dict are: 'address', 'gid', 'home', 'imap'
-        'mail_location', 'name', 'pop3', 'sieve', 'smtp', transport' and
-        'uid'.
+        'mail_location', 'name', 'pop3', 'sieve', 'smtp', transport', 'uid',
+        'uq_bytes', 'uq_messages', 'ql_bytes', and 'ql_messages'.
         """
         self._chk_state()
         if cfg_dget('misc.dovecot_version') >= 0x10200b02:
             sieve_col = 'sieve'
         else:
             sieve_col = 'managesieve'
-        sql = 'SELECT name, smtp, pop3, imap, %s FROM users WHERE uid = %d' % \
-            (sieve_col, self._uid)
         dbc = self._dbh.cursor()
-        dbc.execute(sql)
+        dbc.execute('SELECT name, smtp, pop3, imap, %s, CASE WHEN bytes IS '
+                    'NULL THEN 0 ELSE bytes END, CASE WHEN messages IS NULL '
+                    'THEN 0 ELSE messages END FROM users LEFT JOIN userquota '
+                    'USING (uid) WHERE users.uid = %u' % (sieve_col,
+                        self._uid))
         info = dbc.fetchone()
         dbc.close()
         if info:
-            keys = ('name', 'smtp', 'pop3', 'imap', sieve_col)
+            keys = ('name', 'smtp', 'pop3', 'imap', sieve_col, 'uq_bytes',
+                    'uq_messages')
             info = dict(zip(keys, info))
-            for service in keys[1:]:
+            for service in keys[1:5]:
                 if info[service]:
                     # TP: A service (pop3/imap) is enabled/usable for a user
                     info[service] = _('enabled')
@@ -322,6 +382,8 @@
             info['gid'] = self._domain.gid
             info['home'] = '%s/%s' % (self._domain.directory, self._uid)
             info['mail_location'] = self._mail.mail_location
+            info['ql_bytes'] = self._qlimit.bytes
+            info['ql_messages'] = self._qlimit.messages
             info['transport'] = self._transport.transport
             info['uid'] = self._uid
             return info
@@ -380,7 +442,7 @@
         self._new = True
         self._uid = 0
         self._addr = self._dbh = self._domain = self._passwd = None
-        self._mail = self._transport = None
+        self._mail = self._qlimit = self._transport = None
 
 
 def get_account_by_uid(uid, dbh):