VirtualMailManager/VirtualMailManager.py
changeset 47 191d5a5adc4a
parent 45 9e66138aad0b
child 48 0d5f58f8b8f5
equal deleted inserted replaced
46:7ece710c559d 47:191d5a5adc4a
    14 __date__ = '$Date$'.split()[1]
    14 __date__ = '$Date$'.split()[1]
    15 
    15 
    16 import os
    16 import os
    17 import re
    17 import re
    18 import sys
    18 import sys
    19 import gettext
       
    20 from encodings.idna import ToASCII, ToUnicode
    19 from encodings.idna import ToASCII, ToUnicode
    21 from getpass import getpass
    20 from getpass import getpass
    22 from shutil import rmtree
    21 from shutil import rmtree
    23 from subprocess import Popen, PIPE
    22 from subprocess import Popen, PIPE
    24 
    23 
    36 RE_DOMAIN = """^(?:[a-z0-9-]{1,63}\.){1,}[a-z]{2,6}$"""
    35 RE_DOMAIN = """^(?:[a-z0-9-]{1,63}\.){1,}[a-z]{2,6}$"""
    37 RE_DOMAIN_SRCH = """^[a-z0-9-\.]+$"""
    36 RE_DOMAIN_SRCH = """^[a-z0-9-\.]+$"""
    38 RE_LOCALPART = """[^\w!#$%&'\*\+-\.\/=?^_`{\|}~]"""
    37 RE_LOCALPART = """[^\w!#$%&'\*\+-\.\/=?^_`{\|}~]"""
    39 RE_MAILLOCATION = """^[\w]{1,20}$"""
    38 RE_MAILLOCATION = """^[\w]{1,20}$"""
    40 
    39 
    41 ENCODING_IN = sys.getfilesystemencoding()
       
    42 ENCODING_OUT = sys.stdout.encoding or sys.getfilesystemencoding()
       
    43 
       
    44 gettext.bindtextdomain('vmm', '/usr/local/share/locale')
       
    45 gettext.textdomain('vmm')
       
    46 _ = gettext.gettext
       
    47 
       
    48 class VirtualMailManager:
    40 class VirtualMailManager:
    49     """The main class for vmm"""
    41     """The main class for vmm"""
    50     def __init__(self):
    42     def __init__(self):
    51         """Creates a new VirtualMailManager instance.
    43         """Creates a new VirtualMailManager instance.
    52         Throws a VMMNotRootException if your uid is greater 0.
    44         Throws a VMMNotRootException if your uid is greater 0.
    53         """
    45         """
    54         self.__cfgFileName = '/usr/local/etc/vmm.cfg'
    46         self.__cfgFileName = '/usr/local/etc/vmm.cfg'
    55         self.__permWarnMsg = _("fix permissions for '%s'\n`chmod 0600 %s` would\
    47         self.__permWarnMsg = _(u"fix permissions for »%s«\n`chmod 0600 %s`\
    56  be great.") % (self.__cfgFileName, self.__cfgFileName)
    48  would be great.") % (self.__cfgFileName, self.__cfgFileName)
    57         self.__warnings = []
    49         self.__warnings = []
    58         self.__Cfg = None
    50         self.__Cfg = None
    59         self.__dbh = None
    51         self.__dbh = None
    60 
    52 
    61         if os.geteuid():
    53         if os.geteuid():
    62             raise VMMNotRootException((_("You are not root.\n\tGood bye!\n"),
    54             raise VMMNotRootException((_(u"You are not root.\n\tGood bye!\n"),
    63                 ERR.CONF_NOPERM))
    55                 ERR.CONF_NOPERM))
    64         if self.__chkCfgFile():
    56         if self.__chkCfgFile():
    65             self.__Cfg = Cfg(self.__cfgFileName)
    57             self.__Cfg = Cfg(self.__cfgFileName)
    66             self.__Cfg.load()
    58             self.__Cfg.load()
    67             self.__Cfg.check()
    59             self.__Cfg.check()
    71             self.__chkenv()
    63             self.__chkenv()
    72 
    64 
    73     def __chkCfgFile(self):
    65     def __chkCfgFile(self):
    74         """Checks the configuration file, returns bool"""
    66         """Checks the configuration file, returns bool"""
    75         if not os.path.isfile(self.__cfgFileName):
    67         if not os.path.isfile(self.__cfgFileName):
    76             raise VMMException((_(u"The file '%s' does not exists.") %
    68             raise VMMException((_(u"The file »%s« does not exists.") %
    77                 self.__cfgFileName, ERR.CONF_NOFILE))
    69                 self.__cfgFileName, ERR.CONF_NOFILE))
    78         fstat = os.stat(self.__cfgFileName)
    70         fstat = os.stat(self.__cfgFileName)
    79         try:
    71         try:
    80             fmode = self.__getFileMode()
    72             fmode = self.__getFileMode()
    81         except:
    73         except:
    93             os.makedirs(self.__Cfg.get('domdir', 'base'), 0771)
    85             os.makedirs(self.__Cfg.get('domdir', 'base'), 0771)
    94             os.chown(self.__Cfg.get('domdir', 'base'), 0,
    86             os.chown(self.__Cfg.get('domdir', 'base'), 0,
    95                     self.__Cfg.getint('misc', 'gid_mail'))
    87                     self.__Cfg.getint('misc', 'gid_mail'))
    96             os.umask(old_umask)
    88             os.umask(old_umask)
    97         elif not os.path.isdir(self.__Cfg.get('domdir', 'base')):
    89         elif not os.path.isdir(self.__Cfg.get('domdir', 'base')):
    98             raise VMMException((_('%s is not a directory') %
    90             raise VMMException((_(u'»%s« is not a directory.\n\
       
    91 (vmm.cfg: section "domdir", option "base")') %
    99                 self.__Cfg.get('domdir', 'base'), ERR.NO_SUCH_DIRECTORY))
    92                 self.__Cfg.get('domdir', 'base'), ERR.NO_SUCH_DIRECTORY))
   100         for opt, val in self.__Cfg.items('bin'):
    93         for opt, val in self.__Cfg.items('bin'):
   101             if not os.path.exists(val):
    94             if not os.path.exists(val):
   102                 raise VMMException((_("%s doesn't exists.") % val,
    95                 raise VMMException((_(u'»%s« doesn\'t exists.\n\
   103                     ERR.NO_SUCH_BINARY))
    96 (vmm.cfg: section "bin", option "%s")') % (val, opt), ERR.NO_SUCH_BINARY))
   104             elif not os.access(val, os.X_OK):
    97             elif not os.access(val, os.X_OK):
   105                 raise VMMException((_("%s is not executable.") % val,
    98                 raise VMMException((_(u'»%s« is not executable.\n\
   106                     ERR.NOT_EXECUTABLE))
    99 (vmm.cfg: section "bin", option "%s")') % (val, opt), ERR.NOT_EXECUTABLE))
   107 
   100 
   108     def __getFileMode(self):
   101     def __getFileMode(self):
   109         """Determines the file access mode from file __cfgFileName,
   102         """Determines the file access mode from file __cfgFileName,
   110         returns int.
   103         returns int.
   111         """
   104         """
   127             dbc.execute("SET NAMES 'UTF8'")
   120             dbc.execute("SET NAMES 'UTF8'")
   128             dbc.close()
   121             dbc.close()
   129         except PgSQL.libpq.DatabaseError, e:
   122         except PgSQL.libpq.DatabaseError, e:
   130             raise VMMException((str(e), ERR.DATABASE_ERROR))
   123             raise VMMException((str(e), ERR.DATABASE_ERROR))
   131 
   124 
   132     def __chkLocalpart(self, localpart):
   125     def chkLocalpart(localpart):
   133         """Validates the local part of an e-mail address.
   126         """Validates the local part of an e-mail address.
   134         
   127         
   135         Keyword arguments:
   128         Keyword arguments:
   136         localpart -- the e-mail address that should be validated (str)
   129         localpart -- the e-mail address that should be validated (str)
   137         """
   130         """
       
   131         if len(localpart) < 1:
       
   132             raise VMMException((_(u'No localpart specified.'),
       
   133                 ERR.LOCALPART_INVALID))
   138         if len(localpart) > 64:
   134         if len(localpart) > 64:
   139             raise VMMException((_('The local part is too long'),
   135             raise VMMException((_(u'The local part »%s« is too long') %
   140                 ERR.LOCALPART_TOO_LONG))
   136                 localpart, ERR.LOCALPART_TOO_LONG))
   141         if re.compile(RE_LOCALPART).search(localpart):
   137         ic = re.compile(RE_LOCALPART).findall(localpart)
       
   138         if len(ic):
       
   139             ichrs = ''
       
   140             for c in set(ic):
       
   141                 ichrs += u"»%s« " % c
   142             raise VMMException((
   142             raise VMMException((
   143                 _(u"The local part '%s' contains invalid characters.") %
   143                 _(u"The local part »%s« contains invalid characters: %s") %
   144                 localpart, ERR.LOCALPART_INVALID))
   144                 (localpart, ichrs), ERR.LOCALPART_INVALID))
   145         return localpart
   145         return localpart
   146 
   146     chkLocalpart = staticmethod(chkLocalpart)
   147     def idn2ascii(self, domainname):
   147 
       
   148     def idn2ascii(domainname):
   148         """Converts an idn domainname in punycode.
   149         """Converts an idn domainname in punycode.
   149         
   150         
   150         Keyword arguments:
   151         Keyword arguments:
   151         domainname -- the domainname to convert (str)
   152         domainname -- the domainname to convert (str)
   152         """
   153         """
   153         tmp = []
   154         tmp = []
   154         for label in domainname.split('.'):
   155         for label in domainname.split('.'):
   155             if len(label) == 0:
   156             if len(label) == 0:
   156                 continue
   157                 continue
   157             tmp.append(ToASCII(unicode(label, ENCODING_IN)))
   158             #tmp.append(ToASCII(unicode(label, ENCODING_IN)))
       
   159             tmp.append(ToASCII(label))
   158         return '.'.join(tmp)
   160         return '.'.join(tmp)
   159 
   161     idn2ascii = staticmethod(idn2ascii)
   160     def ace2idna(self, domainname):
   162 
       
   163     def ace2idna(domainname):
   161         """Convertis a domainname from ACE according to IDNA
   164         """Convertis a domainname from ACE according to IDNA
   162         
   165         
   163         Keyword arguments:
   166         Keyword arguments:
   164         domainname -- the domainname to convert (str)
   167         domainname -- the domainname to convert (str)
   165         """
   168         """
   167         for label in domainname.split('.'):
   170         for label in domainname.split('.'):
   168             if len(label) == 0:
   171             if len(label) == 0:
   169                 continue
   172                 continue
   170             tmp.append(ToUnicode(label))
   173             tmp.append(ToUnicode(label))
   171         return '.'.join(tmp)
   174         return '.'.join(tmp)
   172 
   175     ace2idna = staticmethod(ace2idna)
   173     def __chkDomainname(self, domainname):
   176 
       
   177     def chkDomainname(domainname):
   174         """Validates the domain name of an e-mail address.
   178         """Validates the domain name of an e-mail address.
   175         
   179         
   176         Keyword arguments:
   180         Keyword arguments:
   177         domainname -- the domain name that should be validated
   181         domainname -- the domain name that should be validated
   178         """
   182         """
   179         re.compile(RE_ASCII_CHARS)
   183         re.compile(RE_ASCII_CHARS)
   180         if not re.match(RE_ASCII_CHARS, domainname):
   184         if not re.match(RE_ASCII_CHARS, domainname):
   181             domainname = self.idn2ascii(domainname)
   185             domainname = VirtualMailManager.idn2ascii(domainname)
   182         if len(domainname) > 255:
   186         if len(domainname) > 255:
   183             raise VMMException((_('The domain name is too long.'),
   187             raise VMMException((_(u'The domain name is too long.'),
   184                 ERR.DOMAIN_TOO_LONG))
   188                 ERR.DOMAIN_TOO_LONG))
   185         re.compile(RE_DOMAIN)
   189         re.compile(RE_DOMAIN)
   186         if not re.match(RE_DOMAIN, domainname):
   190         if not re.match(RE_DOMAIN, domainname):
   187             raise VMMException((_('The domain name is invalid.'),
   191             raise VMMException((_(u'The domain name is invalid.'),
   188                 ERR.DOMAIN_INVALID))
   192                 ERR.DOMAIN_INVALID))
   189         return domainname
   193         return domainname
   190 
   194     chkDomainname = staticmethod(chkDomainname)
   191     def __chkEmailAddress(self, address):
   195 
       
   196     def chkEmailAddress(address):
   192         try:
   197         try:
   193             localpart, domain = address.split('@')
   198             localpart, domain = address.split('@')
   194         except ValueError:
   199         except ValueError:
   195             raise VMMException((_(u"Missing '@' sign in e-mail address '%s'.") %
   200             raise VMMException((_(u"Missing '@' sign in e-mail address »%s«.") %
   196                 address, ERR.INVALID_ADDRESS))
   201                 address, ERR.INVALID_ADDRESS))
   197         except AttributeError:
   202         except AttributeError:
   198             raise VMMException((_(u"'%s' looks not like an e-mail address.") %
   203             raise VMMException((_(u"»%s« looks not like an e-mail address.") %
   199                 address, ERR.INVALID_ADDRESS))
   204                 address, ERR.INVALID_ADDRESS))
   200         domain = self.__chkDomainname(domain)
   205         domain = VirtualMailManager.chkDomainname(domain)
   201         localpart = self.__chkLocalpart(localpart)
   206         localpart = VirtualMailManager.chkLocalpart(localpart)
   202         return '%s@%s' % (localpart, domain)
   207         return '%s@%s' % (localpart, domain)
       
   208     chkEmailAddress = staticmethod(chkEmailAddress)
   203 
   209 
   204     def __getAccount(self, address, password=None):
   210     def __getAccount(self, address, password=None):
   205         address = self.__chkEmailAddress(address)
       
   206         self.__dbConnect()
   211         self.__dbConnect()
   207         if not password is None:
   212         if not password is None:
   208             password = self.__pwhash(password)
   213             password = self.__pwhash(password)
   209         return Account(self.__dbh, address, password)
   214         return Account(self.__dbh, address, password)
   210 
   215 
   222                 clear0 = ''
   227                 clear0 = ''
   223                 sys.stderr.write('%s\n' % _('Sorry, passwords do not match'))
   228                 sys.stderr.write('%s\n' % _('Sorry, passwords do not match'))
   224         return clear0
   229         return clear0
   225 
   230 
   226     def __getAlias(self, address, destination=None):
   231     def __getAlias(self, address, destination=None):
   227         address = self.__chkEmailAddress(address)
   232         address = VirtualMailManager.chkEmailAddress(address)
   228         if not destination is None:
   233         if not destination is None:
   229             if destination.count('@'):
   234             if destination.count('@'):
   230                 destination = self.__chkEmailAddress(destination)
   235                 destination = VirtualMailManager.chkEmailAddress(destination)
   231             else:
   236             else:
   232                 destination = self.__chkLocalpart(destination)
   237                 destination = VirtualMailManager.chkLocalpart(destination)
   233         self.__dbConnect()
   238         self.__dbConnect()
   234         return Alias(self.__dbh, address, destination)
   239         return Alias(self.__dbh, address, destination)
   235 
   240 
   236     def __getDomain(self, domainname, transport=None):
   241     def __getDomain(self, domainname, transport=None):
   237         domainname = self.__chkDomainname(domainname)
   242         domainname = VirtualMailManager.chkDomainname(domainname)
   238         if transport is None:
   243         if transport is None:
   239             transport = self.__Cfg.get('misc', 'transport')
   244             transport = self.__Cfg.get('misc', 'transport')
   240         self.__dbConnect()
   245         self.__dbConnect()
   241         return Domain(self.__dbh, domainname,
   246         return Domain(self.__dbh, domainname,
   242                 self.__Cfg.get('domdir', 'base'), transport)
   247                 self.__Cfg.get('domdir', 'base'), transport)
   312 
   317 
   313     def __maildirdelete(self, domdir, uid, gid):
   318     def __maildirdelete(self, domdir, uid, gid):
   314         if uid > 0 and gid > 0:
   319         if uid > 0 and gid > 0:
   315             maildir = '%s' % uid
   320             maildir = '%s' % uid
   316             if maildir.count('..') or domdir.count('..'):
   321             if maildir.count('..') or domdir.count('..'):
   317                 raise VMMException((_('FATAL: ".." in maildir path detected.'),
   322                 raise VMMException((_(u'Found ".." in maildir path.'),
   318                     ERR.FOUND_DOTS_IN_PATH))
   323                     ERR.FOUND_DOTS_IN_PATH))
   319             if os.path.isdir(domdir):
   324             if os.path.isdir(domdir):
   320                 os.chdir(domdir)
   325                 os.chdir(domdir)
   321                 if os.path.isdir(maildir):
   326                 if os.path.isdir(maildir):
   322                     mdstat = os.stat(maildir)
   327                     mdstat = os.stat(maildir)
   323                     if (mdstat.st_uid, mdstat.st_gid) != (uid, gid):
   328                     if (mdstat.st_uid, mdstat.st_gid) != (uid, gid):
   324                         raise VMMException((
   329                         raise VMMException((
   325                            _('FATAL: owner/group mismatch in maildir detected'),
   330                           _(u'Owner/group mismatch in maildir detected.'),
   326                            ERR.MAILDIR_PERM_MISMATCH))
   331                           ERR.MAILDIR_PERM_MISMATCH))
   327                     rmtree(maildir, ignore_errors=True)
   332                     rmtree(maildir, ignore_errors=True)
   328                 else:
   333                 else:
   329                     self.__warnings.append(_('No such directory: %s/%s') %
   334                     raise VMMException((_(u"No such directory: %s/%s") %
   330                             (domdir,uid))
   335                         (domdir, uid), ERR.NO_SUCH_DIRECTORY))
   331 
   336 
   332     def __domdirdelete(self, domdir, gid):
   337     def __domdirdelete(self, domdir, gid):
   333         if gid > 0:
   338         if gid > 0:
   334             if not self.__isdir(domdir):
   339             if not self.__isdir(domdir):
   335                 return
   340                 return
   336             basedir = '%s' % self.__Cfg.get('domdir', 'base')
   341             basedir = '%s' % self.__Cfg.get('domdir', 'base')
   337             domdirdirs = domdir.replace(basedir+'/', '').split('/')
   342             domdirdirs = domdir.replace(basedir+'/', '').split('/')
   338             if basedir.count('..') or domdir.count('..'):
   343             if basedir.count('..') or domdir.count('..'):
   339                 raise VMMException(
   344                 raise VMMException(
   340                         (_('FATAL: ".." in domain directory path detected.'),
   345                         (_(u'FATAL: ".." in domain directory path detected.'),
   341                             ERR.FOUND_DOTS_IN_PATH))
   346                             ERR.FOUND_DOTS_IN_PATH))
   342             if os.path.isdir('%s/%s' % (basedir, domdirdirs[0])):
   347             if os.path.isdir('%s/%s' % (basedir, domdirdirs[0])):
   343                 os.chdir('%s/%s' % (basedir, domdirdirs[0]))
   348                 os.chdir('%s/%s' % (basedir, domdirdirs[0]))
   344                 if os.lstat(domdirdirs[1]).st_gid != gid:
   349                 if os.lstat(domdirdirs[1]).st_gid != gid:
   345                     raise VMMException(
   350                     raise VMMException(
   346                     (_('FATAL: group mismatch in domain directory detected'),
   351                     (_(u'FATAL: group mismatch in domain directory detected'),
   347                         ERR.DOMAINDIR_GROUP_MISMATCH))
   352                         ERR.DOMAINDIR_GROUP_MISMATCH))
   348                 rmtree(domdirdirs[1], ignore_errors=True)
   353                 rmtree(domdirdirs[1], ignore_errors=True)
   349 
   354 
   350     def __getSalt(self):
   355     def __getSalt(self):
   351         from random import choice
   356         from random import choice
   428     def setupIsDone(self):
   433     def setupIsDone(self):
   429         """Checks if vmm is configured, returns bool"""
   434         """Checks if vmm is configured, returns bool"""
   430         try:
   435         try:
   431             return self.__Cfg.getboolean('config', 'done')
   436             return self.__Cfg.getboolean('config', 'done')
   432         except ValueError, e:
   437         except ValueError, e:
   433             raise VMMConfigException(_("""Configurtion error: "%s"
   438             raise VMMConfigException(_(u"""Configurtion error: "%s"
   434 (in section "connfig", option "done")'
   439 (in section "connfig", option "done") see also: vmm.cfg(5)\n""") % 
   435 see also: vmm.cfg(5)\n""") % str(e))
   440                 str(e))
   436 
   441 
   437     def configure(self, section=None):
   442     def configure(self, section=None):
   438         """Starts interactive configuration.
   443         """Starts interactive configuration.
   439 
   444 
   440         Configures in interactive mode options in the given section.
   445         Configures in interactive mode options in the given section.
   471         else:
   476         else:
   472             dom.updateTransport(transport, force=True)
   477             dom.updateTransport(transport, force=True)
   473 
   478 
   474     def domain_delete(self, domainname, force=None):
   479     def domain_delete(self, domainname, force=None):
   475         if not force is None and force not in ['deluser','delalias','delall']:
   480         if not force is None and force not in ['deluser','delalias','delall']:
   476             raise VMMDomainException((_(u"Invalid argument: '%s'") % force,
   481             raise VMMDomainException((_(u"Invalid argument: »%s«") % force,
   477                 ERR.INVALID_OPTION))
   482                 ERR.INVALID_OPTION))
   478         dom = self.__getDomain(domainname)
   483         dom = self.__getDomain(domainname)
   479         gid = dom.getID()
   484         gid = dom.getID()
   480         domdir = dom.getDir()
   485         domdir = dom.getDir()
   481         if self.__Cfg.getboolean('misc', 'forcedel') or force == 'delall':
   486         if self.__Cfg.getboolean('misc', 'forcedel') or force == 'delall':
   492     def domain_info(self, domainname, detailed=None):
   497     def domain_info(self, domainname, detailed=None):
   493         dom = self.__getDomain(domainname)
   498         dom = self.__getDomain(domainname)
   494         dominfo = dom.getInfo()
   499         dominfo = dom.getInfo()
   495         if dominfo['domainname'].startswith('xn--'):
   500         if dominfo['domainname'].startswith('xn--'):
   496             dominfo['domainname'] += ' (%s)'\
   501             dominfo['domainname'] += ' (%s)'\
   497                 % self.ace2idna(dominfo['domainname'])
   502                 % VirtualMailManager.ace2idna(dominfo['domainname'])
   498         if dominfo['aliases'] is None:
   503         if dominfo['aliases'] is None:
   499             dominfo['aliases'] = 0
   504             dominfo['aliases'] = 0
   500         if detailed is None:
   505         if detailed is None:
   501             return dominfo
   506             return dominfo
   502         elif detailed == 'detailed':
   507         elif detailed == 'detailed':
   503             return (dominfo, dom.getAliaseNames(), dom.getAccounts(),
   508             return (dominfo, dom.getAliaseNames(), dom.getAccounts(),
   504                     dom.getAliases())
   509                     dom.getAliases())
   505         else:
   510         else:
   506             raise VMMDomainException(("%s: '%s'" % (_('Invalid argument'),
   511             raise VMMDomainException((_(u'Invalid argument: »%s«') % detailed,
   507                 detailed),  ERR.INVALID_OPTION))
   512                 ERR.INVALID_OPTION))
   508 
   513 
   509     def domain_alias_add(self, aliasname, domainname):
   514     def domain_alias_add(self, aliasname, domainname):
   510         """Adds an alias name to the domain.
   515         """Adds an alias name to the domain.
   511         
   516         
   512         Keyword arguments:
   517         Keyword arguments:
   513         aliasname -- the alias name of the domain (str)
   518         aliasname -- the alias name of the domain (str)
   514         domainname -- name of the target domain (str)
   519         domainname -- name of the target domain (str)
   515         """
   520         """
   516         dom = self.__getDomain(domainname)
   521         dom = self.__getDomain(domainname)
   517         aliasname = self.__chkDomainname(aliasname)
   522         # XXX chk by DomainAlias!!!
       
   523         aliasname = VirtualMailManager.chkDomainname(aliasname)
   518         dom.saveAlias(aliasname)
   524         dom.saveAlias(aliasname)
   519 
   525 
   520     def domain_alias_delete(self, aliasname):
   526     def domain_alias_delete(self, aliasname):
   521         """Deletes the specified alias name.
   527         """Deletes the specified alias name.
   522         
   528         
   523         Keyword arguments:
   529         Keyword arguments:
   524         aliasname -- the alias name of the domain (str)
   530         aliasname -- the alias name of the domain (str)
   525         """
   531         """
   526         from Domain import deleteAlias
   532         from Domain import deleteAlias
   527         aliasname = self.__chkDomainname(aliasname)
   533         aliasname = VirtualMailManager.chkDomainname(aliasname)
       
   534         # XXX chk by DomainAlias!!!
   528         self.__dbConnect()
   535         self.__dbConnect()
   529         deleteAlias(self.__dbh, aliasname)
   536         deleteAlias(self.__dbh, aliasname)
   530 
   537 
   531     def domain_list(self, pattern=None):
   538     def domain_list(self, pattern=None):
   532         from Domain import search
   539         from Domain import search
   544                 if not re.match(RE_DOMAIN_SRCH, domain):
   551                 if not re.match(RE_DOMAIN_SRCH, domain):
   545                     raise VMMException((
   552                     raise VMMException((
   546                     _(u"The pattern '%s' contains invalid characters.") %
   553                     _(u"The pattern '%s' contains invalid characters.") %
   547                     pattern, ERR.DOMAIN_INVALID))
   554                     pattern, ERR.DOMAIN_INVALID))
   548             else:
   555             else:
   549                 pattern = self.__chkDomainname(pattern)
   556                 pattern = VirtualMailManager.chkDomainname(pattern)
       
   557                 # XXX chk by domain if not like
   550         self.__dbConnect()
   558         self.__dbConnect()
   551         return search(self.__dbh, pattern=pattern, like=like)
   559         return search(self.__dbh, pattern=pattern, like=like)
   552 
   560 
   553     def user_add(self, emailaddress, password):
   561     def user_add(self, emailaddress, password):
   554         acc = self.__getAccount(emailaddress, password)
   562         acc = self.__getAccount(emailaddress, password)
   570         acc = self.__getAccount(emailaddress)
   578         acc = self.__getAccount(emailaddress)
   571         uid = acc.getUID()
   579         uid = acc.getUID()
   572         gid = acc.getGID()
   580         gid = acc.getGID()
   573         acc.delete()
   581         acc.delete()
   574         if self.__Cfg.getboolean('maildir', 'delete'):
   582         if self.__Cfg.getboolean('maildir', 'delete'):
   575             self.__maildirdelete(acc.getDir('domain'), uid, gid)
   583             try:
       
   584                 self.__maildirdelete(acc.getDir('domain'), uid, gid)
       
   585             except (VMMException), e:
       
   586                 if e[0][1] in [ERR.FOUND_DOTS_IN_PATH,
       
   587                         ERR.MAILDIR_PERM_MISMATCH, ERR.NO_SUCH_DIRECTORY]:
       
   588                     warning = _(u"""\
       
   589 The account has been successfully deleted from the database.
       
   590     But an error occurred while deleting the following directory:
       
   591     »%s«
       
   592     Reason: %s""") % (acc.getDir('home'), e[0][0])
       
   593                     self.__warnings.append(warning)
       
   594                 else:
       
   595                     raise e
   576 
   596 
   577     def alias_info(self, aliasaddress):
   597     def alias_info(self, aliasaddress):
   578         alias = self.__getAlias(aliasaddress)
   598         alias = self.__getAlias(aliasaddress)
   579         return alias.getInfo()
   599         return alias.getInfo()
   580 
   600 
   595         return getAccountByID(uid, self.__dbh)
   615         return getAccountByID(uid, self.__dbh)
   596 
   616 
   597     def user_password(self, emailaddress, password):
   617     def user_password(self, emailaddress, password):
   598         acc = self.__getAccount(emailaddress)
   618         acc = self.__getAccount(emailaddress)
   599         if acc.getUID() == 0:
   619         if acc.getUID() == 0:
   600            raise VMMException((_("Account doesn't exists"),ERR.NO_SUCH_ACCOUNT))
   620            raise VMMException((_(u"Account doesn't exists"),
       
   621                ERR.NO_SUCH_ACCOUNT))
   601         if password is None:
   622         if password is None:
   602             password = self._readpass()
   623             password = self._readpass()
   603         acc.modify('password', self.__pwhash(password))
   624         acc.modify('password', self.__pwhash(password))
   604 
   625 
   605     def user_name(self, emailaddress, name):
   626     def user_name(self, emailaddress, name):