diff -r d0425225ce52 -r 6e1ef32fbd82 VirtualMailManager/cli/handler.py --- 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()