VirtualMailManager/cli/subcommands.py
branchv0.7.x
changeset 643 df1e3b67882a
parent 640 ebacd11563b0
child 653 cf07e4468934
equal deleted inserted replaced
642:4cd9d0a9f42f 643:df1e3b67882a
    77         self.descr = descr
    77         self.descr = descr
    78 
    78 
    79     @property
    79     @property
    80     def usage(self):
    80     def usage(self):
    81         """the command's usage info."""
    81         """the command's usage info."""
    82         return u'%s %s %s' % (prog, self.name, self.args)
    82         return '%s %s %s' % (prog, self.name, self.args)
    83 
    83 
    84     def help_(self):
    84     def help_(self):
    85         """Print the Command's help message to stdout."""
    85         """Print the Command's help message to stdout."""
    86         old_ii = txt_wrpr.initial_indent
    86         old_ii = txt_wrpr.initial_indent
    87         old_si = txt_wrpr.subsequent_indent
    87         old_si = txt_wrpr.subsequent_indent
    96         txt_wrpr.initial_indent = txt_wrpr.subsequent_indent = ' '
    96         txt_wrpr.initial_indent = txt_wrpr.subsequent_indent = ' '
    97         try:
    97         try:
    98             [w_std(txt_wrpr.fill(_(para)) + '\n') for para
    98             [w_std(txt_wrpr.fill(_(para)) + '\n') for para
    99                     in help_msgs[self.name]]
    99                     in help_msgs[self.name]]
   100         except KeyError:
   100         except KeyError:
   101             w_err(1, _(u"Subcommand '%s' is not yet documented." % self.name),
   101             w_err(1, _("Subcommand '%s' is not yet documented." % self.name),
   102                   'see also: vmm(1)')
   102                   'see also: vmm(1)')
   103 
   103 
   104 
   104 
   105 class RunContext(object):
   105 class RunContext(object):
   106     """Contains all information necessary to run a subcommand."""
   106     """Contains all information necessary to run a subcommand."""
   107     __slots__ = ('argc', 'args', 'cget', 'hdlr', 'scmd')
   107     __slots__ = ('argc', 'args', 'cget', 'hdlr', 'scmd')
   108     plan_a_b = _(u'Plan A failed ... trying Plan B: %(subcommand)s %(object)s')
   108     plan_a_b = _('Plan A failed ... trying Plan B: %(subcommand)s %(object)s')
   109 
   109 
   110     def __init__(self, argv, handler, command):
   110     def __init__(self, argv, handler, command):
   111         """Create a new RunContext"""
   111         """Create a new RunContext"""
   112         self.argc = len(argv)
   112         self.argc = len(argv)
   113         self.args = [unicode(arg, ENCODING) for arg in argv]
   113         self.args = [str(arg, ENCODING) for arg in argv]
   114         self.cget = handler.cfg_dget
   114         self.cget = handler.cfg_dget
   115         self.hdlr = handler
   115         self.hdlr = handler
   116         self.scmd = command
   116         self.scmd = command
   117 
   117 
   118 
   118 
   119 def alias_add(ctx):
   119 def alias_add(ctx):
   120     """create a new alias e-mail address"""
   120     """create a new alias e-mail address"""
   121     if ctx.argc < 3:
   121     if ctx.argc < 3:
   122         usage(EX_MISSING_ARGS, _(u'Missing alias address and destination.'),
   122         usage(EX_MISSING_ARGS, _('Missing alias address and destination.'),
   123               ctx.scmd)
   123               ctx.scmd)
   124     elif ctx.argc < 4:
   124     elif ctx.argc < 4:
   125         usage(EX_MISSING_ARGS, _(u'Missing destination address.'), ctx.scmd)
   125         usage(EX_MISSING_ARGS, _('Missing destination address.'), ctx.scmd)
   126     ctx.hdlr.alias_add(ctx.args[2].lower(), *ctx.args[3:])
   126     ctx.hdlr.alias_add(ctx.args[2].lower(), *ctx.args[3:])
   127 
   127 
   128 
   128 
   129 def alias_delete(ctx):
   129 def alias_delete(ctx):
   130     """delete the specified alias e-mail address or one of its destinations"""
   130     """delete the specified alias e-mail address or one of its destinations"""
   131     if ctx.argc < 3:
   131     if ctx.argc < 3:
   132         usage(EX_MISSING_ARGS, _(u'Missing alias address.'), ctx.scmd)
   132         usage(EX_MISSING_ARGS, _('Missing alias address.'), ctx.scmd)
   133     elif ctx.argc < 4:
   133     elif ctx.argc < 4:
   134         ctx.hdlr.alias_delete(ctx.args[2].lower())
   134         ctx.hdlr.alias_delete(ctx.args[2].lower())
   135     else:
   135     else:
   136         ctx.hdlr.alias_delete(ctx.args[2].lower(), ctx.args[3:])
   136         ctx.hdlr.alias_delete(ctx.args[2].lower(), ctx.args[3:])
   137 
   137 
   138 
   138 
   139 def alias_info(ctx):
   139 def alias_info(ctx):
   140     """show the destination(s) of the specified alias"""
   140     """show the destination(s) of the specified alias"""
   141     if ctx.argc < 3:
   141     if ctx.argc < 3:
   142         usage(EX_MISSING_ARGS, _(u'Missing alias address.'), ctx.scmd)
   142         usage(EX_MISSING_ARGS, _('Missing alias address.'), ctx.scmd)
   143     address = ctx.args[2].lower()
   143     address = ctx.args[2].lower()
   144     try:
   144     try:
   145         _print_aliase_info(address, ctx.hdlr.alias_info(address))
   145         _print_aliase_info(address, ctx.hdlr.alias_info(address))
   146     except VMMError, err:
   146     except VMMError as err:
   147         if err.code is ACCOUNT_EXISTS:
   147         if err.code is ACCOUNT_EXISTS:
   148             w_err(0, ctx.plan_a_b % {'subcommand': u'userinfo',
   148             w_err(0, ctx.plan_a_b % {'subcommand': 'userinfo',
   149                   'object': address})
   149                   'object': address})
   150             ctx.scmd = ctx.args[1] = 'userinfo'
   150             ctx.scmd = ctx.args[1] = 'userinfo'
   151             user_info(ctx)
   151             user_info(ctx)
   152         elif err.code is RELOCATED_EXISTS:
   152         elif err.code is RELOCATED_EXISTS:
   153             w_err(0, ctx.plan_a_b % {'subcommand': u'relocatedinfo',
   153             w_err(0, ctx.plan_a_b % {'subcommand': 'relocatedinfo',
   154                   'object': address})
   154                   'object': address})
   155             ctx.scmd = ctx.args[1] = 'relocatedinfo'
   155             ctx.scmd = ctx.args[1] = 'relocatedinfo'
   156             relocated_info(ctx)
   156             relocated_info(ctx)
   157         else:
   157         else:
   158             raise
   158             raise
   159 
   159 
   160 
   160 
   161 def aliasdomain_add(ctx):
   161 def aliasdomain_add(ctx):
   162     """create a new alias for an existing domain"""
   162     """create a new alias for an existing domain"""
   163     if ctx.argc < 3:
   163     if ctx.argc < 3:
   164         usage(EX_MISSING_ARGS, _(u'Missing alias domain name and destination '
   164         usage(EX_MISSING_ARGS, _('Missing alias domain name and destination '
   165                                  u'domain name.'), ctx.scmd)
   165                                  'domain name.'), ctx.scmd)
   166     elif ctx.argc < 4:
   166     elif ctx.argc < 4:
   167         usage(EX_MISSING_ARGS, _(u'Missing destination domain name.'),
   167         usage(EX_MISSING_ARGS, _('Missing destination domain name.'),
   168               ctx.scmd)
   168               ctx.scmd)
   169     ctx.hdlr.aliasdomain_add(ctx.args[2].lower(), ctx.args[3].lower())
   169     ctx.hdlr.aliasdomain_add(ctx.args[2].lower(), ctx.args[3].lower())
   170 
   170 
   171 
   171 
   172 def aliasdomain_delete(ctx):
   172 def aliasdomain_delete(ctx):
   173     """delete the specified alias domain"""
   173     """delete the specified alias domain"""
   174     if ctx.argc < 3:
   174     if ctx.argc < 3:
   175         usage(EX_MISSING_ARGS, _(u'Missing alias domain name.'), ctx.scmd)
   175         usage(EX_MISSING_ARGS, _('Missing alias domain name.'), ctx.scmd)
   176     ctx.hdlr.aliasdomain_delete(ctx.args[2].lower())
   176     ctx.hdlr.aliasdomain_delete(ctx.args[2].lower())
   177 
   177 
   178 
   178 
   179 def aliasdomain_info(ctx):
   179 def aliasdomain_info(ctx):
   180     """show the destination of the given alias domain"""
   180     """show the destination of the given alias domain"""
   181     if ctx.argc < 3:
   181     if ctx.argc < 3:
   182         usage(EX_MISSING_ARGS, _(u'Missing alias domain name.'), ctx.scmd)
   182         usage(EX_MISSING_ARGS, _('Missing alias domain name.'), ctx.scmd)
   183     try:
   183     try:
   184         _print_aliasdomain_info(ctx.hdlr.aliasdomain_info(ctx.args[2].lower()))
   184         _print_aliasdomain_info(ctx.hdlr.aliasdomain_info(ctx.args[2].lower()))
   185     except VMMError, err:
   185     except VMMError as err:
   186         if err.code is ALIASDOMAIN_ISDOMAIN:
   186         if err.code is ALIASDOMAIN_ISDOMAIN:
   187             w_err(0, ctx.plan_a_b % {'subcommand': u'domaininfo',
   187             w_err(0, ctx.plan_a_b % {'subcommand': 'domaininfo',
   188                   'object': ctx.args[2].lower()})
   188                   'object': ctx.args[2].lower()})
   189             ctx.scmd = ctx.args[1] = 'domaininfo'
   189             ctx.scmd = ctx.args[1] = 'domaininfo'
   190             domain_info(ctx)
   190             domain_info(ctx)
   191         else:
   191         else:
   192             raise
   192             raise
   193 
   193 
   194 
   194 
   195 def aliasdomain_switch(ctx):
   195 def aliasdomain_switch(ctx):
   196     """assign the given alias domain to an other domain"""
   196     """assign the given alias domain to an other domain"""
   197     if ctx.argc < 3:
   197     if ctx.argc < 3:
   198         usage(EX_MISSING_ARGS, _(u'Missing alias domain name and destination '
   198         usage(EX_MISSING_ARGS, _('Missing alias domain name and destination '
   199                                  u'domain name.'), ctx.scmd)
   199                                  'domain name.'), ctx.scmd)
   200     elif ctx.argc < 4:
   200     elif ctx.argc < 4:
   201         usage(EX_MISSING_ARGS, _(u'Missing destination domain name.'),
   201         usage(EX_MISSING_ARGS, _('Missing destination domain name.'),
   202               ctx.scmd)
   202               ctx.scmd)
   203     ctx.hdlr.aliasdomain_switch(ctx.args[2].lower(), ctx.args[3].lower())
   203     ctx.hdlr.aliasdomain_switch(ctx.args[2].lower(), ctx.args[3].lower())
   204 
   204 
   205 
   205 
   206 def catchall_add(ctx):
   206 def catchall_add(ctx):
   207     """create a new catchall alias e-mail address"""
   207     """create a new catchall alias e-mail address"""
   208     if ctx.argc < 3:
   208     if ctx.argc < 3:
   209         usage(EX_MISSING_ARGS, _(u'Missing domain and destination.'),
   209         usage(EX_MISSING_ARGS, _('Missing domain and destination.'),
   210               ctx.scmd)
   210               ctx.scmd)
   211     elif ctx.argc < 4:
   211     elif ctx.argc < 4:
   212         usage(EX_MISSING_ARGS, _(u'Missing destination address.'), ctx.scmd)
   212         usage(EX_MISSING_ARGS, _('Missing destination address.'), ctx.scmd)
   213     ctx.hdlr.catchall_add(ctx.args[2].lower(), *ctx.args[3:])
   213     ctx.hdlr.catchall_add(ctx.args[2].lower(), *ctx.args[3:])
   214 
   214 
   215 
   215 
   216 def catchall_delete(ctx):
   216 def catchall_delete(ctx):
   217     """delete the specified destination or all of the catchall destination"""
   217     """delete the specified destination or all of the catchall destination"""
   218     if ctx.argc < 3:
   218     if ctx.argc < 3:
   219         usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd)
   219         usage(EX_MISSING_ARGS, _('Missing domain name.'), ctx.scmd)
   220     elif ctx.argc < 4:
   220     elif ctx.argc < 4:
   221         ctx.hdlr.catchall_delete(ctx.args[2].lower())
   221         ctx.hdlr.catchall_delete(ctx.args[2].lower())
   222     else:
   222     else:
   223         ctx.hdlr.catchall_delete(ctx.args[2].lower(), ctx.args[3:])
   223         ctx.hdlr.catchall_delete(ctx.args[2].lower(), ctx.args[3:])
   224 
   224 
   225 
   225 
   226 def catchall_info(ctx):
   226 def catchall_info(ctx):
   227     """show the catchall destination(s) of the specified domain"""
   227     """show the catchall destination(s) of the specified domain"""
   228     if ctx.argc < 3:
   228     if ctx.argc < 3:
   229         usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd)
   229         usage(EX_MISSING_ARGS, _('Missing domain name.'), ctx.scmd)
   230     address = ctx.args[2].lower()
   230     address = ctx.args[2].lower()
   231     _print_catchall_info(address, ctx.hdlr.catchall_info(address))
   231     _print_catchall_info(address, ctx.hdlr.catchall_info(address))
   232 
   232 
   233 
   233 
   234 def config_get(ctx):
   234 def config_get(ctx):
   235     """show the actual value of the configuration option"""
   235     """show the actual value of the configuration option"""
   236     if ctx.argc < 3:
   236     if ctx.argc < 3:
   237         usage(EX_MISSING_ARGS, _(u"Missing option name."), ctx.scmd)
   237         usage(EX_MISSING_ARGS, _("Missing option name."), ctx.scmd)
   238 
   238 
   239     noop = lambda option: option
   239     noop = lambda option: option
   240     opt_formater = {
   240     opt_formater = {
   241         'misc.dovecot_version': version_str,
   241         'misc.dovecot_version': version_str,
   242         'domain.quota_bytes': human_size,
   242         'domain.quota_bytes': human_size,
   248 
   248 
   249 
   249 
   250 def config_set(ctx):
   250 def config_set(ctx):
   251     """set a new value for the configuration option"""
   251     """set a new value for the configuration option"""
   252     if ctx.argc < 3:
   252     if ctx.argc < 3:
   253         usage(EX_MISSING_ARGS, _(u'Missing option and new value.'), ctx.scmd)
   253         usage(EX_MISSING_ARGS, _('Missing option and new value.'), ctx.scmd)
   254     if ctx.argc < 4:
   254     if ctx.argc < 4:
   255         usage(EX_MISSING_ARGS, _(u'Missing new configuration value.'),
   255         usage(EX_MISSING_ARGS, _('Missing new configuration value.'),
   256               ctx.scmd)
   256               ctx.scmd)
   257     ctx.hdlr.cfg_set(ctx.args[2].lower(), ctx.args[3])
   257     ctx.hdlr.cfg_set(ctx.args[2].lower(), ctx.args[3])
   258 
   258 
   259 
   259 
   260 def configure(ctx):
   260 def configure(ctx):
   266 
   266 
   267 
   267 
   268 def domain_add(ctx):
   268 def domain_add(ctx):
   269     """create a new domain"""
   269     """create a new domain"""
   270     if ctx.argc < 3:
   270     if ctx.argc < 3:
   271         usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd)
   271         usage(EX_MISSING_ARGS, _('Missing domain name.'), ctx.scmd)
   272     elif ctx.argc < 4:
   272     elif ctx.argc < 4:
   273         ctx.hdlr.domain_add(ctx.args[2].lower())
   273         ctx.hdlr.domain_add(ctx.args[2].lower())
   274     else:
   274     else:
   275         ctx.hdlr.domain_add(ctx.args[2].lower(), ctx.args[3])
   275         ctx.hdlr.domain_add(ctx.args[2].lower(), ctx.args[3])
   276     if ctx.cget('domain.auto_postmaster'):
   276     if ctx.cget('domain.auto_postmaster'):
   277         w_std(_(u'Creating account for postmaster@%s') % ctx.args[2].lower())
   277         w_std(_('Creating account for postmaster@%s') % ctx.args[2].lower())
   278         ctx.scmd = 'useradd'
   278         ctx.scmd = 'useradd'
   279         ctx.args = [prog, ctx.scmd, u'postmaster@' + ctx.args[2].lower()]
   279         ctx.args = [prog, ctx.scmd, 'postmaster@' + ctx.args[2].lower()]
   280         ctx.argc = 3
   280         ctx.argc = 3
   281         user_add(ctx)
   281         user_add(ctx)
   282 
   282 
   283 
   283 
   284 def domain_delete(ctx):
   284 def domain_delete(ctx):
   285     """delete the given domain and all its alias domains"""
   285     """delete the given domain and all its alias domains"""
   286     if ctx.argc < 3:
   286     if ctx.argc < 3:
   287         usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd)
   287         usage(EX_MISSING_ARGS, _('Missing domain name.'), ctx.scmd)
   288     elif ctx.argc < 4:
   288     elif ctx.argc < 4:
   289         ctx.hdlr.domain_delete(ctx.args[2].lower())
   289         ctx.hdlr.domain_delete(ctx.args[2].lower())
   290     elif ctx.args[3].lower() == 'force':
   290     elif ctx.args[3].lower() == 'force':
   291         ctx.hdlr.domain_delete(ctx.args[2].lower(), True)
   291         ctx.hdlr.domain_delete(ctx.args[2].lower(), True)
   292     else:
   292     else:
   293         usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % ctx.args[3],
   293         usage(INVALID_ARGUMENT, _("Invalid argument: '%s'") % ctx.args[3],
   294               ctx.scmd)
   294               ctx.scmd)
   295 
   295 
   296 
   296 
   297 def domain_info(ctx):
   297 def domain_info(ctx):
   298     """display information about the given domain"""
   298     """display information about the given domain"""
   299     if ctx.argc < 3:
   299     if ctx.argc < 3:
   300         usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd)
   300         usage(EX_MISSING_ARGS, _('Missing domain name.'), ctx.scmd)
   301     if ctx.argc < 4:
   301     if ctx.argc < 4:
   302         details = None
   302         details = None
   303     else:
   303     else:
   304         details = ctx.args[3].lower()
   304         details = ctx.args[3].lower()
   305         if details not in ('accounts', 'aliasdomains', 'aliases', 'full',
   305         if details not in ('accounts', 'aliasdomains', 'aliases', 'full',
   306                            'relocated', 'catchall'):
   306                            'relocated', 'catchall'):
   307             usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % details,
   307             usage(INVALID_ARGUMENT, _("Invalid argument: '%s'") % details,
   308                   ctx.scmd)
   308                   ctx.scmd)
   309     try:
   309     try:
   310         info = ctx.hdlr.domain_info(ctx.args[2].lower(), details)
   310         info = ctx.hdlr.domain_info(ctx.args[2].lower(), details)
   311     except VMMError, err:
   311     except VMMError as err:
   312         if err.code is DOMAIN_ALIAS_EXISTS:
   312         if err.code is DOMAIN_ALIAS_EXISTS:
   313             w_err(0, ctx.plan_a_b % {'subcommand': u'aliasdomaininfo',
   313             w_err(0, ctx.plan_a_b % {'subcommand': 'aliasdomaininfo',
   314                   'object': ctx.args[2].lower()})
   314                   'object': ctx.args[2].lower()})
   315             ctx.scmd = ctx.args[1] = 'aliasdomaininfo'
   315             ctx.scmd = ctx.args[1] = 'aliasdomaininfo'
   316             aliasdomain_info(ctx)
   316             aliasdomain_info(ctx)
   317         else:
   317         else:
   318             raise
   318             raise
   319     else:
   319     else:
   320         q_limit = u'Storage: %(bytes)s; Messages: %(messages)s'
   320         q_limit = 'Storage: %(bytes)s; Messages: %(messages)s'
   321         if not details:
   321         if not details:
   322             info['bytes'] = human_size(info['bytes'])
   322             info['bytes'] = human_size(info['bytes'])
   323             info['messages'] = locale.format('%d', info['messages'],
   323             info['messages'] = locale.format('%d', info['messages'],
   324                                              True).decode(ENCODING, 'replace')
   324                                              True).decode(ENCODING, 'replace')
   325             info['quota limit/user'] = q_limit % info
   325             info['quota limit/user'] = q_limit % info
   326             _print_info(ctx, info, _(u'Domain'))
   326             _print_info(ctx, info, _('Domain'))
   327         else:
   327         else:
   328             info[0]['bytes'] = human_size(info[0]['bytes'])
   328             info[0]['bytes'] = human_size(info[0]['bytes'])
   329             info[0]['messages'] = locale.format('%d', info[0]['messages'],
   329             info[0]['messages'] = locale.format('%d', info[0]['messages'],
   330                                                 True).decode(ENCODING,
   330                                                 True).decode(ENCODING,
   331                                                              'replace')
   331                                                              'replace')
   332             info[0]['quota limit/user'] = q_limit % info[0]
   332             info[0]['quota limit/user'] = q_limit % info[0]
   333             _print_info(ctx, info[0], _(u'Domain'))
   333             _print_info(ctx, info[0], _('Domain'))
   334             if details == u'accounts':
   334             if details == 'accounts':
   335                 _print_list(info[1], _(u'accounts'))
   335                 _print_list(info[1], _('accounts'))
   336             elif details == u'aliasdomains':
   336             elif details == 'aliasdomains':
   337                 _print_list(info[1], _(u'alias domains'))
   337                 _print_list(info[1], _('alias domains'))
   338             elif details == u'aliases':
   338             elif details == 'aliases':
   339                 _print_list(info[1], _(u'aliases'))
   339                 _print_list(info[1], _('aliases'))
   340             elif details == u'relocated':
   340             elif details == 'relocated':
   341                 _print_list(info[1], _(u'relocated users'))
   341                 _print_list(info[1], _('relocated users'))
   342             elif details == u'catchall':
   342             elif details == 'catchall':
   343                 _print_list(info[1], _(u'catch-all destinations'))
   343                 _print_list(info[1], _('catch-all destinations'))
   344             else:
   344             else:
   345                 _print_list(info[1], _(u'alias domains'))
   345                 _print_list(info[1], _('alias domains'))
   346                 _print_list(info[2], _(u'accounts'))
   346                 _print_list(info[2], _('accounts'))
   347                 _print_list(info[3], _(u'aliases'))
   347                 _print_list(info[3], _('aliases'))
   348                 _print_list(info[4], _(u'relocated users'))
   348                 _print_list(info[4], _('relocated users'))
   349                 _print_list(info[5], _(u'catch-all destinations'))
   349                 _print_list(info[5], _('catch-all destinations'))
   350 
   350 
   351 
   351 
   352 def domain_quota(ctx):
   352 def domain_quota(ctx):
   353     """update the quota limit of the specified domain"""
   353     """update the quota limit of the specified domain"""
   354     if ctx.argc < 3:
   354     if ctx.argc < 3:
   355         usage(EX_MISSING_ARGS, _(u'Missing domain name and storage value.'),
   355         usage(EX_MISSING_ARGS, _('Missing domain name and storage value.'),
   356               ctx.scmd)
   356               ctx.scmd)
   357     if ctx.argc < 4:
   357     if ctx.argc < 4:
   358         usage(EX_MISSING_ARGS, _(u'Missing storage value.'), ctx.scmd)
   358         usage(EX_MISSING_ARGS, _('Missing storage value.'), ctx.scmd)
   359     messages = 0
   359     messages = 0
   360     force = None
   360     force = None
   361     try:
   361     try:
   362         bytes_ = size_in_bytes(ctx.args[3])
   362         bytes_ = size_in_bytes(ctx.args[3])
   363     except (ValueError, TypeError):
   363     except (ValueError, TypeError):
   364         usage(INVALID_ARGUMENT, _(u"Invalid storage value: '%s'") %
   364         usage(INVALID_ARGUMENT, _("Invalid storage value: '%s'") %
   365               ctx.args[3], ctx.scmd)
   365               ctx.args[3], ctx.scmd)
   366     if ctx.argc < 5:
   366     if ctx.argc < 5:
   367         pass
   367         pass
   368     elif ctx.argc < 6:
   368     elif ctx.argc < 6:
   369         try:
   369         try:
   370             messages = int(ctx.args[4])
   370             messages = int(ctx.args[4])
   371         except ValueError:
   371         except ValueError:
   372             if ctx.args[4].lower() != 'force':
   372             if ctx.args[4].lower() != 'force':
   373                 usage(INVALID_ARGUMENT,
   373                 usage(INVALID_ARGUMENT,
   374                       _(u"Neither a valid number of messages nor the keyword "
   374                       _("Neither a valid number of messages nor the keyword "
   375                         u"'force': '%s'") % ctx.args[4], ctx.scmd)
   375                         "'force': '%s'") % ctx.args[4], ctx.scmd)
   376             force = 'force'
   376             force = 'force'
   377     else:
   377     else:
   378         try:
   378         try:
   379             messages = int(ctx.args[4])
   379             messages = int(ctx.args[4])
   380         except ValueError:
   380         except ValueError:
   381             usage(INVALID_ARGUMENT,
   381             usage(INVALID_ARGUMENT,
   382                   _(u"Not a valid number of messages: '%s'") % ctx.args[4],
   382                   _("Not a valid number of messages: '%s'") % ctx.args[4],
   383                   ctx.scmd)
   383                   ctx.scmd)
   384         if ctx.args[5].lower() != 'force':
   384         if ctx.args[5].lower() != 'force':
   385             usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % ctx.args[5],
   385             usage(INVALID_ARGUMENT, _("Invalid argument: '%s'") % ctx.args[5],
   386                   ctx.scmd)
   386                   ctx.scmd)
   387         force = 'force'
   387         force = 'force'
   388     ctx.hdlr.domain_quotalimit(ctx.args[2].lower(), bytes_, messages, force)
   388     ctx.hdlr.domain_quotalimit(ctx.args[2].lower(), bytes_, messages, force)
   389 
   389 
   390 
   390 
   391 def domain_services(ctx):
   391 def domain_services(ctx):
   392     """allow all named service and block the uncredited."""
   392     """allow all named service and block the uncredited."""
   393     if ctx.argc < 3:
   393     if ctx.argc < 3:
   394         usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd)
   394         usage(EX_MISSING_ARGS, _('Missing domain name.'), ctx.scmd)
   395     services = []
   395     services = []
   396     force = False
   396     force = False
   397     if ctx.argc is 3:
   397     if ctx.argc is 3:
   398         pass
   398         pass
   399     elif ctx.argc is 4:
   399     elif ctx.argc is 4:
   401         if arg in SERVICES:
   401         if arg in SERVICES:
   402             services.append(arg)
   402             services.append(arg)
   403         elif arg == 'force':
   403         elif arg == 'force':
   404             force = True
   404             force = True
   405         else:
   405         else:
   406             usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % arg,
   406             usage(INVALID_ARGUMENT, _("Invalid argument: '%s'") % arg,
   407                   ctx.scmd)
   407                   ctx.scmd)
   408     else:
   408     else:
   409         services.extend([service.lower() for service in ctx.args[3:-1]])
   409         services.extend([service.lower() for service in ctx.args[3:-1]])
   410         arg = ctx.args[-1].lower()
   410         arg = ctx.args[-1].lower()
   411         if arg == 'force':
   411         if arg == 'force':
   412             force = True
   412             force = True
   413         else:
   413         else:
   414             services.append(arg)
   414             services.append(arg)
   415         unknown = [service for service in services if service not in SERVICES]
   415         unknown = [service for service in services if service not in SERVICES]
   416         if unknown:
   416         if unknown:
   417             usage(INVALID_ARGUMENT, _(u'Invalid service arguments: %s') %
   417             usage(INVALID_ARGUMENT, _('Invalid service arguments: %s') %
   418                   ' '.join(unknown), ctx.scmd)
   418                   ' '.join(unknown), ctx.scmd)
   419     ctx.hdlr.domain_services(ctx.args[2].lower(), (None, 'force')[force],
   419     ctx.hdlr.domain_services(ctx.args[2].lower(), (None, 'force')[force],
   420                              *services)
   420                              *services)
   421 
   421 
   422 
   422 
   423 def domain_transport(ctx):
   423 def domain_transport(ctx):
   424     """update the transport of the specified domain"""
   424     """update the transport of the specified domain"""
   425     if ctx.argc < 3:
   425     if ctx.argc < 3:
   426         usage(EX_MISSING_ARGS, _(u'Missing domain name and new transport.'),
   426         usage(EX_MISSING_ARGS, _('Missing domain name and new transport.'),
   427               ctx.scmd)
   427               ctx.scmd)
   428     if ctx.argc < 4:
   428     if ctx.argc < 4:
   429         usage(EX_MISSING_ARGS, _(u'Missing new transport.'), ctx.scmd)
   429         usage(EX_MISSING_ARGS, _('Missing new transport.'), ctx.scmd)
   430     if ctx.argc < 5:
   430     if ctx.argc < 5:
   431         ctx.hdlr.domain_transport(ctx.args[2].lower(), ctx.args[3])
   431         ctx.hdlr.domain_transport(ctx.args[2].lower(), ctx.args[3])
   432     else:
   432     else:
   433         force = ctx.args[4].lower()
   433         force = ctx.args[4].lower()
   434         if force != 'force':
   434         if force != 'force':
   435             usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % force,
   435             usage(INVALID_ARGUMENT, _("Invalid argument: '%s'") % force,
   436                   ctx.scmd)
   436                   ctx.scmd)
   437         ctx.hdlr.domain_transport(ctx.args[2].lower(), ctx.args[3], force)
   437         ctx.hdlr.domain_transport(ctx.args[2].lower(), ctx.args[3], force)
   438 
   438 
   439 
   439 
   440 def domain_note(ctx):
   440 def domain_note(ctx):
   441     """update the note of the given domain"""
   441     """update the note of the given domain"""
   442     if ctx.argc < 3:
   442     if ctx.argc < 3:
   443         usage(EX_MISSING_ARGS, _(u'Missing domain name.'),
   443         usage(EX_MISSING_ARGS, _('Missing domain name.'),
   444               ctx.scmd)
   444               ctx.scmd)
   445     elif ctx.argc < 4:
   445     elif ctx.argc < 4:
   446         note = None
   446         note = None
   447     else:
   447     else:
   448         note = ' '.join(ctx.args[3:])
   448         note = ' '.join(ctx.args[3:])
   450 
   450 
   451 
   451 
   452 def get_user(ctx):
   452 def get_user(ctx):
   453     """get the address of the user with the given UID"""
   453     """get the address of the user with the given UID"""
   454     if ctx.argc < 3:
   454     if ctx.argc < 3:
   455         usage(EX_MISSING_ARGS, _(u'Missing UID.'), ctx.scmd)
   455         usage(EX_MISSING_ARGS, _('Missing UID.'), ctx.scmd)
   456     _print_info(ctx, ctx.hdlr.user_by_uid(ctx.args[2]), _(u'Account'))
   456     _print_info(ctx, ctx.hdlr.user_by_uid(ctx.args[2]), _('Account'))
   457 
   457 
   458 
   458 
   459 def help_(ctx):
   459 def help_(ctx):
   460     """print help messages."""
   460     """print help messages."""
   461     if ctx.argc > 2:
   461     if ctx.argc > 2:
   462         hlptpc = ctx.args[2].lower()
   462         hlptpc = ctx.args[2].lower()
   463         if hlptpc in cmd_map:
   463         if hlptpc in cmd_map:
   464             topic = hlptpc
   464             topic = hlptpc
   465         else:
   465         else:
   466             for scmd in cmd_map.itervalues():
   466             for scmd in cmd_map.values():
   467                 if scmd.alias == hlptpc:
   467                 if scmd.alias == hlptpc:
   468                     topic = scmd.name
   468                     topic = scmd.name
   469                     break
   469                     break
   470             else:
   470             else:
   471                 usage(INVALID_ARGUMENT, _(u"Unknown help topic: '%s'") %
   471                 usage(INVALID_ARGUMENT, _("Unknown help topic: '%s'") %
   472                       ctx.args[2], ctx.scmd)
   472                       ctx.args[2], ctx.scmd)
   473         if topic != u'help':
   473         if topic != 'help':
   474             return cmd_map[topic].help_()
   474             return cmd_map[topic].help_()
   475 
   475 
   476     old_ii = txt_wrpr.initial_indent
   476     old_ii = txt_wrpr.initial_indent
   477     old_si = txt_wrpr.subsequent_indent
   477     old_si = txt_wrpr.subsequent_indent
   478     txt_wrpr.initial_indent = ' '
   478     txt_wrpr.initial_indent = ' '
   479     # len(max(_overview.iterkeys(), key=len)) #Py25
   479     # len(max(_overview.iterkeys(), key=len)) #Py25
   480     txt_wrpr.subsequent_indent = 20 * ' '
   480     txt_wrpr.subsequent_indent = 20 * ' '
   481     order = cmd_map.keys()
   481     order = sorted(list(cmd_map.keys()))
   482     order.sort()
   482 
   483 
   483     w_std(_('List of available subcommands:') + '\n')
   484     w_std(_(u'List of available subcommands:') + '\n')
       
   485     for key in order:
   484     for key in order:
   486         w_std('\n'.join(txt_wrpr.wrap('%-18s %s' % (key, cmd_map[key].descr))))
   485         w_std('\n'.join(txt_wrpr.wrap('%-18s %s' % (key, cmd_map[key].descr))))
   487 
   486 
   488     txt_wrpr.initial_indent = old_ii
   487     txt_wrpr.initial_indent = old_ii
   489     txt_wrpr.subsequent_indent = old_si
   488     txt_wrpr.subsequent_indent = old_si
   500     _print_domain_list(gids, domains, matching)
   499     _print_domain_list(gids, domains, matching)
   501 
   500 
   502 
   501 
   503 def list_pwschemes(ctx_unused):
   502 def list_pwschemes(ctx_unused):
   504     """Prints all usable password schemes and password encoding suffixes."""
   503     """Prints all usable password schemes and password encoding suffixes."""
   505     keys = (_(u'Usable password schemes'), _(u'Usable encoding suffixes'))
   504     keys = (_('Usable password schemes'), _('Usable encoding suffixes'))
   506     old_ii, old_si = txt_wrpr.initial_indent, txt_wrpr.subsequent_indent
   505     old_ii, old_si = txt_wrpr.initial_indent, txt_wrpr.subsequent_indent
   507     txt_wrpr.initial_indent = txt_wrpr.subsequent_indent = '\t'
   506     txt_wrpr.initial_indent = txt_wrpr.subsequent_indent = '\t'
   508     txt_wrpr.width = txt_wrpr.width - 8
   507     txt_wrpr.width = txt_wrpr.width - 8
   509 
   508 
   510     for key, value in zip(keys, list_schemes()):
   509     for key, value in zip(keys, list_schemes()):
   547 
   546 
   548 def relocated_add(ctx):
   547 def relocated_add(ctx):
   549     """create a new record for a relocated user"""
   548     """create a new record for a relocated user"""
   550     if ctx.argc < 3:
   549     if ctx.argc < 3:
   551         usage(EX_MISSING_ARGS,
   550         usage(EX_MISSING_ARGS,
   552               _(u'Missing relocated address and destination.'), ctx.scmd)
   551               _('Missing relocated address and destination.'), ctx.scmd)
   553     elif ctx.argc < 4:
   552     elif ctx.argc < 4:
   554         usage(EX_MISSING_ARGS, _(u'Missing destination address.'), ctx.scmd)
   553         usage(EX_MISSING_ARGS, _('Missing destination address.'), ctx.scmd)
   555     ctx.hdlr.relocated_add(ctx.args[2].lower(), ctx.args[3])
   554     ctx.hdlr.relocated_add(ctx.args[2].lower(), ctx.args[3])
   556 
   555 
   557 
   556 
   558 def relocated_delete(ctx):
   557 def relocated_delete(ctx):
   559     """delete the record of the relocated user"""
   558     """delete the record of the relocated user"""
   560     if ctx.argc < 3:
   559     if ctx.argc < 3:
   561         usage(EX_MISSING_ARGS, _(u'Missing relocated address.'), ctx.scmd)
   560         usage(EX_MISSING_ARGS, _('Missing relocated address.'), ctx.scmd)
   562     ctx.hdlr.relocated_delete(ctx.args[2].lower())
   561     ctx.hdlr.relocated_delete(ctx.args[2].lower())
   563 
   562 
   564 
   563 
   565 def relocated_info(ctx):
   564 def relocated_info(ctx):
   566     """print information about a relocated user"""
   565     """print information about a relocated user"""
   567     if ctx.argc < 3:
   566     if ctx.argc < 3:
   568         usage(EX_MISSING_ARGS, _(u'Missing relocated address.'), ctx.scmd)
   567         usage(EX_MISSING_ARGS, _('Missing relocated address.'), ctx.scmd)
   569     relocated = ctx.args[2].lower()
   568     relocated = ctx.args[2].lower()
   570     try:
   569     try:
   571         _print_relocated_info(addr=relocated,
   570         _print_relocated_info(addr=relocated,
   572                               dest=ctx.hdlr.relocated_info(relocated))
   571                               dest=ctx.hdlr.relocated_info(relocated))
   573     except VMMError, err:
   572     except VMMError as err:
   574         if err.code is ACCOUNT_EXISTS:
   573         if err.code is ACCOUNT_EXISTS:
   575             w_err(0, ctx.plan_a_b % {'subcommand': u'userinfo',
   574             w_err(0, ctx.plan_a_b % {'subcommand': 'userinfo',
   576                   'object': relocated})
   575                   'object': relocated})
   577             ctx.scmd = ctx.args[1] = 'userinfoi'
   576             ctx.scmd = ctx.args[1] = 'userinfoi'
   578             user_info(ctx)
   577             user_info(ctx)
   579         elif err.code is ALIAS_EXISTS:
   578         elif err.code is ALIAS_EXISTS:
   580             w_err(0, ctx.plan_a_b % {'subcommand': u'aliasinfo',
   579             w_err(0, ctx.plan_a_b % {'subcommand': 'aliasinfo',
   581                   'object': relocated})
   580                   'object': relocated})
   582             ctx.scmd = ctx.args[1] = 'aliasinfo'
   581             ctx.scmd = ctx.args[1] = 'aliasinfo'
   583             alias_info(ctx)
   582             alias_info(ctx)
   584         else:
   583         else:
   585             raise
   584             raise
   586 
   585 
   587 
   586 
   588 def user_add(ctx):
   587 def user_add(ctx):
   589     """create a new e-mail user with the given address"""
   588     """create a new e-mail user with the given address"""
   590     if ctx.argc < 3:
   589     if ctx.argc < 3:
   591         usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd)
   590         usage(EX_MISSING_ARGS, _('Missing e-mail address.'), ctx.scmd)
   592     elif ctx.argc < 4:
   591     elif ctx.argc < 4:
   593         password = None
   592         password = None
   594     else:
   593     else:
   595         password = ctx.args[3]
   594         password = ctx.args[3]
   596     gen_pass = ctx.hdlr.user_add(ctx.args[2].lower(), password)
   595     gen_pass = ctx.hdlr.user_add(ctx.args[2].lower(), password)
   597     if ctx.argc < 4 and gen_pass:
   596     if ctx.argc < 4 and gen_pass:
   598         w_std(_(u"Generated password: %s") % gen_pass)
   597         w_std(_("Generated password: %s") % gen_pass)
   599 
   598 
   600 
   599 
   601 def user_delete(ctx):
   600 def user_delete(ctx):
   602     """delete the specified user"""
   601     """delete the specified user"""
   603     if ctx.argc < 3:
   602     if ctx.argc < 3:
   604         usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd)
   603         usage(EX_MISSING_ARGS, _('Missing e-mail address.'), ctx.scmd)
   605     elif ctx.argc < 4:
   604     elif ctx.argc < 4:
   606         ctx.hdlr.user_delete(ctx.args[2].lower())
   605         ctx.hdlr.user_delete(ctx.args[2].lower())
   607     elif ctx.args[3].lower() == 'force':
   606     elif ctx.args[3].lower() == 'force':
   608         ctx.hdlr.user_delete(ctx.args[2].lower(), True)
   607         ctx.hdlr.user_delete(ctx.args[2].lower(), True)
   609     else:
   608     else:
   610         usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % ctx.args[3],
   609         usage(INVALID_ARGUMENT, _("Invalid argument: '%s'") % ctx.args[3],
   611               ctx.scmd)
   610               ctx.scmd)
   612 
   611 
   613 
   612 
   614 def user_info(ctx):
   613 def user_info(ctx):
   615     """display information about the given address"""
   614     """display information about the given address"""
   616     if ctx.argc < 3:
   615     if ctx.argc < 3:
   617         usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd)
   616         usage(EX_MISSING_ARGS, _('Missing e-mail address.'), ctx.scmd)
   618     if ctx.argc < 4:
   617     if ctx.argc < 4:
   619         details = None
   618         details = None
   620     else:
   619     else:
   621         details = ctx.args[3].lower()
   620         details = ctx.args[3].lower()
   622         if details not in ('aliases', 'du', 'full'):
   621         if details not in ('aliases', 'du', 'full'):
   623             usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % details,
   622             usage(INVALID_ARGUMENT, _("Invalid argument: '%s'") % details,
   624                   ctx.scmd)
   623                   ctx.scmd)
   625     try:
   624     try:
   626         info = ctx.hdlr.user_info(ctx.args[2].lower(), details)
   625         info = ctx.hdlr.user_info(ctx.args[2].lower(), details)
   627     except VMMError, err:
   626     except VMMError as err:
   628         if err.code is ALIAS_EXISTS:
   627         if err.code is ALIAS_EXISTS:
   629             w_err(0, ctx.plan_a_b % {'subcommand': u'aliasinfo',
   628             w_err(0, ctx.plan_a_b % {'subcommand': 'aliasinfo',
   630                   'object': ctx.args[2].lower()})
   629                   'object': ctx.args[2].lower()})
   631             ctx.scmd = ctx.args[1] = 'aliasinfo'
   630             ctx.scmd = ctx.args[1] = 'aliasinfo'
   632             alias_info(ctx)
   631             alias_info(ctx)
   633         elif err.code is RELOCATED_EXISTS:
   632         elif err.code is RELOCATED_EXISTS:
   634             w_err(0, ctx.plan_a_b % {'subcommand': u'relocatedinfo',
   633             w_err(0, ctx.plan_a_b % {'subcommand': 'relocatedinfo',
   635                   'object': ctx.args[2].lower()})
   634                   'object': ctx.args[2].lower()})
   636             ctx.scmd = ctx.args[1] = 'relocatedinfo'
   635             ctx.scmd = ctx.args[1] = 'relocatedinfo'
   637             relocated_info(ctx)
   636             relocated_info(ctx)
   638         else:
   637         else:
   639             raise
   638             raise
   643                     info['uq_bytes'], True, info['ql_domaindefault'])
   642                     info['uq_bytes'], True, info['ql_domaindefault'])
   644             info['quota messages'] = \
   643             info['quota messages'] = \
   645                 _format_quota_usage(info['ql_messages'],
   644                 _format_quota_usage(info['ql_messages'],
   646                                     info['uq_messages'],
   645                                     info['uq_messages'],
   647                                     domaindefault=info['ql_domaindefault'])
   646                                     domaindefault=info['ql_domaindefault'])
   648             _print_info(ctx, info, _(u'Account'))
   647             _print_info(ctx, info, _('Account'))
   649         else:
   648         else:
   650             info[0]['quota storage'] = _format_quota_usage(info[0]['ql_bytes'],
   649             info[0]['quota storage'] = _format_quota_usage(info[0]['ql_bytes'],
   651                     info[0]['uq_bytes'], True, info[0]['ql_domaindefault'])
   650                     info[0]['uq_bytes'], True, info[0]['ql_domaindefault'])
   652             info[0]['quota messages'] = \
   651             info[0]['quota messages'] = \
   653                 _format_quota_usage(info[0]['ql_messages'],
   652                 _format_quota_usage(info[0]['ql_messages'],
   654                                     info[0]['uq_messages'],
   653                                     info[0]['uq_messages'],
   655                                     domaindefault=info[0]['ql_domaindefault'])
   654                                     domaindefault=info[0]['ql_domaindefault'])
   656             _print_info(ctx, info[0], _(u'Account'))
   655             _print_info(ctx, info[0], _('Account'))
   657             _print_list(info[1], _(u'alias addresses'))
   656             _print_list(info[1], _('alias addresses'))
   658 
   657 
   659 
   658 
   660 def user_name(ctx):
   659 def user_name(ctx):
   661     """set or update the real name for an address"""
   660     """set or update the real name for an address"""
   662     if ctx.argc < 3:
   661     if ctx.argc < 3:
   663         usage(EX_MISSING_ARGS, _(u"Missing e-mail address and user's name."),
   662         usage(EX_MISSING_ARGS, _("Missing e-mail address and user's name."),
   664               ctx.scmd)
   663               ctx.scmd)
   665     elif ctx.argc < 4:
   664     elif ctx.argc < 4:
   666         name = None
   665         name = None
   667     else:
   666     else:
   668         name = ctx.args[3]
   667         name = ctx.args[3]
   670 
   669 
   671 
   670 
   672 def user_password(ctx):
   671 def user_password(ctx):
   673     """update the password for the given address"""
   672     """update the password for the given address"""
   674     if ctx.argc < 3:
   673     if ctx.argc < 3:
   675         usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd)
   674         usage(EX_MISSING_ARGS, _('Missing e-mail address.'), ctx.scmd)
   676     elif ctx.argc < 4:
   675     elif ctx.argc < 4:
   677         password = None
   676         password = None
   678     else:
   677     else:
   679         password = ctx.args[3]
   678         password = ctx.args[3]
   680     ctx.hdlr.user_password(ctx.args[2].lower(), password)
   679     ctx.hdlr.user_password(ctx.args[2].lower(), password)
   681 
   680 
   682 
   681 
   683 def user_note(ctx):
   682 def user_note(ctx):
   684     """update the note of the given address"""
   683     """update the note of the given address"""
   685     if ctx.argc < 3:
   684     if ctx.argc < 3:
   686         usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'),
   685         usage(EX_MISSING_ARGS, _('Missing e-mail address.'),
   687               ctx.scmd)
   686               ctx.scmd)
   688     elif ctx.argc < 4:
   687     elif ctx.argc < 4:
   689         note = None
   688         note = None
   690     else:
   689     else:
   691         note = ' '.join(ctx.args[3:])
   690         note = ' '.join(ctx.args[3:])
   693 
   692 
   694 
   693 
   695 def user_quota(ctx):
   694 def user_quota(ctx):
   696     """update the quota limit for the given address"""
   695     """update the quota limit for the given address"""
   697     if ctx.argc < 3:
   696     if ctx.argc < 3:
   698         usage(EX_MISSING_ARGS, _(u'Missing e-mail address and storage value.'),
   697         usage(EX_MISSING_ARGS, _('Missing e-mail address and storage value.'),
   699               ctx.scmd)
   698               ctx.scmd)
   700     elif ctx.argc < 4:
   699     elif ctx.argc < 4:
   701         usage(EX_MISSING_ARGS, _(u'Missing storage value.'), ctx.scmd)
   700         usage(EX_MISSING_ARGS, _('Missing storage value.'), ctx.scmd)
   702     if ctx.args[3] != 'domain':
   701     if ctx.args[3] != 'domain':
   703         try:
   702         try:
   704             bytes_ = size_in_bytes(ctx.args[3])
   703             bytes_ = size_in_bytes(ctx.args[3])
   705         except (ValueError, TypeError):
   704         except (ValueError, TypeError):
   706             usage(INVALID_ARGUMENT, _(u"Invalid storage value: '%s'") %
   705             usage(INVALID_ARGUMENT, _("Invalid storage value: '%s'") %
   707                   ctx.args[3], ctx.scmd)
   706                   ctx.args[3], ctx.scmd)
   708     else:
   707     else:
   709         bytes_ = ctx.args[3]
   708         bytes_ = ctx.args[3]
   710     if ctx.argc < 5:
   709     if ctx.argc < 5:
   711         messages = 0
   710         messages = 0
   712     else:
   711     else:
   713         try:
   712         try:
   714             messages = int(ctx.args[4])
   713             messages = int(ctx.args[4])
   715         except ValueError:
   714         except ValueError:
   716             usage(INVALID_ARGUMENT,
   715             usage(INVALID_ARGUMENT,
   717                   _(u"Not a valid number of messages: '%s'") % ctx.args[4],
   716                   _("Not a valid number of messages: '%s'") % ctx.args[4],
   718                   ctx.scmd)
   717                   ctx.scmd)
   719     ctx.hdlr.user_quotalimit(ctx.args[2].lower(), bytes_, messages)
   718     ctx.hdlr.user_quotalimit(ctx.args[2].lower(), bytes_, messages)
   720 
   719 
   721 
   720 
   722 def user_services(ctx):
   721 def user_services(ctx):
   723     """allow all named service and block the uncredited."""
   722     """allow all named service and block the uncredited."""
   724     if ctx.argc < 3:
   723     if ctx.argc < 3:
   725         usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd)
   724         usage(EX_MISSING_ARGS, _('Missing e-mail address.'), ctx.scmd)
   726     services = []
   725     services = []
   727     if ctx.argc >= 4:
   726     if ctx.argc >= 4:
   728         services.extend([service.lower() for service in ctx.args[3:]])
   727         services.extend([service.lower() for service in ctx.args[3:]])
   729         unknown = [service for service in services if service not in SERVICES]
   728         unknown = [service for service in services if service not in SERVICES]
   730         if unknown and ctx.args[3] != 'domain':
   729         if unknown and ctx.args[3] != 'domain':
   731             usage(INVALID_ARGUMENT, _(u'Invalid service arguments: %s') %
   730             usage(INVALID_ARGUMENT, _('Invalid service arguments: %s') %
   732                   ' '.join(unknown), ctx.scmd)
   731                   ' '.join(unknown), ctx.scmd)
   733     ctx.hdlr.user_services(ctx.args[2].lower(), *services)
   732     ctx.hdlr.user_services(ctx.args[2].lower(), *services)
   734 
   733 
   735 
   734 
   736 def user_transport(ctx):
   735 def user_transport(ctx):
   737     """update the transport of the given address"""
   736     """update the transport of the given address"""
   738     if ctx.argc < 3:
   737     if ctx.argc < 3:
   739         usage(EX_MISSING_ARGS, _(u'Missing e-mail address and transport.'),
   738         usage(EX_MISSING_ARGS, _('Missing e-mail address and transport.'),
   740               ctx.scmd)
   739               ctx.scmd)
   741     if ctx.argc < 4:
   740     if ctx.argc < 4:
   742         usage(EX_MISSING_ARGS, _(u'Missing transport.'), ctx.scmd)
   741         usage(EX_MISSING_ARGS, _('Missing transport.'), ctx.scmd)
   743     ctx.hdlr.user_transport(ctx.args[2].lower(), ctx.args[3])
   742     ctx.hdlr.user_transport(ctx.args[2].lower(), ctx.args[3])
   744 
   743 
   745 
   744 
   746 def usage(errno, errmsg, subcommand=None):
   745 def usage(errno, errmsg, subcommand=None):
   747     """print usage message for the given command or all commands.
   746     """print usage message for the given command or all commands.
   748     When errno > 0, sys,exit(errno) will interrupt the program.
   747     When errno > 0, sys,exit(errno) will interrupt the program.
   749     """
   748     """
   750     if subcommand and subcommand in cmd_map:
   749     if subcommand and subcommand in cmd_map:
   751         w_err(errno, _(u"Error: %s") % errmsg,
   750         w_err(errno, _("Error: %s") % errmsg,
   752               _(u"usage: ") + cmd_map[subcommand].usage)
   751               _("usage: ") + cmd_map[subcommand].usage)
   753 
   752 
   754     # TP: Please adjust translated words like the original text.
   753     # TP: Please adjust translated words like the original text.
   755     # (It's a table header.) Extract from usage text:
   754     # (It's a table header.) Extract from usage text:
   756     # usage: vmm subcommand arguments
   755     # usage: vmm subcommand arguments
   757     #   short long
   756     #   short long
   758     #   subcommand                arguments
   757     #   subcommand                arguments
   759     #
   758     #
   760     #   da    domainadd           fqdn [transport]
   759     #   da    domainadd           fqdn [transport]
   761     #   dd    domaindelete        fqdn [force]
   760     #   dd    domaindelete        fqdn [force]
   762     u_head = _(u"""usage: %s subcommand arguments
   761     u_head = _("""usage: %s subcommand arguments
   763   short long
   762   short long
   764   subcommand                arguments\n""") % prog
   763   subcommand                arguments\n""") % prog
   765     order = cmd_map.keys()
   764     order = sorted(list(cmd_map.keys()))
   766     order.sort()
       
   767     w_err(0, u_head)
   765     w_err(0, u_head)
   768     for key in order:
   766     for key in order:
   769         scmd = cmd_map[key]
   767         scmd = cmd_map[key]
   770         w_err(0, '  %-5s %-19s %s' % (scmd.alias, scmd.name, scmd.args))
   768         w_err(0, '  %-5s %-19s %s' % (scmd.alias, scmd.name, scmd.args))
   771     w_err(errno, '', _(u"Error: %s") % errmsg)
   769     w_err(errno, '', _("Error: %s") % errmsg)
   772 
   770 
   773 
   771 
   774 def version(ctx_unused):
   772 def version(ctx_unused):
   775     """Write version and copyright information to stdout."""
   773     """Write version and copyright information to stdout."""
   776     w_std('%s, %s %s (%s %s)\nPython %s %s %s\n\n%s\n%s %s' % (prog,
   774     w_std('%s, %s %s (%s %s)\nPython %s %s %s\n\n%s\n%s %s' % (prog,
   777     # TP: The words 'from', 'version' and 'on' are used in
   775     # TP: The words 'from', 'version' and 'on' are used in
   778     # the version information, e.g.:
   776     # the version information, e.g.:
   779     # vmm, version 0.5.2 (from 09/09/09)
   777     # vmm, version 0.5.2 (from 09/09/09)
   780     # Python 2.5.4 on FreeBSD
   778     # Python 2.5.4 on FreeBSD
   781         _(u'version'), __version__, _(u'from'),
   779         _('version'), __version__, _('from'),
   782         strftime(locale.nl_langinfo(locale.D_FMT),
   780         strftime(locale.nl_langinfo(locale.D_FMT),
   783             strptime(__date__, '%Y-%m-%d')).decode(ENCODING, 'replace'),
   781             strptime(__date__, '%Y-%m-%d')).decode(ENCODING, 'replace'),
   784         os.sys.version.split()[0], _(u'on'), os.uname()[0],
   782         os.sys.version.split()[0], _('on'), os.uname()[0],
   785         __copyright__, prog,
   783         __copyright__, prog,
   786         _(u'is free software and comes with ABSOLUTELY NO WARRANTY.')))
   784         _('is free software and comes with ABSOLUTELY NO WARRANTY.')))
   787 
   785 
   788 
   786 
   789 def update_cmd_map():
   787 def update_cmd_map():
   790     """Update the cmd_map, after gettext's _ was installed."""
   788     """Update the cmd_map, after gettext's _ was installed."""
   791     cmd = Command
   789     cmd = Command
   792     cmd_map.update({
   790     cmd_map.update({
   793     # Account commands
   791     # Account commands
   794     'getuser': cmd('getuser', 'gu', get_user, 'uid',
   792     'getuser': cmd('getuser', 'gu', get_user, 'uid',
   795                    _(u'get the address of the user with the given UID')),
   793                    _('get the address of the user with the given UID')),
   796     'useradd': cmd('useradd', 'ua', user_add, 'address [password]',
   794     'useradd': cmd('useradd', 'ua', user_add, 'address [password]',
   797                    _(u'create a new e-mail user with the given address')),
   795                    _('create a new e-mail user with the given address')),
   798     'userdelete': cmd('userdelete', 'ud', user_delete, 'address [force]',
   796     'userdelete': cmd('userdelete', 'ud', user_delete, 'address [force]',
   799                       _(u'delete the specified user')),
   797                       _('delete the specified user')),
   800     'userinfo': cmd('userinfo', 'ui', user_info, 'address [details]',
   798     'userinfo': cmd('userinfo', 'ui', user_info, 'address [details]',
   801                     _(u'display information about the given address')),
   799                     _('display information about the given address')),
   802     'username': cmd('username', 'un', user_name, 'address [name]',
   800     'username': cmd('username', 'un', user_name, 'address [name]',
   803                     _(u'set, update or delete the real name for an address')),
   801                     _('set, update or delete the real name for an address')),
   804     'userpassword': cmd('userpassword', 'up', user_password,
   802     'userpassword': cmd('userpassword', 'up', user_password,
   805                         'address [password]',
   803                         'address [password]',
   806                         _(u'update the password for the given address')),
   804                         _('update the password for the given address')),
   807     'userquota': cmd('userquota', 'uq', user_quota,
   805     'userquota': cmd('userquota', 'uq', user_quota,
   808                      'address storage [messages] | address domain',
   806                      'address storage [messages] | address domain',
   809                      _(u'update the quota limit for the given address')),
   807                      _('update the quota limit for the given address')),
   810     'userservices': cmd('userservices', 'us', user_services,
   808     'userservices': cmd('userservices', 'us', user_services,
   811                         'address [service ...] | address domain',
   809                         'address [service ...] | address domain',
   812                         _(u'enables the specified services and disables all '
   810                         _('enables the specified services and disables all '
   813                           u'not specified services')),
   811                           'not specified services')),
   814     'usertransport': cmd('usertransport', 'ut', user_transport,
   812     'usertransport': cmd('usertransport', 'ut', user_transport,
   815                          'address transport | address domain',
   813                          'address transport | address domain',
   816                          _(u'update the transport of the given address')),
   814                          _('update the transport of the given address')),
   817     'usernote': cmd('usernote', 'uo', user_note, 'address [note]',
   815     'usernote': cmd('usernote', 'uo', user_note, 'address [note]',
   818                     _(u'set, update or delete the note of the given address')),
   816                     _('set, update or delete the note of the given address')),
   819     # Alias commands
   817     # Alias commands
   820     'aliasadd': cmd('aliasadd', 'aa', alias_add, 'address destination ...',
   818     'aliasadd': cmd('aliasadd', 'aa', alias_add, 'address destination ...',
   821                     _(u'create a new alias e-mail address with one or more '
   819                     _('create a new alias e-mail address with one or more '
   822                       u'destinations')),
   820                       'destinations')),
   823     'aliasdelete': cmd('aliasdelete', 'ad', alias_delete,
   821     'aliasdelete': cmd('aliasdelete', 'ad', alias_delete,
   824                        'address [destination ...]',
   822                        'address [destination ...]',
   825                        _(u'delete the specified alias e-mail address or one '
   823                        _('delete the specified alias e-mail address or one '
   826                          u'of its destinations')),
   824                          'of its destinations')),
   827     'aliasinfo': cmd('aliasinfo', 'ai', alias_info, 'address',
   825     'aliasinfo': cmd('aliasinfo', 'ai', alias_info, 'address',
   828                      _(u'show the destination(s) of the specified alias')),
   826                      _('show the destination(s) of the specified alias')),
   829     # AliasDomain commands
   827     # AliasDomain commands
   830     'aliasdomainadd': cmd('aliasdomainadd', 'ada', aliasdomain_add,
   828     'aliasdomainadd': cmd('aliasdomainadd', 'ada', aliasdomain_add,
   831                           'fqdn destination',
   829                           'fqdn destination',
   832                           _(u'create a new alias for an existing domain')),
   830                           _('create a new alias for an existing domain')),
   833     'aliasdomaindelete': cmd('aliasdomaindelete', 'add', aliasdomain_delete,
   831     'aliasdomaindelete': cmd('aliasdomaindelete', 'add', aliasdomain_delete,
   834                              'fqdn', _(u'delete the specified alias domain')),
   832                              'fqdn', _('delete the specified alias domain')),
   835     'aliasdomaininfo': cmd('aliasdomaininfo', 'adi', aliasdomain_info, 'fqdn',
   833     'aliasdomaininfo': cmd('aliasdomaininfo', 'adi', aliasdomain_info, 'fqdn',
   836                          _(u'show the destination of the given alias domain')),
   834                          _('show the destination of the given alias domain')),
   837     'aliasdomainswitch': cmd('aliasdomainswitch', 'ads', aliasdomain_switch,
   835     'aliasdomainswitch': cmd('aliasdomainswitch', 'ads', aliasdomain_switch,
   838                              'fqdn destination', _(u'assign the given alias '
   836                              'fqdn destination', _('assign the given alias '
   839                              'domain to an other domain')),
   837                              'domain to an other domain')),
   840     # CatchallAlias commands
   838     # CatchallAlias commands
   841     'catchalladd': cmd('catchalladd', 'caa', catchall_add,
   839     'catchalladd': cmd('catchalladd', 'caa', catchall_add,
   842                        'fqdn destination ...',
   840                        'fqdn destination ...',
   843                        _(u'add one or more catch-all destinations for a '
   841                        _('add one or more catch-all destinations for a '
   844                          u'domain')),
   842                          'domain')),
   845     'catchalldelete': cmd('catchalldelete', 'cad', catchall_delete,
   843     'catchalldelete': cmd('catchalldelete', 'cad', catchall_delete,
   846                        'fqdn [destination ...]',
   844                        'fqdn [destination ...]',
   847                        _(u'delete the specified catch-all destination or all '
   845                        _('delete the specified catch-all destination or all '
   848                          u'of a domain\'s destinations')),
   846                          'of a domain\'s destinations')),
   849     'catchallinfo': cmd('catchallinfo', 'cai', catchall_info, 'fqdn',
   847     'catchallinfo': cmd('catchallinfo', 'cai', catchall_info, 'fqdn',
   850                         _(u'show the catch-all destination(s) of the '
   848                         _('show the catch-all destination(s) of the '
   851                           u'specified domain')),
   849                           'specified domain')),
   852     # Domain commands
   850     # Domain commands
   853     'domainadd': cmd('domainadd', 'da', domain_add, 'fqdn [transport]',
   851     'domainadd': cmd('domainadd', 'da', domain_add, 'fqdn [transport]',
   854                      _(u'create a new domain')),
   852                      _('create a new domain')),
   855     'domaindelete': cmd('domaindelete', 'dd', domain_delete, 'fqdn [force]',
   853     'domaindelete': cmd('domaindelete', 'dd', domain_delete, 'fqdn [force]',
   856                       _(u'delete the given domain and all its alias domains')),
   854                       _('delete the given domain and all its alias domains')),
   857     'domaininfo': cmd('domaininfo', 'di', domain_info, 'fqdn [details]',
   855     'domaininfo': cmd('domaininfo', 'di', domain_info, 'fqdn [details]',
   858                       _(u'display information about the given domain')),
   856                       _('display information about the given domain')),
   859     'domainquota': cmd('domainquota', 'dq', domain_quota,
   857     'domainquota': cmd('domainquota', 'dq', domain_quota,
   860                        'fqdn storage [messages] [force]',
   858                        'fqdn storage [messages] [force]',
   861                        _(u'update the quota limit of the specified domain')),
   859                        _('update the quota limit of the specified domain')),
   862     'domainservices': cmd('domainservices', 'ds', domain_services,
   860     'domainservices': cmd('domainservices', 'ds', domain_services,
   863                           'fqdn [service ...] [force]',
   861                           'fqdn [service ...] [force]',
   864                           _(u'enables the specified services and disables all '
   862                           _('enables the specified services and disables all '
   865                             u'not specified services of the given domain')),
   863                             'not specified services of the given domain')),
   866     'domaintransport': cmd('domaintransport', 'dt', domain_transport,
   864     'domaintransport': cmd('domaintransport', 'dt', domain_transport,
   867                            'fqdn transport [force]',
   865                            'fqdn transport [force]',
   868                            _(u'update the transport of the specified domain')),
   866                            _('update the transport of the specified domain')),
   869     'domainnote': cmd('domainnote', 'do', domain_note, 'fqdn [note]',
   867     'domainnote': cmd('domainnote', 'do', domain_note, 'fqdn [note]',
   870                      _(u'set, update or delete the note of the given domain')),
   868                      _('set, update or delete the note of the given domain')),
   871     # List commands
   869     # List commands
   872     'listdomains': cmd('listdomains', 'ld', list_domains, '[pattern]',
   870     'listdomains': cmd('listdomains', 'ld', list_domains, '[pattern]',
   873                       _(u'list all domains or search for domains by pattern')),
   871                       _('list all domains or search for domains by pattern')),
   874     'listaddresses': cmd('listaddresses', 'll', list_addresses, '[pattern]',
   872     'listaddresses': cmd('listaddresses', 'll', list_addresses, '[pattern]',
   875                          _(u'list all addresses or search for addresses by '
   873                          _('list all addresses or search for addresses by '
   876                            u'pattern')),
   874                            'pattern')),
   877     'listusers': cmd('listusers', 'lu', list_users, '[pattern]',
   875     'listusers': cmd('listusers', 'lu', list_users, '[pattern]',
   878                      _(u'list all user accounts or search for accounts by '
   876                      _('list all user accounts or search for accounts by '
   879                        u'pattern')),
   877                        'pattern')),
   880     'listaliases': cmd('listaliases', 'la', list_aliases, '[pattern]',
   878     'listaliases': cmd('listaliases', 'la', list_aliases, '[pattern]',
   881                       _(u'list all aliases or search for aliases by pattern')),
   879                       _('list all aliases or search for aliases by pattern')),
   882     'listrelocated': cmd('listrelocated', 'lr', list_relocated, '[pattern]',
   880     'listrelocated': cmd('listrelocated', 'lr', list_relocated, '[pattern]',
   883                          _(u'list all relocated users or search for relocated '
   881                          _('list all relocated users or search for relocated '
   884                            u'users by pattern')),
   882                            'users by pattern')),
   885     # Relocated commands
   883     # Relocated commands
   886     'relocatedadd': cmd('relocatedadd', 'ra', relocated_add,
   884     'relocatedadd': cmd('relocatedadd', 'ra', relocated_add,
   887                         'address newaddress',
   885                         'address newaddress',
   888                         _(u'create a new record for a relocated user')),
   886                         _('create a new record for a relocated user')),
   889     'relocateddelete': cmd('relocateddelete', 'rd', relocated_delete,
   887     'relocateddelete': cmd('relocateddelete', 'rd', relocated_delete,
   890                            'address',
   888                            'address',
   891                            _(u'delete the record of the relocated user')),
   889                            _('delete the record of the relocated user')),
   892     'relocatedinfo': cmd('relocatedinfo', 'ri', relocated_info, 'address',
   890     'relocatedinfo': cmd('relocatedinfo', 'ri', relocated_info, 'address',
   893                          _(u'print information about a relocated user')),
   891                          _('print information about a relocated user')),
   894     # cli commands
   892     # cli commands
   895     'configget': cmd('configget', 'cg', config_get, 'option',
   893     'configget': cmd('configget', 'cg', config_get, 'option',
   896                      _('show the actual value of the configuration option')),
   894                      _('show the actual value of the configuration option')),
   897     'configset': cmd('configset', 'cs', config_set, 'option value',
   895     'configset': cmd('configset', 'cs', config_set, 'option value',
   898                       _('set a new value for the configuration option')),
   896                       _('set a new value for the configuration option')),
   899     'configure': cmd('configure', 'cf', configure, '[section]',
   897     'configure': cmd('configure', 'cf', configure, '[section]',
   900                      _(u'start interactive configuration mode')),
   898                      _('start interactive configuration mode')),
   901     'listpwschemes': cmd('listpwschemes', 'lp', list_pwschemes, '',
   899     'listpwschemes': cmd('listpwschemes', 'lp', list_pwschemes, '',
   902                          _(u'lists all usable password schemes and password '
   900                          _('lists all usable password schemes and password '
   903                            u'encoding suffixes')),
   901                            'encoding suffixes')),
   904     'help': cmd('help', 'h', help_, '[subcommand]',
   902     'help': cmd('help', 'h', help_, '[subcommand]',
   905                 _(u'show a help overview or help for the given subcommand')),
   903                 _('show a help overview or help for the given subcommand')),
   906     'version': cmd('version', 'v', version, '',
   904     'version': cmd('version', 'v', version, '',
   907                    _(u'show version and copyright information')),
   905                    _('show version and copyright information')),
   908     })
   906     })
   909 
   907 
   910 
   908 
   911 def _get_order(ctx):
   909 def _get_order(ctx):
   912     """returns a tuple with (key, 1||0) tuples. Used by functions, which
   910     """returns a tuple with (key, 1||0) tuples. Used by functions, which
   913     get a dict from the handler."""
   911     get a dict from the handler."""
   914     order = ()
   912     order = ()
   915     if ctx.scmd == 'domaininfo':
   913     if ctx.scmd == 'domaininfo':
   916         order = ((u'domain name', 0), (u'gid', 1), (u'domain directory', 0),
   914         order = (('domain name', 0), ('gid', 1), ('domain directory', 0),
   917                  (u'quota limit/user', 0), (u'active services', 0),
   915                  ('quota limit/user', 0), ('active services', 0),
   918                  (u'transport', 0), (u'alias domains', 0), (u'accounts', 0),
   916                  ('transport', 0), ('alias domains', 0), ('accounts', 0),
   919                  (u'aliases', 0), (u'relocated', 0), (u'catch-all dests', 0))
   917                  ('aliases', 0), ('relocated', 0), ('catch-all dests', 0))
   920     elif ctx.scmd == 'userinfo':
   918     elif ctx.scmd == 'userinfo':
   921         if ctx.argc == 4 and ctx.args[3] != u'aliases' or \
   919         if ctx.argc == 4 and ctx.args[3] != 'aliases' or \
   922            ctx.cget('account.disk_usage'):
   920            ctx.cget('account.disk_usage'):
   923             order = ((u'address', 0), (u'name', 0), (u'uid', 1), (u'gid', 1),
   921             order = (('address', 0), ('name', 0), ('uid', 1), ('gid', 1),
   924                      (u'home', 0), (u'mail_location', 0),
   922                      ('home', 0), ('mail_location', 0),
   925                      (u'quota storage', 0), (u'quota messages', 0),
   923                      ('quota storage', 0), ('quota messages', 0),
   926                      (u'disk usage', 0), (u'transport', 0), (u'smtp', 1),
   924                      ('disk usage', 0), ('transport', 0), ('smtp', 1),
   927                      (u'pop3', 1), (u'imap', 1), (u'sieve', 1))
   925                      ('pop3', 1), ('imap', 1), ('sieve', 1))
   928         else:
   926         else:
   929             order = ((u'address', 0), (u'name', 0), (u'uid', 1), (u'gid', 1),
   927             order = (('address', 0), ('name', 0), ('uid', 1), ('gid', 1),
   930                      (u'home', 0), (u'mail_location', 0),
   928                      ('home', 0), ('mail_location', 0),
   931                      (u'quota storage', 0), (u'quota messages', 0),
   929                      ('quota storage', 0), ('quota messages', 0),
   932                      (u'transport', 0), (u'smtp', 1), (u'pop3', 1),
   930                      ('transport', 0), ('smtp', 1), ('pop3', 1),
   933                      (u'imap', 1), (u'sieve', 1))
   931                      ('imap', 1), ('sieve', 1))
   934     elif ctx.scmd == 'getuser':
   932     elif ctx.scmd == 'getuser':
   935         order = ((u'uid', 1), (u'gid', 1), (u'address', 0))
   933         order = (('uid', 1), ('gid', 1), ('address', 0))
   936     return order
   934     return order
   937 
   935 
   938 
   936 
   939 def _format_quota_usage(limit, used, human=False, domaindefault=False):
   937 def _format_quota_usage(limit, used, human=False, domaindefault=False):
   940     """Put quota's limit / usage / percentage in a formatted string."""
   938     """Put quota's limit / usage / percentage in a formatted string."""
   954         q_usage['percent'] = locale.format('%6.2f', 100. / limit * used, True)
   952         q_usage['percent'] = locale.format('%6.2f', 100. / limit * used, True)
   955     else:
   953     else:
   956         q_usage['percent'] = locale.format('%6.2f', 0, True)
   954         q_usage['percent'] = locale.format('%6.2f', 0, True)
   957     fmt = format_domain_default if domaindefault else lambda s: s
   955     fmt = format_domain_default if domaindefault else lambda s: s
   958     # TP: e.g.: [  0.00%] 21.09 KiB/1.00 GiB
   956     # TP: e.g.: [  0.00%] 21.09 KiB/1.00 GiB
   959     return fmt(_(u'[%(percent)s%%] %(used)s/%(limit)s') % q_usage)
   957     return fmt(_('[%(percent)s%%] %(used)s/%(limit)s') % q_usage)
   960 
   958 
   961 
   959 
   962 def _print_info(ctx, info, title):
   960 def _print_info(ctx, info, title):
   963     """Print info dicts."""
   961     """Print info dicts."""
   964     # TP: used in e.g. 'Domain information' or 'Account information'
   962     # TP: used in e.g. 'Domain information' or 'Account information'
   965     msg = u'%s %s' % (title, _(u'information'))
   963     msg = '%s %s' % (title, _('information'))
   966     w_std(msg, u'-' * len(msg))
   964     w_std(msg, '-' * len(msg))
   967     for key, upper in _get_order(ctx):
   965     for key, upper in _get_order(ctx):
   968         if upper:
   966         if upper:
   969             w_std(u'\t%s: %s' % (key.upper().ljust(17, u'.'), info[key]))
   967             w_std('\t%s: %s' % (key.upper().ljust(17, '.'), info[key]))
   970         else:
   968         else:
   971             w_std(u'\t%s: %s' % (key.title().ljust(17, u'.'), info[key]))
   969             w_std('\t%s: %s' % (key.title().ljust(17, '.'), info[key]))
   972     print
   970     print()
   973     note = info.get('note')
   971     note = info.get('note')
   974     if note:
   972     if note:
   975         _print_note(note + '\n')
   973         _print_note(note + '\n')
   976 
   974 
   977 
   975 
   978 def _print_note(note):
   976 def _print_note(note):
   979     msg = _(u'Note')
   977     msg = _('Note')
   980     w_std(msg, u'-' * len(msg))
   978     w_std(msg, '-' * len(msg))
   981     old_ii = txt_wrpr.initial_indent
   979     old_ii = txt_wrpr.initial_indent
   982     old_si = txt_wrpr.subsequent_indent
   980     old_si = txt_wrpr.subsequent_indent
   983     txt_wrpr.initial_indent = txt_wrpr.subsequent_indent = '\t'
   981     txt_wrpr.initial_indent = txt_wrpr.subsequent_indent = '\t'
   984     txt_wrpr.width -= 8
   982     txt_wrpr.width -= 8
   985     for para in note.split('\n'):
   983     for para in note.split('\n'):
   990 
   988 
   991 
   989 
   992 def _print_list(alist, title):
   990 def _print_list(alist, title):
   993     """Print a list."""
   991     """Print a list."""
   994     # TP: used in e.g. 'Existing alias addresses' or 'Existing accounts'
   992     # TP: used in e.g. 'Existing alias addresses' or 'Existing accounts'
   995     msg = u'%s %s' % (_(u'Existing'), title)
   993     msg = '%s %s' % (_('Existing'), title)
   996     w_std(msg, u'-' * len(msg))
   994     w_std(msg, '-' * len(msg))
   997     if alist:
   995     if alist:
   998         if title != _(u'alias domains'):
   996         if title != _('alias domains'):
   999             w_std(*(u'\t%s' % item for item in alist))
   997             w_std(*('\t%s' % item for item in alist))
  1000         else:
   998         else:
  1001             for domain in alist:
   999             for domain in alist:
  1002                 if not domain.startswith('xn--'):
  1000                 if not domain.startswith('xn--'):
  1003                     w_std(u'\t%s' % domain)
  1001                     w_std('\t%s' % domain)
  1004                 else:
  1002                 else:
  1005                     w_std(u'\t%s (%s)' % (domain, domain.decode('idna')))
  1003                     w_std('\t%s (%s)' % (domain, domain.decode('idna')))
  1006         print
  1004         print()
  1007     else:
  1005     else:
  1008         w_std(_(u'\tNone'), '')
  1006         w_std(_('\tNone'), '')
  1009 
  1007 
  1010 
  1008 
  1011 def _print_aliase_info(alias, destinations):
  1009 def _print_aliase_info(alias, destinations):
  1012     """Print the alias address and all its destinations"""
  1010     """Print the alias address and all its destinations"""
  1013     title = _(u'Alias information')
  1011     title = _('Alias information')
  1014     w_std(title, u'-' * len(title))
  1012     w_std(title, '-' * len(title))
  1015     w_std(_(u'\tMail for %s will be redirected to:') % alias)
  1013     w_std(_('\tMail for %s will be redirected to:') % alias)
  1016     w_std(*(u'\t     * %s' % dest for dest in destinations))
  1014     w_std(*('\t     * %s' % dest for dest in destinations))
  1017     print
  1015     print()
  1018 
  1016 
  1019 
  1017 
  1020 def _print_catchall_info(domain, destinations):
  1018 def _print_catchall_info(domain, destinations):
  1021     """Print the catchall destinations of a domain"""
  1019     """Print the catchall destinations of a domain"""
  1022     title = _(u'Catch-all information')
  1020     title = _('Catch-all information')
  1023     w_std(title, u'-' * len(title))
  1021     w_std(title, '-' * len(title))
  1024     w_std(_(u'\tMail to unknown local-parts in domain %s will be sent to:')
  1022     w_std(_('\tMail to unknown local-parts in domain %s will be sent to:')
  1025           % domain)
  1023           % domain)
  1026     w_std(*(u'\t     * %s' % dest for dest in destinations))
  1024     w_std(*('\t     * %s' % dest for dest in destinations))
  1027     print
  1025     print()
  1028 
  1026 
  1029 
  1027 
  1030 def _print_relocated_info(**kwargs):
  1028 def _print_relocated_info(**kwargs):
  1031     """Print the old and new addresses of a relocated user."""
  1029     """Print the old and new addresses of a relocated user."""
  1032     title = _(u'Relocated information')
  1030     title = _('Relocated information')
  1033     w_std(title, u'-' * len(title))
  1031     w_std(title, '-' * len(title))
  1034     w_std(_(u"\tUser '%(addr)s' has moved to '%(dest)s'") % kwargs, '')
  1032     w_std(_("\tUser '%(addr)s' has moved to '%(dest)s'") % kwargs, '')
  1035 
  1033 
  1036 
  1034 
  1037 def _format_domain(domain, main=True):
  1035 def _format_domain(domain, main=True):
  1038     """format (prefix/convert) the domain name."""
  1036     """format (prefix/convert) the domain name."""
  1039     if domain.startswith('xn--'):
  1037     if domain.startswith('xn--'):
  1040         domain = u'%s (%s)' % (domain, domain.decode('idna'))
  1038         domain = '%s (%s)' % (domain, domain.decode('idna'))
  1041     if main:
  1039     if main:
  1042         return u'\t[+] %s' % domain
  1040         return '\t[+] %s' % domain
  1043     return u'\t[-]     %s' % domain
  1041     return '\t[-]     %s' % domain
  1044 
  1042 
  1045 
  1043 
  1046 def _print_domain_list(dids, domains, matching):
  1044 def _print_domain_list(dids, domains, matching):
  1047     """Print a list of (matching) domains/alias domains."""
  1045     """Print a list of (matching) domains/alias domains."""
  1048     title = _(u'Matching domains') if matching else _(u'Existing domains')
  1046     title = _('Matching domains') if matching else _('Existing domains')
  1049     w_std(title, '-' * len(title))
  1047     w_std(title, '-' * len(title))
  1050     if domains:
  1048     if domains:
  1051         for did in dids:
  1049         for did in dids:
  1052             if domains[did][0] is not None:
  1050             if domains[did][0] is not None:
  1053                 w_std(_format_domain(domains[did][0]))
  1051                 w_std(_format_domain(domains[did][0]))
  1054             if len(domains[did]) > 1:
  1052             if len(domains[did]) > 1:
  1055                 w_std(*(_format_domain(a, False) for a in domains[did][1:]))
  1053                 w_std(*(_format_domain(a, False) for a in domains[did][1:]))
  1056     else:
  1054     else:
  1057         w_std(_('\tNone'))
  1055         w_std(_('\tNone'))
  1058     print
  1056     print()
  1059 
  1057 
  1060 
  1058 
  1061 def _print_address_list(which, dids, addresses, matching):
  1059 def _print_address_list(which, dids, addresses, matching):
  1062     """Print a list of (matching) addresses."""
  1060     """Print a list of (matching) addresses."""
  1063     _trans = {
  1061     _trans = {
  1069         TYPE_ALIAS | TYPE_RELOCATED: _('aliases and relocated users'),
  1067         TYPE_ALIAS | TYPE_RELOCATED: _('aliases and relocated users'),
  1070         TYPE_ACCOUNT | TYPE_ALIAS | TYPE_RELOCATED: _('addresses'),
  1068         TYPE_ACCOUNT | TYPE_ALIAS | TYPE_RELOCATED: _('addresses'),
  1071     }
  1069     }
  1072     try:
  1070     try:
  1073         if matching:
  1071         if matching:
  1074             title = _(u'Matching %s') % _trans[which]
  1072             title = _('Matching %s') % _trans[which]
  1075         else:
  1073         else:
  1076             title = _(u'Existing %s') % _trans[which]
  1074             title = _('Existing %s') % _trans[which]
  1077         w_std(title, '-' * len(title))
  1075         w_std(title, '-' * len(title))
  1078     except KeyError:
  1076     except KeyError:
  1079         raise VMMError(_("Invalid address type for list: '%s'") % which,
  1077         raise VMMError(_("Invalid address type for list: '%s'") % which,
  1080                        INVALID_ARGUMENT)
  1078                        INVALID_ARGUMENT)
  1081     if addresses:
  1079     if addresses:
  1097                 else:
  1095                 else:
  1098                     leader = '[%s+]' % _trans[atype]
  1096                     leader = '[%s+]' % _trans[atype]
  1099                 w_std('\t%s %s' % (leader, addr))
  1097                 w_std('\t%s %s' % (leader, addr))
  1100     else:
  1098     else:
  1101         w_std(_('\tNone'))
  1099         w_std(_('\tNone'))
  1102     print
  1100     print()
  1103 
  1101 
  1104 
  1102 
  1105 def _print_aliasdomain_info(info):
  1103 def _print_aliasdomain_info(info):
  1106     """Print alias domain information."""
  1104     """Print alias domain information."""
  1107     title = _(u'Alias domain information')
  1105     title = _('Alias domain information')
  1108     for key in ('alias', 'domain'):
  1106     for key in ('alias', 'domain'):
  1109         if info[key].startswith('xn--'):
  1107         if info[key].startswith('xn--'):
  1110             info[key] = u'%s (%s)' % (info[key], info[key].decode('idna'))
  1108             info[key] = '%s (%s)' % (info[key], info[key].decode('idna'))
  1111     w_std(title, '-' * len(title),
  1109     w_std(title, '-' * len(title),
  1112           _('\tThe alias domain %(alias)s belongs to:\n\t    * %(domain)s') %
  1110           _('\tThe alias domain %(alias)s belongs to:\n\t    * %(domain)s') %
  1113           info, '')
  1111           info, '')
  1114 
  1112 
  1115 del _
  1113 del _