vmm
author Pascal Volk <neverseen@users.sourceforge.net>
Fri, 24 Jul 2009 02:16:34 +0000
changeset 98 e992f03aace7
parent 95 fc008eb12186
child 102 485d3f7d6981
permissions -rwxr-xr-x
* 'VirtualMailManager/Config.py' - Config.configure(): Eliminated another UnicodeEncodeError Thanks to samfisch for reporting this bug (also rev 110)

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# Copyright 2007-2008 VEB IT
# See COPYING for distribution information.
# $Id$

"""This is the vmm main script."""

from VirtualMailManager.constants.VERSION import VERSION, REVISION, RELDATE

__author__ = 'Pascal Volk <p.volk@veb-it.de>'
__version__ = VERSION
__revision__ = 'rev '+'$Rev$'.split()[1]
__date__ = '$Date$'.split()[1]

import locale
import os
import sys
import gettext
from time import strftime, strptime

from VirtualMailManager.VirtualMailManager import VirtualMailManager
import VirtualMailManager.Exceptions as VMME
import VirtualMailManager.constants.EXIT as EXIT
import VirtualMailManager.constants.ERROR as ERR


def w_err(code, *args):
    for arg in args:
        sys.stderr.write(arg.encode(ENCODING, 'replace'))
        sys.stderr.write('\n')
    sys.exit(code)

def w_std(*args):
    for arg in args:
        sys.stdout.write(arg.encode(ENCODING, 'replace'))
        sys.stdout.write('\n')

