VirtualMailManager/Handler.py
branchv0.6.x
changeset 196 65a3163bd113
parent 192 0854fb9f3bc5
child 197 d2712e8c724e
equal deleted inserted replaced
195:05dd49fc3ea1 196:65a3163bd113
    40 
    40 
    41 
    41 
    42 class Handler(object):
    42 class Handler(object):
    43     """Wrapper class to simplify the access on all the stuff from
    43     """Wrapper class to simplify the access on all the stuff from
    44     VirtualMailManager"""
    44     VirtualMailManager"""
    45     __slots__ = ('_Cfg', '_cfgFileName', '__dbh', '_scheme', '__warnings',
    45     __slots__ = ('_Cfg', '_cfgFileName', '_dbh', '_scheme', '__warnings',
    46                  '_postconf')
    46                  '_postconf')
    47 
    47 
    48     def __init__(self, skip_some_checks=False):
    48     def __init__(self, skip_some_checks=False):
    49         """Creates a new Handler instance.
    49         """Creates a new Handler instance.
    50 
    50 
    56         Throws a VMMNotRootException if your uid is greater 0.
    56         Throws a VMMNotRootException if your uid is greater 0.
    57         """
    57         """
    58         self._cfgFileName = ''
    58         self._cfgFileName = ''
    59         self.__warnings = []
    59         self.__warnings = []
    60         self._Cfg = None
    60         self._Cfg = None
    61         self.__dbh = None
    61         self._dbh = None
    62 
    62 
    63         if os.geteuid():
    63         if os.geteuid():
    64             raise VMMNotRootException(_(u"You are not root.\n\tGood bye!\n"),
    64             raise VMMNotRootException(_(u"You are not root.\n\tGood bye!\n"),
    65                 ERR.CONF_NOPERM)
    65                 ERR.CONF_NOPERM)
    66         if self.__chkCfgFile():
    66         if self.__chkCfgFile():
   127                 else:
   127                 else:
   128                     raise
   128                     raise
   129 
   129 
   130     def __dbConnect(self):
   130     def __dbConnect(self):
   131         """Creates a pyPgSQL.PgSQL.connection instance."""
   131         """Creates a pyPgSQL.PgSQL.connection instance."""
   132         if self.__dbh is None or (isinstance(self.__dbh, PgSQL.Connection) and
   132         if self._dbh is None or (isinstance(self._dbh, PgSQL.Connection) and
   133                                   not self.__dbh._isOpen):
   133                                   not self._dbh._isOpen):
   134             try:
   134             try:
   135                 self.__dbh = PgSQL.connect(
   135                 self._dbh = PgSQL.connect(
   136                         database=self._Cfg.dget('database.name'),
   136                         database=self._Cfg.dget('database.name'),
   137                         user=self._Cfg.pget('database.user'),
   137                         user=self._Cfg.pget('database.user'),
   138                         host=self._Cfg.dget('database.host'),
   138                         host=self._Cfg.dget('database.host'),
   139                         password=self._Cfg.pget('database.pass'),
   139                         password=self._Cfg.pget('database.pass'),
   140                         client_encoding='utf8', unicode_results=True)
   140                         client_encoding='utf8', unicode_results=True)
   141                 dbc = self.__dbh.cursor()
   141                 dbc = self._dbh.cursor()
   142                 dbc.execute("SET NAMES 'UTF8'")
   142                 dbc.execute("SET NAMES 'UTF8'")
   143                 dbc.close()
   143                 dbc.close()
   144             except PgSQL.libpq.DatabaseError, e:
   144             except PgSQL.libpq.DatabaseError, e:
   145                 raise VMMException(str(e), ERR.DATABASE_ERROR)
   145                 raise VMMException(str(e), ERR.DATABASE_ERROR)
   146 
   146 
   155             return True
   155             return True
   156     _exists = staticmethod(_exists)
   156     _exists = staticmethod(_exists)
   157 
   157 
   158     def accountExists(dbh, address):
   158     def accountExists(dbh, address):
   159         sql = "SELECT gid FROM users WHERE gid = (SELECT gid FROM domain_name\
   159         sql = "SELECT gid FROM users WHERE gid = (SELECT gid FROM domain_name\
   160  WHERE domainname = '%s') AND local_part = '%s'" % (address._domainname,
   160  WHERE domainname = '%s') AND local_part = '%s'" % (address.domainname,
   161             address._localpart)
   161             address.localpart)
   162         return Handler._exists(dbh, sql)
   162         return Handler._exists(dbh, sql)
   163     accountExists = staticmethod(accountExists)
   163     accountExists = staticmethod(accountExists)
   164 
   164 
   165     def aliasExists(dbh, address):
   165     def aliasExists(dbh, address):
   166         sql = "SELECT DISTINCT gid FROM alias WHERE gid = (SELECT gid FROM\
   166         sql = "SELECT DISTINCT gid FROM alias WHERE gid = (SELECT gid FROM\
   167  domain_name WHERE domainname = '%s') AND address = '%s'" % (
   167  domain_name WHERE domainname = '%s') AND address = '%s'" % (
   168                 address._domainname, address._localpart)
   168                 address.domainname, address.localpart)
   169         return Handler._exists(dbh, sql)
   169         return Handler._exists(dbh, sql)
   170     aliasExists = staticmethod(aliasExists)
   170     aliasExists = staticmethod(aliasExists)
   171 
   171 
   172     def relocatedExists(dbh, address):
   172     def relocatedExists(dbh, address):
   173         sql = "SELECT gid FROM relocated WHERE gid = (SELECT gid FROM\
   173         sql = "SELECT gid FROM relocated WHERE gid = (SELECT gid FROM\
   174  domain_name WHERE domainname = '%s') AND address = '%s'" % (
   174  domain_name WHERE domainname = '%s') AND address = '%s'" % (
   175                 address._domainname, address._localpart)
   175                 address.domainname, address.localpart)
   176         return Handler._exists(dbh, sql)
   176         return Handler._exists(dbh, sql)
   177     relocatedExists = staticmethod(relocatedExists)
   177     relocatedExists = staticmethod(relocatedExists)
   178 
   178 
   179     def __getAccount(self, address, password=None):
   179     def __getAccount(self, address, password=None):
   180         self.__dbConnect()
   180         self.__dbConnect()
   181         address = EmailAddress(address)
   181         address = EmailAddress(address)
   182         if not password is None:
   182         if not password is None:
   183             password = self.__pwhash(password)
   183             password = self.__pwhash(password)
   184         return Account(self.__dbh, address, password)
   184         return Account(self._dbh, address, password)
   185 
   185 
   186     def __getAlias(self, address, destination=None):
   186     def __getAlias(self, address):
       
   187         self.__dbConnect()
       
   188         address = EmailAddress(address)
       
   189         return Alias(self._dbh, address)
       
   190 
       
   191     def __getRelocated(self, address, destination=None):
   187         self.__dbConnect()
   192         self.__dbConnect()
   188         address = EmailAddress(address)
   193         address = EmailAddress(address)
   189         if destination is not None:
   194         if destination is not None:
   190             destination = EmailAddress(destination)
   195             destination = EmailAddress(destination)
   191         return Alias(self.__dbh, address, destination)
   196         return Relocated(self._dbh, address, destination)
   192 
       
   193     def __getRelocated(self, address, destination=None):
       
   194         self.__dbConnect()
       
   195         address = EmailAddress(address)
       
   196         if destination is not None:
       
   197             destination = EmailAddress(destination)
       
   198         return Relocated(self.__dbh, address, destination)
       
   199 
   197 
   200     def __getDomain(self, domainname, transport=None):
   198     def __getDomain(self, domainname, transport=None):
   201         if transport is None:
   199         if transport is None:
   202             transport = self._Cfg.dget('misc.transport')
   200             transport = self._Cfg.dget('misc.transport')
   203         self.__dbConnect()
   201         self.__dbConnect()
   204         return Domain(self.__dbh, domainname,
   202         return Domain(self._dbh, domainname,
   205                 self._Cfg.dget('misc.base_directory'), transport)
   203                 self._Cfg.dget('misc.base_directory'), transport)
   206 
   204 
   207     def __getDiskUsage(self, directory):
   205     def __getDiskUsage(self, directory):
   208         """Estimate file space usage for the given directory.
   206         """Estimate file space usage for the given directory.
   209 
   207 
   472         Keyword arguments:
   470         Keyword arguments:
   473         aliasname -- the name of the alias domain (str)
   471         aliasname -- the name of the alias domain (str)
   474         domainname -- name of the target domain (str)
   472         domainname -- name of the target domain (str)
   475         """
   473         """
   476         dom = self.__getDomain(domainname)
   474         dom = self.__getDomain(domainname)
   477         aliasDom = AliasDomain(self.__dbh, aliasname, dom)
   475         aliasDom = AliasDomain(self._dbh, aliasname, dom)
   478         aliasDom.save()
   476         aliasDom.save()
   479 
   477 
   480     def aliasDomainInfo(self, aliasname):
   478     def aliasDomainInfo(self, aliasname):
   481         self.__dbConnect()
   479         self.__dbConnect()
   482         aliasDom = AliasDomain(self.__dbh, aliasname, None)
   480         aliasDom = AliasDomain(self._dbh, aliasname, None)
   483         return aliasDom.info()
   481         return aliasDom.info()
   484 
   482 
   485     def aliasDomainSwitch(self, aliasname, domainname):
   483     def aliasDomainSwitch(self, aliasname, domainname):
   486         """Modifies the target domain of an existing alias domain.
   484         """Modifies the target domain of an existing alias domain.
   487 
   485 
   488         Keyword arguments:
   486         Keyword arguments:
   489         aliasname -- the name of the alias domain (str)
   487         aliasname -- the name of the alias domain (str)
   490         domainname -- name of the new target domain (str)
   488         domainname -- name of the new target domain (str)
   491         """
   489         """
   492         dom = self.__getDomain(domainname)
   490         dom = self.__getDomain(domainname)
   493         aliasDom = AliasDomain(self.__dbh, aliasname, dom)
   491         aliasDom = AliasDomain(self._dbh, aliasname, dom)
   494         aliasDom.switch()
   492         aliasDom.switch()
   495 
   493 
   496     def aliasDomainDelete(self, aliasname):
   494     def aliasDomainDelete(self, aliasname):
   497         """Deletes the specified alias domain.
   495         """Deletes the specified alias domain.
   498 
   496 
   499         Keyword arguments:
   497         Keyword arguments:
   500         aliasname -- the name of the alias domain (str)
   498         aliasname -- the name of the alias domain (str)
   501         """
   499         """
   502         self.__dbConnect()
   500         self.__dbConnect()
   503         aliasDom = AliasDomain(self.__dbh, aliasname, None)
   501         aliasDom = AliasDomain(self._dbh, aliasname, None)
   504         aliasDom.delete()
   502         aliasDom.delete()
   505 
   503 
   506     def domainList(self, pattern=None):
   504     def domainList(self, pattern=None):
   507         from Domain import search
   505         from Domain import search
   508         like = False
   506         like = False
   518                 if not re.match(RE_DOMAIN_SRCH, domain):
   516                 if not re.match(RE_DOMAIN_SRCH, domain):
   519                     raise VMMException(
   517                     raise VMMException(
   520                     _(u"The pattern “%s” contains invalid characters.") %
   518                     _(u"The pattern “%s” contains invalid characters.") %
   521                     pattern, ERR.DOMAIN_INVALID)
   519                     pattern, ERR.DOMAIN_INVALID)
   522         self.__dbConnect()
   520         self.__dbConnect()
   523         return search(self.__dbh, pattern=pattern, like=like)
   521         return search(self._dbh, pattern=pattern, like=like)
   524 
   522 
   525     def userAdd(self, emailaddress, password):
   523     def userAdd(self, emailaddress, password):
   526         if password is None or (isinstance(password, basestring) and
   524         if password is None or (isinstance(password, basestring) and
   527                                 not len(password)):
   525                                 not len(password)):
   528             raise ValueError('could not accept password: %r' % password)
   526             raise ValueError('could not accept password: %r' % password)
   534                  self._Cfg.dget('account.imap'),
   532                  self._Cfg.dget('account.imap'),
   535                  self._Cfg.dget('account.sieve'))
   533                  self._Cfg.dget('account.sieve'))
   536         self.__mailDirMake(acc.getDir('domain'), acc.getUID(), acc.getGID())
   534         self.__mailDirMake(acc.getDir('domain'), acc.getUID(), acc.getGID())
   537 
   535 
   538     def aliasAdd(self, aliasaddress, targetaddress):
   536     def aliasAdd(self, aliasaddress, targetaddress):
   539         alias = self.__getAlias(aliasaddress, targetaddress)
   537         """Creates a new `Alias` entry for the given *aliasaddress* with
   540         alias.save(long(self._postconf.read('virtual_alias_expansion_limit')))
   538         the given *targetaddress*."""
   541         gid = self.__getDomain(alias._dest._domainname).getID()
   539         alias = self.__getAlias(aliasaddress)
   542         if gid > 0 and (not Handler.accountExists(self.__dbh, alias._dest) and
   540         destination = EmailAddress(targetaddress)
   543                         not Handler.aliasExists(self.__dbh, alias._dest)):
   541         alias.addDestination(destination,
       
   542                     long(self._postconf.read('virtual_alias_expansion_limit')))
       
   543         gid = self.__getDomain(destination.domainname).getID()
       
   544         if gid > 0 and (not Handler.accountExists(self._dbh, destination) and
       
   545                         not Handler.aliasExists(self._dbh, destination)):
   544             self.__warnings.append(
   546             self.__warnings.append(
   545                 _(u"The destination account/alias “%s” doesn't exist.") %
   547                 _(u"The destination account/alias “%s” doesn't exist.") %
   546                                    alias._dest)
   548                                    destination)
   547 
   549 
   548     def userDelete(self, emailaddress, force=None):
   550     def userDelete(self, emailaddress, force=None):
   549         if force not in [None, 'delalias']:
   551         if force not in [None, 'delalias']:
   550             raise VMMException(_(u"Invalid argument: “%s”") % force,
   552             raise VMMException(_(u"Invalid argument: “%s”") % force,
   551                     ERR.INVALID_AGUMENT)
   553                     ERR.INVALID_AGUMENT)
   568                     self.__warnings.append(warning)
   570                     self.__warnings.append(warning)
   569                 else:
   571                 else:
   570                     raise
   572                     raise
   571 
   573 
   572     def aliasInfo(self, aliasaddress):
   574     def aliasInfo(self, aliasaddress):
       
   575         """Returns an iterator object for all destinations (`EmailAddress`
       
   576         instances) for the `Alias` with the given *aliasaddress*."""
   573         alias = self.__getAlias(aliasaddress)
   577         alias = self.__getAlias(aliasaddress)
   574         return alias.getInfo()
   578         try:
       
   579             return alias.getDestinations()
       
   580         except VMMAliasException, e:
       
   581             if e.code() == ERR.NO_SUCH_ALIAS:
       
   582                 if Handler.accountExists(self._dbh, alias._addr):
       
   583                     raise VMMException(
       
   584                         _(u'There is already an account with address “%s”.') %
       
   585                                        aliasaddress, ERR.ACCOUNT_EXISTS)
       
   586                 if Handler.relocatedExists(self._dbh, alias._addr):
       
   587                     raise VMMException(_(u'There is already a relocated user \
       
   588 with the address “%s”.') %
       
   589                                        aliasaddress, ERR.RELOCATED_EXISTS)
       
   590                 raise
       
   591             else:
       
   592                 raise
   575 
   593 
   576     def aliasDelete(self, aliasaddress, targetaddress=None):
   594     def aliasDelete(self, aliasaddress, targetaddress=None):
   577         alias = self.__getAlias(aliasaddress, targetaddress)
   595         """Deletes the `Alias` *aliasaddress* with all its destinations from
   578         alias.delete()
   596         the database. If *targetaddress* is not ``None``, only this
       
   597         destination will be removed from the alias."""
       
   598         alias = self.__getAlias(aliasaddress)
       
   599         if targetaddress is None:
       
   600             alias.delete()
       
   601         else:
       
   602             alias.delDestination(EmailAddress(targetaddress))
   579 
   603 
   580     def userInfo(self, emailaddress, details=None):
   604     def userInfo(self, emailaddress, details=None):
   581         if details not in (None, 'du', 'aliases', 'full'):
   605         if details not in (None, 'du', 'aliases', 'full'):
   582             raise VMMException(_(u'Invalid argument: “%s”') % details,
   606             raise VMMException(_(u'Invalid argument: “%s”') % details,
   583                                ERR.INVALID_AGUMENT)
   607                                ERR.INVALID_AGUMENT)
   592         return info
   616         return info
   593 
   617 
   594     def userByID(self, uid):
   618     def userByID(self, uid):
   595         from Handler.Account import getAccountByID
   619         from Handler.Account import getAccountByID
   596         self.__dbConnect()
   620         self.__dbConnect()
   597         return getAccountByID(uid, self.__dbh)
   621         return getAccountByID(uid, self._dbh)
   598 
   622 
   599     def userPassword(self, emailaddress, password):
   623     def userPassword(self, emailaddress, password):
   600         if password is None or (isinstance(password, basestring) and
   624         if password is None or (isinstance(password, basestring) and
   601                                 not len(password)):
   625                                 not len(password)):
   602             raise ValueError('could not accept password: %r' % password)
   626             raise ValueError('could not accept password: %r' % password)
   645     def relocatedDelete(self, emailaddress):
   669     def relocatedDelete(self, emailaddress):
   646         relocated = self.__getRelocated(emailaddress)
   670         relocated = self.__getRelocated(emailaddress)
   647         relocated.delete()
   671         relocated.delete()
   648 
   672 
   649     def __del__(self):
   673     def __del__(self):
   650         if isinstance(self.__dbh, PgSQL.Connection) and self.__dbh._isOpen:
   674         if isinstance(self._dbh, PgSQL.Connection) and self._dbh._isOpen:
   651             self.__dbh.close()
   675             self._dbh.close()