* 'VirtualMailManager/EmailAddress.py'
authorPascal Volk <neverseen@users.sourceforge.net>
Mon, 08 Sep 2008 05:30:17 +0000
changeset 76 14c0a092d7d2
parent 75 af813ede1e19
child 77 9e5af7210c29
* '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()
TODO
VirtualMailManager/Account.py
VirtualMailManager/Alias.py
VirtualMailManager/AliasDomain.py
VirtualMailManager/Config.py
VirtualMailManager/Domain.py
VirtualMailManager/EmailAddress.py
VirtualMailManager/Exceptions.py
VirtualMailManager/MailLocation.py
VirtualMailManager/Relocated.py
VirtualMailManager/Transport.py
VirtualMailManager/VirtualMailManager.py
VirtualMailManager/constants/ERROR.py
VirtualMailManager/constants/EXIT.py
pgsql-virtual_mailbox_domains.cf
vmm
--- 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
 
--- 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:
--- 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)
 
--- 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.
--- 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.
--- 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.
--- /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 <p.volk@veb-it.de>'
+__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
+
--- 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)
-
--- 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.
--- /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 <p.volk@veb-it.de>'
+__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)
+
--- 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.
--- 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):
--- 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
--- 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.
--- 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'
--- 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])