def usage(excode=0, errMsg=None):
    u_head = _("""\
Usage: %s SUBCOMMAND OBJECT ARGS*
  short long
  subcommand               object             args (* = optional)\n""")\
          % __prog__

    u_body = """\
  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    'Users 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, '%s: %s\n' % (_('Error'), errMsg))
    else:
        w_std(u_head, u_body)
        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(), "%s: %s\n" % (_('Error'), e.msg()))

def _getOrder():
    order = ()
    if argv[1] in ['di', 'domaininfo']:
        order = (('domainname', 0), ('gid', 1), ('transport', 0),
                ('domaindir', 0), ('aliasdomains', 0), ('accounts', 0),
                ('aliases', 0), ('relocated', 0))
    elif argv[1] in ['ui', 'userinfo']:
        if argc == 4 and argv[3] != 'aliases'\
        or vmm.cfgGetBoolean('maildir', 'diskusage'):
            order = (('address', 0), ('name', 0), ('uid', 1), ('gid', 1),
                    ('transport', 0), ('maildir', 0), ('disk usage', 0),
                    ('smtp', 1), ('pop3', 1), ('imap', 1), ('managesieve', 1))
        else:
            order = (('address', 0), ('name', 0), ('uid', 1), ('gid', 1),
                    ('transport', 0), ('maildir', 0), ('smtp', 1), ('pop3', 1),
                    ('imap', 1), ('managesieve', 1))
    elif argv[1] in ['gu', 'getuser']:
        order = (('uid', 1), ('gid', 1), ('address', 0))
    return order

def _printInfo(info, title):
    msg = '%s %s' % (title, _('information'))
    w_std ('%s\n%s' % (msg, '-'*len(msg)))
    for k,u in _getOrder():
        if u:
            w_std('\t%s: %s' % (k.upper().ljust(15, '.'), info[k]))
        else:
            w_std('\t%s: %s' % (k.title().ljust(15, '.'), info[k]))
    print

def _printList(alist, title):
    msg = '%s %s' % (_('Available'), title)
    w_std('%s\n%s' % (msg, '-'*len(msg)))
    if len(alist) > 0:
        if title != _('alias domains'):
            for val in alist:
                w_std('\t%s' % val)
        else:
            for dom in alist:
                if not dom.startswith('xn--'):
                    w_std('\t%s' % dom)
                else:
                    w_std('\t%s (%s)' % (dom, vmm.ace2idna(dom)))
    else:
        w_std(_('\tNone'))
    print

def _printAliases(alias, targets):
    msg = _('Alias information')
    w_std('%s\n%s' % (msg, '-'*len(msg)))
    w_std(_('\tMail for %s will be redirected to:') % alias)
    if len(targets) > 0:
        for target in targets:
            w_std('\t     * %s' % target)
    else:
        w_std(_('\tNone'))
    print

def _printRelocated(addr_dest):
    msg = _(u'Relocated information')
    w_std('%s\n%s' % (msg, '-'*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 = '%s (%s)' % (domain, vmm.ace2idna(domain))
    if main:
        return '\t[+] %s' %  domain
    else:
        return '\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], vmm.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 need_setup or len(argv) < 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.'))
    elif argc < 4:
        _printInfo(vmm.domainInfo(argv[2].lower()), _('Domain'))
    else:
        details = argv[3].lower()
        infos = vmm.domainInfo(argv[2].lower(), details)
        _printInfo(infos[0], _('Domain'))
        if details == 'accounts':
            _printList(infos[1], _('accounts'))
        elif details == 'aliasdomains':
            _printList(infos[1], _('alias domains'))
        elif details == 'aliases':
            _printList(infos[1], _('aliases'))
        elif details == 'relocated':
            _printList(infos[1], _('relocated users'))
        else:
            _printList(infos[1], _('alias domains'))
            _printList(infos[2], _('accounts'))
            _printList(infos[3], _('aliases'))
            _printList(infos[4], _('relocated users'))

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.'))
    else:
        _printAliasDomInfo(vmm.aliasDomainInfo(argv[2].lower()))

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.'))
    elif argc < 4:
        _printInfo(vmm.userInfo(argv[2].lower()), 'Account')
    else:
        arg3 = argv[3].lower()
        info = vmm.userInfo(argv[2].lower(), arg3)
        if arg3 in ['aliases', 'full']:
            _printInfo(info[0], 'Account')
            _printList(info[1], _(u'alias addresses'))
        else:
            _printInfo(info, 'Account')

def user_name():
    if argc < 3:
        usage(EXIT.MISSING_ARGS, _(u'Missing e-mail address and users name.'))
    if argc < 4:
        usage(EXIT.MISSING_ARGS, _('Missing users 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'))
    else:
        _printAliases(argv[2].lower(), vmm.aliasInfo(argv[2].lower()))

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'))
    else:
        relocated = argv[2].lower()
        _printRelocated({'addr': relocated,
            'dest': vmm.relocatedInfo(relocated)})

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 %s)\nPython %s %s %s\n" % (__prog__, _('version'),
        __version__, REVISION, _('from'), strftime(
            locale.nl_langinfo(locale.D_FMT), strptime(RELDATE, '%Y-%m-%d')),
        sys.version.split()[0], _(u'on'), os.uname()[0]))

#def main():
if __name__ == '__main__':
    __prog__ = os.path.basename(sys.argv[0])
    locale.setlocale(locale.LC_ALL, '')
    ENCODING = locale.nl_langinfo(locale.CODESET)
    gettext.install(__prog__, '/usr/local/share/locale', unicode=1)
    argv = [unicode(arg, ENCODING) for arg in sys.argv]
    argc = len(sys.argv)

    if argc < 2:
        usage(EXIT.MISSING_ARGS)

    vmm = get_vmm()
    try:
        need_setup = not vmm.setupIsDone()
        if   argv[1] in ['cf', 'configure'] or need_setup:
            configure()
        elif argv[1] in ['da', 'domainadd']:
            domain_add()
        elif argv[1] in ['di', 'domaininfo']:
            domain_info()
        elif argv[1] in ['dt', 'domaintransport']:
            domain_transport()
        elif argv[1] in ['dd', 'domaindelete']:
            domain_delete()
        elif argv[1] in ['ada', 'aliasdomainadd']:
            alias_domain_add()
        elif argv[1] in ['adi', 'aliasdomaininfo']:
            alias_domain_info()
        elif argv[1] in ['ads', 'aliasdomainswitch']:
            alias_domain_switch()
        elif argv[1] in ['add', 'aliasdomaindelete']:
            alias_domain_delete()
        elif argv[1] in ['ua', 'useradd']:
            user_add()
        elif argv[1] in ['ui', 'userinfo']:
            user_info()
        elif argv[1] in ['un', 'username']:
            user_name()
        elif argv[1] in ['up', 'userpassword']:
            user_password()
        elif argv[1] in ['ut', 'usertransport']:
            user_transport()
        elif argv[1] in ['u0', 'userdisable']:
            user_disable()
        elif argv[1] in ['u1', 'userenable']:
            user_enable()
        elif argv[1] in ['ud', 'userdelete']:
            user_delete()
        elif argv[1] in ['aa', 'aliasadd']:
            alias_add()
        elif argv[1] in ['ai', 'aliasinfo']:
            alias_info()
        elif argv[1] in ['ad', 'aliasdelete']:
            alias_delete()
        elif argv[1] in ['ra', 'relocatedadd']:
            relocated_add()
        elif argv[1] in ['ri', 'relocatedinfo']:
            relocated_info()
        elif argv[1] in ['rd', 'relocateddelete']:
            relocated_delete()
        elif argv[1] in ['gu', 'getuser']:
            user_byID()
        elif argv[1] in ['ld', 'listdomains']:
            domain_list()
        elif argv[1] in ['h', 'help']:
            usage()
        elif argv[1] in ['v', 'version']:
            show_version()
        else:
            usage(EXIT.UNKNOWN_COMMAND,
                    u"%s: »%s«" % (_('Unknown subcommand'), argv[1]))
        show_warnings()
    except (EOFError, KeyboardInterrupt):
        w_err(EXIT.USER_INTERRUPT, '\n%s!\n' % _(u'Ouch'))
    except (VMME.VMMConfigException, VMME.VMMException), e:
        if e.code() != ERR.DATABASE_ERROR:
            w_err(e.code(), "%s: %s" % (_(u'Error'), e.msg()))
        else:
            w_err(e.code(), "%s" % unicode(e.msg(), ENCODING, 'replace'))