VMM/*: Moved some methods from classes to modules __init__. v0.6.x
authorPascal Volk <neverseen@users.sourceforge.net>
Fri, 05 Feb 2010 20:13:32 +0000
branchv0.6.x
changeset 185 6e1ef32fbd82
parent 184 d0425225ce52
child 186 18757fd45e60
VMM/*: Moved some methods from classes to modules __init__. - Adjusted many import statements. - Small adjustments and whitespace cosmetics in Config.py
VirtualMailManager/Account.py
VirtualMailManager/Alias.py
VirtualMailManager/AliasDomain.py
VirtualMailManager/Config.py
VirtualMailManager/Domain.py
VirtualMailManager/EmailAddress.py
VirtualMailManager/MailLocation.py
VirtualMailManager/Relocated.py
VirtualMailManager/Transport.py
VirtualMailManager/__init__.py
VirtualMailManager/cli/handler.py
VirtualMailManager/ext/Postconf.py
vmm
--- a/VirtualMailManager/Account.py	Thu Feb 04 19:08:01 2010 +0000
+++ b/VirtualMailManager/Account.py	Fri Feb 05 20:13:32 2010 +0000
@@ -4,12 +4,12 @@
 
 """Virtual Mail Manager's Account class to manage e-mail accounts."""
 
-from __main__ import ERR
-from Exceptions import VMMAccountException as AccE
-from Domain import Domain
-from Transport import Transport
-from MailLocation import MailLocation
-from EmailAddress import EmailAddress
+import VirtualMailManager.constants.ERROR as ERR
+from VirtualMailManager.Domain import Domain
+from VirtualMailManager.EmailAddress import EmailAddress
+from VirtualMailManager.Exceptions import VMMAccountException as AccE
+from VirtualMailManager.MailLocation import MailLocation
+from VirtualMailManager.Transport import Transport
 import VirtualMailManager as VMM
 
 class Account(object):
--- a/VirtualMailManager/Alias.py	Thu Feb 04 19:08:01 2010 +0000
+++ b/VirtualMailManager/Alias.py	Fri Feb 05 20:13:32 2010 +0000
@@ -4,10 +4,10 @@
 
 """Virtual Mail Manager's Alias class to manage e-mail aliases."""
 
-from __main__ import ERR
-from Exceptions import VMMAliasException as VMMAE
-from Domain import Domain
-from EmailAddress import EmailAddress
+import VirtualMailManager.constants.ERROR as ERR
+from VirtualMailManager.Domain import Domain
+from VirtualMailManager.EmailAddress import EmailAddress
+from VirtualMailManager.Exceptions import VMMAliasException as VMMAE
 import VirtualMailManager as VMM
 
 class Alias(object):
--- a/VirtualMailManager/AliasDomain.py	Thu Feb 04 19:08:01 2010 +0000
+++ b/VirtualMailManager/AliasDomain.py	Fri Feb 05 20:13:32 2010 +0000
@@ -4,16 +4,16 @@
 
 """Virtual Mail Manager's AliasDomain class to manage alias domains."""
 
-from __main__ import ERR
-from Exceptions import VMMAliasDomainException as VADE
-import VirtualMailManager as VMM
+import VirtualMailManager.constants.ERROR as ERR
+from VirtualMailManager import chk_domainname
+from VirtualMailManager.Exceptions import VMMAliasDomainException as VADE
 
 class AliasDomain(object):
     """Class to manage e-mail alias domains."""
     __slots__ = ('__gid', '__name', '_domain', '_dbh')
     def __init__(self, dbh, domainname, targetDomain=None):
         self._dbh = dbh
-        self.__name = VMM.VirtualMailManager.chkDomainname(domainname)
+        self.__name = chk_domainname(domainname)
         self.__gid = 0
         self._domain = targetDomain
         self._exists()
--- a/VirtualMailManager/Config.py	Thu Feb 04 19:08:01 2010 +0000
+++ b/VirtualMailManager/Config.py	Fri Feb 05 20:13:32 2010 +0000
@@ -36,10 +36,13 @@
 from shutil import copy2
 from ConfigParser import (Error, MissingSectionHeaderError, NoOptionError,
                           NoSectionError, ParsingError, RawConfigParser)
-from cStringIO import StringIO
+from cStringIO import StringIO# TODO: move interactive stff to cli
+
+import VirtualMailManager.constants.ERROR as ERR
 
-from __main__ import os, ENCODING, ERR, get_unicode, w_std
-from Exceptions import VMMConfigException
+from VirtualMailManager import ENCODING, exec_ok, get_unicode, is_dir
+from VirtualMailManager.cli import w_std# move to cli
+from VirtualMailManager.Exceptions import VMMConfigException
 
 
 class BadOptionError(Error):
@@ -62,9 +65,9 @@
 
 class LazyConfig(RawConfigParser):
     """The **lazy** derivate of the `RawConfigParser`.
-    
+
     There are two additional getters:
-    
+
     `LazyConfig.pget()`
         The polymorphic getter, which returns a option's value with the
         appropriate type.
@@ -127,7 +130,7 @@
             * `BadOptionError`
             * `NoSectionError`
             * `NoOptionError`
-        """ 
+        """
         sect_opt = section_option.lower().split('.')
         if len(sect_opt) != 2:# do we need a regexp to check the format?
             raise BadOptionError(
@@ -158,13 +161,13 @@
 
     def dget(self, option):
         """Returns the value of the `option`.
-        
+
         If the option could not be found in the configuration file, the
         configured default value, from ``LazyConfig._cfg`` will be
         returned.
-        
+
         Arguments:
-        
+
         `option` : string
             the configuration option in the form
             "``section``\ **.**\ ``option``"
@@ -188,7 +191,7 @@
 
     def set(self, option, value):
         """Set the value of an option.
-        
+
         Throws a ``ValueError`` if `value` couldn't be converted to
         ``LazyConfigOption.cls``"""
         section, option = self.__get_section_option(option)
@@ -202,7 +205,7 @@
 
     def has_section(self, section):
         """Checks if ``section`` is a known configuration section."""
-        return section.lower() in self._cfg 
+        return section.lower() in self._cfg
 
     def has_option(self, option):
         """Checks if the option (section\ **.**\ option) is a known
@@ -214,7 +217,6 @@
             return False
 
 
-
 class LazyConfigOption(object):
     """A simple container class for configuration settings.
 
@@ -266,7 +268,7 @@
         """Creates a new Config instance
 
         Arguments:
-     
+
         ``filename``
             path to the configuration file
         """
@@ -290,11 +292,9 @@
                 'smtp' :            LCO(bool_t, True,  self.get_boolean),
             },
             'bin': {
-                'dovecotpw': LCO(str, '/usr/sbin/dovecotpw', self.get,
-                                 self.exec_ok),
-                'du':        LCO(str, '/usr/bin/du', self.get, self.exec_ok),
-                'postconf':  LCO(str, '/usr/sbin/postconf', self.get,
-                                 self.exec_ok),
+                'dovecotpw': LCO(str, '/usr/sbin/dovecotpw', self.get, exec_ok),
+                'du':        LCO(str, '/usr/bin/du',        self.get, exec_ok),
+                'postconf':  LCO(str, '/usr/sbin/postconf', self.get, exec_ok),
             },
             'database': {
                 'host': LCO(str, 'localhost', self.get),
@@ -313,7 +313,7 @@
                 'name':    LCO(str, 'Maildir',                     self.get),
             },
             'misc': {
-                'base_directory':  LCO(str, '/srv/mail', self.get, self.is_dir),
+                'base_directory':  LCO(str, '/srv/mail', self.get, is_dir),
                 'dovecot_version': LCO(int, 12,          self.getint),
                 'gid_mail':        LCO(int, 8,           self.getint),
                 'password_scheme': LCO(str, 'CRAM-MD5',  self.get,
@@ -341,6 +341,8 @@
 
         Raises a VMMConfigException if the check fails.
         """
+        # TODO: There are only two settings w/o defaults.
+        #       So there is no need for cStringIO
         if not self.__chkCfg():
             errmsg = StringIO()
             errmsg.write(_(u'Missing options, which have no default value.\n'))
@@ -356,36 +358,10 @@
         """Returns an iterator object for all configuration sections."""
         return self._cfg.iterkeys()
 
-    def is_dir(self, path):
-        """Checks if ``path`` is a directory.
-        
-        Throws a `ConfigValueError` if ``path`` is not a directory.
-        """
-        path = self.__expand_path(path)
-        if not os.path.isdir(path):
-            raise ConfigValueError(_(u'“%s” is not a directory') % \
-                                   get_unicode(path))
-        return path
-
-    def exec_ok(self, binary):
-        """Checks if the ``binary`` exists and if it is executable.
-        
-        Throws a `ConfigValueError` if the ``binary`` isn't a file or is
-        not executable.
-        """
-        binary = self.__expand_path(binary)
-        if not os.path.isfile(binary):
-            raise ConfigValueError(_(u'“%s” is not a file') % \
-                                   get_unicode(binary))
-        if not os.access(binary, os.X_OK):
-            raise ConfigValueError(_(u'File is not executable: “%s”') % \
-                                   get_unicode(binary))
-        return binary
-
     def known_scheme(self, scheme):
         """Converts ``scheme`` to upper case and checks if is known by
         Dovecot (listed in VirtualMailManager.SCHEMES).
-        
+
         Throws a `ConfigValueError` if the scheme is not listed in
         VirtualMailManager.SCHEMES.
         """
@@ -404,6 +380,8 @@
         Arguments:
         sections -- list of strings with section names
         """
+        # TODO: Derivate CliConfig from Config an move the interactive
+        #       stuff to CliConfig
         input_fmt = _(u'Enter new value for option %(option)s \
 [%(current_value)s]: ')
         failures = 0
@@ -435,6 +413,7 @@
 
     def __saveChanges(self):
         """Writes changes to the configuration file."""
+        # TODO: Move interactive stuff to CliConfig
         copy2(self.__cfgFileName, self.__cfgFileName+'.bak')
         self.__cfgFile = open(self.__cfgFileName, 'w')
         self.write(self.__cfgFile)
@@ -442,7 +421,7 @@
 
     def __chkCfg(self):
         """Checks all section's options for settings w/o default values.
-        
+
         Returns ``True`` if everything is fine, else ``False``."""
         errors = False
         for section in self._cfg.iterkeys():
@@ -456,10 +435,3 @@
                 self.__missing[section] = missing
         return not errors
 
-    def __expand_path(self, path):
-        """Expands paths, starting with ``.`` or ``~``, to an absolute path."""
-        if path.startswith('.'):
-            return os.path.abspath(path)
-        if path.startswith('~'):
-            return os.path.expanduser(path)
-        return path
--- a/VirtualMailManager/Domain.py	Thu Feb 04 19:08:01 2010 +0000
+++ b/VirtualMailManager/Domain.py	Fri Feb 05 20:13:32 2010 +0000
@@ -6,13 +6,17 @@
 
 from random import choice
 
-from __main__ import ERR
-from Exceptions import VMMDomainException as VMMDE
-import VirtualMailManager as VMM
-from Transport import Transport
+from VirtualMailManager import chk_domainname
+from VirtualMailManager.constants.ERROR import \
+     ACCOUNT_AND_ALIAS_PRESENT, ACCOUNT_PRESENT, ALIAS_PRESENT, \
+     DOMAIN_ALIAS_EXISTS, DOMAIN_EXISTS, NO_SUCH_DOMAIN
+from VirtualMailManager.Exceptions import VMMDomainException as VMMDE
+from VirtualMailManager.Transport import Transport
+
 
 MAILDIR_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz'
 
+
 class Domain(object):
     """Class to manage e-mail domains."""
     __slots__ = ('_basedir','_domaindir','_id','_name','_transport','_dbh')
@@ -25,7 +29,7 @@
         transport -- default vmm.cfg/misc/transport  (str)
         """
         self._dbh = dbh
-        self._name = VMM.VirtualMailManager.chkDomainname(domainname)
+        self._name = chk_domainname(domainname)
         self._basedir = basedir
         if transport is not None:
             self._transport = Transport(self._dbh, transport=transport)
@@ -34,8 +38,8 @@
         self._id = 0
         self._domaindir = None
         if not self._exists() and self._isAlias():
-            raise VMMDE(_(u"The domain “%s” is an alias domain.") %self._name,
-                    ERR.DOMAIN_ALIAS_EXISTS)
+            raise VMMDE(_(u"The domain “%s” is an alias domain.") % self._name,
+                        DOMAIN_ALIAS_EXISTS)
 
     def _exists(self):
         """Checks if the domain already exists.
@@ -120,13 +124,11 @@
             hasAlias = False
         if hasUser and hasAlias:
             raise VMMDE(_(u'There are accounts and aliases.'),
-                ERR.ACCOUNT_AND_ALIAS_PRESENT)
+                ACCOUNT_AND_ALIAS_PRESENT)
         elif hasUser:
-            raise VMMDE(_(u'There are accounts.'),
-                ERR.ACCOUNT_PRESENT)
+            raise VMMDE(_(u'There are accounts.'), ACCOUNT_PRESENT)
         elif hasAlias:
-            raise VMMDE(_(u'There are aliases.'),
-                ERR.ALIAS_PRESENT)
+            raise VMMDE(_(u'There are aliases.'), ALIAS_PRESENT)
 
     def save(self):
         """Stores the new domain in the database."""
@@ -141,7 +143,7 @@
             dbc.close()
         else:
             raise VMMDE(_(u'The domain “%s” already exists.') % self._name,
-                ERR.DOMAIN_EXISTS)
+                        DOMAIN_EXISTS)
 
     def delete(self, delUser=False, delAlias=False):
         """Deletes the domain.
@@ -159,7 +161,7 @@
             dbc.close()
         else:
             raise VMMDE(_(u"The domain “%s” doesn't exist.") % self._name,
-                ERR.NO_SUCH_DOMAIN)
+                        NO_SUCH_DOMAIN)
 
     def updateTransport(self, transport, force=False):
         """Sets a new transport for the domain.
@@ -185,7 +187,7 @@
             dbc.close()
         else:
             raise VMMDE(_(u"The domain “%s” doesn't exist.") % self._name,
-                ERR.NO_SUCH_DOMAIN)
+                        NO_SUCH_DOMAIN)
 
     def getID(self):
         """Returns the ID of the domain."""
@@ -216,7 +218,7 @@
         dbc.close()
         if info is None:
             raise VMMDE(_(u"The domain “%s” doesn't exist.") % self._name,
-                    ERR.NO_SUCH_DOMAIN)
+                        NO_SUCH_DOMAIN)
         else:
             keys = ['gid', 'domainname', 'transport', 'domaindir',
                     'aliasdomains', 'accounts', 'aliases', 'relocated']
@@ -278,7 +280,7 @@
 
 def search(dbh, pattern=None, like=False):
     if pattern is not None and like is False:
-        pattern = VMM.VirtualMailManager.chkDomainname(pattern)
+        pattern = chk_domainname(pattern)
     sql = 'SELECT gid, domainname, is_primary FROM domain_name'
     if pattern is None:
         pass
--- a/VirtualMailManager/EmailAddress.py	Thu Feb 04 19:08:01 2010 +0000
+++ b/VirtualMailManager/EmailAddress.py	Fri Feb 05 20:13:32 2010 +0000
@@ -4,9 +4,11 @@
 
 """Virtual Mail Manager's EmailAddress class to handle e-mail addresses."""
 
-from __main__ import re, ERR
-from Exceptions import VMMEmailAddressException as VMMEAE
-import VirtualMailManager as VMM
+import re
+
+import VirtualMailManager.constants.ERROR as ERR
+from VirtualMailManager import chk_domainname
+from VirtualMailManager.Exceptions import VMMEmailAddressException as VMMEAE
 
 RE_LOCALPART = """[^\w!#$%&'\*\+-\.\/=?^_`{\|}~]"""
 
@@ -40,15 +42,15 @@
             localpart, domain = address.split('@')
         except ValueError:
             raise VMMEAE(_(u"Missing '@' sign in e-mail address “%s”.") %
-                address, ERR.INVALID_ADDRESS)
+                         address, ERR.INVALID_ADDRESS)
         except AttributeError:
             raise VMMEAE(_(u"“%s” doesn't look like an e-mail address.") %
-                address, ERR.INVALID_ADDRESS)
+                         address, ERR.INVALID_ADDRESS)
         if len(domain) > 0:
-            domain = VMM.VirtualMailManager.chkDomainname(domain)
+            domain = chk_domainname(domain)
         else:
-            raise VMMEAE(_(u"Missing domain name after “%s@”.") %
-                    localpart, ERR.DOMAIN_NO_NAME)
+            raise VMMEAE(_(u"Missing domain name after “%s@”.") % localpart,
+                         ERR.DOMAIN_NO_NAME)
         localpart = self.__chkLocalpart(localpart)
         self._localpart, self._domainname = localpart, domain
 
--- a/VirtualMailManager/MailLocation.py	Thu Feb 04 19:08:01 2010 +0000
+++ b/VirtualMailManager/MailLocation.py	Fri Feb 05 20:13:32 2010 +0000
@@ -5,8 +5,10 @@
 """Virtual Mail Manager's MailLocation class to manage the mail_location
 for accounts."""
 
-from __main__ import re, ERR
-from Exceptions import VMMMailLocationException as MLE
+import re
+
+import VirtualMailManager.constants.ERROR as ERR
+from VirtualMailManager.Exceptions import VMMMailLocationException as MLE
 
 RE_MAILLOCATION = """^\w{1,20}$"""
 
--- a/VirtualMailManager/Relocated.py	Thu Feb 04 19:08:01 2010 +0000
+++ b/VirtualMailManager/Relocated.py	Fri Feb 05 20:13:32 2010 +0000
@@ -4,10 +4,10 @@
 
 """Virtual Mail Manager's Relocated class to manage relocated users."""
 
-from __main__ import ERR
-from Exceptions import VMMRelocatedException as VMMRE
-from Domain import Domain
-from EmailAddress import EmailAddress
+import VirtualMailManager.constants.ERROR as ERR
+from VirtualMailManager.Domain import Domain
+from VirtualMailManager.EmailAddress import EmailAddress
+from VirtualMailManager.Exceptions import VMMRelocatedException as VMMRE
 import VirtualMailManager as VMM
 
 class Relocated(object):
--- a/VirtualMailManager/Transport.py	Thu Feb 04 19:08:01 2010 +0000
+++ b/VirtualMailManager/Transport.py	Fri Feb 05 20:13:32 2010 +0000
@@ -5,8 +5,8 @@
 """Virtual Mail Manager's Transport class to manage the transport for
 domains and accounts."""
 
-from __main__ import ERR
-from Exceptions import VMMTransportException
+import VirtualMailManager.constants.ERROR as ERR
+from VirtualMailManager.Exceptions import VMMTransportException
 
 class Transport(object):
     """A wrapper class that provides access to the transport table"""
--- a/VirtualMailManager/__init__.py	Thu Feb 04 19:08:01 2010 +0000
+++ b/VirtualMailManager/__init__.py	Fri Feb 05 20:13:32 2010 +0000
@@ -8,8 +8,25 @@
 import re
 import locale
 
-from constants.VERSION import *
-import constants.ERROR as ERR
+from encodings.idna import ToASCII, ToUnicode
+
+from VirtualMailManager.constants.ERROR import \
+     DOMAIN_INVALID, DOMAIN_TOO_LONG, NOT_EXECUTABLE, NO_SUCH_BINARY, \
+     NO_SUCH_DIRECTORY
+from VirtualMailManager.constants.VERSION import *
+from VirtualMailManager.Exceptions import VMMException
+
+
+__all__ = [
+    # imported modules
+    'os', 're', 'locale',
+    # version information from VERSION
+    '__author__', '__date__', '__version__',
+    # error codes
+    'ENCODING', 'ace2idna', 'chk_domainname', 'exec_ok', 'expand_path',
+    'get_unicode', 'idn2ascii', 'is_dir',
+]
+
 
 # Try to set all of the locales according to the current
 # environment variables and get the character encoding.
@@ -19,24 +36,9 @@
     locale.setlocale(locale.LC_ALL, 'C')
 ENCODING = locale.nl_langinfo(locale.CODESET)
 
-def w_std(*args):
-    """Writes each arg of args, encoded in the current ENCODING, to stdout and
-    appends a newline."""
-    _write = os.sys.stdout.write
-    for arg in args:
-        _write(arg.encode(ENCODING, 'replace'))
-        _write('\n')
+RE_ASCII_CHARS = """^[\x20-\x7E]*$"""
+RE_DOMAIN = """^(?:[a-z0-9-]{1,63}\.){1,}[a-z]{2,6}$"""
 
-def w_err(code, *args):
-    """Writes each arg of args, encoded in the current ENCODING, to stderr and
-    appends a newline.
-    This function additional interrupts the program execution and uses 'code'
-    system exit status."""
-    _write = os.sys.stderr.write
-    for arg in args:
-        _write(arg.encode(ENCODING, 'replace'))
-        _write('\n')
-    os.sys.exit(code)
 
 def get_unicode(string):
     """Converts `string` to `unicode`, if necessary."""
@@ -44,14 +46,61 @@
         return string
     return unicode(string, ENCODING, 'replace')
 
-__all__ = [
-        # imported modules
-        'os', 're', 'locale',
-        # version information from VERSION
-        '__author__', '__date__', '__version__',
-        # error codes
-        'ERR',
-        # defined stuff
-        'ENCODING', 'get_unicode', 'w_std', 'w_err'
-        ]
-# EOF
+def expand_path(path):
+    """Expands paths, starting with ``.`` or ``~``, to an absolute path."""
+    if path.startswith('.'):
+        return os.path.abspath(path)
+    if path.startswith('~'):
+        return os.path.expanduser(path)
+    return path
+
+def is_dir(path):
+    """Checks if ``path`` is a directory.
+
+    Throws a `VMMException` if ``path`` is not a directory.
+    """
+    path = expand_path(path)
+    if not os.path.isdir(path):
+        raise VMMException(_(u'“%s” is not a directory') % get_unicode(path),
+                           NO_SUCH_DIRECTORY)
+    return path
+
+def exec_ok(binary):
+    """Checks if the ``binary`` exists and if it is executable.
+
+    Throws a `VMMException` if the ``binary`` isn't a file or is not
+    executable.
+    """
+    binary = expand_path(binary)
+    if not os.path.isfile(binary):
+        raise VMMException(_(u'“%s” is not a file') % get_unicode(binary),
+                           NO_SUCH_BINARY)
+    if not os.access(binary, os.X_OK):
+        raise VMMException(_(u'File is not executable: “%s”') % \
+                           get_unicode(binary), NOT_EXECUTABLE)
+    return binary
+
+def idn2ascii(domainname):
+    """Converts the idn domain name `domainname` into punycode."""
+    return '.'.join([ToASCII(lbl) for lbl in domainname.split('.') if lbl])
+
+def ace2idna(domainname):
+    """Converts the domain name `domainname` from ACE according to IDNA."""
+    return u'.'.join([ToUnicode(lbl) for lbl in domainname.split('.') if lbl])
+
+def chk_domainname(domainname):
+    """Returns the validated domain name `domainname`.
+
+    It also converts the name of the domain from IDN to ASCII, if necessary.
+
+    Throws an VMMException, if the domain name is too long or doesn't look
+    like a valid domain name (label.label.label).
+    """
+    if not re.match(RE_ASCII_CHARS, domainname):
+        domainname = idn2ascii(domainname)
+    if len(domainname) > 255:
+        raise VMMException(_(u'The domain name is too long.'), DOMAIN_TOO_LONG)
+    if not re.match(RE_DOMAIN, domainname):
+        raise VMMException(_(u'The domain name “%s” is invalid.') % domainname,
+                           DOMAIN_INVALID)
+    return domainname
--- a/VirtualMailManager/cli/handler.py	Thu Feb 04 19:08:01 2010 +0000
+++ b/VirtualMailManager/cli/handler.py	Fri Feb 05 20:13:32 2010 +0000
@@ -2,40 +2,49 @@
 # Copyright (c) 2007 - 2010, Pascal Volk
 # See COPYING for distribution information.
 
-"""The main class for vmm."""
+"""
+   VirtualMailManager.Handler
 
+   A wrapper class. It wraps round all other classes and does some
+   dependencies checks.
 
-from encodings.idna import ToASCII, ToUnicode
-from getpass import getpass
+   Additionally it communicates with the PostgreSQL database, creates
+   or deletes directories of domains or users.
+"""
+
+import os
+import re
+
 from shutil import rmtree
 from subprocess import Popen, PIPE
 
 from pyPgSQL import PgSQL # python-pgsql - http://pypgsql.sourceforge.net
 
-from __main__ import os, re, ENCODING, ERR, w_std
-from ext.Postconf import Postconf
-from Account import Account
-from Alias import Alias
-from AliasDomain import AliasDomain
-from Config import Config as Cfg
-from Domain import Domain
-from EmailAddress import EmailAddress
-from Exceptions import *
-from Relocated import Relocated
+import VirtualMailManager.constants.ERROR as ERR
+from VirtualMailManager import ENCODING, ace2idna, exec_ok, read_pass
+from VirtualMailManager.Account import Account
+from VirtualMailManager.Alias import Alias
+from VirtualMailManager.AliasDomain import AliasDomain
+from VirtualMailManager.Config import Config as Cfg
+from VirtualMailManager.Domain import Domain
+from VirtualMailManager.EmailAddress import EmailAddress
+from VirtualMailManager.Exceptions import *
+from VirtualMailManager.Relocated import Relocated
+from VirtualMailManager.ext.Postconf import Postconf
 
 SALTCHARS = './0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
-RE_ASCII_CHARS = """^[\x20-\x7E]*$"""
-RE_DOMAIN = """^(?:[a-z0-9-]{1,63}\.){1,}[a-z]{2,6}$"""
 RE_DOMAIN_SRCH = """^[a-z0-9-\.]+$"""
 RE_LOCALPART = """[^\w!#$%&'\*\+-\.\/=?^_`{\|}~]"""
 RE_MBOX_NAMES = """^[\x20-\x25\x27-\x7E]*$"""
 
-class VirtualMailManager(object):
-    """The main class for vmm"""
+class Handler(object):
+    """Wrapper class to simplify the access on all the stuff from
+    VirtualMailManager"""
+    # TODO: accept a LazyConfig object as argument
     __slots__ = ('__Cfg', '__cfgFileName', '__dbh', '__scheme', '__warnings',
                  '_postconf')
     def __init__(self):
-        """Creates a new VirtualMailManager instance.
+        """Creates a new Handler instance.
         Throws a VMMNotRootException if your uid is greater 0.
         """
         self.__cfgFileName = ''
@@ -93,18 +102,25 @@
 (vmm.cfg: section "misc", option "base_directory")') %
                                  basedir, ERR.NO_SUCH_DIRECTORY)
         for opt, val in self.__Cfg.items('bin'):
-            if not os.path.exists(val):
-                raise VMMException(_(u'“%(binary)s” doesn\'t exist.\n\
+            try:
+                exec_ok(val)
+            except VMMException, e:
+                code = e.code()
+                if code is ERR.NO_SUCH_BINARY:
+                    raise VMMException(_(u'“%(binary)s” doesn\'t exist.\n\
 (vmm.cfg: section "bin", option "%(option)s")') %{'binary': val,'option': opt},
-                    ERR.NO_SUCH_BINARY)
-            elif not os.access(val, os.X_OK):
-                raise VMMException(_(u'“%(binary)s” is not executable.\n\
+                                       ERR.NO_SUCH_BINARY)
+                elif code is ERR.NOT_EXECUTABLE:
+                    raise VMMException(_(u'“%(binary)s” is not executable.\n\
 (vmm.cfg: section "bin", option "%(option)s")') %{'binary': val,'option': opt},
-                    ERR.NOT_EXECUTABLE)
+                                       ERR.NOT_EXECUTABLE)
+                else:
+                    raise
 
     def __dbConnect(self):
         """Creates a pyPgSQL.PgSQL.connection instance."""
-        if self.__dbh is None or not self.__dbh._isOpen:
+        if self.__dbh is None or (isinstance(self.__dbh, PgSQL.Connection) and
+                                  not self.__dbh._isOpen):
             try:
                 self.__dbh = PgSQL.connect(
                         database=self.__Cfg.dget('database.name'),
@@ -118,42 +134,6 @@
             except PgSQL.libpq.DatabaseError, e:
                 raise VMMException(str(e), ERR.DATABASE_ERROR)
 
-    def idn2ascii(domainname):
-        """Converts an idn domainname in punycode.
-
-        Arguments:
-        domainname -- the domainname to convert (unicode)
-        """
-        return '.'.join([ToASCII(lbl) for lbl in domainname.split('.') if lbl])
-    idn2ascii = staticmethod(idn2ascii)
-
-    def ace2idna(domainname):
-        """Convertis a domainname from ACE according to IDNA
-
-        Arguments:
-        domainname -- the domainname to convert (str)
-        """
-        return u'.'.join([ToUnicode(lbl) for lbl in domainname.split('.')\
-                if lbl])
-    ace2idna = staticmethod(ace2idna)
-
-    def chkDomainname(domainname):
-        """Validates the domain name of an e-mail address.
-
-        Keyword arguments:
-        domainname -- the domain name that should be validated
-        """
-        if not re.match(RE_ASCII_CHARS, domainname):
-            domainname = VirtualMailManager.idn2ascii(domainname)
-        if len(domainname) > 255:
-            raise VMMException(_(u'The domain name is too long.'),
-                ERR.DOMAIN_TOO_LONG)
-        if not re.match(RE_DOMAIN, domainname):
-            raise VMMException(_(u'The domain name “%s” is invalid.') %\
-                    domainname, ERR.DOMAIN_INVALID)
-        return domainname
-    chkDomainname = staticmethod(chkDomainname)
-
     def _exists(dbh, query):
         dbc = dbh.cursor()
         dbc.execute(query)
@@ -169,46 +149,23 @@
         sql = "SELECT gid FROM users WHERE gid = (SELECT gid FROM domain_name\
  WHERE domainname = '%s') AND local_part = '%s'" % (address._domainname,
             address._localpart)
-        return VirtualMailManager._exists(dbh, sql)
+        return Handler._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 = '%s') AND address = '%s'" %\
             (address._domainname, address._localpart)
-        return VirtualMailManager._exists(dbh, sql)
+        return Handler._exists(dbh, sql)
     aliasExists = staticmethod(aliasExists)
 
     def relocatedExists(dbh, address):
         sql = "SELECT gid FROM relocated WHERE gid = (SELECT gid FROM\
  domain_name WHERE domainname = '%s') AND address = '%s'" %\
             (address._domainname, address._localpart)
-        return VirtualMailManager._exists(dbh, sql)
+        return Handler._exists(dbh, sql)
     relocatedExists = staticmethod(relocatedExists)
 
-    def _readpass(self):
-        # TP: Please preserve the trailing space.
-        readp_msg0 = _(u'Enter new password: ').encode(ENCODING, 'replace')
-        # TP: Please preserve the trailing space.
-        readp_msg1 = _(u'Retype new password: ').encode(ENCODING, 'replace')
-        mismatched = True
-        flrs = 0
-        while mismatched:
-            if flrs > 2:
-                raise VMMException(_(u'Too many failures - try again later.'),
-                        ERR.VMM_TOO_MANY_FAILURES)
-            clear0 = getpass(prompt=readp_msg0)
-            clear1 = getpass(prompt=readp_msg1)
-            if clear0 != clear1:
-                flrs += 1
-                w_std(_(u'Sorry, passwords do not match'))
-                continue
-            if len(clear0) < 1:
-                flrs += 1
-                w_std(_(u'Sorry, empty passwords are not permitted'))
-                continue
-            mismatched = False
-        return clear0
 
     def __getAccount(self, address, password=None):
         self.__dbConnect()
@@ -500,8 +457,7 @@
         dom = self.__getDomain(domainname)
         dominfo = dom.getInfo()
         if dominfo['domainname'].startswith('xn--'):
-            dominfo['domainname'] += ' (%s)'\
-                % VirtualMailManager.ace2idna(dominfo['domainname'])
+            dominfo['domainname'] += ' (%s)' % ace2idna(dominfo['domainname'])
         if details is None:
             return dominfo
         elif details == 'accounts':
@@ -575,7 +531,7 @@
     def userAdd(self, emailaddress, password):
         acc = self.__getAccount(emailaddress, password)
         if password is None:
-            password = self._readpass()
+            password = read_pass()
             acc.setPassword(self.__pwhash(password))
         acc.save(self.__Cfg.dget('maildir.name'),
                  self.__Cfg.dget('misc.dovecot_version'),
@@ -589,8 +545,8 @@
         alias = self.__getAlias(aliasaddress, targetaddress)
         alias.save(long(self._postconf.read('virtual_alias_expansion_limit')))
         gid = self.__getDomain(alias._dest._domainname).getID()
-        if gid > 0 and not VirtualMailManager.accountExists(self.__dbh,
-        alias._dest) and not VirtualMailManager.aliasExists(self.__dbh,
+        if gid > 0 and not Handler.accountExists(self.__dbh,
+        alias._dest) and not Handler.aliasExists(self.__dbh,
         alias._dest):
             self.__warnings.append(
                 _(u"The destination account/alias “%s” doesn't exist.")%\
@@ -642,7 +598,7 @@
         return info
 
     def userByID(self, uid):
-        from Account import getAccountByID
+        from Handler.Account import getAccountByID
         self.__dbConnect()
         return getAccountByID(uid, self.__dbh)
 
@@ -651,7 +607,7 @@
         if acc.getUID() == 0:
            raise VMMException(_(u"Account doesn't exist"), ERR.NO_SUCH_ACCOUNT)
         if password is None:
-            password = self._readpass()
+            password = read_pass()
         acc.modify('password', self.__pwhash(password, user=emailaddress))
 
     def userName(self, emailaddress, name):
@@ -695,5 +651,5 @@
         relocated.delete()
 
     def __del__(self):
-        if not self.__dbh is None and self.__dbh._isOpen:
+        if isinstance(self.__dbh, PgSQL.Connection) and self.__dbh._isOpen:
             self.__dbh.close()
--- a/VirtualMailManager/ext/Postconf.py	Thu Feb 04 19:08:01 2010 +0000
+++ b/VirtualMailManager/ext/Postconf.py	Fri Feb 05 20:13:32 2010 +0000
@@ -4,9 +4,10 @@
 
 """A small - r/o - wrapper class for Postfix' postconf."""
 
+import re
 from subprocess import Popen, PIPE
 
-from __main__ import re, ERR
+import VirtualMailManager.constants.ERROR as ERR
 from VirtualMailManager.Exceptions import VMMException
 
 RE_PC_PARAMS = """^\w+$"""
--- a/vmm	Thu Feb 04 19:08:01 2010 +0000
+++ b/vmm	Fri Feb 05 20:13:32 2010 +0000
@@ -9,6 +9,10 @@
 from time import strftime, strptime
 
 from VirtualMailManager import *
+from VirtualMailManager.cli import w_std, w_err
+
+
+# TODO: FIXME
 from VirtualMailManager.VirtualMailManager import VirtualMailManager
 import VirtualMailManager.Exceptions as VMME
 import VirtualMailManager.constants.EXIT as EXIT
@@ -123,7 +127,7 @@
                 if not dom.startswith('xn--'):
                     w_std(u'\t%s' % dom)
                 else:
-                    w_std(u'\t%s (%s)' % (dom, vmm.ace2idna(dom)))
+                    w_std(u'\t%s (%s)' % (dom, ace2idna(dom)))
     else:
         w_std(_(u'\tNone'))
     print
@@ -147,7 +151,7 @@
 
 def _formatDom(domain, main=True):
     if domain.startswith('xn--'):
-        domain = u'%s (%s)' % (domain, vmm.ace2idna(domain))
+        domain = u'%s (%s)' % (domain, ace2idna(domain))
     if main:
         return u'\t[+] %s' %  domain
     else:
@@ -174,7 +178,7 @@
     msg = _('Alias domain information')
     for k in ['alias', 'domain']:
         if info[k].startswith('xn--'):
-            info[k] = "%s (%s)" % (info[k], vmm.ace2idna(info[k]))
+            info[k] = "%s (%s)" % (info[k], ace2idna(info[k]))
     w_std('%s\n%s' % (msg, '-'*len(msg)))
     w_std(
         _('\tThe alias domain %(alias)s belongs to:\n\t    * %(domain)s')%info)