VirtualMailManager/cli/handler.py
branchv0.6.x
changeset 185 6e1ef32fbd82
parent 184 d0425225ce52
--- 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()