vmm: Renamed to VirtualMailManager/cli/main.py. Splitted subcommands v0.6.x
authorPascal Volk <neverseen@users.sourceforge.net>
Thu, 05 Aug 2010 05:08:02 +0000
branchv0.6.x
changeset 340 4515afec62e5
parent 339 abff2de9eed0
child 341 6709d0faf2f5
vmm: Renamed to VirtualMailManager/cli/main.py. Splitted subcommands out to VirtualMailManager/cli/subcommands.py. vmm: New created with minimal code.
VirtualMailManager/cli/__init__.py
VirtualMailManager/cli/main.py
VirtualMailManager/cli/subcommands.py
vmm
--- a/VirtualMailManager/cli/__init__.py	Thu Aug 05 02:38:20 2010 +0000
+++ b/VirtualMailManager/cli/__init__.py	Thu Aug 05 05:08:02 2010 +0000
@@ -19,11 +19,12 @@
 from VirtualMailManager.errors import VMMError
 
 
-__all__ = ('get_winsize', 'read_pass', 'w_err', 'w_std')
+__all__ = ('prog', 'get_winsize', 'read_pass', 'w_err', 'w_std')
 
 _ = lambda msg: msg
 _std_write = os.sys.stdout.write
 _err_write = os.sys.stderr.write
+prog = os.path.basename(os.sys.argv[0])
 
 
 def w_std(*args):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/VirtualMailManager/cli/main.py	Thu Aug 05 05:08:02 2010 +0000
