VirtualMailManager/alias.py
branchv0.6.x
changeset 320 011066435e6f
parent 316 31d8931dc535
child 322 94bd10e237e5
equal deleted inserted replaced
319:f4956b4ceba1 320:011066435e6f
       
     1 # -*- coding: UTF-8 -*-
       
     2 # Copyright (c) 2007 - 2010, Pascal Volk
       
     3 # See COPYING for distribution information.
       
     4 """
       
     5     VirtualMailManager.alias
       
     6     ~~~~~~~~~~~~~~~~~~~~~~~~
       
     7 
       
     8     Virtual Mail Manager's Alias class to manage e-mail aliases.
       
     9 """
       
    10 
       
    11 from VirtualMailManager.domain import get_gid
       
    12 from VirtualMailManager.emailaddress import EmailAddress
       
    13 from VirtualMailManager.errors import AliasError as AErr
       
    14 from VirtualMailManager.ext.postconf import Postconf
       
    15 from VirtualMailManager.pycompat import all
       
    16 from VirtualMailManager.constants import \
       
    17      ALIAS_EXCEEDS_EXPANSION_LIMIT, NO_SUCH_ALIAS, NO_SUCH_DOMAIN
       
    18 
       
    19 
       
    20 _ = lambda msg: msg
       
    21 cfg_dget = lambda option: None
       
    22 
       
    23 
       
    24 class Alias(object):
       
    25     """Class to manage e-mail aliases."""
       
    26     __slots__ = ('_addr', '_dests', '_gid', '_dbh')
       
    27 
       
    28     def __init__(self, dbh, address):
       
    29         assert isinstance(address, EmailAddress)
       
    30         self._addr = address
       
    31         self._dbh = dbh
       
    32         self._gid = get_gid(self._dbh, self._addr.domainname)
       
    33         if not self._gid:
       
    34             raise AErr(_(u"The domain '%s' doesn't exist.") %
       
    35                        self._addr.domainname, NO_SUCH_DOMAIN)
       
    36         self._dests = []
       
    37 
       
    38         self.__load_dests()
       
    39 
       
    40     def __load_dests(self):
       
    41         """Loads all known destination addresses into the _dests list."""
       
    42         dbc = self._dbh.cursor()
       
    43         dbc.execute('SELECT destination FROM alias WHERE gid = %s AND '
       
    44                     'address = %s', self._gid, self._addr.localpart)
       
    45         dests = dbc.fetchall()
       
    46         if dbc.rowcount > 0:
       
    47             self._dests.extend(EmailAddress(dest[0]) for dest in dests)
       
    48         dbc.close()
       
    49 
       
    50     def __check_expansion(self, count_new):
       
    51         """Checks the current expansion limit of the alias."""
       
    52         postconf = Postconf(cfg_dget('bin.postconf'))
       
    53         limit = long(postconf.read('virtual_alias_expansion_limit'))
       
    54         dcount = len(self._dests)
       
    55         failed = False
       
    56         if dcount == limit or dcount + count_new > limit:
       
    57             failed = True
       
    58             errmsg = _(
       
    59 u"""Can't add %(count_new)i new destination(s) to alias '%(address)s'.
       
    60 Currently this alias expands into %(count)i/%(limit)i recipients.
       
    61 %(count_new)i additional destination(s) will render this alias unusable.
       
    62 Hint: Increase Postfix' virtual_alias_expansion_limit""")
       
    63         elif dcount > limit:
       
    64             failed = True
       
    65             errmsg = _(
       
    66 u"""Can't add %(count_new)i new destination(s) to alias '%(address)s'.
       
    67 This alias already exceeds its expansion limit (%(count)i/%(limit)i).
       
    68 So its unusable, all messages addressed to this alias will be bounced.
       
    69 Hint: Delete some destination addresses.""")
       
    70         if failed:
       
    71             raise AErr(errmsg % {'address': self._addr, 'count': dcount,
       
    72                                  'limit': limit, 'count_new': count_new},
       
    73                        ALIAS_EXCEEDS_EXPANSION_LIMIT)
       
    74 
       
    75     def __delete(self, destination=None):
       
    76         """Deletes a destination from the alias, if ``destination`` is
       
    77         not ``None``.  If ``destination`` is None, the alias with all
       
    78         its destination addresses will be deleted.
       
    79 
       
    80         """
       
    81         dbc = self._dbh.cursor()
       
    82         if not destination:
       
    83             dbc.execute('DELETE FROM alias WHERE gid = %s AND address = %s',
       
    84                         self._gid, self._addr.localpart)
       
    85         else:
       
    86             dbc.execute('DELETE FROM alias WHERE gid = %s AND address = %s '
       
    87                         'AND destination = %s', self._gid,
       
    88                         self._addr.localpart, str(destination))
       
    89         if dbc.rowcount > 0:
       
    90             self._dbh.commit()
       
    91         dbc.close()
       
    92 
       
    93     def __len__(self):
       
    94         """Returns the number of destinations of the alias."""
       
    95         return len(self._dests)
       
    96 
       
    97     @property
       
    98     def address(self):
       
    99         """The Alias' EmailAddress instance."""
       
   100         return self._addr
       
   101 
       
   102     def add_destinations(self, destinations, warnings=None):
       
   103         """Adds the `EmailAddress`es from *destinations* list to the
       
   104         destinations of the alias.
       
   105 
       
   106         Destinations, that are already assigned to the alias, will be
       
   107         removed from *destinations*.  When done, this method will return
       
   108         a set with all destinations, that were saved in the database.
       
   109         """
       
   110         destinations = set(destinations)
       
   111         assert destinations and \
       
   112                 all(isinstance(dest, EmailAddress) for dest in destinations)
       
   113         if not warnings is None:
       
   114             assert isinstance(warnings, list)
       
   115         if self._addr in destinations:
       
   116             destinations.remove(self._addr)
       
   117             if not warnings is None:
       
   118                 warnings.append(self._addr)
       
   119         duplicates = destinations.intersection(set(self._dests))
       
   120         if duplicates:
       
   121             destinations.difference_update(set(self._dests))
       
   122             if not warnings is None:
       
   123                 warnings.extend(duplicates)
       
   124         if not destinations:
       
   125             return destinations
       
   126         self.__check_expansion(len(destinations))
       
   127         dbc = self._dbh.cursor()
       
   128         dbc.executemany("INSERT INTO alias VALUES (%d, '%s', %%s)" %
       
   129                         (self._gid, self._addr.localpart),
       
   130                         (str(destination) for destination in destinations))
       
   131         self._dbh.commit()
       
   132         dbc.close()
       
   133         self._dests.extend(destinations)
       
   134         return destinations
       
   135 
       
   136     def del_destination(self, destination):
       
   137         """Deletes the specified ``destination`` address from the alias."""
       
   138         assert isinstance(destination, EmailAddress)
       
   139         if not self._dests:
       
   140             raise AErr(_(u"The alias '%s' doesn't exist.") % self._addr,
       
   141                        NO_SUCH_ALIAS)
       
   142         if not destination in self._dests:
       
   143             raise AErr(_(u"The address '%(addr)s' isn't a destination of "
       
   144                          u"the alias '%(alias)s'.") % {'addr': self._addr,
       
   145                        'alias': destination}, NO_SUCH_ALIAS)
       
   146         self.__delete(destination)
       
   147         self._dests.remove(destination)
       
   148 
       
   149     def get_destinations(self):
       
   150         """Returns an iterator for all destinations of the alias."""
       
   151         if not self._dests:
       
   152             raise AErr(_(u"The alias '%s' doesn't exist.") % self._addr,
       
   153                        NO_SUCH_ALIAS)
       
   154         return iter(self._dests)
       
   155 
       
   156     def delete(self):
       
   157         """Deletes the alias with all its destinations."""
       
   158         if not self._dests:
       
   159             raise AErr(_(u"The alias '%s' doesn't exist.") % self._addr,
       
   160                        NO_SUCH_ALIAS)
       
   161         self.__delete()
       
   162         del self._dests[:]
       
   163 
       
   164 del _, cfg_dget