--- /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))