@@ -0,0 +1,66 @@
+# -*- coding: UTF-8 -*-
+# Copyright 2007 - 2010, Pascal Volk
+# See COPYING for distribution information.
+"""
+    VirtualMailManager.cli.main
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    VirtualMailManager's command line interface.
+"""
+
+from VirtualMailManager import ENCODING, errors
+from VirtualMailManager.config import ConfigValueError
+from VirtualMailManager.cli import w_err
+from VirtualMailManager.cli.handler import CliHandler
+from VirtualMailManager.constants import DATABASE_ERROR, EX_MISSING_ARGS, \
+     EX_SUCCESS, EX_UNKNOWN_COMMAND, EX_USER_INTERRUPT
+from VirtualMailManager.cli.subcommands import RunContext, cmd_map, usage
+
+
+_ = lambda msg: msg
+
+def _get_handler():
+    """Try to get a CliHandler. Exit the program when an error occurs."""
+    try:
+        handler = CliHandler()
+        handler.cfg_install()
+    except (errors.NotRootError, errors.PermissionError, errors.VMMError,
+            errors.ConfigError, ConfigValueError), err:
+        w_err(err.code, _(u'Error: %s') % err.msg)
+    else:
+        return handler
+
+
+def run(argv):
+    if len(argv) < 2:
+        usage(EX_MISSING_ARGS, _(u"You must specify a subcommand at least"))
+
+    sub_cmd = argv[1].lower()
+    if sub_cmd in cmd_map:
+        cmd_func = cmd_map[sub_cmd].func
+    else:
+        for cmd in cmd_map.itervalues():
+            if cmd.alias == sub_cmd:
+                cmd_func = cmd.func
+                sub_cmd = cmd.name
+                break
+        else:
+            usage(EX_UNKNOWN_COMMAND, _(u"Unknown subcommand: '%s'") % sub_cmd)
+
+    handler = _get_handler()
+    run_ctx = RunContext(argv, handler, sub_cmd)
+    try:
+        cmd_func(run_ctx)
+    except (EOFError, KeyboardInterrupt):
+        # TP: We have to cry, because root has killed/interrupted vmm
+        # with Ctrl+C or Ctrl+D.
+        w_err(EX_USER_INTERRUPT, '', _(u'Ouch!'), '')
+    except errors.VMMError, err:
+        if err.code != DATABASE_ERROR:
+            w_err(err.code, _(u'Error: %s') % err.msg)
+        w_err(err.code, unicode(err.msg, ENCODING, 'replace'))
+    if handler.has_warnings():
+        w_err(0, _(u'Warnings:'), *handler.get_warnings())
+    return EX_SUCCESS
+
+del _
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/VirtualMailManager/cli/subcommands.py	Thu Aug 05 05:08:02 2010 +0000
@@ -0,0 +1,700 @@
+# -*- coding: UTF-8 -*-
+# Copyright 2007 - 2010, Pascal Volk
+# See COPYING for distribution information.
+"""
+    VirtualMailManager.cli.subcommands
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    VirtualMailManager's cli subcommands.
+"""
+
+import locale
+import os
+
+from textwrap import TextWrapper
+from time import strftime, strptime
+
+from VirtualMailManager import ENCODING
+from VirtualMailManager.account import SERVICES
+from VirtualMailManager.cli import get_winsize, prog, w_err, w_std
+from VirtualMailManager.constants import __copyright__, __date__, \
+     __version__, ACCOUNT_EXISTS, ALIAS_EXISTS, ALIASDOMAIN_ISDOMAIN, \
+     DOMAIN_ALIAS_EXISTS, INVALID_ARGUMENT, EX_MISSING_ARGS, RELOCATED_EXISTS
+from VirtualMailManager.errors import VMMError
+
+__all__ = (
+    'Command', 'RunContext', 'cmd_map', 'usage', 'alias_add', 'alias_delete',
+    'alias_info', 'aliasdomain_add', 'aliasdomain_delete', 'aliasdomain_info',
+    'aliasdomain_switch', 'configure', 'domain_add', 'domain_delete',
+    'domain_info', 'domain_transport', 'get_user', 'help_', 'list_domains',
+    'relocated_add', 'relocated_delete', 'relocated_info', 'user_add',
+    'user_delete', 'user_disable', 'user_enable', 'user_info', 'user_name',
+    'user_password', 'user_transport', 'version',
+)
+
+_ = lambda msg: msg
+txt_wrpr = TextWrapper(width=get_winsize()[1] - 1)
+
+
+class Command(object):
+    """Container class for command information."""
+    __slots__ = ('name', 'alias', 'func', 'args', 'descr')
+
+    def __init__(self, name, alias, func, args, descr):
+        """Create a new Command instance.
+
+        Arguments:
+
+        `name` : str
+          the command name, e.g. ``addalias``
+        `alias` : str
+          the command's short alias, e.g. ``aa``
+        `func` : callable
+          the function to handle the command
+        `args` : str
+          argument placeholders, e.g. ``aliasaddress``
+        `descr` : str
+          short description of the command
+        """
+        self.name = name
+        self.alias = alias
+        self.func = func
+        self.args = args
+        self.descr = descr
+
+    @property
+    def usage(self):
+        """the command's usage info."""
+        return u'%s %s %s' % (prog, self.name, self.args)
+
+
+class RunContext(object):
+    """Contains all information necessary to run a subcommand."""
+    __slots__ = ('argc', 'args', 'cget', 'hdlr', 'scmd')
+    plan_a_b = _(u'Plan A failed ... trying Plan B: %(subcommand)s %(object)s')
+
+    def __init__(self, argv, handler, command):
+        """Create a new RunContext"""
+        self.argc = len(argv)
+        self.args = [unicode(arg, ENCODING) for arg in argv]
+        self.cget = handler.cfg_dget
+        self.hdlr = handler
+        self.scmd = command
+
+
+def alias_add(ctx):
+    """create a new alias e-mail address"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing alias address and destination.'),
+              ctx.scmd)
+    elif ctx.argc < 4:
+        usage(EX_MISSING_ARGS, _(u'Missing destination address.'), ctx.scmd)
+    ctx.hdlr.alias_add(ctx.args[2].lower(), *ctx.args[3:])
+
+
+def alias_delete(ctx):
+    """delete the specified alias e-mail address or one of its destinations"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing alias address.'), ctx.scmd)
+    elif ctx.argc < 4:
+        ctx.hdlr.alias_delete(ctx.args[2].lower())
+    else:
+        ctx.hdlr.alias_delete(ctx.args[2].lower(), ctx.args[3])
+
+
+def alias_info(ctx):
+    """show the destination(s) of the specified alias"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing alias address.'), ctx.scmd)
+    address = ctx.args[2].lower()
+    try:
+        _print_aliase_info(address, ctx.hdlr.alias_info(address))
+    except VMMError, err:
+        if err.code is ACCOUNT_EXISTS:
+            w_err(0, ctx.plan_a_b % {'subcommand': u'userinfo',
+                  'object': address})
+            ctx.args[1] = 'userinfo'
+            user_info(ctx)
+        elif err.code is RELOCATED_EXISTS:
+            w_std(0, ctx.plan_a_b % {'subcommand': u'relocatedinfo',
+                  'object': address})
+            ctx.args[1] = 'relocatedinfo'
+            relocated_info(ctx)
+        else:
+            raise
+
+
+def aliasdomain_add(ctx):
+    """create a new alias for an existing domain"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing alias domain name and destination '
+                                 u'domain name.'), ctx.scmd)
+    elif ctx.argc < 4:
+        usage(EX_MISSING_ARGS, _(u'Missing destination domain name.'),
+              ctx.scmd)
+    ctx.hdlr.aliasdomain_add(ctx.args[2].lower(), ctx.args[3].lower())
+
+
+def aliasdomain_delete(ctx):
+    """delete the specified alias domain"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing alias domain name.'), ctx.scmd)
+    ctx.hdlr.aliasdomain_delete(ctx.args[2].lower())
+
+
+def aliasdomain_info(ctx):
+    """show the destination of the given alias domain"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing alias domain name.'), ctx.scmd)
+    try:
+        _print_aliasdomain_info(ctx.hdlr.aliasdomain_info(ctx.args[2].lower()))
+    except VMMError, err:
+        if err.code is ALIASDOMAIN_ISDOMAIN:
+            w_err(0, ctx.plan_a_b % {'subcommand': u'domaininfo',
+                  'object': ctx.args[2].lower()})
+            ctx.args[1] = 'domaininfo'
+            domain_info(ctx)
+        else:
+            raise
+
+
+def aliasdomain_switch(ctx):
+    """assign the given alias domain to an other domain"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing alias domain name and destination '
+                                 u'domain name.'), ctx.scmd)
+    elif ctx.argc < 4:
+        usage(EX_MISSING_ARGS, _(u'Missing destination domain name.'),
+              ctx.scmd)
+    ctx.hdlr.aliasdomain_switch(ctx.args[2].lower(), ctx.args[3].lower())
+
+
+def configure(ctx):
+    """start interactive configuration modus"""
+    if ctx.argc < 3:
+        ctx.hdlr.configure()
+    else:
+        ctx.hdlr.configure(ctx.args[2].lower())
+
+
+def domain_add(ctx):
+    """create a new domain"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd)
+    elif ctx.argc < 4:
+        ctx.hdlr.domain_add(ctx.args[2].lower())
+    else:
+        ctx.hdlr.domain_add(ctx.args[2].lower(), ctx.args[3])
+
+
+def domain_delete(ctx):
+    """delete the given domain and all its alias domains"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd)
+    elif ctx.argc < 4:
+        ctx.hdlr.domain_delete(ctx.args[2].lower())
+    elif ctx.args[3].lower() == 'force':
+        ctx.hdlr.domain_delete(ctx.args[2].lower(), True)
+    else:
+        usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % ctx.args[3],
+              ctx.scmd)
+
+
+def domain_info(ctx):
+    """display information about the given domain"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd)
+    if ctx.argc < 4:
+        details = None
+    else:
+        details = ctx.args[3].lower()
+        if details not in ('accounts', 'aliasdomains', 'aliases', 'full',
+                           'relocated'):
+            usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % details,
+                  ctx.scmd)
+    try:
+        info = ctx.hdlr.domain_info(ctx.args[2].lower(), details)
+    except VMMError, err:
+        if err.code is DOMAIN_ALIAS_EXISTS:
+            w_err(0, ctx.plan_a_b % {'subcommand': u'aliasdomaininfo',
+                  'object': ctx.args[2].lower()})
+            ctx.args[1] = 'aliasdomaininfo'
+            aliasdomain_info(ctx)
+        else:
+            raise
+    else:
+        if not details:
+            _print_info(ctx, info, _(u'Domain'))
+        else:
+            _print_info(ctx, info[0], _(u'Domain'))
+            if details == u'accounts':
+                _print_list(info[1], _(u'accounts'))
+            elif details == u'aliasdomains':
+                _print_list(info[1], _(u'alias domains'))
+            elif details == u'aliases':
+                _print_list(info[1], _(u'aliases'))
+            elif details == u'relocated':
+                _print_list(info[1], _(u'relocated users'))
+            else:
+                _print_list(info[1], _(u'alias domains'))
+                _print_list(info[2], _(u'accounts'))
+                _print_list(info[3], _(u'aliases'))
+                _print_list(info[4], _(u'relocated users'))
+
+
+def domain_transport(ctx):
+    """update the transport of the specified domain"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing domain name and new transport.'),
+              ctx.scmd)
+    if ctx.argc < 4:
+        usage(EX_MISSING_ARGS, _(u'Missing new transport.'), ctx.scmd)
+    if ctx.argc < 5:
+        ctx.hdlr.domain_transport(ctx.args[2].lower(), ctx.args[3])
+    else:
+        force = ctx.args[4].lower()
+        if force != 'force':
+            usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % force,
+                  ctx.scmd)
+        ctx.hdlr.domain_transport(ctx.args[2].lower(), ctx.args[3], force)
+
+
+def get_user(ctx):
+    """get the address of the user with the given UID"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing userid.'), ctx.scmd)
+    _print_info(ctx, ctx.hdlr.user_by_uid(ctx.args[2]), _(u'Account'))
+
+
+def help_(ctx):
+    """print help messgaes."""
+    if ctx.argc > 2:
+        hlptpc = ctx.args[2].lower()
+        if hlptpc in cmd_map:
+            topic = hlptpc
+        else:
+            for scmd in cmd_map.itervalues():
+                if scmd.alias == hlptpc:
+                    topic = scmd.name
+                    break
+            else:
+                usage(INVALID_ARGUMENT, _(u"Unknown help topic: '%s'") %
+                      ctx.args[2], ctx.scmd)
+        # FIXME
+        w_err(1, "'help %s' not yet implemented." % topic, 'see also: vmm(1)')
+
+    old_ii = txt_wrpr.initial_indent
+    old_si = txt_wrpr.subsequent_indent
+    txt_wrpr.initial_indent = ' '
+    # len(max(_overview.iterkeys(), key=len)) #Py25
+    txt_wrpr.subsequent_indent = 20 * ' '
+    order = cmd_map.keys()
+    order.sort()
+
+    w_std(_(u'List of available subcommands:') + '\n')
+    for key in order:
+        w_std('\n'.join(txt_wrpr.wrap('%-18s %s' % (key, cmd_map[key].descr))))
+
+    txt_wrpr.initial_indent = old_ii
+    txt_wrpr.subsequent_indent = old_si
+    txt_wrpr.initial_indent = ''
+
+
+def list_domains(ctx):
+    """list all domains / search domains by pattern"""
+    matching = ctx.argc > 2
+    if matching:
+        gids, domains = ctx.hdlr.domain_list(ctx.args[2].lower())
+    else:
+        gids, domains = ctx.hdlr.domain_list()
+    _print_domain_list(gids, domains, matching)
+
+
+def relocated_add(ctx):
+    """create a new record for a relocated user"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS,
+              _(u'Missing relocated address and destination.'), ctx.scmd)
+    elif ctx.argc < 4:
+        usage(EX_MISSING_ARGS, _(u'Missing destination address.'), ctx.scmd)
+    ctx.hdlr.relocated_add(ctx.args[2].lower(), ctx.args[3])
+
+
+def relocated_delete(ctx):
+    """delete the record of the relocated user"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing relocated address.'), ctx.scmd)
+    ctx.hdlr.relocated_delete(ctx.args[2].lower())
+
+
+def relocated_info(ctx):
+    """print information about a relocated user"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing relocated address.'), ctx.scmd)
+    relocated = ctx.args[2].lower()
+    try:
+        _print_relocated_info(addr=relocated,
+                              dest=ctx.hdlr.relocated_info(relocated))
+    except VMMError, err:
+        if err.code is ACCOUNT_EXISTS:
+            w_err(0, ctx.plan_a_b % {'subcommand': u'userinfo',
+                  'object': relocated})
+            ctx.args[1] = 'userinfoi'
+            user_info(ctx)
+        elif err.code is ALIAS_EXISTS:
+            w_err(0, ctx.plan_a_b % {'subcommand': u'aliasinfo',
+                  'object': relocated})
+            ctx.args[1] = 'aliasinfo'
+            alias_info(ctx)
+        else:
+            raise
+
+
+def user_add(ctx):
+    """create a new e-mail user with the given address"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd)
+    elif ctx.argc < 4:
+        password = None
+    else:
+        password = ctx.args[3]
+    ctx.hdlr.user_add(ctx.args[2].lower(), password)
+
+
+def user_delete(ctx):
+    """delete the specified user"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd)
+    elif ctx.argc < 4:
+        ctx.hdlr.user_delete(ctx.args[2].lower())
+    elif ctx.args[3].lower() == 'force':
+        ctx.hdlr.user_delete(ctx.args[2].lower(), True)
+    else:
+        usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % ctx.args[3],
+              ctx.scmd)
+
+
+def user_disable(ctx):
+    """deactivate all/the given service(s) for a user"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd)
+    elif ctx.argc < 4:
+        ctx.hdlr.user_disable(ctx.args[2].lower())
+    else:
+        services = [service.lower() for service in ctx.args[3:]]
+        unknown = [service for service in services if service not in SERVICES]
+        if unknown:
+            usage(INVALID_ARGUMENT, _(u"Invalid service arguments: %s") %
+                  ' '.join(unknown), ctx.scmd)
+        ctx.hdlr.user_disable(ctx.args[2].lower(), services)
+
+
+def user_enable(ctx):
+    """activate all or the given service(s) for a user"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd)
+    elif ctx.argc < 4:
+        ctx.hdlr.user_enable(ctx.args[2].lower())
+    else:
+        services = [service.lower() for service in ctx.args[3:]]
+        unknown = [service for service in services if service not in SERVICES]
+        if unknown:
+            usage(INVALID_ARGUMENT, _(u"Invalid service arguments: %s") %
+                  ' '.join(unknown), ctx.scmd)
+        ctx.hdlr.user_enable(ctx.args[2].lower(), services)
+
+
+def user_info(ctx):
+    """display information about the given address"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd)
+    if ctx.argc < 4:
+        details = None
+    else:
+        details = ctx.args[3].lower()
+        if details not in ('aliases', 'du', 'full'):
+            usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % details,
+                  ctx.scmd)
+    try:
+        info = ctx.hdlr.user_info(ctx.args[2].lower(), details)
+    except VMMError, err:
+        if err.code is ALIAS_EXISTS:
+            w_err(0, ctx.plan_a_b % {'subcommand': u'aliasinfo',
+                  'object': ctx.args[2].lower()})
+            ctx.args[1] = 'aliasinfo'
+            alias_info(ctx)
+        elif err.code is RELOCATED_EXISTS:
+            w_err(0, ctx.plan_a_b % {'subcommand': u'relocatedinfo',
+                  'object': ctx.args[2].lower()})
+            ctx.args[1] = 'relocatedinfo'
+            relocated_info(ctx)
+        else:
+            raise
+    else:
+        if details in (None, 'du'):
+            _print_info(ctx, info, _(u'Account'))
+        else:
+            _print_info(ctx, info[0], _(u'Account'))
+            _print_list(info[1], _(u'alias addresses'))
+
+
+def user_name(ctx):
+    """set or update the real name for an address"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing e-mail address and user’s name.'),
+              ctx.scmd)
+    if ctx.argc < 4:
+        usage(EX_MISSING_ARGS, _(u'Missing user’s name.'), ctx.scmd)
+    ctx.hdlr.user_name(ctx.args[2].lower(), ctx.args[3])
+
+
+def user_password(ctx):
+    """update the password for the given address"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd)
+    elif ctx.argc < 4:
+        password = None
+    else:
+        password = ctx.args[3]
+    ctx.hdlr.user_password(ctx.args[2].lower(), password)
+
+
+def user_transport(ctx):
+    """update the transport of the given address"""
+    if ctx.argc < 3:
+        usage(EX_MISSING_ARGS, _(u'Missing e-mail address and transport.'),
+              ctx.scmd)
+    if ctx.argc < 4:
+        usage(EX_MISSING_ARGS, _(u'Missing transport.'), ctx.scmd)
+    ctx.hdlr.user_transport(ctx.args[2].lower(), ctx.args[3])
+
+
+def usage(errno, errmsg, subcommand=None):
+    """print usage message for the given command or all commands.
+    When errno > 0, sys,exit(errno) will interrupt the program.
+    """
+    if subcommand and subcommand in cmd_map:
+        w_err(errno, _(u"Error: %s") % errmsg,
+              _(u"usage: ") + cmd_map[subcommand].usage)
+
+    # TP: Please adjust translated words like the original text.
+    # (It's a table header.) Extract from usage text:
+    # usage: vmm subcommand arguments
+    #   short long
+    #   subcommand                arguments
+    #
+    #   da    domainadd           fqdn [transport]
+    #   dd    domaindelete        fqdn [force]
+    u_head = _(u"""usage: %s subcommand arguments
+  short long
+  subcommand                arguments\n""") % prog
+    order = cmd_map.keys()
+    order.sort()
+    w_err(0, u_head)
+    for key in order:
+        scmd = cmd_map[key]
+        w_err(0, '  %-5s %-19s %s' % (scmd.alias, scmd.name, scmd.args))
+    w_err(errno, '', _(u"Error: %s") % errmsg)
+
+
+def version(ctx):
+    """Write version and copyright information to stdout."""
+    w_std('%s, %s %s (%s %s)\nPython %s %s %s\n\n%s\n%s %s' % (prog,
+    # TP: The words 'from', 'version' and 'on' are used in
+    # the version information, e.g.:
+    # vmm, version 0.5.2 (from 09/09/09)
+    # Python 2.5.4 on FreeBSD
+        _(u'version'), __version__, _(u'from'),
+        strftime(locale.nl_langinfo(locale.D_FMT),
+            strptime(__date__, '%Y-%m-%d')).decode(ENCODING, 'replace'),
+        os.sys.version.split()[0], _(u'on'), os.uname()[0],
+        __copyright__, prog,
+        _(u'is free software and comes with ABSOLUTELY NO WARRANTY.')))
+
+cmd = Command
+cmd_map = {  # {{{
+    # Account commands
+    'getuser': cmd('getuser', 'gu', get_user, 'uid',
+                   _(u'get the address of the user with the given UID')),
+    'useradd': cmd('useradd', 'ua', user_add, 'address [password]',
+                   _(u'create a new e-mail user with the given address')),
+    'userdelete': cmd('userdelete', 'ud', user_delete, 'address [force]',
+                      _(u'delete the specified user')),
+    'userdisable': cmd('userdisable', 'u0', user_disable,
+                       'address [service ...]',
+                       _(u'deactivate all/the given service(s) for a user')),
+    'userenable': cmd('userenable', 'u1', user_enable, 'address [service ...]',
+                      _(u'activate all or the given service(s) for a user')),
+    'userinfo': cmd('userinfo', 'ui', user_info, 'address [details]',
+                    _(u'display information about the given address')),
+    'username': cmd('username', 'un', user_name, 'address name',
+                    _(u'set or update the real name for an address')),
+    'userpassword': cmd('userpassword', 'up', user_password,
+                        'address [password]',
+                        _(u'update the password for the given address')),
+    'usertransport': cmd('usertransport', 'ut', user_transport,
+                         'address transport',
+                         _(u'update the transport of the given address')),
+    # Alias commands
+    'aliasadd': cmd('aliasadd', 'aa', alias_add, 'address destination ...',
+                    _(u'create a new alias e-mail address')),
+    'aliasdelete': cmd('aliasdelete', 'ad', alias_delete,
+                       'address [destination]',
+                       _(u'delete the specified alias e-mail address or one '
+                         u'of its destinations')),
+    'aliasinfo': cmd('aliasinfo', 'ai', alias_info, 'address',
+                     _(u'show the destination(s) of the specified alias')),
+    # AliasDomain commands
+    'aliasdomainadd': cmd('aliasdomainadd', 'ada', aliasdomain_add,
+                          'fqdn destination',
+                          _(u'create a new alias for an existing domain')),
+    'aliasdomaindelete': cmd('aliasdomaindelete', 'add', aliasdomain_delete,
+                             'fqdn', _(u'delete the specified alias domain')),
+    'aliasdomaininfo': cmd('aliasdomaininfo', 'adi', aliasdomain_info, 'fqdn',
+                         _(u'show the destination of the given alias domain')),
+    'aliasdomainswitch': cmd('aliasdomainswitch', 'ads', aliasdomain_switch,
+                             'fqdn destination',
+                       _(u'assign the given alias domain to an other domain')),
+    # Domain commands
+    'domainadd': cmd('domainadd', 'da', domain_add, 'fqdn [transport]',
+                     _(u'create a new domain')),
+    'domaindelete': cmd('domaindelete', 'dd', domain_delete, 'fqdn [force]',
+                      _(u'delete the given domain and all its alias domains')),
+    'domaininfo': cmd('domaininfo', 'di', domain_info, 'fqdn [details]',
+                      _(u'display information about the given domain')),
+    'domaintransport': cmd('domaintransport', 'dt', domain_transport,
+                           'fqdn transport [force]',
+                           _(u'update the transport of the specified domain')),
+    'listdomains': cmd('listdomains', 'ld', list_domains, '[pattern]',
+                       _(u'list all domains / search domains by pattern')),
+    # Relocated commands
+    'relocatedadd': cmd('relocatedadd', 'ra', relocated_add,
+                        'address newaddress',
+                        _(u'create a new record for a relocated user')),
+    'relocateddelete': cmd('relocateddelete', 'rd', relocated_delete,
+                           'address',
+                           _(u'delete the record of the relocated user')),
+    'relocatedinfo': cmd('relocatedinfo', 'ri', relocated_info, 'address',
+                         _(u'print information about a relocated user')),
+    # cli commands
+    'configure': cmd('configure', 'cf', configure, '[section]',
+                     _(u'start interactive configuration modus')),
+    'help': cmd('help', 'h', help_, '[subcommand]',
+                _(u'show a help overview or help for the given subcommand')),
+    'version': cmd('version', 'v', version, '',
+                   _(u'show version and copyright information')),
+}  # }}}
+
+
+def _get_order(ctx):
+    """returns a tuple with (key, 1||0) tuples. Used by functions, which
+    get a dict from the handler."""
+    order = ()
+    if ctx.scmd == 'domaininfo':
+        order = ((u'domainname', 0), (u'gid', 1), (u'transport', 0),
+                 (u'domaindir', 0), (u'aliasdomains', 0), (u'accounts', 0),
+                 (u'aliases', 0), (u'relocated', 0))
+    elif ctx.scmd == 'userinfo':
+        dc12 = ctx.cget('misc.dovecot_version') >= 0x10200b02
+        sieve = (u'managesieve', u'sieve')[dc12]
+        if ctx.argc == 4 and ctx.args[3] != u'aliases' or \
+           ctx.cget('account.disk_usage'):
+            order = ((u'address', 0), (u'name', 0), (u'uid', 1), (u'gid', 1),
+                     (u'home', 0), (u'mail_location', 0), (u'disk usage', 0),
+                     (u'transport', 0), (u'smtp', 1), (u'pop3', 1),
+                     (u'imap', 1), (sieve, 1))
+        else:
+            order = ((u'address', 0), (u'name', 0), (u'uid', 1), (u'gid', 1),
+                     (u'home', 0), (u'mail_location', 0), (u'transport', 0),
+                     (u'smtp', 1), (u'pop3', 1), (u'imap', 1), (sieve, 1))
+    elif ctx.scmd == 'getuser':
+        order = ((u'uid', 1), (u'gid', 1), (u'address', 0))
+    return order
+
+
+def _print_info(ctx, info, title):
+    """Print info dicts."""
+    # TP: used in e.g. 'Domain information' or 'Account information'
+    msg = u'%s %s' % (title, _(u'information'))
+    w_std(msg, u'-' * len(msg))
+    for key, upper in _get_order(ctx):
+        if upper:
+            w_std(u'\t%s: %s' % (key.upper().ljust(15, u'.'), info[key]))
+        else:
+            w_std(u'\t%s: %s' % (key.title().ljust(15, u'.'), info[key]))
+    print
+
+
+def _print_list(alist, title):
+    """Print a list."""
+    # TP: used in e.g. 'Available alias addresses' or 'Available accounts'
+    msg = u'%s %s' % (_(u'Available'), title)
+    w_std(msg, u'-' * len(msg))
+    if alist:
+        if title != _(u'alias domains'):
+            w_std(*(u'\t%s' % item for item in alist))
+        else:
+            for domain in alist:
+                if not domain.startswith('xn--'):
+                    w_std(u'\t%s' % domain)
+                else:
+                    w_std(u'\t%s (%s)' % (domain, domain.decode('idna')))
+        print
+    else:
+        w_std(_(u'\tNone'), '')
+
+
+def _print_aliase_info(alias, destinations):
+    """Print the alias address and all its destinations"""
+    title = _(u'Alias information')
+    w_std(title, u'-' * len(title))
+    w_std(_(u'\tMail for %s will be redirected to:') % alias)
+    w_std(*(u'\t     * %s' % dest for dest in destinations))
+    print
+
+
+def _print_relocated_info(**kwargs):
+    """Print the old and new addresses of a relocated user."""
+    title = _(u'Relocated information')
+    w_std(title, u'-' * len(title))
+    w_std(_(u"\tUser '%(addr)s' has moved to '%(dest)s'") % kwargs, '')
+
+
+def _format_domain(domain, main=True):
+    """format (prefix/convert) the domain name."""
+    if domain.startswith('xn--'):
+        domain = u'%s (%s)' % (domain, domain.decode('idna'))
+    if main:
+        return u'\t[+] %s' % domain
+    return u'\t[-]     %s' % domain
+
+
+def _print_domain_list(dids, domains, matching):
+    """Print a list of (matching) domains/alias domains."""
+    if matching:
+        title = _(u'Matching domains')
+    else:
+        title = _(u'Available domains')
+    w_std(title, '-' * len(title))
+    if domains:
+        for did in dids:
+            if domains[did][0] is not None:
+                w_std(_format_domain(domains[did][0]))
+            if len(domains[did]) > 1:
+                w_std(*(_format_domain(a, False) for a in domains[did][1:]))
+    else:
+        w_std(_('\tNone'))
+    print
+
+
+def _print_aliasdomain_info(info):
+    """Print alias domain information."""
+    title = _(u'Alias domain information')
+    for key in ('alias', 'domain'):
+        if info[key].startswith('xn--'):
+            info[key] = u'%s (%s)' % (info[key], info[key].decode('idna'))
+    w_std(title, '-' * len(title),
+          _('\tThe alias domain %(alias)s belongs to:\n\t    * %(domain)s') %
+          info, '')
+
+del _
--- a/vmm	Thu Aug 05 02:38:20 2010 +0000
+++ b/vmm	Thu Aug 05 05:08:02 2010 +0000
@@ -5,527 +5,11 @@
 
 """This is the vmm main script."""
 
