VirtualMailManager/Alias.py
branchv0.6.x
changeset 196 65a3163bd113
parent 185 6e1ef32fbd82
child 198 02d467e4fbab
equal deleted inserted replaced
195:05dd49fc3ea1 196:65a3163bd113
     1 # -*- coding: UTF-8 -*-
     1 # -*- coding: UTF-8 -*-
     2 # Copyright (c) 2007 - 2010, Pascal Volk
     2 # Copyright (c) 2007 - 2010, Pascal Volk
     3 # See COPYING for distribution information.
     3 # See COPYING for distribution information.
     4 
     4 
     5 """Virtual Mail Manager's Alias class to manage e-mail aliases."""
     5 """
       
     6     VirtualMailManager.Alias
     6 
     7 
     7 import VirtualMailManager.constants.ERROR as ERR
     8     Virtual Mail Manager's Alias class to manage e-mail aliases.
       
     9 """
       
    10 
     8 from VirtualMailManager.Domain import Domain
    11 from VirtualMailManager.Domain import Domain
     9 from VirtualMailManager.EmailAddress import EmailAddress
    12 from VirtualMailManager.EmailAddress import EmailAddress
    10 from VirtualMailManager.Exceptions import VMMAliasException as VMMAE
    13 from VirtualMailManager.Exceptions import VMMAliasException as VMMAE
    11 import VirtualMailManager as VMM
    14 from VirtualMailManager.constants.ERROR import ALIAS_ADDR_DEST_IDENTICAL, \
       
    15      ALIAS_EXCEEDS_EXPANSION_LIMIT, ALIAS_EXISTS, NO_SUCH_ALIAS, NO_SUCH_DOMAIN
       
    16 
       
    17 
       
    18 _ = lambda msg: msg
       
    19 
    12 
    20 
    13 class Alias(object):
    21 class Alias(object):
    14     """Class to manage e-mail aliases."""
    22     """Class to manage e-mail aliases."""
    15     __slots__ = ('_addr', '_dest', '_gid', '_isNew', '_dbh')
    23     __slots__ = ('_addr', '_dests', '_gid', '_dbh')
    16     def __init__(self, dbh, address, destination=None):
    24 
       
    25     def __init__(self, dbh, address):
    17         if isinstance(address, EmailAddress):
    26         if isinstance(address, EmailAddress):
    18             self._addr = address
    27             self._addr = address
    19         else:
    28         else:
    20             raise TypeError("Argument 'address' is not an EmailAddress")
    29             raise TypeError("Argument 'address' is not an EmailAddress")
    21         if destination is None:
       
    22             self._dest = None
       
    23         elif isinstance(destination, EmailAddress):
       
    24             self._dest = destination
       
    25         else:
       
    26             raise TypeError("Argument 'destination' is not an EmailAddress")
       
    27         if address == destination:
       
    28             raise VMMAE(_(u"Address and destination are identical."),
       
    29                 ERR.ALIAS_ADDR_DEST_IDENTICAL)
       
    30         self._dbh = dbh
    30         self._dbh = dbh
    31         self._gid = 0
    31         self._gid = 0
    32         self._isNew = False
    32         self._dests = []
    33         self._setAddr()
       
    34         if not self._dest is None:
       
    35             self._exists()
       
    36         if VMM.VirtualMailManager.accountExists(self._dbh, self._addr):
       
    37             raise VMMAE(_(u"There is already an account with address “%s”.") %\
       
    38                     self._addr, ERR.ACCOUNT_EXISTS)
       
    39         if VMM.VirtualMailManager.relocatedExists(self._dbh, self._addr):
       
    40             raise VMMAE(
       
    41               _(u"There is already a relocated user with the address “%s”.") %\
       
    42                     self._addr, ERR.RELOCATED_EXISTS)
       
    43 
    33 
    44     def _exists(self):
    34         self.__set_gid()
    45         dbc = self._dbh.cursor()
    35         self.__load_dests()
    46         dbc.execute("SELECT gid FROM alias WHERE gid=%s AND address=%s\
       
    47  AND destination=%s", self._gid, self._addr._localpart, str(self._dest))
       
    48         gid = dbc.fetchone()
       
    49         dbc.close()
       
    50         if gid is None:
       
    51             self._isNew = True
       
    52 
    36 
    53     def _setAddr(self):
    37     def __set_gid(self):
    54         dom = Domain(self._dbh, self._addr._domainname)
    38         """Sets the alias' _gid based on its _addr.domainname."""
       
    39         dom = Domain(self._dbh, self._addr.domainname)
    55         self._gid = dom.getID()
    40         self._gid = dom.getID()
    56         if self._gid == 0:
    41         if self._gid == 0:
    57             raise VMMAE(_(u"The domain “%s” doesn't exist.") %\
    42             raise VMMAE(_(u"The domain “%s” doesn't exist.") %
    58                     self._addr._domainname, ERR.NO_SUCH_DOMAIN)
    43                         self._addr.domainname, NO_SUCH_DOMAIN)
    59 
    44 
    60     def _checkExpansion(self, limit):
    45     def __load_dests(self):
       
    46         """Loads all known destination addresses into the _dests list."""
    61         dbc = self._dbh.cursor()
    47         dbc = self._dbh.cursor()
    62         dbc.execute('SELECT count(gid) FROM alias where gid=%s AND address=%s',
    48         dbc.execute(
    63                 self._gid, self._addr._localpart)
    49                 'SELECT destination FROM alias WHERE gid=%s AND address=%s',
    64         curEx = dbc.fetchone()[0]
    50                     self._gid, self._addr.localpart)
       
    51         dests = iter(dbc.fetchall())
       
    52         if dbc.rowcount > 0:
       
    53             dest_add = self._dests.append
       
    54             for dest in dests:
       
    55                 dest_add(EmailAddress(dest[0]))
    65         dbc.close()
    56         dbc.close()
    66         if curEx == limit:
    57 
    67             errmsg = _(u"""Can't add new destination to alias “%(address)s”.
    58     def __check_expansion(self, limit):
    68 Currently this alias expands into %(count)i recipients.
    59         """Checks the current expansion limit of the alias."""
       
    60         dcount = len(self._dests)
       
    61         failed = False
       
    62         if dcount == limit:
       
    63             failed = True
       
    64             errmsg = _(
       
    65 u"""Can't add new destination to alias “%(address)s”.
       
    66 Currently this alias expands into %(count)i/%(limit)i recipients.
    69 One more destination will render this alias unusable.
    67 One more destination will render this alias unusable.
    70 Hint: Increase Postfix' virtual_alias_expansion_limit
    68 Hint: Increase Postfix' virtual_alias_expansion_limit""")
    71 """) % {'address': self._addr, 'count': curEx}
    69         elif dcount > limit:
    72             raise VMMAE(errmsg, ERR.ALIAS_EXCEEDS_EXPANSION_LIMIT)
    70             failed = True
       
    71             errmsg = _(
       
    72 u"""Can't add new destination to alias “%(address)s”.
       
    73 This alias already exceeds it's expansion limit (%(count)i/%(limit)i).
       
    74 So its unusable, all messages addressed to this alias will be bounced.
       
    75 Hint: Delete some destination addresses.""")
       
    76         if failed:
       
    77             raise VMMAE(errmsg % {'address': self._addr, 'count': dcount,
       
    78                                   'limit': limit},
       
    79                         ALIAS_EXCEEDS_EXPANSION_LIMIT)
    73 
    80 
    74     def save(self, expansion_limit):
    81     def __delete(self, destination=None):
    75         if self._dest is None:
    82         """Deletes a destination from the alias, if ``destination`` is not
    76            raise VMMAE(_(u"No destination address specified for alias."),
    83         ``None``. If ``destination`` is None, the alias with all it's
    77                ERR.ALIAS_MISSING_DEST)
    84         destination addresses will be deleted."""
    78         if self._isNew:
    85         dbc = self._dbh.cursor()
    79             self._checkExpansion(expansion_limit)
    86         if destination is None:
       
    87             dbc.execute("DELETE FROM alias WHERE gid=%s AND address=%s",
       
    88                         self._gid, self._addr.localpart)
       
    89         else:
       
    90             dbc.execute("DELETE FROM alias WHERE gid=%s AND address=%s AND \
       
    91  destination=%s",
       
    92                         self._gid, self._addr.localpart, str(destination))
       
    93         if dbc.rowcount > 0:
       
    94             self._dbh.commit()
       
    95         dbc.close()
       
    96 
       
    97     def __len__(self):
       
    98         """Returns the number of destinations of the alias."""
       
    99         return len(self._dests)
       
   100 
       
   101     def addDestination(self, destination, expansion_limit):
       
   102         """Adds the ``destination`` `EmailAddress` to the alias."""
       
   103         if not isinstance(destination, EmailAddress):
       
   104             raise TypeError("Argument 'destination' is not an EmailAddress")
       
   105         if self._addr == destination:
       
   106             raise VMMAE(_(u"Address and destination are identical."),
       
   107                         ALIAS_ADDR_DEST_IDENTICAL)
       
   108         if not destination in self._dests:
       
   109             self.__check_expansion(expansion_limit)
    80             dbc = self._dbh.cursor()
   110             dbc = self._dbh.cursor()
    81             dbc.execute("INSERT INTO alias (gid, address, destination) VALUES\
   111             dbc.execute('INSERT INTO alias (gid, address, destination) \
    82  (%s, %s, %s)", self._gid, self._addr._localpart, str(self._dest))
   112 VALUES (%s, %s, %s)',
       
   113                         self._gid, self._addr.localpart, str(destination))
    83             self._dbh.commit()
   114             self._dbh.commit()
    84             dbc.close()
   115             dbc.close()
       
   116             self._dests.append(destination)
    85         else:
   117         else:
    86             raise VMMAE(
   118             raise VMMAE(_(
    87                _(u"The alias “%(a)s” with destination “%(d)s” already exists.")\
   119                 u'The alias “%(a)s” has already the destination “%(d)s”.') %
    88                        % {'a': self._addr, 'd': self._dest}, ERR.ALIAS_EXISTS)
   120                         {'a': self._addr, 'd': destination}, ALIAS_EXISTS)
    89 
   121 
    90     def getInfo(self):
   122     def delDestination(self, destination):
    91         dbc = self._dbh.cursor()
   123         """Deletes the specified ``destination`` address from the alias."""
    92         dbc.execute('SELECT destination FROM alias WHERE gid=%s AND address=%s',
   124         if not isinstance(destination, EmailAddress):
    93                 self._gid, self._addr._localpart)
   125             raise TypeError("Argument 'destination' is not an EmailAddress")
    94         destinations = dbc.fetchall()
   126         if not self._dests:
    95         dbc.close()
   127             raise VMMAE(_(u"The alias “%s” doesn't exist.") % self._addr,
    96         if len(destinations) > 0:
   128                         NO_SUCH_ALIAS)
    97             targets = [destination[0] for destination in destinations]
   129         if not destination in self._dests:
    98             return targets
   130             raise VMMAE(_(u"The address “%(d)s” isn't a destination of \
       
   131 the alias “%(a)s”.") %
       
   132                         {'a': self._addr, 'd': destination}, NO_SUCH_ALIAS)
       
   133         self.__delete(destination)
       
   134         self._dests.remove(destination)
       
   135 
       
   136     def getDestinations(self):
       
   137         """Returns an iterator for all destinations of the alias."""
       
   138         if self._dests:
       
   139             return iter(self._dests)
    99         else:
   140         else:
   100             raise VMMAE(_(u"The alias “%s” doesn't exist.") % self._addr,
   141             raise VMMAE(_(u"The alias “%s” doesn't exist.") % self._addr,
   101                     ERR.NO_SUCH_ALIAS)
   142                         NO_SUCH_ALIAS)
   102 
   143 
   103     def delete(self):
   144     def delete(self):
   104         dbc = self._dbh.cursor()
   145         """Deletes the alias with all it's destinations."""
   105         if self._dest is None:
   146         if self._dests:
   106             dbc.execute("DELETE FROM alias WHERE gid=%s AND address=%s",
   147             self.__delete()
   107                     self._gid, self._addr._localpart)
   148             del self._dests[:]
   108         else:
   149         else:
   109             dbc.execute("DELETE FROM alias WHERE gid=%s AND address=%s AND \
   150             raise VMMAE(_(u"The alias “%s” doesn't exist.") % self._addr,
   110  destination=%s", self._gid, self._addr._localpart, str(self._dest))
   151                         NO_SUCH_ALIAS)
   111         rowcount = dbc.rowcount
       
   112         dbc.close()
       
   113         if rowcount > 0:
       
   114             self._dbh.commit()
       
   115         else:
       
   116             if self._dest is None:
       
   117                 msg = _(u"The alias “%s” doesn't exist.") % self._addr
       
   118             else:
       
   119                 msg = _(u"The alias “%(a)s” with destination “%(d)s” doesn't\
       
   120  exist.") % {'a': self._addr, 'd': self._dest}
       
   121             raise VMMAE(msg, ERR.NO_SUCH_ALIAS)
       
   122 
   152 
       
   153 
       
   154 del _