VirtualMailManager/Handler.py
branchv0.6.x
changeset 316 31d8931dc535
parent 310 644e2cc4a441
child 318 4dc2edf02d11
equal deleted inserted replaced
315:81bccfd14355 316:31d8931dc535
    18 from shutil import rmtree
    18 from shutil import rmtree
    19 from subprocess import Popen, PIPE
    19 from subprocess import Popen, PIPE
    20 
    20 
    21 from pyPgSQL import PgSQL  # python-pgsql - http://pypgsql.sourceforge.net
    21 from pyPgSQL import PgSQL  # python-pgsql - http://pypgsql.sourceforge.net
    22 
    22 
    23 import VirtualMailManager.constants.ERROR as ERR
       
    24 from VirtualMailManager.Account import Account
    23 from VirtualMailManager.Account import Account
    25 from VirtualMailManager.Alias import Alias
    24 from VirtualMailManager.Alias import Alias
    26 from VirtualMailManager.AliasDomain import AliasDomain
    25 from VirtualMailManager.AliasDomain import AliasDomain
    27 from VirtualMailManager.common import exec_ok
    26 from VirtualMailManager.common import exec_ok
    28 from VirtualMailManager.Config import Config as Cfg
    27 from VirtualMailManager.Config import Config as Cfg
       
    28 from VirtualMailManager.constants import \
       
    29      ACCOUNT_EXISTS, ALIAS_EXISTS, CONF_NOFILE, CONF_NOPERM, CONF_WRONGPERM, \
       
    30      DATABASE_ERROR, DOMAINDIR_GROUP_MISMATCH, DOMAIN_INVALID, \
       
    31      FOUND_DOTS_IN_PATH, INVALID_ARGUMENT, MAILDIR_PERM_MISMATCH, \
       
    32      NOT_EXECUTABLE, NO_SUCH_ACCOUNT, NO_SUCH_ALIAS, NO_SUCH_BINARY, \
       
    33      NO_SUCH_DIRECTORY, NO_SUCH_RELOCATED, RELOCATED_EXISTS
    29 from VirtualMailManager.Domain import Domain, get_gid
    34 from VirtualMailManager.Domain import Domain, get_gid
    30 from VirtualMailManager.EmailAddress import EmailAddress
    35 from VirtualMailManager.EmailAddress import EmailAddress
    31 from VirtualMailManager.errors import \
    36 from VirtualMailManager.errors import \
    32      DomainError, NotRootError, PermissionError, VMMError
    37      DomainError, NotRootError, PermissionError, VMMError
    33 from VirtualMailManager.mailbox import new as new_mailbox
    38 from VirtualMailManager.mailbox import new as new_mailbox
    41 RE_DOMAIN_SEARCH = """^[a-z0-9-\.]+$"""
    46 RE_DOMAIN_SEARCH = """^[a-z0-9-\.]+$"""
    42 TYPE_ACCOUNT = 0x1
    47 TYPE_ACCOUNT = 0x1
    43 TYPE_ALIAS = 0x2
    48 TYPE_ALIAS = 0x2
    44 TYPE_RELOCATED = 0x4
    49 TYPE_RELOCATED = 0x4
    45 OTHER_TYPES = {
    50 OTHER_TYPES = {
    46     TYPE_ACCOUNT: (_(u'an account'), ERR.ACCOUNT_EXISTS),
    51     TYPE_ACCOUNT: (_(u'an account'), ACCOUNT_EXISTS),
    47     TYPE_ALIAS: (_(u'an alias'), ERR.ALIAS_EXISTS),
    52     TYPE_ALIAS: (_(u'an alias'), ALIAS_EXISTS),
    48     TYPE_RELOCATED: (_(u'a relocated user'), ERR.RELOCATED_EXISTS),
    53     TYPE_RELOCATED: (_(u'a relocated user'), RELOCATED_EXISTS),
    49 }
    54 }
    50 
    55 
    51 
    56 
    52 class Handler(object):
    57 class Handler(object):
    53     """Wrapper class to simplify the access on all the stuff from
    58     """Wrapper class to simplify the access on all the stuff from
    69         self._Cfg = None
    74         self._Cfg = None
    70         self._dbh = None
    75         self._dbh = None
    71 
    76 
    72         if os.geteuid():
    77         if os.geteuid():
    73             raise NotRootError(_(u"You are not root.\n\tGood bye!\n"),
    78             raise NotRootError(_(u"You are not root.\n\tGood bye!\n"),
    74                                ERR.CONF_NOPERM)
    79                                CONF_NOPERM)
    75         if self.__chkCfgFile():
    80         if self.__chkCfgFile():
    76             self._Cfg = Cfg(self._cfgFileName)
    81             self._Cfg = Cfg(self._cfgFileName)
    77             self._Cfg.load()
    82             self._Cfg.load()
    78         if not skip_some_checks:
    83         if not skip_some_checks:
    79             self._Cfg.check()
    84             self._Cfg.check()
    85             if os.path.isfile(tmp):
    90             if os.path.isfile(tmp):
    86                 self._cfgFileName = tmp
    91                 self._cfgFileName = tmp
    87                 break
    92                 break
    88         if not len(self._cfgFileName):
    93         if not len(self._cfgFileName):
    89             raise VMMError(_(u"No 'vmm.cfg' found in: "
    94             raise VMMError(_(u"No 'vmm.cfg' found in: "
    90                              u"/root:/usr/local/etc:/etc"), ERR.CONF_NOFILE)
    95                              u"/root:/usr/local/etc:/etc"), CONF_NOFILE)
    91 
    96 
    92     def __chkCfgFile(self):
    97     def __chkCfgFile(self):
    93         """Checks the configuration file, returns bool"""
    98         """Checks the configuration file, returns bool"""
    94         self.__findCfgFile()
    99         self.__findCfgFile()
    95         fstat = os.stat(self._cfgFileName)
   100         fstat = os.stat(self._cfgFileName)
    97         if fmode % 100 and fstat.st_uid != fstat.st_gid or \
   102         if fmode % 100 and fstat.st_uid != fstat.st_gid or \
    98            fmode % 10 and fstat.st_uid == fstat.st_gid:
   103            fmode % 10 and fstat.st_uid == fstat.st_gid:
    99             raise PermissionError(_(u"wrong permissions for '%(file)s': "
   104             raise PermissionError(_(u"wrong permissions for '%(file)s': "
   100                                     u"%(perms)s\n`chmod 0600 %(file)s` would "
   105                                     u"%(perms)s\n`chmod 0600 %(file)s` would "
   101                                     u"be great.") % {'file': self._cfgFileName,
   106                                     u"be great.") % {'file': self._cfgFileName,
   102                                   'perms': fmode}, ERR.CONF_WRONGPERM)
   107                                   'perms': fmode}, CONF_WRONGPERM)
   103         else:
   108         else:
   104             return True
   109             return True
   105 
   110 
   106     def _chkenv(self):
   111     def _chkenv(self):
   107         """"""
   112         """"""
   112             os.chown(basedir, 0, 0)
   117             os.chown(basedir, 0, 0)
   113             os.umask(old_umask)
   118             os.umask(old_umask)
   114         elif not os.path.isdir(basedir):
   119         elif not os.path.isdir(basedir):
   115             raise VMMError(_(u"'%s' is not a directory.\n(vmm.cfg: section "
   120             raise VMMError(_(u"'%s' is not a directory.\n(vmm.cfg: section "
   116                              u"'misc', option 'base_directory')") % basedir,
   121                              u"'misc', option 'base_directory')") % basedir,
   117                            ERR.NO_SUCH_DIRECTORY)
   122                            NO_SUCH_DIRECTORY)
   118         for opt, val in self._Cfg.items('bin'):
   123         for opt, val in self._Cfg.items('bin'):
   119             try:
   124             try:
   120                 exec_ok(val)
   125                 exec_ok(val)
   121             except VMMError, err:
   126             except VMMError, err:
   122                 if err.code is ERR.NO_SUCH_BINARY:
   127                 if err.code is NO_SUCH_BINARY:
   123                     raise VMMError(_(u"'%(binary)s' doesn't exist.\n(vmm.cfg: "
   128                     raise VMMError(_(u"'%(binary)s' doesn't exist.\n(vmm.cfg: "
   124                                      u"section 'bin', option '%(option)s')") %
   129                                      u"section 'bin', option '%(option)s')") %
   125                                    {'binary': val, 'option': opt}, err.code)
   130                                    {'binary': val, 'option': opt}, err.code)
   126                 elif err.code is ERR.NOT_EXECUTABLE:
   131                 elif err.code is NOT_EXECUTABLE:
   127                     raise VMMError(_(u"'%(binary)s' is not executable.\n"
   132                     raise VMMError(_(u"'%(binary)s' is not executable.\n"
   128                                      u"(vmm.cfg: section 'bin', option "
   133                                      u"(vmm.cfg: section 'bin', option "
   129                                      u"'%(option)s')") % {'binary': val,
   134                                      u"'%(option)s')") % {'binary': val,
   130                                    'option': opt}, err.code)
   135                                    'option': opt}, err.code)
   131                 else:
   136                 else:
   144                         client_encoding='utf8', unicode_results=True)
   149                         client_encoding='utf8', unicode_results=True)
   145                 dbc = self._dbh.cursor()
   150                 dbc = self._dbh.cursor()
   146                 dbc.execute("SET NAMES 'UTF8'")
   151                 dbc.execute("SET NAMES 'UTF8'")
   147                 dbc.close()
   152                 dbc.close()
   148             except PgSQL.libpq.DatabaseError, e:
   153             except PgSQL.libpq.DatabaseError, e:
   149                 raise VMMError(str(e), ERR.DATABASE_ERROR)
   154                 raise VMMError(str(e), DATABASE_ERROR)
   150 
   155 
   151     def _chk_other_address_types(self, address, exclude):
   156     def _chk_other_address_types(self, address, exclude):
   152         """Checks if the EmailAddress *address* is known as `TYPE_ACCOUNT`,
   157         """Checks if the EmailAddress *address* is known as `TYPE_ACCOUNT`,
   153         `TYPE_ALIAS` or `TYPE_RELOCATED`, but not as the `TYPE_*` specified
   158         `TYPE_ALIAS` or `TYPE_RELOCATED`, but not as the `TYPE_*` specified
   154         by *exclude*.  If the *address* is known as one of the `TYPE_*`s
   159         by *exclude*.  If the *address* is known as one of the `TYPE_*`s
   244     def __userDirDelete(self, domdir, uid, gid):
   249     def __userDirDelete(self, domdir, uid, gid):
   245         if uid > 0 and gid > 0:
   250         if uid > 0 and gid > 0:
   246             userdir = '%s' % uid
   251             userdir = '%s' % uid
   247             if userdir.count('..') or domdir.count('..'):
   252             if userdir.count('..') or domdir.count('..'):
   248                 raise VMMError(_(u'Found ".." in home directory path.'),
   253                 raise VMMError(_(u'Found ".." in home directory path.'),
   249                                    ERR.FOUND_DOTS_IN_PATH)
   254                                FOUND_DOTS_IN_PATH)
   250             if os.path.isdir(domdir):
   255             if os.path.isdir(domdir):
   251                 os.chdir(domdir)
   256                 os.chdir(domdir)
   252                 if os.path.isdir(userdir):
   257                 if os.path.isdir(userdir):
   253                     mdstat = os.stat(userdir)
   258                     mdstat = os.stat(userdir)
   254                     if (mdstat.st_uid, mdstat.st_gid) != (uid, gid):
   259                     if (mdstat.st_uid, mdstat.st_gid) != (uid, gid):
   255                         raise VMMError(_(
   260                         raise VMMError(_(u'Detected owner/group mismatch in '
   256                           u'Detected owner/group mismatch in home directory.'),
   261                                          u'home directory.'),
   257                           ERR.MAILDIR_PERM_MISMATCH)
   262                                        MAILDIR_PERM_MISMATCH)
   258                     rmtree(userdir, ignore_errors=True)
   263                     rmtree(userdir, ignore_errors=True)
   259                 else:
   264                 else:
   260                     raise VMMError(_(u"No such directory: %s") %
   265                     raise VMMError(_(u"No such directory: %s") %
   261                         os.path.join(domdir, userdir), ERR.NO_SUCH_DIRECTORY)
   266                                    os.path.join(domdir, userdir),
       
   267                                    NO_SUCH_DIRECTORY)
   262 
   268 
   263     def __domDirDelete(self, domdir, gid):
   269     def __domDirDelete(self, domdir, gid):
   264         if gid > 0:
   270         if gid > 0:
   265             if not self.__isdir(domdir):
   271             if not self.__isdir(domdir):
   266                 return
   272                 return
   267             basedir = self._Cfg.dget('misc.base_directory')
   273             basedir = self._Cfg.dget('misc.base_directory')
   268             domdirdirs = domdir.replace(basedir + '/', '').split('/')
   274             domdirdirs = domdir.replace(basedir + '/', '').split('/')
   269             domdirparent = os.path.join(basedir, domdirdirs[0])
   275             domdirparent = os.path.join(basedir, domdirdirs[0])
   270             if basedir.count('..') or domdir.count('..'):
   276             if basedir.count('..') or domdir.count('..'):
   271                 raise VMMError(_(u'Found ".." in domain directory path.'),
   277                 raise VMMError(_(u'Found ".." in domain directory path.'),
   272                         ERR.FOUND_DOTS_IN_PATH)
   278                                FOUND_DOTS_IN_PATH)
   273             if os.path.isdir(domdirparent):
   279             if os.path.isdir(domdirparent):
   274                 os.chdir(domdirparent)
   280                 os.chdir(domdirparent)
   275                 if os.lstat(domdirdirs[1]).st_gid != gid:
   281                 if os.lstat(domdirdirs[1]).st_gid != gid:
   276                     raise VMMError(_(
   282                     raise VMMError(_(u'Detected group mismatch in domain '
   277                         u'Detected group mismatch in domain directory.'),
   283                                      u'directory.'), DOMAINDIR_GROUP_MISMATCH)
   278                         ERR.DOMAINDIR_GROUP_MISMATCH)
       
   279                 rmtree(domdirdirs[1], ignore_errors=True)
   284                 rmtree(domdirdirs[1], ignore_errors=True)
   280 
   285 
   281     def hasWarnings(self):
   286     def hasWarnings(self):
   282         """Checks if warnings are present, returns bool."""
   287         """Checks if warnings are present, returns bool."""
   283         return bool(len(self.__warnings))
   288         return bool(len(self.__warnings))
   319         dom.save()
   324         dom.save()
   320         self.__make_domain_dir(dom)
   325         self.__make_domain_dir(dom)
   321 
   326 
   322     def domainTransport(self, domainname, transport, force=None):
   327     def domainTransport(self, domainname, transport, force=None):
   323         if force is not None and force != 'force':
   328         if force is not None and force != 'force':
   324             raise DomainError(_(u"Invalid argument: “%s”") % force,
   329             raise DomainError(_(u"Invalid argument: '%s'") % force,
   325                 ERR.INVALID_OPTION)
   330                               INVALID_ARGUMENT)
   326         dom = self.__getDomain(domainname)
   331         dom = self.__getDomain(domainname)
   327         trsp = Transport(self._dbh, transport=transport)
   332         trsp = Transport(self._dbh, transport=transport)
   328         if force is None:
   333         if force is None:
   329             dom.update_transport(trsp)
   334             dom.update_transport(trsp)
   330         else:
   335         else:
   331             dom.update_transport(trsp, force=True)
   336             dom.update_transport(trsp, force=True)
   332 
   337 
   333     def domainDelete(self, domainname, force=None):
   338     def domainDelete(self, domainname, force=None):
   334         if force and force not in ('deluser', 'delalias', 'delall'):
   339         if force and force not in ('deluser', 'delalias', 'delall'):
   335             raise DomainError(_(u"Invalid argument: '%s'") % force,
   340             raise DomainError(_(u"Invalid argument: '%s'") % force,
   336                               ERR.INVALID_OPTION)
   341                               INVALID_ARGUMENT)
   337         dom = self.__getDomain(domainname)
   342         dom = self.__getDomain(domainname)
   338         gid = dom.gid
   343         gid = dom.gid
   339         domdir = dom.directory
   344         domdir = dom.directory
   340         if self._Cfg.dget('domain.force_deletion') or force == 'delall':
   345         if self._Cfg.dget('domain.force_deletion') or force == 'delall':
   341             dom.delete(True, True)
   346             dom.delete(True, True)
   350 
   355 
   351     def domainInfo(self, domainname, details=None):
   356     def domainInfo(self, domainname, details=None):
   352         if details not in [None, 'accounts', 'aliasdomains', 'aliases', 'full',
   357         if details not in [None, 'accounts', 'aliasdomains', 'aliases', 'full',
   353                            'relocated']:
   358                            'relocated']:
   354             raise VMMError(_(u'Invalid argument: “%s”') % details,
   359             raise VMMError(_(u'Invalid argument: “%s”') % details,
   355                                ERR.INVALID_AGUMENT)
   360                            INVALID_ARGUMENT)
   356         dom = self.__getDomain(domainname)
   361         dom = self.__getDomain(domainname)
   357         dominfo = dom.get_info()
   362         dominfo = dom.get_info()
   358         if dominfo['domainname'].startswith('xn--'):
   363         if dominfo['domainname'].startswith('xn--'):
   359             dominfo['domainname'] += ' (%s)' % \
   364             dominfo['domainname'] += ' (%s)' % \
   360                                      dominfo['domainname'].decode('idna')
   365                                      dominfo['domainname'].decode('idna')
   426         like = False
   431         like = False
   427         if pattern and (pattern.startswith('%') or pattern.endswith('%')):
   432         if pattern and (pattern.startswith('%') or pattern.endswith('%')):
   428             like = True
   433             like = True
   429             if not re.match(RE_DOMAIN_SEARCH, pattern.strip('%')):
   434             if not re.match(RE_DOMAIN_SEARCH, pattern.strip('%')):
   430                 raise VMMError(_(u"The pattern '%s' contains invalid "
   435                 raise VMMError(_(u"The pattern '%s' contains invalid "
   431                                  u"characters.") % pattern, ERR.DOMAIN_INVALID)
   436                                  u"characters.") % pattern, DOMAIN_INVALID)
   432         self.__dbConnect()
   437         self.__dbConnect()
   433         return search(self._dbh, pattern=pattern, like=like)
   438         return search(self._dbh, pattern=pattern, like=like)
   434 
   439 
   435     def user_add(self, emailaddress, password):
   440     def user_add(self, emailaddress, password):
   436         """Wrapper around Account.set_password() and Account.save()."""
   441         """Wrapper around Account.set_password() and Account.save()."""
   468 
   473 
   469     def user_delete(self, emailaddress, force=None):
   474     def user_delete(self, emailaddress, force=None):
   470         """Wrapper around Account.delete(...)"""
   475         """Wrapper around Account.delete(...)"""
   471         if force not in (None, 'delalias'):
   476         if force not in (None, 'delalias'):
   472             raise VMMError(_(u"Invalid argument: '%s'") % force,
   477             raise VMMError(_(u"Invalid argument: '%s'") % force,
   473                            ERR.INVALID_AGUMENT)
   478                            INVALID_ARGUMENT)
   474         acc = self.__getAccount(emailaddress)
   479         acc = self.__getAccount(emailaddress)
   475         if not acc:
   480         if not acc:
   476             raise VMMError(_(u"The account '%s' doesn't exist.") %
   481             raise VMMError(_(u"The account '%s' doesn't exist.") %
   477                            acc.address, ERR.NO_SUCH_ACCOUNT)
   482                            acc.address, NO_SUCH_ACCOUNT)
   478         uid = acc.uid
   483         uid = acc.uid
   479         gid = acc.gid
   484         gid = acc.gid
   480         dom_dir = acc.domain_directory
   485         dom_dir = acc.domain_directory
   481         acc_dir = acc.home
   486         acc_dir = acc.home
   482         acc.delete(bool(force))
   487         acc.delete(bool(force))
   483         if self._Cfg.dget('account.delete_directory'):
   488         if self._Cfg.dget('account.delete_directory'):
   484             try:
   489             try:
   485                 self.__userDirDelete(dom_dir, uid, gid)
   490                 self.__userDirDelete(dom_dir, uid, gid)
   486             except VMMError, err:
   491             except VMMError, err:
   487                 if err.code in (ERR.FOUND_DOTS_IN_PATH,
   492                 if err.code in (FOUND_DOTS_IN_PATH, MAILDIR_PERM_MISMATCH,
   488                         ERR.MAILDIR_PERM_MISMATCH, ERR.NO_SUCH_DIRECTORY):
   493                                 NO_SUCH_DIRECTORY):
   489                     warning = _(u"""\
   494                     warning = _(u"""\
   490 The account has been successfully deleted from the database.
   495 The account has been successfully deleted from the database.
   491     But an error occurred while deleting the following directory:
   496     But an error occurred while deleting the following directory:
   492     “%(directory)s”
   497     “%(directory)s”
   493     Reason: %(reason)s""") % \
   498     Reason: %(reason)s""") % \
   502         alias = self.__getAlias(aliasaddress)
   507         alias = self.__getAlias(aliasaddress)
   503         if alias:
   508         if alias:
   504             return alias.get_destinations()
   509             return alias.get_destinations()
   505         if not self._is_other_address(alias.address, TYPE_ALIAS):
   510         if not self._is_other_address(alias.address, TYPE_ALIAS):
   506             raise VMMError(_(u"The alias '%s' doesn't exist.") %
   511             raise VMMError(_(u"The alias '%s' doesn't exist.") %
   507                            alias.address, ERR.NO_SUCH_ALIAS)
   512                            alias.address, NO_SUCH_ALIAS)
   508 
   513 
   509     def aliasDelete(self, aliasaddress, targetaddress=None):
   514     def aliasDelete(self, aliasaddress, targetaddress=None):
   510         """Deletes the `Alias` *aliasaddress* with all its destinations from
   515         """Deletes the `Alias` *aliasaddress* with all its destinations from
   511         the database. If *targetaddress* is not ``None``, only this
   516         the database. If *targetaddress* is not ``None``, only this
   512         destination will be removed from the alias."""
   517         destination will be removed from the alias."""
   518 
   523 
   519     def user_info(self, emailaddress, details=None):
   524     def user_info(self, emailaddress, details=None):
   520         """Wrapper around Account.get_info(...)"""
   525         """Wrapper around Account.get_info(...)"""
   521         if details not in (None, 'du', 'aliases', 'full'):
   526         if details not in (None, 'du', 'aliases', 'full'):
   522             raise VMMError(_(u"Invalid argument: '%s'") % details,
   527             raise VMMError(_(u"Invalid argument: '%s'") % details,
   523                            ERR.INVALID_AGUMENT)
   528                            INVALID_ARGUMENT)
   524         acc = self.__getAccount(emailaddress)
   529         acc = self.__getAccount(emailaddress)
   525         if not acc:
   530         if not acc:
   526             if not self._is_other_address(acc.address, TYPE_ACCOUNT):
   531             if not self._is_other_address(acc.address, TYPE_ACCOUNT):
   527                 raise VMMError(_(u"The account '%s' doesn't exist.") %
   532                 raise VMMError(_(u"The account '%s' doesn't exist.") %
   528                                acc.address, ERR.NO_SUCH_ACCOUNT)
   533                                acc.address, NO_SUCH_ACCOUNT)
   529         info = acc.get_info()
   534         info = acc.get_info()
   530         if self._Cfg.dget('account.disk_usage') or details in ('du', 'full'):
   535         if self._Cfg.dget('account.disk_usage') or details in ('du', 'full'):
   531             path = os.path.join(acc.home, acc.mail_location.directory)
   536             path = os.path.join(acc.home, acc.mail_location.directory)
   532             info['disk usage'] = self.__getDiskUsage(path)
   537             info['disk usage'] = self.__getDiskUsage(path)
   533             if details in (None, 'du'):
   538             if details in (None, 'du'):
   545 
   550 
   546     def user_password(self, emailaddress, password):
   551     def user_password(self, emailaddress, password):
   547         """Wrapper for Account.modify('password' ...)."""
   552         """Wrapper for Account.modify('password' ...)."""
   548         if not isinstance(password, basestring) or not password:
   553         if not isinstance(password, basestring) or not password:
   549             raise VMMError(_(u"Could not accept password: '%s'") % password,
   554             raise VMMError(_(u"Could not accept password: '%s'") % password,
   550                            ERR.INVALID_AGUMENT)
   555                            INVALID_ARGUMENT)
   551         acc = self.__getAccount(emailaddress)
   556         acc = self.__getAccount(emailaddress)
   552         if not acc:
   557         if not acc:
   553             raise VMMError(_(u"The account '%s' doesn't exist.") %
   558             raise VMMError(_(u"The account '%s' doesn't exist.") %
   554                            acc.address, ERR.NO_SUCH_ACCOUNT)
   559                            acc.address, NO_SUCH_ACCOUNT)
   555         acc.modify('password', password)
   560         acc.modify('password', password)
   556 
   561 
   557     def user_name(self, emailaddress, name):
   562     def user_name(self, emailaddress, name):
   558         """Wrapper for Account.modify('name', ...)."""
   563         """Wrapper for Account.modify('name', ...)."""
   559         if not isinstance(name, basestring) or not name:
   564         if not isinstance(name, basestring) or not name:
   560             raise VMMError(_(u"Could not accept name: '%s'") % name,
   565             raise VMMError(_(u"Could not accept name: '%s'") % name,
   561                            ERR.INVALID_AGUMENT)
   566                            INVALID_ARGUMENT)
   562         acc = self.__getAccount(emailaddress)
   567         acc = self.__getAccount(emailaddress)
   563         if not acc:
   568         if not acc:
   564             raise VMMError(_(u"The account '%s' doesn't exist.") %
   569             raise VMMError(_(u"The account '%s' doesn't exist.") %
   565                            acc.address, ERR.NO_SUCH_ACCOUNT)
   570                            acc.address, NO_SUCH_ACCOUNT)
   566         acc.modify('name', name)
   571         acc.modify('name', name)
   567 
   572 
   568     def user_transport(self, emailaddress, transport):
   573     def user_transport(self, emailaddress, transport):
   569         """Wrapper for Account.modify('transport', ...)."""
   574         """Wrapper for Account.modify('transport', ...)."""
   570         if not isinstance(transport, basestring) or not transport:
   575         if not isinstance(transport, basestring) or not transport:
   571             raise VMMError(_(u"Could not accept transport: '%s'") % transport,
   576             raise VMMError(_(u"Could not accept transport: '%s'") % transport,
   572                            ERR.INVALID_AGUMENT)
   577                            INVALID_ARGUMENT)
   573         acc = self.__getAccount(emailaddress)
   578         acc = self.__getAccount(emailaddress)
   574         if not acc:
   579         if not acc:
   575             raise VMMError(_(u"The account '%s' doesn't exist.") %
   580             raise VMMError(_(u"The account '%s' doesn't exist.") %
   576                            acc.address, ERR.NO_SUCH_ACCOUNT)
   581                            acc.address, NO_SUCH_ACCOUNT)
   577         acc.modify('transport', transport)
   582         acc.modify('transport', transport)
   578 
   583 
   579     def user_disable(self, emailaddress, service=None):
   584     def user_disable(self, emailaddress, service=None):
   580         """Wrapper for Account.disable(service)"""
   585         """Wrapper for Account.disable(service)"""
   581         if service not in (None, 'all', 'imap', 'pop3', 'smtp', 'sieve'):
   586         if service not in (None, 'all', 'imap', 'pop3', 'smtp', 'sieve'):
   582             raise VMMError(_(u"Could not accept service: '%s'") % service,
   587             raise VMMError(_(u"Could not accept service: '%s'") % service,
   583                            ERR.INVALID_AGUMENT)
   588                            INVALID_ARGUMENT)
   584         acc = self.__getAccount(emailaddress)
   589         acc = self.__getAccount(emailaddress)
   585         if not acc:
   590         if not acc:
   586             raise VMMError(_(u"The account '%s' doesn't exist.") %
   591             raise VMMError(_(u"The account '%s' doesn't exist.") %
   587                            acc.address, ERR.NO_SUCH_ACCOUNT)
   592                            acc.address, NO_SUCH_ACCOUNT)
   588         acc.disable(service)
   593         acc.disable(service)
   589 
   594 
   590     def user_enable(self, emailaddress, service=None):
   595     def user_enable(self, emailaddress, service=None):
   591         """Wrapper for Account.enable(service)"""
   596         """Wrapper for Account.enable(service)"""
   592         if service not in (None, 'all', 'imap', 'pop3', 'smtp', 'sieve'):
   597         if service not in (None, 'all', 'imap', 'pop3', 'smtp', 'sieve'):
   593             raise VMMError(_(u"Could not accept service: '%s'") % service,
   598             raise VMMError(_(u"Could not accept service: '%s'") % service,
   594                            ERR.INVALID_AGUMENT)
   599                            INVALID_ARGUMENT)
   595         acc = self.__getAccount(emailaddress)
   600         acc = self.__getAccount(emailaddress)
   596         if not acc:
   601         if not acc:
   597             raise VMMError(_(u"The account '%s' doesn't exist.") %
   602             raise VMMError(_(u"The account '%s' doesn't exist.") %
   598                            acc.address, ERR.NO_SUCH_ACCOUNT)
   603                            acc.address, NO_SUCH_ACCOUNT)
   599         acc.enable(service)
   604         acc.enable(service)
   600 
   605 
   601     def relocatedAdd(self, emailaddress, targetaddress):
   606     def relocatedAdd(self, emailaddress, targetaddress):
   602         """Creates a new `Relocated` entry in the database. If there is
   607         """Creates a new `Relocated` entry in the database. If there is
   603         already a relocated user with the given *emailaddress*, only the
   608         already a relocated user with the given *emailaddress*, only the
   611         relocated = self.__getRelocated(emailaddress)
   616         relocated = self.__getRelocated(emailaddress)
   612         if relocated:
   617         if relocated:
   613             return relocated.get_info()
   618             return relocated.get_info()
   614         if not self._is_other_address(relocated.address, TYPE_RELOCATED):
   619         if not self._is_other_address(relocated.address, TYPE_RELOCATED):
   615             raise VMMError(_(u"The relocated user '%s' doesn't exist.") %
   620             raise VMMError(_(u"The relocated user '%s' doesn't exist.") %
   616                            relocated.address, ERR.NO_SUCH_RELOCATED)
   621                            relocated.address, NO_SUCH_RELOCATED)
   617 
   622 
   618     def relocatedDelete(self, emailaddress):
   623     def relocatedDelete(self, emailaddress):
   619         """Deletes the relocated user with the given *emailaddress* from
   624         """Deletes the relocated user with the given *emailaddress* from
   620         the database."""
   625         the database."""
   621         relocated = self.__getRelocated(emailaddress)
   626         relocated = self.__getRelocated(emailaddress)