-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
-
-
-def usage(excode=0, errMsg=None):
-    # TP: Please adjust translated words like the original text.
-    # (It's a table header.) Extract from usage text:
-    # Usage: vmm SUBCOMMAND OBJECT ARGS*
-    #  short long
-    #  subcommand               object             args (* = optional)
-    #
-    #  da    domainadd          domain.tld         transport*
-    #  di    domaininfo         domain.tld         details*
-    u_head = _(u"""\
-Usage: %s SUBCOMMAND OBJECT ARGS*
-  short long
-  subcommand               object             args (* = optional)\n""")\
-          % __prog__
-
-    u_body = u"""\
-  da    domainadd          domain.tld         transport*
-  di    domaininfo         domain.tld         details*
-  dt    domaintransport    domain.tld         transport force*
-  dd    domaindelete       domain.tld         delalias*|deluser*|delall*
-  ada   aliasdomainadd     aliasdomain.tld    domain.tld
-  adi   aliasdomaininfo    aliasdomain.tld
-  ads   aliasdomainswitch  aliasdomain.tld    domain.tld
-  add   aliasdomaindelete  aliasdomain.tld
-  ua    useradd            user@domain.tld    password*
-  ui    userinfo           user@domain.tld    details*
-  un    username           user@domain.tld    "user’s name"
-  up    userpassword       user@domain.tld    password*
-  ut    usertransport      user@domain.tld    transport
-  u0    userdisable        user@domain.tld    service*
-  u1    userenable         user@domain.tld    service*
-  ud    userdelete         user@domain.tld    delalias*
-  aa    aliasadd           alias@domain.tld   user@domain.tld
-  ai    aliasinfo          alias@domain.tld
-  ad    aliasdelete        alias@domain.tld   user@domain.tld*
-  ra    relocatedadd       exaddr@domain.tld  user@domain.tld
-  ri    relocatedinfo      exaddr@domain.tld
-  rf    relocateddelete    exaddr@domain.tld
-  gu    getuser            userid
-  ld    listdomains                           pattern*
-  cf    configure                             section*
-  h     help
-  v     version
-"""
-    if excode > 0:
-        if errMsg is None:
-            w_err(excode, u_head, u_body)
-        else:
-            w_err(excode, u_head, u_body, _(u'Error: %s\n') % errMsg)
-    else:
-        w_std(u_head, u_body)
-        os.sys.exit(excode)
-
-def get_vmm():
-    try:
-        vmm = VirtualMailManager()
-        return vmm
-    except (VMME.VMMException, VMME.VMMNotRootException, VMME.VMMPermException,
-            VMME.VMMConfigException), e:
-        w_err(e.code(), _(u'Error: %s\n') % e.msg())
-
-def _getOrder():
-    order = ()
-    if vmm.cfgDget('misc.dovecot_version') > 11:
-        sieve_name = u'sieve'
-    else:
-        sieve_name = u'managesieve'
-    if argv[1] in (u'di', u'domaininfo'):
-        order = ((u'domainname', 0), (u'gid', 1), (u'transport', 0),
-                (u'domaindir', 0), (u'aliasdomains', 0), (u'accounts', 0),
-                (u'aliases', 0), (u'relocated', 0))
-    elif argv[1] in (u'ui', u'userinfo'):
-        if argc == 4 and argv[3] != u'aliases'\
-        or vmm.cfgDget('account.disk_usage'):
-            order = ((u'address', 0), (u'name', 0), (u'uid', 1), (u'gid', 1),
-                    (u'transport', 0), (u'maildir', 0), (u'disk usage', 0),
-                    (u'smtp', 1), (u'pop3', 1), (u'imap', 1), (sieve_name, 1))
-        else:
-            order = ((u'address', 0), (u'name', 0), (u'uid', 1), (u'gid', 1),
-                    (u'transport', 0), (u'maildir', 0), (u'smtp', 1),
-                    (u'pop3', 1), (u'imap', 1), (sieve_name, 1))
-    elif argv[1] in (u'gu', u'getuser'):
-        order = ((u'uid', 1), (u'gid', 1), (u'address', 0))
-    return order
-
-def _printInfo(info, title):
-    # TP: e.g. 'Domain information' or 'Account information'
-    msg = u'%s %s' % (title, _(u'information'))
-    w_std (u'%s\n%s' % (msg, u'-'*len(msg)))
-    for k,u in _getOrder():
-        if u:
-            w_std(u'\t%s: %s' % (k.upper().ljust(15, u'.'), info[k]))
-        else:
-            w_std(u'\t%s: %s' % (k.title().ljust(15, u'.'), info[k]))
-    print
-
-def _printList(alist, title):
-    # TP: e.g. 'Available alias addresses' or 'Available accounts'
-    msg = u'%s %s' % (_(u'Available'), title)
-    w_std(u'%s\n%s' % (msg, u'-'*len(msg)))
-    if len(alist) > 0:
-        if title != _(u'alias domains'):
-            for val in alist:
-                w_std(u'\t%s' % val)
-        else:
-            for dom in alist:
-                if not dom.startswith('xn--'):
-                    w_std(u'\t%s' % dom)
-                else:
-                    w_std(u'\t%s (%s)' % (dom, ace2idna(dom)))
-    else:
-        w_std(_(u'\tNone'))
-    print
-
-def _printAliases(alias, targets):
-    msg = _(u'Alias information')
-    w_std(u'%s\n%s' % (msg, u'-'*len(msg)))
-    w_std(_(u'\tMail for %s will be redirected to:') % alias)
-    if len(targets) > 0:
-        for target in targets:
-            w_std(u'\t     * %s' % target)
-    else:
-        w_std(_(u'\tNone'))
-    print
-
-def _printRelocated(addr_dest):
-    msg = _(u'Relocated information')
-    w_std(u'%s\n%s' % (msg, u'-'*len(msg)))
-    w_std(_(u'\tUser “%(addr)s” has moved to “%(dest)s”') % addr_dest)
-    print
-
-def _formatDom(domain, main=True):
-    if domain.startswith('xn--'):
-        domain = u'%s (%s)' % (domain, ace2idna(domain))
-    if main:
-        return u'\t[+] %s' %  domain
-    else:
-        return u'\t[-]     %s' % domain
-
-def _printDomList(dids, domains):
-    if argc < 3:
-        msg = _('Available domains')
-    else:
-        msg = _('Matching domains')
-    w_std('%s\n%s' % (msg, '-'*len(msg)))
-    if not len(domains):
-        w_std(_('\tNone'))
-    else:
-        for id in dids:
-            if domains[id][0] is not None:
-                w_std(_formatDom(domains[id][0]))
-            if len(domains[id]) > 1:
-                for alias in domains[id][1:]:
-                    w_std(_formatDom(alias, main=False))
-    print
-
-def _printAliasDomInfo(info):
-    msg = _('Alias domain information')
-    for k in ['alias', 'domain']:
-        if info[k].startswith('xn--'):
-            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)
-    print
-
-def configure():
-    if argc < 3:
-        vmm.configure()
-    else:
-        vmm.configure(argv[2])
-
-def domain_add():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing domain name.'))
-    elif argc < 4:
-        vmm.domainAdd(argv[2].lower())
-    else:
-        vmm.domainAdd(argv[2].lower(), argv[3])
-
-def domain_delete():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing domain name.'))
-    elif argc < 4:
-        vmm.domainDelete(argv[2].lower())
-    else:
-        vmm.domainDelete(argv[2].lower(), argv[3])
-
-def domain_info():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing domain name.'))
-    try:
-        if argc < 4:
-            _printInfo(vmm.domainInfo(argv[2].lower()), _(u'Domain'))
-        else:
-            details = argv[3].lower()
-            infos = vmm.domainInfo(argv[2].lower(), details)
-            _printInfo(infos[0], _(u'Domain'))
-            if details == u'accounts':
-                _printList(infos[1], _(u'accounts'))
-            elif details == u'aliasdomains':
-                _printList(infos[1], _(u'alias domains'))
-            elif details == u'aliases':
-                _printList(infos[1], _(u'aliases'))
-            elif details == u'relocated':
-                _printList(infos[1], _(u'relocated users'))
-            else:
-                _printList(infos[1], _(u'alias domains'))
-                _printList(infos[2], _(u'accounts'))
-                _printList(infos[3], _(u'aliases'))
-                _printList(infos[4], _(u'relocated users'))
-    except VMME.VMMDomainException, e:
-        if e.code() is ERR.DOMAIN_ALIAS_EXISTS:
-            w_std(plan_a_b % {'subcommand': u'aliasdomaininfo',
-                'object': argv[2].lower()})
-            alias_domain_info()
-        else:
-            raise e
-
-def domain_transport():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing domain name and new transport.'))
-    if argc < 4:
-        usage(EXIT.MISSING_ARGS, _(u'Missing new transport.'))
-    elif argc < 5:
-        vmm.domainTransport(argv[2].lower(), argv[3])
-    else:
-        vmm.domainTransport(argv[2].lower(), argv[3], argv[4])
-
-def alias_domain_add():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS,
-                _(u'Missing alias domain name and target domain name.'))
-    elif argc < 4:
-        usage(EXIT.MISSING_ARGS, _(u'Missing target domain name.'))
-    else:
-        vmm.aliasDomainAdd(argv[2].lower(), argv[3].lower())
-
-def alias_domain_info():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing alias domain name.'))
-    try:
-        _printAliasDomInfo(vmm.aliasDomainInfo(argv[2].lower()))
-    except VMME.VMMAliasDomainException, e:
-        if e.code() is ERR.ALIASDOMAIN_ISDOMAIN:
-            w_std(plan_a_b % {'subcommand': u'domaininfo',
-                'object': argv[2].lower()})
-            argv[1] = u'di' # necessary manipulation to get the order
-            domain_info()
-        else:
-            raise e
-
-def alias_domain_switch():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS,
-                _(u'Missing alias domain name and target domain name.'))
-    elif argc < 4:
-        usage(EXIT.MISSING_ARGS, _(u'Missing target domain name.'))
-    else:
-        vmm.aliasDomainSwitch(argv[2].lower(), argv[3].lower())
-
-def alias_domain_delete():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing alias domain name.'))
-    else:
-        vmm.aliasDomainDelete(argv[2].lower())
-
-def user_add():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing e-mail address.'))
-    elif argc < 4:
-        password = None
-    else:
-        password = argv[3]
-    vmm.userAdd(argv[2].lower(), password)
-
-def user_delete():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing e-mail address.'))
-    elif argc < 4:
-        vmm.userDelete(argv[2].lower())
-    else:
-        vmm.userDelete(argv[2].lower(), argv[3].lower())
-
-def user_info():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing e-mail address.'))
-    try:
-        if argc < 4:
-            _printInfo(vmm.userInfo(argv[2].lower()), u'Account')
-        else:
-            arg3 = argv[3].lower()
-            info = vmm.userInfo(argv[2].lower(), arg3)
-            if arg3 in ['aliases', 'full']:
-                _printInfo(info[0], u'Account')
-                _printList(info[1], _(u'alias addresses'))
-            else:
-                _printInfo(info, u'Account')
-    except VMME.VMMAccountException, e:
-        if e.code() is ERR.ALIAS_EXISTS:
-            w_std(plan_a_b % {'subcommand': u'aliasinfo',
-                'object': argv[2].lower()})
-            alias_info()
-        elif e.code() is ERR.RELOCATED_EXISTS:
-            w_std(plan_a_b % {'subcommand': u'relocatedinfo',
-                'object': argv[2].lower()})
-            relocated_info()
-        else:
-            raise e
-
-def user_name():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing e-mail address and user’s name.'))
-    if argc < 4:
-        usage(EXIT.MISSING_ARGS, _(u'Missing user’s name.'))
-    else:
-        vmm.userName(argv[2].lower(), argv[3])
-
-def user_transport():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing e-mail address and transport.'))
-    if argc <4:
-        usage(EXIT.MISSING_ARGS, _(u'Missing transport.'))
-    else:
-        vmm.userTransport(argv[2].lower(), argv[3])
-
-def user_enable():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing e-mail address.'))
-    elif argc < 4:
-        vmm.userEnable(argv[2].lower())
-    else:
-        vmm.userEnable(argv[2].lower(), argv[3].lower())
-
-def user_disable():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing e-mail address.'))
-    elif argc < 4:
-        vmm.userDisable(argv[2].lower())
-    else:
-        vmm.userDisable(argv[2].lower(), argv[3].lower())
-
-def user_password():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing e-mail address.'))
-    elif argc < 4:
-        password = None
-    else:
-        password = argv[3]
-    vmm.userPassword(argv[2].lower(), password)
-
-def alias_add():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing alias address and destination.'))
-    elif argc < 4:
-        usage(EXIT.MISSING_ARGS, _(u'Missing destination address.'))
-    else:
-        vmm.aliasAdd(argv[2].lower(), argv[3])
-
-def alias_info():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing alias address'))
-    try:
-        _printAliases(argv[2].lower(), vmm.aliasInfo(argv[2].lower()))
-    except VMME.VMMException, e:
-        if e.code() is ERR.ACCOUNT_EXISTS:
-            w_std(plan_a_b % {'subcommand': u'userinfo',
-                              'object': argv[2].lower()})
-            argv[1] = u'ui' # necessary manipulation to get the order
-            user_info()
-        elif e.code() is ERR.RELOCATED_EXISTS:
-            w_std(plan_a_b % {'subcommand': u'relocatedinfo',
-                              'object': argv[2].lower()})
-            relocated_info()
-        else:
-            raise
-
-def alias_delete():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing alias address'))
-    elif argc < 4:
-        vmm.aliasDelete(argv[2].lower())
-    else:
-        vmm.aliasDelete(argv[2].lower(), argv[3].lower())
-
-def relocated_add():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS,
-                _(u'Missing relocated address and destination.'))
-    elif argc < 4:
-        usage(EXIT.MISSING_ARGS, _(u'Missing destination address.'))
-    else:
-        vmm.relocatedAdd(argv[2].lower(), argv[3])
-
-def relocated_info():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing relocated address'))
-    relocated = argv[2].lower()
-    try:
-        _printRelocated({'addr': relocated,
-            'dest': vmm.relocatedInfo(relocated)})
-    except VMME.VMMRelocatedException, e:
-        if e.code() is ERR.ACCOUNT_EXISTS:
-            w_std(plan_a_b % {'subcommand': u'userinfo', 'object': relocated})
-            argv[1] = u'ui' # necessary manipulation to get the order
-            user_info()
-        elif e.code() is ERR.ALIAS_EXISTS:
-            w_std(plan_a_b % {'subcommand': u'aliasinfo', 'object': relocated})
-            alias_info()
-        else:
-            raise e
-
-def relocated_delete():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing relocated address'))
-    else:
-        vmm.relocatedDelete(argv[2].lower())
-
-def user_byID():
-    if argc < 3:
-        usage(EXIT.MISSING_ARGS, _(u'Missing userid'))
-    else:
-        _printInfo(vmm.userByID(argv[2]), u'Account')
-
-def domain_list():
-    if argc < 3:
-        order, doms = vmm.domainList()
-    else:
-        order, doms = vmm.domainList(argv[2].lower())
-    _printDomList(order, doms)
-
-def show_warnings():
-    if vmm.hasWarnings():
-        w_std(_(u'Warnings:'))
-        for warning in vmm.getWarnings():
-            w_std( " * %s" % warning)
-
-def show_version():
-    w_std('%s, %s %s (%s %s)\nPython %s %s %s\n\n%s %s' % (__prog__,
-    # TP: The words 'from', 'version' and 'on' are used in the version
-    # information:
-    # vmm, version 0.5.2 (from 09/09/09)
-    # Python 2.5.4 on FreeBSD
-        _(u'version'), __version__, _(u'from'),
-        strftime(locale.nl_langinfo(locale.D_FMT),
-            strptime(__date__, '%Y-%m-%d')).decode(ENCODING, 'replace'),
-        os.sys.version.split()[0], _(u'on'), os.uname()[0], __prog__,
-        _(u'is free software and comes with ABSOLUTELY NO WARRANTY.')))
-
-def main():
-    subcommand = os.sys.argv[1]
-    known_subcommand = False
-    try:
-        for s, l, f in subcmd_func.__iter__():
-            if subcommand in (s, l):
-                known_subcommand = True
-                f()
-        if not known_subcommand:
-            usage(EXIT.UNKNOWN_COMMAND, _(u'Unknown subcommand: “%s”')% argv[1])
-        show_warnings()
-    except (EOFError, KeyboardInterrupt):
-        # TP: We have to cry, because root has killed/interrupted vmm
-        # with Ctrl+C or Ctrl+D.
-        w_err(EXIT.USER_INTERRUPT, _(u'\nOuch!\n'))
-    except (VMME.VMMConfigException, VMME.VMMException), e:
-        if e.code() != ERR.DATABASE_ERROR:
-            w_err(e.code(), _(u'Error: %s') % e.msg())
-        else:
-            w_err(e.code(), unicode(e.msg(), ENCODING, 'replace'))
+import sys
 
 if __name__ == '__main__':
