VirtualMailManager/VirtualMailManager.py
changeset 76 14c0a092d7d2
parent 70 a3663ad491bf
child 78 8f1e501b1bb1
equal deleted inserted replaced
75:af813ede1e19 76:14c0a092d7d2
     1 #!/usr/bin/env python
       
     2 # -*- coding: UTF-8 -*-
     1 # -*- coding: UTF-8 -*-
     3 # Copyright 2007-2008 VEB IT
     2 # Copyright 2007-2008 VEB IT
     4 # See COPYING for distribution information.
     3 # See COPYING for distribution information.
     5 # $Id$
     4 # $Id$
     6 
     5 
    21 from shutil import rmtree
    20 from shutil import rmtree
    22 from subprocess import Popen, PIPE
    21 from subprocess import Popen, PIPE
    23 
    22 
    24 from pyPgSQL import PgSQL # python-pgsql - http://pypgsql.sourceforge.net
    23 from pyPgSQL import PgSQL # python-pgsql - http://pypgsql.sourceforge.net
    25 
    24 
    26 from Exceptions import *
       
    27 import constants.ERROR as ERR
    25 import constants.ERROR as ERR
    28 from Config import Config as Cfg
       
    29 from Account import Account
    26 from Account import Account
    30 from Alias import Alias
    27 from Alias import Alias
       
    28 from AliasDomain import AliasDomain
       
    29 from Config import Config as Cfg
    31 from Domain import Domain
    30 from Domain import Domain
    32 from AliasDomain import AliasDomain
    31 from EmailAddress import EmailAddress
       
    32 from Exceptions import *
       
    33 from Relocated import Relocated
    33 
    34 
    34 SALTCHARS = './0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    35 SALTCHARS = './0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    35 RE_ASCII_CHARS = """^[\x20-\x7E]*$"""
    36 RE_ASCII_CHARS = """^[\x20-\x7E]*$"""
    36 RE_DOMAIN = """^(?:[a-z0-9-]{1,63}\.){1,}[a-z]{2,6}$"""
    37 RE_DOMAIN = """^(?:[a-z0-9-]{1,63}\.){1,}[a-z]{2,6}$"""
    37 RE_DOMAIN_SRCH = """^[a-z0-9-\.]+$"""
    38 RE_DOMAIN_SRCH = """^[a-z0-9-\.]+$"""
   112             dbc.execute("SET NAMES 'UTF8'")
   113             dbc.execute("SET NAMES 'UTF8'")
   113             dbc.close()
   114             dbc.close()
   114         except PgSQL.libpq.DatabaseError, e:
   115         except PgSQL.libpq.DatabaseError, e:
   115             raise VMMException(str(e), ERR.DATABASE_ERROR)
   116             raise VMMException(str(e), ERR.DATABASE_ERROR)
   116 
   117 
   117     def chkLocalpart(localpart):
       
   118         """Validates the local part of an e-mail address.
       
   119         
       
   120         Keyword arguments:
       
   121         localpart -- the e-mail address that should be validated (str)
       
   122         """
       
   123         if len(localpart) < 1:
       
   124             raise VMMException(_(u'No localpart specified.'),
       
   125                 ERR.LOCALPART_INVALID)
       
   126         if len(localpart) > 64:
       
   127             raise VMMException(_(u'The local part »%s« is too long') %
       
   128                 localpart, ERR.LOCALPART_TOO_LONG)
       
   129         ic = re.compile(RE_LOCALPART).findall(localpart)
       
   130         if len(ic):
       
   131             ichrs = ''
       
   132             for c in set(ic):
       
   133                 ichrs += u"»%s« " % c
       
   134             raise VMMException(_(u"The local part »%(lpart)s« contains invalid\
       
   135  characters: %(ichrs)s") % {'lpart': localpart, 'ichrs': ichrs},
       
   136                 ERR.LOCALPART_INVALID)
       
   137         return localpart
       
   138     chkLocalpart = staticmethod(chkLocalpart)
       
   139 
       
   140     def idn2ascii(domainname):
   118     def idn2ascii(domainname):
   141         """Converts an idn domainname in punycode.
   119         """Converts an idn domainname in punycode.
   142         
   120         
   143         Keyword arguments:
   121         Keyword arguments:
   144         domainname -- the domainname to convert (str)
   122         domainname -- the domainname to convert (str)
   177         if len(domainname) > 255:
   155         if len(domainname) > 255:
   178             raise VMMException(_(u'The domain name is too long.'),
   156             raise VMMException(_(u'The domain name is too long.'),
   179                 ERR.DOMAIN_TOO_LONG)
   157                 ERR.DOMAIN_TOO_LONG)
   180         re.compile(RE_DOMAIN)
   158         re.compile(RE_DOMAIN)
   181         if not re.match(RE_DOMAIN, domainname):
   159         if not re.match(RE_DOMAIN, domainname):
   182             raise VMMException(_(u'The domain name is invalid.'),
   160             raise VMMException(_(u'The domain name »%s« is invalid.') %\
   183                 ERR.DOMAIN_INVALID)
   161                     domainname, ERR.DOMAIN_INVALID)
   184         return domainname
   162         return domainname
   185     chkDomainname = staticmethod(chkDomainname)
   163     chkDomainname = staticmethod(chkDomainname)
   186 
   164 
   187     def chkEmailAddress(address):
   165     def _exists(dbh, query):
   188         try:
   166         dbc = dbh.cursor()
   189             localpart, domain = address.split('@')
   167         dbc.execute(query)
   190         except ValueError:
   168         gid = dbc.fetchone()
   191             raise VMMException(_(u"Missing '@' sign in e-mail address »%s«.") %
   169         dbc.close()
   192                 address, ERR.INVALID_ADDRESS)
   170         if gid is None:
   193         except AttributeError:
   171             return False
   194             raise VMMException(_(u"»%s« looks not like an e-mail address.") %
   172         else:
   195                 address, ERR.INVALID_ADDRESS)
   173             return True
   196         if len(domain) > 0:
   174     _exists = staticmethod(_exists)
   197             domain = VirtualMailManager.chkDomainname(domain)
   175 
   198         else:
   176     def accountExists(dbh, address):
   199             raise VMMException(_(u"Missing domain name after »%s@«.") %
   177         sql = "SELECT gid FROM users WHERE gid = (SELECT gid FROM domain_name\
   200                     localpart, ERR.DOMAIN_NO_NAME)
   178  WHERE domainname = '%(_domainname)s') AND local_part = '%(_localpart)s'" %\
   201         localpart = VirtualMailManager.chkLocalpart(localpart)
   179             address.__dict__
   202         return '%s@%s' % (localpart, domain)
   180         return VirtualMailManager._exists(dbh, sql)
   203     chkEmailAddress = staticmethod(chkEmailAddress)
   181     accountExists = staticmethod(accountExists)
       
   182 
       
   183     def aliasExists(dbh, address):
       
   184         sql = "SELECT DISTINCT gid FROM alias WHERE gid = (SELECT gid FROM\
       
   185  domain_name WHERE domainname = '%(_domainname)s') AND address =\
       
   186  '%(_localpart)s'" % address.__dict__
       
   187         return VirtualMailManager._exists(dbh, sql)
       
   188     aliasExists = staticmethod(aliasExists)
       
   189 
       
   190     def relocatedExists(dbh, address):
       
   191         sql = "SELECT gid FROM relocated WHERE gid = (SELECT gid FROM\
       
   192  domain_name WHERE domainname = '%(_domainname)s') AND address =\
       
   193  '%(_localpart)s'" % address.__dict__
       
   194         return VirtualMailManager._exists(dbh, sql)
       
   195     relocatedExists = staticmethod(relocatedExists)
   204 
   196 
   205     def __getAccount(self, address, password=None):
   197     def __getAccount(self, address, password=None):
   206         self.__dbConnect()
   198         self.__dbConnect()
       
   199         address = EmailAddress(address)
   207         if not password is None:
   200         if not password is None:
   208             password = self.__pwhash(password)
   201             password = self.__pwhash(password)
   209         return Account(self.__dbh, address, password)
   202         return Account(self.__dbh, address, password)
   210 
   203 
   211     def _readpass(self):
   204     def _readpass(self):
   212         clear0 = ''
   205         mismatched = True
   213         clear1 = '1'
   206         while mismatched:
   214         while clear0 != clear1:
   207             clear0 = getpass(prompt=_('Enter new password: '))
   215             while len(clear0) < 1:
       
   216                 clear0 = getpass(prompt=_('Enter new password: '))
       
   217                 if len(clear0) < 1:
       
   218                     sys.stderr.write('%s\n'
       
   219                             % _('Sorry, empty passwords are not permitted'))
       
   220             clear1 = getpass(prompt=_('Retype new password: '))
   208             clear1 = getpass(prompt=_('Retype new password: '))
   221             if clear0 != clear1:
   209             if clear0 != clear1:
   222                 clear0 = ''
       
   223                 sys.stderr.write('%s\n' % _('Sorry, passwords do not match'))
   210                 sys.stderr.write('%s\n' % _('Sorry, passwords do not match'))
       
   211                 continue
       
   212             if len(clear0) < 1 or len(clear1) < 1:
       
   213                 sys.stderr.write('%s\n'
       
   214                         % _('Sorry, empty passwords are not permitted'))
       
   215                 continue
       
   216             mismatched = False
   224         return clear0
   217         return clear0
   225 
   218 
   226     def __getAlias(self, address, destination=None):
   219     def __getAlias(self, address, destination=None):
   227         self.__dbConnect()
   220         self.__dbConnect()
       
   221         address = EmailAddress(address)
       
   222         if destination is not None:
       
   223             destination = EmailAddress(destination)
   228         return Alias(self.__dbh, address, destination)
   224         return Alias(self.__dbh, address, destination)
   229 
   225 
   230     def __getDomain(self, domainname, transport=None):
   226     def __getDomain(self, domainname, transport=None):
   231         if transport is None:
   227         if transport is None:
   232             transport = self.__Cfg.get('misc', 'transport')
   228             transport = self.__Cfg.get('misc', 'transport')