# HG changeset patch # User Pascal Volk # Date 1220851817 0 # Node ID 14c0a092d7d257dffae6b68d26607ef6437017ac # Parent af813ede1e1991913d8868a517c6a0eb9dd47325 * 'VirtualMailManager/EmailAddress.py' - Added to repository - to simplify/reduce address validation. * 'VirtualMailManager/Relocated.py' - Added to repository * 'VirtualMailManager/Exceptions.py' - Added exception classes for class EmailAddress and class Relocated * 'VirtualMailManager/constants/ERROR.py' - Updated - Removed shebang * 'VirtualMailManager/VirtualMailManager.py' - Moved static methods chkLocalpart() and chkEmailAddress to new class EmailAddress - Added static methods accountExists(), aliasExists(), relocatedExists() and _exists() - Fixed a bug in VirtualMailManager._readpass() - Integrated class EmailAddress * 'VirtualMailManager/Alias.py' - Integrated class EmailAddress - Removed Alias._isAccount() * 'VirtualMailManager/Account.py' - Integrated class EmailAddress - Removed Account._isAlias() * 'VirtualMailManager/AliasDomain.py' * 'VirtualMailManager/Config.py' * 'VirtualMailManager/Domain.py' * 'VirtualMailManager/MailLocation.py' * 'VirtualMailManager/Transport.py' * 'VirtualMailManager/constants/EXIT.py' - Removed shebang * 'vmm' - more detailed error messages from alias_add() diff -r af813ede1e19 -r 14c0a092d7d2 TODO --- a/TODO Sat Sep 06 03:07:28 2008 +0000 +++ b/TODO Mon Sep 08 05:30:17 2008 +0000 @@ -5,4 +5,5 @@ - VirtualMailManager/Alias.py - check if account exists, when destination is in the same domain + - avoid looping aliases diff -r af813ede1e19 -r 14c0a092d7d2 VirtualMailManager/Account.py --- a/VirtualMailManager/Account.py Sat Sep 06 03:07:28 2008 +0000 +++ b/VirtualMailManager/Account.py Mon Sep 08 05:30:17 2008 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: UTF-8 -*- # Copyright 2007-2008 VEB IT # See COPYING for distribution information. @@ -17,6 +16,7 @@ from Domain import Domain from Transport import Transport from MailLocation import MailLocation +from EmailAddress import EmailAddress import VirtualMailManager as VMM import constants.ERROR as ERR @@ -25,9 +25,10 @@ def __init__(self, dbh, address, password=None): self._dbh = dbh self._base = None - self._addr = VMM.VirtualMailManager.chkEmailAddress(address) - self._localpart = None - self._name = None + if isinstance(address, EmailAddress): + self._addr = address + else: + raise TypeError("Argument 'address' is not an EmailAddress") self._uid = 0 self._gid = 0 self._mid = 0 @@ -35,15 +36,19 @@ self._passwd = password self._setAddr() self._exists() - if self._isAlias(): + if VMM.VirtualMailManager.aliasExists(self._dbh, self._addr): raise AccE(_(u"There is already an alias with the address »%s«.") %\ - address, ERR.ALIAS_EXISTS) + self._addr, ERR.ALIAS_EXISTS) + if VMM.VirtualMailManager.relocatedExists(self._dbh, self._addr): + raise AccE( + _(u"There is already an relocated user with the address »%s«.") %\ + self._addr, ERR.RELOCATED_EXISTS) def _exists(self): dbc = self._dbh.cursor() dbc.execute("SELECT uid, mid, tid FROM users \ WHERE gid=%s AND local_part=%s", - self._gid, self._localpart) + self._gid, self._addr._localpart) result = dbc.fetchone() dbc.close() if result is not None: @@ -52,24 +57,12 @@ else: return False - def _isAlias(self): - dbc = self._dbh.cursor() - dbc.execute("SELECT gid FROM alias WHERE gid=%s AND address=%s", - self._gid, self._localpart) - gid = dbc.fetchone() - dbc.close() - if gid is not None: - return True - else: - return False - def _setAddr(self): - self._localpart, d = self._addr.split('@') - dom = Domain(self._dbh, d) + dom = Domain(self._dbh, self._addr._domainname) self._gid = dom.getID() if self._gid == 0: - raise AccE(_(u"The domain »%s« doesn't exist yet.") % d, - ERR.NO_SUCH_DOMAIN) + raise AccE(_(u"The domain »%s« doesn't exist yet.") %\ + self._addr._domainname, ERR.NO_SUCH_DOMAIN) self._base = dom.getDir() self._tid = dom.getTransportID() @@ -96,15 +89,15 @@ if service in ['smtp', 'pop3', 'imap', 'managesieve']: dbc.execute( "UPDATE users SET %s=%s WHERE local_part='%s' AND gid=%s" - % (service, state, self._localpart, self._gid)) + % (service, state, self._addr._localpart, self._gid)) elif state: dbc.execute("UPDATE users SET smtp = TRUE, pop3 = TRUE,\ imap = TRUE, managesieve = TRUE WHERE local_part = %s AND gid = %s", - self._localpart, self._gid) + self._addr._localpart, self._gid) else: dbc.execute("UPDATE users SET smtp = FALSE, pop3 = FALSE,\ imap = FALSE, managesieve = FALSE WHERE local_part = %s AND gid = %s", - self._localpart, self._gid) + self._addr._localpart, self._gid) if dbc.rowcount > 0: self._dbh.commit() dbc.close() @@ -146,8 +139,8 @@ dbc.execute("""INSERT INTO users (local_part, passwd, uid, gid,\ mid, tid, smtp, pop3, imap, managesieve)\ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", - self._localpart, self._passwd, self._uid, self._gid, self._mid, - self._tid, smtp, pop3, imap, managesieve) + self._addr._localpart, self._passwd, self._uid, self._gid, + self._mid, self._tid, smtp, pop3, imap, managesieve) self._dbh.commit() dbc.close() else: @@ -163,14 +156,14 @@ dbc = self._dbh.cursor() if what == 'password': dbc.execute("UPDATE users SET passwd=%s WHERE local_part=%s AND\ - gid=%s", value, self._localpart, self._gid) + gid=%s", value, self._addr._localpart, self._gid) elif what == 'transport': self._tid = Transport(self._dbh, transport=value).getID() dbc.execute("UPDATE users SET tid=%s WHERE local_part=%s AND\ - gid=%s", self._tid, self._localpart, self._gid) + gid=%s", self._tid, self._addr._localpart, self._gid) else: dbc.execute("UPDATE users SET name=%s WHERE local_part=%s AND\ - gid=%s", value, self._localpart, self._gid) + gid=%s", value, self._addr._localpart, self._gid) if dbc.rowcount > 0: self._dbh.commit() dbc.close() @@ -179,7 +172,7 @@ dbc = self._dbh.cursor() dbc.execute("SELECT name, uid, gid, mid, tid, smtp, pop3, imap, \ managesieve FROM users WHERE local_part=%s AND gid=%s", - self._localpart, self._gid) + self._addr._localpart, self._gid) info = dbc.fetchone() dbc.close() if info is None: @@ -209,7 +202,7 @@ dbc = self._dbh.cursor() if delalias == 'delalias': dbc.execute("DELETE FROM users WHERE gid=%s AND local_part=%s", - self._gid, self._localpart) + self._gid, self._addr._localpart) u_rc = dbc.rowcount # delete also all aliases where the destination address is the same # as for this account. @@ -220,7 +213,7 @@ a_count = self.__aliaseCount() if a_count == 0: dbc.execute("DELETE FROM users WHERE gid=%s AND local_part=%s", - self._gid, self._localpart) + self._gid, self._addr._localpart) if dbc.rowcount > 0: self._dbh.commit() else: diff -r af813ede1e19 -r 14c0a092d7d2 VirtualMailManager/Alias.py --- a/VirtualMailManager/Alias.py Sat Sep 06 03:07:28 2008 +0000 +++ b/VirtualMailManager/Alias.py Mon Sep 08 05:30:17 2008 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: UTF-8 -*- # Copyright 2007-2008 VEB IT # See COPYING for distribution information. @@ -15,62 +14,55 @@ from Exceptions import VMMAliasException as VMMAE from Domain import Domain +from EmailAddress import EmailAddress import constants.ERROR as ERR import VirtualMailManager as VMM class Alias: - """Class to manage e-mail accounts.""" + """Class to manage e-mail aliases.""" def __init__(self, dbh, address, destination=None): + if isinstance(address, EmailAddress): + self._addr = address + else: + raise TypeError("Argument 'address' is not an EmailAddress") + if destination is None: + self._dest = None + elif isinstance(destination, EmailAddress): + self._dest = destination + else: + raise TypeError("Argument 'destination' is not an EmailAddress") if address == destination: raise VMMAE(_(u"Address and destination are identical."), ERR.ALIAS_ADDR_DEST_IDENTICAL) self._dbh = dbh - self._addr = VMM.VirtualMailManager.chkEmailAddress(address) - if destination is None: - self._dest = None - elif destination.count('@'): - self._dest = VMM.VirtualMailManager.chkEmailAddress(destination) - else: - self._dest = VMM.VirtualMailManager.chkLocalpart(destination) - self._localpart = None self._gid = 0 self._isNew = False self._setAddr() if not self._dest is None: self._exists() - if self._isAccount(): - raise VMMAE(_(u"There is already an account with address »%s«.") % + if VMM.VirtualMailManager.accountExists(self._dbh, self._addr): + raise VMMAE(_(u"There is already an account with address »%s«.") %\ self._addr, ERR.ACCOUNT_EXISTS) + if VMM.VirtualMailManager.relocatedExists(self._dbh, self._addr): + raise VMMAE( + _(u"There is already an relocated user with the address »%s«.") %\ + self._addr, ERR.RELOCATED_EXISTS) def _exists(self): dbc = self._dbh.cursor() dbc.execute("SELECT gid FROM alias WHERE gid=%s AND address=%s\ - AND destination=%s", self._gid, self._localpart, self._dest) + AND destination=%s", self._gid, self._addr._localpart, str(self._dest)) gid = dbc.fetchone() dbc.close() if gid is None: self._isNew = True - else: - self._isNew = False - def _isAccount(self): - dbc = self._dbh.cursor() - dbc.execute("SELECT uid FROM users WHERE gid=%s AND local_part=%s", - self._gid, self._localpart) - uid = dbc.fetchone() - dbc.close() - if uid is not None: - return True - else: - return False - def _setAddr(self): - self._localpart, d = self._addr.split('@') - dom = Domain(self._dbh, d) + dom = Domain(self._dbh, self._addr._domainname) self._gid = dom.getID() if self._gid == 0: - raise VMMAE(_(u"The domain »%s« doesn't exist yet.") % d, - ERR.NO_SUCH_DOMAIN) + raise VMMAE(_(u"The domain »%s« doesn't exist yet.") %\ + self._addr._domainname, ERR.NO_SUCH_DOMAIN) def save(self): if self._dest is None: @@ -79,17 +71,18 @@ if self._isNew: dbc = self._dbh.cursor() dbc.execute("INSERT INTO alias (gid, address, destination) VALUES\ - (%s, %s, %s)", self._gid, self._localpart, self._dest) + (%s, %s, %s)", self._gid, self._addr._localpart, str(self._dest)) self._dbh.commit() dbc.close() else: - raise VMMAE(_(u"The alias »%s« already exists.") % self._addr, - ERR.ALIAS_EXISTS) + raise VMMAE( + _(u"The alias »%(a)s« with destination »%(d)s« already exists.")\ + % {'a': self._addr, 'd': self._dest}, ERR.ALIAS_EXISTS) def getInfo(self): dbc = self._dbh.cursor() dbc.execute('SELECT destination FROM alias WHERE gid=%s AND address=%s', - self._gid, self._localpart) + self._gid, self._addr._localpart) destinations = dbc.fetchall() dbc.close() if len(destinations) > 0: @@ -105,15 +98,19 @@ dbc = self._dbh.cursor() if self._dest is None: dbc.execute("DELETE FROM alias WHERE gid=%s AND address=%s", - self._gid, self._localpart) + self._gid, self._addr._localpart) else: dbc.execute("DELETE FROM alias WHERE gid=%s AND address=%s AND \ - destination=%s", self._gid, self._localpart, self._dest) + destination=%s", self._gid, self._addr._localpart, str(self._dest)) rowcount = dbc.rowcount dbc.close() if rowcount > 0: self._dbh.commit() else: - raise VMMAE(_(u"The alias »%s« doesn't exists.") % self._addr, - ERR.NO_SUCH_ALIAS) + if self._dest is None: + msg = u"The alias »%s« doesn't exists." % self._addr + else: + msg = u"The alias »%(a)s« with destination »%(d)s« doesn't\ + exists." % {'a': self._addr, 'd': self._dest} + raise VMMAE(_(msg), ERR.NO_SUCH_ALIAS) diff -r af813ede1e19 -r 14c0a092d7d2 VirtualMailManager/AliasDomain.py --- a/VirtualMailManager/AliasDomain.py Sat Sep 06 03:07:28 2008 +0000 +++ b/VirtualMailManager/AliasDomain.py Mon Sep 08 05:30:17 2008 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: UTF-8 -*- # Copyright 2008 VEB IT # See COPYING for distribution information. diff -r af813ede1e19 -r 14c0a092d7d2 VirtualMailManager/Config.py --- a/VirtualMailManager/Config.py Sat Sep 06 03:07:28 2008 +0000 +++ b/VirtualMailManager/Config.py Mon Sep 08 05:30:17 2008 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: UTF-8 -*- # Copyright 2007-2008 VEB IT # See COPYING for distribution information. diff -r af813ede1e19 -r 14c0a092d7d2 VirtualMailManager/Domain.py --- a/VirtualMailManager/Domain.py Sat Sep 06 03:07:28 2008 +0000 +++ b/VirtualMailManager/Domain.py Mon Sep 08 05:30:17 2008 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: UTF-8 -*- # Copyright 2007-2008 VEB IT # See COPYING for distribution information. diff -r af813ede1e19 -r 14c0a092d7d2 VirtualMailManager/EmailAddress.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/VirtualMailManager/EmailAddress.py Mon Sep 08 05:30:17 2008 +0000 @@ -0,0 +1,87 @@ +# -*- coding: UTF-8 -*- +# Copyright 2008 VEB IT +# See COPYING for distribution information. +# $Id$ + +"""Virtual Mail Manager's EmailAddress class to handle e-mail addresses.""" + +from constants.VERSION import VERSION + +__author__ = 'Pascal Volk ' +__version__ = VERSION +__revision__ = 'rev '+'$Rev$'.split()[1] +__date__ = '$Date$'.split()[1] + +import re + +from Exceptions import VMMEmailAddressException as VMMEAE +import VirtualMailManager as VMM +import constants.ERROR as ERR + +RE_LOCALPART = """[^\w!#$%&'\*\+-\.\/=?^_`{\|}~]""" + +class EmailAddress(object): + def __init__(self, address): + self._localpart = None + self._domainname = None + self.__chkAddress(address) + + def __eq__(self, other): + if hasattr(other, '_localpart') and hasattr(other, '_domainname'): + return self._localpart == other._localpart\ + and self._domainname == other._domainname + else: + return NotImplemented + + def __ne__(self, other): + if hasattr(other, '_localpart') and hasattr(other, '_domainname'): + return not self._localpart == other._localpart\ + and self._domainname == other._domainname + else: + return NotImplemented + + def __repr__(self): + return "EmailAddress('%s@%s')" % (self._localpart, self._domainname) + + def __str__(self): + return "%s@%s" % (self._localpart, self._domainname) + + def __chkAddress(self, address): + try: + localpart, domain = address.split('@') + except ValueError: + raise VMMEAE(_(u"Missing '@' sign in e-mail address »%s«.") % + address, ERR.INVALID_ADDRESS) + except AttributeError: + raise VMMEAE(_(u"»%s« looks not like an e-mail address.") % + address, ERR.INVALID_ADDRESS) + if len(domain) > 0: + domain = VMM.VirtualMailManager.chkDomainname(domain) + else: + raise VMMEAE(_(u"Missing domain name after »%s@«.") % + localpart, ERR.DOMAIN_NO_NAME) + localpart = self.__chkLocalpart(localpart) + self._localpart, self._domainname = localpart, domain + + def __chkLocalpart(self, localpart): + """Validates the local part of an e-mail address. + + Keyword arguments: + localpart -- of the e-mail address that should be validated (str) + """ + if len(localpart) < 1: + raise VMMEAE(_(u'No localpart specified.'), + ERR.LOCALPART_INVALID) + if len(localpart) > 64: + raise VMMEAE(_(u'The local part »%s« is too long') % + localpart, ERR.LOCALPART_TOO_LONG) + ic = re.compile(RE_LOCALPART).findall(localpart) + if len(ic): + ichrs = '' + for c in set(ic): + ichrs += u"»%s« " % c + raise VMMEAE(_(u"The local part »%(lpart)s« contains invalid\ + characters: %(ichrs)s") % {'lpart': localpart, 'ichrs': ichrs}, + ERR.LOCALPART_INVALID) + return localpart + diff -r af813ede1e19 -r 14c0a092d7d2 VirtualMailManager/Exceptions.py --- a/VirtualMailManager/Exceptions.py Sat Sep 06 03:07:28 2008 +0000 +++ b/VirtualMailManager/Exceptions.py Mon Sep 08 05:30:17 2008 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: UTF-8 -*- # Copyright 2007-2008 VEB IT # See COPYING for distribution information. @@ -65,13 +64,22 @@ def __init__(self, msg, code): VMMException.__init__(self, msg, code) +class VMMEmailAddressException(VMMException): + """Exception class for EmailAddress exceptions""" + def __init__(self, msg, code): + VMMException.__init__(self, msg, code) + class VMMMailLocationException(VMMException): """Exception class for MailLocation exceptions""" def __init__(self, msg, code): VMMException.__init__(self, msg, code) +class VMMRelocatedException(VMMException): + """Exception class for Relocated exceptions""" + def __init__(self, msg, code): + VMMException.__init__(self, msg, code) + class VMMTransportException(VMMException): """Exception class for Transport exceptions""" def __init__(self, msg, code): VMMException.__init__(self, msg, code) - diff -r af813ede1e19 -r 14c0a092d7d2 VirtualMailManager/MailLocation.py --- a/VirtualMailManager/MailLocation.py Sat Sep 06 03:07:28 2008 +0000 +++ b/VirtualMailManager/MailLocation.py Mon Sep 08 05:30:17 2008 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: UTF-8 -*- # Copyright 2008 VEB IT # See COPYING for distribution information. diff -r af813ede1e19 -r 14c0a092d7d2 VirtualMailManager/Relocated.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/VirtualMailManager/Relocated.py Mon Sep 08 05:30:17 2008 +0000 @@ -0,0 +1,107 @@ +# -*- coding: UTF-8 -*- +# Copyright 2008 VEB IT +# See COPYING for distribution information. +# $Id$ + +"""Virtual Mail Manager's Relocated class to manage relocated users.""" + +from constants.VERSION import VERSION + +__author__ = 'Pascal Volk ' +__version__ = VERSION +__revision__ = 'rev '+'$Rev$'.split()[1] +__date__ = '$Date$'.split()[1] + +from Exceptions import VMMRelocatedException as VMMRE +from Domain import Domain +from EmailAddress import EmailAddress +import constants.ERROR as ERR +import VirtualMailManager as VMM + +class Relocated: + """Class to manage e-mail addresses of relocated users.""" + def __init__(self, dbh, address, destination=None): + if isinstance(address, EmailAddress): + self._addr = address + else: + raise TypeError("Argument 'address' is not an EmailAddress") + if destination is None: + self._dest = None + elif isinstance(destination, EmailAddress): + self._dest = destination + else: + raise TypeError("Argument 'destination' is not an EmailAddress") + if address == destination: + raise VMMRE(_(u"Address and destination are identical."), + ERR.RELOCATED_ADDR_DEST_IDENTICAL) + self._dbh = dbh + self._gid = 0 + self._isNew = False + self._setAddr() + self._exists() + if VMM.VirtualMailManager.accountExists(self._dbh, self._addr): + raise VMMRE(_(u"There is already an account with address »%s«.") %\ + self._addr, ERR.ACCOUNT_EXISTS) + if VMM.VirtualMailManager.aliasExists(self._dbh, self._addr): + raise VMMRE( + _(u"There is already an alias with the address »%s«.") %\ + self._addr, ERR.ALIAS_EXISTS) + + def _exists(self): + dbc = self._dbh.cursor() + dbc.execute("SELECT gid FROM relocated WHERE gid = %s AND address = %s", + self._gid, self._addr._localpart) + gid = dbc.fetchone() + dbc.close() + if gid is None: + self._isNew = True + + def _setAddr(self): + dom = Domain(self._dbh, self._addr._domainname) + self._gid = dom.getID() + if self._gid == 0: + raise VMMRE(_(u"The domain »%s« doesn't exist yet.") %\ + self._addr._domainname, ERR.NO_SUCH_DOMAIN) + + def save(self): + if self._dest is None: + raise VMMRE(_(u"No destination address for relocated user denoted."), + ERR.RELOCATED_MISSING_DEST) + if self._isNew: + dbc = self._dbh.cursor() + dbc.execute("INSERT INTO relocated VALUES (%s, %s, %s)", + self._gid, self._addr._localpart, str(self._dest)) + self._dbh.commit() + dbc.close() + else: + raise VMMRE( + _(u"The relocated user »%s« already exists.") % self._addr, + ERR.RELOCATED_EXISTS) + + def getInfo(self): + dbc = self._dbh.cursor() + dbc.execute('SELECT destination FROM relocated WHERE gid=%s\ + AND address=%s', + self._gid, self._addr._localpart) + destination = dbc.fetchone() + dbc.close() + if destination is not None: + return destination[0] + else: + raise VMMRE( + _(u"The relocated user »%s« doesn't exists.") % self._addr, + ERR.NO_SUCH_RELOCATED) + + def delete(self): + dbc = self._dbh.cursor() + dbc.execute("DELETE FROM relocated WHERE gid = %s AND address = %s", + self._gid, self._addr._localpart) + rowcount = dbc.rowcount + dbc.close() + if rowcount > 0: + self._dbh.commit() + else: + raise VMMRE( + _(u"The relocated user »%s« doesn't exists.") % self._addr, + ERR.NO_SUCH_RELOCATED) + diff -r af813ede1e19 -r 14c0a092d7d2 VirtualMailManager/Transport.py --- a/VirtualMailManager/Transport.py Sat Sep 06 03:07:28 2008 +0000 +++ b/VirtualMailManager/Transport.py Mon Sep 08 05:30:17 2008 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: UTF-8 -*- # Copyright 2008 VEB IT # See COPYING for distribution information. diff -r af813ede1e19 -r 14c0a092d7d2 VirtualMailManager/VirtualMailManager.py --- a/VirtualMailManager/VirtualMailManager.py Sat Sep 06 03:07:28 2008 +0000 +++ b/VirtualMailManager/VirtualMailManager.py Mon Sep 08 05:30:17 2008 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: UTF-8 -*- # Copyright 2007-2008 VEB IT # See COPYING for distribution information. @@ -23,13 +22,15 @@ from pyPgSQL import PgSQL # python-pgsql - http://pypgsql.sourceforge.net -from Exceptions import * import constants.ERROR as ERR -from Config import Config as Cfg from Account import Account from Alias import Alias +from AliasDomain import AliasDomain +from Config import Config as Cfg from Domain import Domain -from AliasDomain import AliasDomain +from EmailAddress import EmailAddress +from Exceptions import * +from Relocated import Relocated SALTCHARS = './0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' RE_ASCII_CHARS = """^[\x20-\x7E]*$""" @@ -114,29 +115,6 @@ except PgSQL.libpq.DatabaseError, e: raise VMMException(str(e), ERR.DATABASE_ERROR) - def chkLocalpart(localpart): - """Validates the local part of an e-mail address. - - Keyword arguments: - localpart -- the e-mail address that should be validated (str) - """ - if len(localpart) < 1: - raise VMMException(_(u'No localpart specified.'), - ERR.LOCALPART_INVALID) - if len(localpart) > 64: - raise VMMException(_(u'The local part »%s« is too long') % - localpart, ERR.LOCALPART_TOO_LONG) - ic = re.compile(RE_LOCALPART).findall(localpart) - if len(ic): - ichrs = '' - for c in set(ic): - ichrs += u"»%s« " % c - raise VMMException(_(u"The local part »%(lpart)s« contains invalid\ - characters: %(ichrs)s") % {'lpart': localpart, 'ichrs': ichrs}, - ERR.LOCALPART_INVALID) - return localpart - chkLocalpart = staticmethod(chkLocalpart) - def idn2ascii(domainname): """Converts an idn domainname in punycode. @@ -179,52 +157,70 @@ ERR.DOMAIN_TOO_LONG) re.compile(RE_DOMAIN) if not re.match(RE_DOMAIN, domainname): - raise VMMException(_(u'The domain name is invalid.'), - ERR.DOMAIN_INVALID) + raise VMMException(_(u'The domain name »%s« is invalid.') %\ + domainname, ERR.DOMAIN_INVALID) return domainname chkDomainname = staticmethod(chkDomainname) - def chkEmailAddress(address): - try: - localpart, domain = address.split('@') - except ValueError: - raise VMMException(_(u"Missing '@' sign in e-mail address »%s«.") % - address, ERR.INVALID_ADDRESS) - except AttributeError: - raise VMMException(_(u"»%s« looks not like an e-mail address.") % - address, ERR.INVALID_ADDRESS) - if len(domain) > 0: - domain = VirtualMailManager.chkDomainname(domain) + def _exists(dbh, query): + dbc = dbh.cursor() + dbc.execute(query) + gid = dbc.fetchone() + dbc.close() + if gid is None: + return False else: - raise VMMException(_(u"Missing domain name after »%s@«.") % - localpart, ERR.DOMAIN_NO_NAME) - localpart = VirtualMailManager.chkLocalpart(localpart) - return '%s@%s' % (localpart, domain) - chkEmailAddress = staticmethod(chkEmailAddress) + return True + _exists = staticmethod(_exists) + + def accountExists(dbh, address): + sql = "SELECT gid FROM users WHERE gid = (SELECT gid FROM domain_name\ + WHERE domainname = '%(_domainname)s') AND local_part = '%(_localpart)s'" %\ + address.__dict__ + return VirtualMailManager._exists(dbh, sql) + accountExists = staticmethod(accountExists) + + def aliasExists(dbh, address): + sql = "SELECT DISTINCT gid FROM alias WHERE gid = (SELECT gid FROM\ + domain_name WHERE domainname = '%(_domainname)s') AND address =\ + '%(_localpart)s'" % address.__dict__ + return VirtualMailManager._exists(dbh, sql) + aliasExists = staticmethod(aliasExists) + + def relocatedExists(dbh, address): + sql = "SELECT gid FROM relocated WHERE gid = (SELECT gid FROM\ + domain_name WHERE domainname = '%(_domainname)s') AND address =\ + '%(_localpart)s'" % address.__dict__ + return VirtualMailManager._exists(dbh, sql) + relocatedExists = staticmethod(relocatedExists) def __getAccount(self, address, password=None): self.__dbConnect() + address = EmailAddress(address) if not password is None: password = self.__pwhash(password) return Account(self.__dbh, address, password) def _readpass(self): - clear0 = '' - clear1 = '1' - while clear0 != clear1: - while len(clear0) < 1: - clear0 = getpass(prompt=_('Enter new password: ')) - if len(clear0) < 1: - sys.stderr.write('%s\n' - % _('Sorry, empty passwords are not permitted')) + mismatched = True + while mismatched: + clear0 = getpass(prompt=_('Enter new password: ')) clear1 = getpass(prompt=_('Retype new password: ')) if clear0 != clear1: - clear0 = '' sys.stderr.write('%s\n' % _('Sorry, passwords do not match')) + continue + if len(clear0) < 1 or len(clear1) < 1: + sys.stderr.write('%s\n' + % _('Sorry, empty passwords are not permitted')) + continue + mismatched = False return clear0 def __getAlias(self, address, destination=None): self.__dbConnect() + address = EmailAddress(address) + if destination is not None: + destination = EmailAddress(destination) return Alias(self.__dbh, address, destination) def __getDomain(self, domainname, transport=None): diff -r af813ede1e19 -r 14c0a092d7d2 VirtualMailManager/constants/ERROR.py --- a/VirtualMailManager/constants/ERROR.py Sat Sep 06 03:07:28 2008 +0000 +++ b/VirtualMailManager/constants/ERROR.py Mon Sep 08 05:30:17 2008 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: UTF-8 -*- # Copyright 2007-2008 VEB IT # See COPYING for distribution information. @@ -41,7 +40,11 @@ NO_SUCH_BINARY = 54 NO_SUCH_DIRECTORY = 55 NO_SUCH_DOMAIN = 56 -TRANSPORT_INIT = 57 -UNKNOWN_MAILLOCATION_ID = 58 -UNKNOWN_SERVICE = 59 -UNKNOWN_TRANSPORT_ID = 60 +NO_SUCH_RELOCATED = 57 +RELOCATED_ADDR_DEST_IDENTICAL = 58 +RELOCATED_EXISTS = 59 +RELOCATED_MISSING_DEST = 60 +TRANSPORT_INIT = 61 +UNKNOWN_MAILLOCATION_ID = 62 +UNKNOWN_SERVICE = 63 +UNKNOWN_TRANSPORT_ID = 64 diff -r af813ede1e19 -r 14c0a092d7d2 VirtualMailManager/constants/EXIT.py --- a/VirtualMailManager/constants/EXIT.py Sat Sep 06 03:07:28 2008 +0000 +++ b/VirtualMailManager/constants/EXIT.py Mon Sep 08 05:30:17 2008 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: UTF-8 -*- # Copyright 2007-2008 VEB IT # See COPYING for distribution information. diff -r af813ede1e19 -r 14c0a092d7d2 pgsql-virtual_mailbox_domains.cf --- a/pgsql-virtual_mailbox_domains.cf Sat Sep 06 03:07:28 2008 +0000 +++ b/pgsql-virtual_mailbox_domains.cf Mon Sep 08 05:30:17 2008 +0000 @@ -9,4 +9,4 @@ dbname = mailsys # Postfix 2.2 and later The SQL query template. See pgsql_table(5). -query = SELECT gid FROM postfix_gid WHERE domainname = '%d' +query = SELECT gid FROM postfix_gid WHERE domainname = '%s' diff -r af813ede1e19 -r 14c0a092d7d2 vmm --- a/vmm Sat Sep 06 03:07:28 2008 +0000 +++ b/vmm Mon Sep 08 05:30:17 2008 +0000 @@ -317,8 +317,10 @@ vmm.userPassword(argv[2].lower(), password) def alias_add(): - if argc < 4: + if argc < 3: usage(EXIT.MISSING_ARGS, _(u'Missing alias address and destination.')) + elif argc < 4: + usage(EXIT.MISSING_ARGS, _(u'Missing destination address.')) else: vmm.aliasAdd(argv[2].lower(), argv[3])