-    __prog__ = os.path.basename(os.sys.argv[0])
-    argv = [unicode(arg, ENCODING) for arg in os.sys.argv]
-    argc = len(os.sys.argv)
-    plan_a_b =_(u'Plan A failed ... trying Plan B: %(subcommand)s %(object)s')
-
-    if argc < 2:
-        usage(EXIT.MISSING_ARGS)
-
-    vmm = get_vmm()
-
-    subcmd_func = (
-        #short  long                 function
-        ('da',  'domainadd',         domain_add),
-        ('di',  'domaininfo',        domain_info),
-        ('dt',  'domaintransport',   domain_transport),
-        ('dd',  'domaindelete',      domain_delete),
-        ('ada', 'aliasdomainadd',    alias_domain_add),
-        ('adi', 'aliasdomaininfo',   alias_domain_info),
-        ('ads', 'aliasdomainswitch', alias_domain_switch),
-        ('add', 'aliasdomaindelete', alias_domain_delete),
-        ('ua',  'useradd',           user_add),
-        ('ui',  'userinfo',          user_info),
-        ('un',  'username',          user_name),
-        ('up',  'userpassword',      user_password),
-        ('ut',  'usertransport',     user_transport),
-        ('u0',  'userdisable',       user_disable),
-        ('u1',  'userenable',        user_enable),
-        ('ud',  'userdelete',        user_delete),
-        ('aa',  'aliasadd',          alias_add),
-        ('ai',  'aliasinfo',         alias_info),
-        ('ad',  'aliasdelete',       alias_delete),
-        ('ra',  'relocatedadd',      relocated_add),
-        ('ri',  'relocatedinfo',     relocated_info),
-        ('rd',  'relocateddelete',   relocated_delete),
-        ('cf',  'configure',         configure),
-        ('gu',  'getuser',           user_byID),
-        ('ld',  'listdomains',       domain_list),
-        ('h',   'help',              usage),
-        ('v',   'version',           show_version),)
-
-    main()
+    # replace the script's cwd (/usr/local/sbin) with our module dir
+    # (the location of the VirtualMailManager directory)
+    sys.path[0] = '/usr/local/lib/vmm'
+    from VirtualMailManager.cli.main import run
+    sys.exit(run(sys.argv))