# HG changeset patch # User Pascal Volk # Date 1265400812 0 # Node ID 6e1ef32fbd82eb2a1530191245331cfb356f110c # Parent d0425225ce52c0956c80b7dff0617d66375cdfe8 VMM/*: Moved some methods from classes to modules __init__. - Adjusted many import statements. - Small adjustments and whitespace cosmetics in Config.py diff -r d0425225ce52 -r 6e1ef32fbd82 VirtualMailManager/Account.py --- 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): diff -r d0425225ce52 -r 6e1ef32fbd82 VirtualMailManager/Alias.py --- 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): diff -r d0425225ce52 -r 6e1ef32fbd82 VirtualMailManager/AliasDomain.py --- 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() diff -r d0425225ce52 -r 6e1ef32fbd82 VirtualMailManager/Config.py --- 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 diff -r d0425225ce52 -r 6e1ef32fbd82 VirtualMailManager/Domain.py --- 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 diff -r d0425225ce52 -r 6e1ef32fbd82 VirtualMailManager/EmailAddress.py --- 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 diff -r d0425225ce52 -r 6e1ef32fbd82 VirtualMailManager/MailLocation.py --- 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}$""" diff -r d0425225ce52 -r 6e1ef32fbd82 VirtualMailManager/Relocated.py --- 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): diff -r d0425225ce52 -r 6e1ef32fbd82 VirtualMailManager/Transport.py --- 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""" diff -r d0425225ce52 -r 6e1ef32fbd82 VirtualMailManager/__init__.py --- 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 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() diff -r d0425225ce52 -r 6e1ef32fbd82 VirtualMailManager/ext/Postconf.py --- 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+$""" diff -r d0425225ce52 -r 6e1ef32fbd82 vmm --- 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)