VirtualMailManager/Domain.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 Domain class to manage e-mail domains."""
       
     6 
       
     7 from random import choice
       
     8 
       
     9 from __main__ import ERR
       
    10 from Exceptions import VMMDomainException as VMMDE
       
    11 import VirtualMailManager as VMM
       
    12 from Transport import Transport
       
    13 
       
    14 MAILDIR_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz'
       
    15 
       
    16 class Domain(object):
       
    17     """Class to manage e-mail domains."""
       
    18     __slots__ = ('_basedir','_domaindir','_id','_name','_transport','_dbh')
       
    19     def __init__(self, dbh, domainname, basedir=None, transport=None):
       
    20         """Creates a new Domain instance.
       
    21 
       
    22         Keyword arguments:
       
    23         dbh -- a pyPgSQL.PgSQL.connection
       
    24         domainname -- name of the domain (str)
       
    25         transport -- default vmm.cfg/misc/transport  (str)
       
    26         """
       
    27         self._dbh = dbh
       
    28         self._name = VMM.VirtualMailManager.chkDomainname(domainname)
       
    29         self._basedir = basedir
       
    30         if transport is not None:
       
    31             self._transport = Transport(self._dbh, transport=transport)
       
    32         else:
       
    33             self._transport = transport
       
    34         self._id = 0
       
    35         self._domaindir = None
       
    36         if not self._exists() and self._isAlias():
       
    37             raise VMMDE(_(u"The domain “%s” is an alias domain.") %self._name,
       
    38                     ERR.DOMAIN_ALIAS_EXISTS)
       
    39 
       
    40     def _exists(self):
       
    41         """Checks if the domain already exists.
       
    42 
       
    43         If the domain exists _id will be set and returns True, otherwise False
       
    44         will be returned.
       
    45         """
       
    46         dbc = self._dbh.cursor()
       
    47         dbc.execute("SELECT gid, tid, domaindir FROM domain_data WHERE gid =\
       
    48  (SELECT gid FROM domain_name WHERE domainname = %s AND is_primary)",
       
    49                 self._name)
       
    50         result = dbc.fetchone()
       
    51         dbc.close()
       
    52         if result is not None:
       
    53             self._id, self._domaindir = result[0], result[2]
       
    54             self._transport = Transport(self._dbh, tid=result[1])
       
    55             return True
       
    56         else:
       
    57             return False
       
    58 
       
    59     def _isAlias(self):
       
    60         """Checks if self._name is known for an alias domain."""
       
    61         dbc = self._dbh.cursor()
       
    62         dbc.execute('SELECT is_primary FROM domain_name WHERE domainname = %s',
       
    63                 self._name)
       
    64         result = dbc.fetchone()
       
    65         dbc.close()
       
    66         if result is not None and not result[0]:
       
    67             return True
       
    68         else:
       
    69             return False
       
    70 
       
    71     def _setID(self):
       
    72         """Sets the ID of the domain."""
       
    73         dbc = self._dbh.cursor()
       
    74         dbc.execute("SELECT nextval('domain_gid')")
       
    75         self._id = dbc.fetchone()[0]
       
    76         dbc.close()
       
    77 
       
    78     def _prepare(self):
       
    79         self._setID()
       
    80         self._domaindir = "%s/%s/%i" % (self._basedir, choice(MAILDIR_CHARS),
       
    81                 self._id)
       
    82 
       
    83     def _has(self, what):
       
    84         """Checks if aliases or accounts are assigned to the domain.
       
    85 
       
    86         If there are assigned accounts or aliases True will be returned,
       
    87         otherwise False will be returned.
       
    88 
       
    89         Keyword arguments:
       
    90         what -- 'alias' or 'users' (strings)
       
    91         """
       
    92         if what not in ['alias', 'users']:
       
    93             return False
       
    94         dbc = self._dbh.cursor()
       
    95         if what == 'users':
       
    96             dbc.execute("SELECT count(gid) FROM users WHERE gid=%s", self._id)
       
    97         else:
       
    98             dbc.execute("SELECT count(gid) FROM alias WHERE gid=%s", self._id)
       
    99         count = dbc.fetchone()
       
   100         dbc.close()
       
   101         if count[0] > 0:
       
   102             return True
       
   103         else:
       
   104             return False
       
   105 
       
   106     def _chkDelete(self, delUser, delAlias):
       
   107         """Checks dependencies for deletion.
       
   108 
       
   109         Keyword arguments:
       
   110         delUser -- ignore available accounts (bool)
       
   111         delAlias -- ignore available aliases (bool)
       
   112         """
       
   113         if not delUser:
       
   114             hasUser = self._has('users')
       
   115         else:
       
   116             hasUser = False
       
   117         if not delAlias:
       
   118             hasAlias = self._has('alias')
       
   119         else:
       
   120             hasAlias = False
       
   121         if hasUser and hasAlias:
       
   122             raise VMMDE(_(u'There are accounts and aliases.'),
       
   123                 ERR.ACCOUNT_AND_ALIAS_PRESENT)
       
   124         elif hasUser:
       
   125             raise VMMDE(_(u'There are accounts.'),
       
   126                 ERR.ACCOUNT_PRESENT)
       
   127         elif hasAlias:
       
   128             raise VMMDE(_(u'There are aliases.'),
       
   129                 ERR.ALIAS_PRESENT)
       
   130 
       
   131     def save(self):
       
   132         """Stores the new domain in the database."""
       
   133         if self._id < 1:
       
   134             self._prepare()
       
   135             dbc = self._dbh.cursor()
       
   136             dbc.execute("INSERT INTO domain_data (gid, tid, domaindir)\
       
   137  VALUES (%s, %s, %s)", self._id, self._transport.getID(), self._domaindir)
       
   138             dbc.execute("INSERT INTO domain_name (domainname, gid, is_primary)\
       
   139  VALUES (%s, %s, %s)", self._name, self._id, True)
       
   140             self._dbh.commit()
       
   141             dbc.close()
       
   142         else:
       
   143             raise VMMDE(_(u'The domain “%s” already exists.') % self._name,
       
   144                 ERR.DOMAIN_EXISTS)
       
   145 
       
   146     def delete(self, delUser=False, delAlias=False):
       
   147         """Deletes the domain.
       
   148 
       
   149         Keyword arguments:
       
   150         delUser -- force deletion of available accounts (bool)
       
   151         delAlias -- force deletion of available aliases (bool)
       
   152         """
       
   153         if self._id > 0:
       
   154             self._chkDelete(delUser, delAlias)
       
   155             dbc = self._dbh.cursor()
       
   156             for t in ('alias','users','relocated','domain_name','domain_data'):
       
   157                 dbc.execute("DELETE FROM %s WHERE gid = %d" % (t, self._id))
       
   158             self._dbh.commit()
       
   159             dbc.close()
       
   160         else:
       
   161             raise VMMDE(_(u"The domain “%s” doesn't exist.") % self._name,
       
   162                 ERR.NO_SUCH_DOMAIN)
       
   163 
       
   164     def updateTransport(self, transport, force=False):
       
   165         """Sets a new transport for the domain.
       
   166 
       
   167         Keyword arguments:
       
   168         transport -- the new transport (str)
       
   169         force -- True/False force new transport for all accounts (bool)
       
   170         """
       
   171         if self._id > 0:
       
   172             if transport == self._transport.getTransport():
       
   173                 return
       
   174             trsp = Transport(self._dbh, transport=transport)
       
   175             dbc = self._dbh.cursor()
       
   176             dbc.execute("UPDATE domain_data SET tid = %s WHERE gid = %s",
       
   177                     trsp.getID(), self._id)
       
   178             if dbc.rowcount > 0:
       
   179                 self._dbh.commit()
       
   180             if force:
       
   181                 dbc.execute("UPDATE users SET tid = %s WHERE gid = %s",
       
   182                         trsp.getID(), self._id)
       
   183                 if dbc.rowcount > 0:
       
   184                     self._dbh.commit()
       
   185             dbc.close()
       
   186         else:
       
   187             raise VMMDE(_(u"The domain “%s” doesn't exist.") % self._name,
       
   188                 ERR.NO_SUCH_DOMAIN)
       
   189 
       
   190     def getID(self):
       
   191         """Returns the ID of the domain."""
       
   192         return self._id
       
   193 
       
   194     def getDir(self):
       
   195         """Returns the directory of the domain."""
       
   196         return self._domaindir
       
   197 
       
   198     def getTransport(self):
       
   199         """Returns domain's transport."""
       
   200         return self._transport.getTransport()
       
   201 
       
   202     def getTransportID(self):
       
   203         """Returns the ID from the domain's transport."""
       
   204         return self._transport.getID()
       
   205 
       
   206     def getInfo(self):
       
   207         """Returns a dictionary with information about the domain."""
       
   208         sql = """\
       
   209 SELECT gid, domainname, transport, domaindir, aliasdomains, accounts,
       
   210        aliases, relocated
       
   211   FROM vmm_domain_info
       
   212  WHERE gid = %i""" % self._id
       
   213         dbc = self._dbh.cursor()
       
   214         dbc.execute(sql)
       
   215         info = dbc.fetchone()
       
   216         dbc.close()
       
   217         if info is None:
       
   218             raise VMMDE(_(u"The domain “%s” doesn't exist.") % self._name,
       
   219                     ERR.NO_SUCH_DOMAIN)
       
   220         else:
       
   221             keys = ['gid', 'domainname', 'transport', 'domaindir',
       
   222                     'aliasdomains', 'accounts', 'aliases', 'relocated']
       
   223             return dict(zip(keys, info))
       
   224 
       
   225     def getAccounts(self):
       
   226         """Returns a list with all accounts from the domain."""
       
   227         dbc = self._dbh.cursor()
       
   228         dbc.execute("SELECT local_part from users where gid = %s ORDER BY\
       
   229  local_part", self._id)
       
   230         users = dbc.fetchall()
       
   231         dbc.close()
       
   232         accounts = []
       
   233         if len(users) > 0:
       
   234             addr = u'@'.join
       
   235             _dom = self._name
       
   236             accounts = [addr((account[0], _dom)) for account in users]
       
   237         return accounts
       
   238 
       
   239     def getAliases(self):
       
   240         """Returns a list with all aliases from the domain."""
       
   241         dbc = self._dbh.cursor()
       
   242         dbc.execute("SELECT DISTINCT address FROM alias WHERE gid = %s\
       
   243  ORDER BY address",  self._id)
       
   244         addresses = dbc.fetchall()
       
   245         dbc.close()
       
   246         aliases = []
       
   247         if len(addresses) > 0:
       
   248             addr = u'@'.join
       
   249             _dom = self._name
       
   250             aliases = [addr((alias[0], _dom)) for alias in addresses]
       
   251         return aliases
       
   252 
       
   253     def getRelocated(self):
       
   254         """Returns a list with all addresses from relocated users."""
       
   255         dbc = self._dbh.cursor()
       
   256         dbc.execute("SELECT address FROM relocated WHERE gid = %s\
       
   257  ORDER BY address", self._id)
       
   258         addresses = dbc.fetchall()
       
   259         dbc.close()
       
   260         relocated = []
       
   261         if len(addresses) > 0:
       
   262             addr = u'@'.join
       
   263             _dom = self._name
       
   264             relocated = [addr((address[0], _dom)) for address in addresses]
       
   265         return relocated
       
   266 
       
   267     def getAliaseNames(self):
       
   268         """Returns a list with all alias names from the domain."""
       
   269         dbc = self._dbh.cursor()
       
   270         dbc.execute("SELECT domainname FROM domain_name WHERE gid = %s\
       
   271  AND NOT is_primary ORDER BY domainname", self._id)
       
   272         anames = dbc.fetchall()
       
   273         dbc.close()
       
   274         aliasdomains = []
       
   275         if len(anames) > 0:
       
   276             aliasdomains = [aname[0] for aname in anames]
       
   277         return aliasdomains
       
   278 
       
   279 def search(dbh, pattern=None, like=False):
       
   280     if pattern is not None and like is False:
       
   281         pattern = VMM.VirtualMailManager.chkDomainname(pattern)
       
   282     sql = 'SELECT gid, domainname, is_primary FROM domain_name'
       
   283     if pattern is None:
       
   284         pass
       
   285     elif like:
       
   286         sql += " WHERE domainname LIKE '%s'" % pattern
       
   287     else:
       
   288         sql += " WHERE domainname = '%s'" % pattern
       
   289     sql += ' ORDER BY is_primary DESC, domainname'
       
   290     dbc = dbh.cursor()
       
   291     dbc.execute(sql)
       
   292     result = dbc.fetchall()
       
   293     dbc.close()
       
   294 
       
   295     gids = [domain[0] for domain in result if domain[2]]
       
   296     domains = {}
       
   297     for gid, domain, is_primary in result:
       
   298         if is_primary:
       
   299             if not gid in domains:
       
   300                 domains[gid] = [domain]
       
   301             else:
       
   302                 domains[gid].insert(0, domain)
       
   303         else:
       
   304             if gid in gids:
       
   305                 if gid in domains:
       
   306                     domains[gid].append(domain)
       
   307                 else:
       
   308                     domains[gid] = [domain]
       
   309             else:
       
   310                 gids.append(gid)
       
   311                 domains[gid] = [None, domain]
       
   312     return gids, domains
       
   313