VirtualMailManager/Account.py
changeset 571 a4aead244f75
parent 465 c0e1fb1b0145
parent 570 28230a8230bf
child 572 3238c58d01ae
equal deleted inserted replaced
465:c0e1fb1b0145 571:a4aead244f75
     1 # -*- coding: UTF-8 -*-
       
     2 # Copyright (c) 2007 - 2010, Pascal Volk
       
     3 # See COPYING for distribution information.
       
     4 
       
     5 """Virtual Mail Manager's Account class to manage e-mail accounts."""
       
     6 
       
     7 from __main__ import ERR
       
     8 from Exceptions import VMMAccountException as AccE
       
     9 from Domain import Domain
       
    10 from Transport import Transport
       
    11 from MailLocation import MailLocation
       
    12 from EmailAddress import EmailAddress
       
    13 import VirtualMailManager as VMM
       
    14 
       
    15 class Account(object):
       
    16     """Class to manage e-mail accounts."""
       
    17     __slots__ = ('_addr','_base','_gid','_mid','_passwd','_tid','_uid','_dbh')
       
    18     def __init__(self, dbh, address, password=None):
       
    19         self._dbh = dbh
       
    20         self._base = None
       
    21         if isinstance(address, EmailAddress):
       
    22             self._addr = address
       
    23         else:
       
    24             raise TypeError("Argument 'address' is not an EmailAddress")
       
    25         self._uid = 0
       
    26         self._gid = 0
       
    27         self._mid = 0
       
    28         self._tid = 0
       
    29         self._passwd = password
       
    30         self._setAddr()
       
    31         self._exists()
       
    32         if self._uid < 1 and VMM.VirtualMailManager.aliasExists(self._dbh,
       
    33                 self._addr):
       
    34             # TP: Hm, what quotation marks should be used?
       
    35             # If you are unsure have a look at:
       
    36             # http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
       
    37             raise AccE(_(u"There is already an alias with the address “%s”.") %\
       
    38                     self._addr, ERR.ALIAS_EXISTS)
       
    39         if self._uid < 1 and VMM.VirtualMailManager.relocatedExists(self._dbh,
       
    40                 self._addr):
       
    41             raise AccE(
       
    42               _(u"There is already a relocated user with the address “%s”.") %\
       
    43                     self._addr, ERR.RELOCATED_EXISTS)
       
    44 
       
    45     def _exists(self):
       
    46         dbc = self._dbh.cursor()
       
    47         dbc.execute("SELECT uid, mid, tid FROM users \
       
    48 WHERE gid=%s AND local_part=%s",
       
    49                 self._gid, self._addr._localpart)
       
    50         result = dbc.fetchone()
       
    51         dbc.close()
       
    52         if result is not None:
       
    53             self._uid, self._mid, self._tid = result
       
    54             return True
       
    55         else:
       
    56             return False
       
    57 
       
    58     def _setAddr(self):
       
    59         dom = Domain(self._dbh, self._addr._domainname)
       
    60         self._gid = dom.getID()
       
    61         if self._gid == 0:
       
    62             raise AccE(_(u"The domain “%s” doesn't exist.") %\
       
    63                     self._addr._domainname, ERR.NO_SUCH_DOMAIN)
       
    64         self._base = dom.getDir()
       
    65         self._tid = dom.getTransportID()
       
    66 
       
    67     def _setID(self):
       
    68         dbc = self._dbh.cursor()
       
    69         dbc.execute("SELECT nextval('users_uid')")
       
    70         self._uid = dbc.fetchone()[0]
       
    71         dbc.close()
       
    72 
       
    73     def _prepare(self, maillocation):
       
    74         self._setID()
       
    75         self._mid = MailLocation(self._dbh, maillocation=maillocation).getID()
       
    76 
       
    77     def _switchState(self, state, dcvers, service):
       
    78         if not isinstance(state, bool):
       
    79             return False
       
    80         if not service in (None, 'all', 'imap', 'pop3', 'sieve', 'smtp'):
       
    81             raise AccE(_(u"Unknown service “%s”.") % service,
       
    82                     ERR.UNKNOWN_SERVICE)
       
    83         if self._uid < 1:
       
    84             raise AccE(_(u"The account “%s” doesn't exist.") % self._addr,
       
    85                     ERR.NO_SUCH_ACCOUNT)
       
    86         if dcvers > 11:
       
    87             sieve_col = 'sieve'
       
    88         else:
       
    89             sieve_col = 'managesieve'
       
    90         if service in ('smtp', 'pop3', 'imap'):
       
    91             sql = 'UPDATE users SET %s = %s WHERE uid = %d' % (service, state,
       
    92                     self._uid)
       
    93         elif service == 'sieve':
       
    94             sql = 'UPDATE users SET %s = %s WHERE uid = %d' % (sieve_col,
       
    95                     state, self._uid)
       
    96         else:
       
    97             sql = 'UPDATE users SET smtp = %(s)s, pop3 = %(s)s, imap = %(s)s,\
       
    98  %(col)s = %(s)s WHERE uid = %(uid)d' % {
       
    99                 's': state, 'col': sieve_col, 'uid': self._uid}
       
   100         dbc = self._dbh.cursor()
       
   101         dbc.execute(sql)
       
   102         if dbc.rowcount > 0:
       
   103             self._dbh.commit()
       
   104         dbc.close()
       
   105 
       
   106     def __aliaseCount(self):
       
   107         dbc = self._dbh.cursor()
       
   108         q = "SELECT COUNT(destination) FROM alias WHERE destination = '%s'"\
       
   109             %self._addr
       
   110         dbc.execute(q)
       
   111         a_count = dbc.fetchone()[0]
       
   112         dbc.close()
       
   113         return a_count
       
   114 
       
   115     def setPassword(self, password):
       
   116         self._passwd = password
       
   117 
       
   118     def getUID(self):
       
   119         return self._uid
       
   120 
       
   121     def getGID(self):
       
   122         return self._gid
       
   123 
       
   124     def getDir(self, directory):
       
   125         if directory == 'domain':
       
   126             return '%s' % self._base
       
   127         elif directory == 'home':
       
   128             return '%s/%i' % (self._base, self._uid)
       
   129 
       
   130     def enable(self, dcvers, service=None):
       
   131         self._switchState(True, dcvers, service)
       
   132 
       
   133     def disable(self, dcvers, service=None):
       
   134         self._switchState(False, dcvers, service)
       
   135 
       
   136     def save(self, maillocation, dcvers, smtp, pop3, imap, sieve):
       
   137         if self._uid < 1:
       
   138             if dcvers > 11:
       
   139                 sieve_col = 'sieve'
       
   140             else:
       
   141                 sieve_col = 'managesieve'
       
   142             self._prepare(maillocation)
       
   143             sql = "INSERT INTO users (local_part, passwd, uid, gid, mid, tid,\
       
   144  smtp, pop3, imap, %s) VALUES ('%s', '%s', %d, %d, %d, %d, %s, %s, %s, %s)" % (
       
   145                 sieve_col, self._addr._localpart, self._passwd, self._uid,
       
   146                 self._gid, self._mid, self._tid, smtp, pop3, imap, sieve)
       
   147             dbc = self._dbh.cursor()
       
   148             dbc.execute(sql)
       
   149             self._dbh.commit()
       
   150             dbc.close()
       
   151         else:
       
   152             raise AccE(_(u'The account “%s” already exists.') % self._addr,
       
   153                     ERR.ACCOUNT_EXISTS)
       
   154 
       
   155     def modify(self, what, value):
       
   156         if self._uid == 0:
       
   157             raise AccE(_(u"The account “%s” doesn't exist.") % self._addr,
       
   158                     ERR.NO_SUCH_ACCOUNT)
       
   159         if what not in ['name', 'password', 'transport']:
       
   160             return False
       
   161         dbc = self._dbh.cursor()
       
   162         if what == 'password':
       
   163             dbc.execute('UPDATE users SET passwd = %s WHERE uid = %s',
       
   164                     value, self._uid)
       
   165         elif what == 'transport':
       
   166             self._tid = Transport(self._dbh, transport=value).getID()
       
   167             dbc.execute('UPDATE users SET tid = %s WHERE uid = %s',
       
   168                     self._tid, self._uid)
       
   169         else:
       
   170             dbc.execute('UPDATE users SET name = %s WHERE uid = %s',
       
   171                     value, self._uid)
       
   172         if dbc.rowcount > 0:
       
   173             self._dbh.commit()
       
   174         dbc.close()
       
   175 
       
   176     def getInfo(self, dcvers):
       
   177         if dcvers > 11:
       
   178             sieve_col = 'sieve'
       
   179         else:
       
   180             sieve_col = 'managesieve'
       
   181         sql = 'SELECT name, uid, gid, mid, tid, smtp, pop3, imap, %s\
       
   182  FROM users WHERE uid = %d' % (sieve_col, self._uid)
       
   183         dbc = self._dbh.cursor()
       
   184         dbc.execute(sql)
       
   185         info = dbc.fetchone()
       
   186         dbc.close()
       
   187         if info is None:
       
   188             raise AccE(_(u"The account “%s” doesn't exist.") % self._addr,
       
   189                     ERR.NO_SUCH_ACCOUNT)
       
   190         else:
       
   191             keys = ['name', 'uid', 'gid', 'maildir', 'transport', 'smtp',
       
   192                     'pop3', 'imap', sieve_col]
       
   193             info = dict(zip(keys, info))
       
   194             for service in ('smtp', 'pop3', 'imap', sieve_col):
       
   195                 if bool(info[service]):
       
   196                     # TP: A service (pop3/imap/…) is enabled/usable for a user
       
   197                     info[service] = _('enabled')
       
   198                 else:
       
   199                     # TP: A service (pop3/imap) isn't enabled/usable for a user
       
   200                     info[service] = _('disabled')
       
   201             info['address'] = self._addr
       
   202             info['maildir'] = '%s/%s/%s' % (self._base, info['uid'],
       
   203                     MailLocation(self._dbh,
       
   204                         mid=info['maildir']).getMailLocation())
       
   205             info['transport'] = Transport(self._dbh,
       
   206                     tid=info['transport']).getTransport()
       
   207             return info
       
   208 
       
   209     def getAliases(self):
       
   210         dbc = self._dbh.cursor()
       
   211         dbc.execute("SELECT address ||'@'|| domainname FROM alias, domain_name\
       
   212  WHERE destination = %s AND domain_name.gid = alias.gid\
       
   213  AND domain_name.is_primary ORDER BY address", str(self._addr))
       
   214         addresses = dbc.fetchall()
       
   215         dbc.close()
       
   216         aliases = []
       
   217         if len(addresses) > 0:
       
   218             aliases = [alias[0] for alias in addresses]
       
   219         return aliases
       
   220 
       
   221     def delete(self, delalias):
       
   222         if self._uid < 1:
       
   223             raise AccE(_(u"The account “%s” doesn't exist.") % self._addr,
       
   224                     ERR.NO_SUCH_ACCOUNT)
       
   225         dbc = self._dbh.cursor()
       
   226         if delalias == 'delalias':
       
   227             dbc.execute('DELETE FROM users WHERE uid= %s', self._uid)
       
   228             u_rc = dbc.rowcount
       
   229             # delete also all aliases where the destination address is the same
       
   230             # as for this account.
       
   231             dbc.execute("DELETE FROM alias WHERE destination = %s",
       
   232                     str(self._addr))
       
   233             if u_rc > 0 or dbc.rowcount > 0:
       
   234                 self._dbh.commit()
       
   235         else: # check first for aliases
       
   236             a_count = self.__aliaseCount()
       
   237             if a_count == 0:
       
   238                 dbc.execute('DELETE FROM users WHERE uid = %s', self._uid)
       
   239                 if dbc.rowcount > 0:
       
   240                     self._dbh.commit()
       
   241             else:
       
   242                 dbc.close()
       
   243                 raise AccE(
       
   244                   _(u"There are %(count)d aliases with the destination address\
       
   245  “%(address)s”.") %{'count': a_count, 'address': self._addr}, ERR.ALIAS_PRESENT)
       
   246         dbc.close()
       
   247 
       
   248 def getAccountByID(uid, dbh):
       
   249     try:
       
   250         uid = long(uid)
       
   251     except ValueError:
       
   252         raise AccE(_(u'uid must be an int/long.'), ERR.INVALID_AGUMENT)
       
   253     if uid < 1:
       
   254         raise AccE(_(u'uid must be greater than 0.'), ERR.INVALID_AGUMENT)
       
   255     dbc = dbh.cursor()
       
   256     dbc.execute("SELECT local_part||'@'|| domain_name.domainname AS address,\
       
   257  uid, users.gid FROM users LEFT JOIN domain_name ON (domain_name.gid \
       
   258  = users.gid AND is_primary) WHERE uid = %s;", uid)
       
   259     info = dbc.fetchone()
       
   260     dbc.close()
       
   261     if info is None:
       
   262         raise AccE(_(u"There is no account with the UID “%d”.") % uid,
       
   263                 ERR.NO_SUCH_ACCOUNT)
       
   264     keys = ['address', 'uid', 'gid']
       
   265     info = dict(zip(keys, info))
       
   266     return info
       
   267