|
1 # -*- coding: UTF-8 -*- |
|
2 # Copyright 2007 - 2010, Pascal Volk |
|
3 # See COPYING for distribution information. |
|
4 """ |
|
5 VirtualMailManager.cli.subcommands |
|
6 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
7 |
|
8 VirtualMailManager's cli subcommands. |
|
9 """ |
|
10 |
|
11 import locale |
|
12 import os |
|
13 |
|
14 from textwrap import TextWrapper |
|
15 from time import strftime, strptime |
|
16 |
|
17 from VirtualMailManager import ENCODING |
|
18 from VirtualMailManager.account import SERVICES |
|
19 from VirtualMailManager.cli import get_winsize, prog, w_err, w_std |
|
20 from VirtualMailManager.constants import __copyright__, __date__, \ |
|
21 __version__, ACCOUNT_EXISTS, ALIAS_EXISTS, ALIASDOMAIN_ISDOMAIN, \ |
|
22 DOMAIN_ALIAS_EXISTS, INVALID_ARGUMENT, EX_MISSING_ARGS, RELOCATED_EXISTS |
|
23 from VirtualMailManager.errors import VMMError |
|
24 |
|
25 __all__ = ( |
|
26 'Command', 'RunContext', 'cmd_map', 'usage', 'alias_add', 'alias_delete', |
|
27 'alias_info', 'aliasdomain_add', 'aliasdomain_delete', 'aliasdomain_info', |
|
28 'aliasdomain_switch', 'configure', 'domain_add', 'domain_delete', |
|
29 'domain_info', 'domain_transport', 'get_user', 'help_', 'list_domains', |
|
30 'relocated_add', 'relocated_delete', 'relocated_info', 'user_add', |
|
31 'user_delete', 'user_disable', 'user_enable', 'user_info', 'user_name', |
|
32 'user_password', 'user_transport', 'version', |
|
33 ) |
|
34 |
|
35 _ = lambda msg: msg |
|
36 txt_wrpr = TextWrapper(width=get_winsize()[1] - 1) |
|
37 |
|
38 |
|
39 class Command(object): |
|
40 """Container class for command information.""" |
|
41 __slots__ = ('name', 'alias', 'func', 'args', 'descr') |
|
42 |
|
43 def __init__(self, name, alias, func, args, descr): |
|
44 """Create a new Command instance. |
|
45 |
|
46 Arguments: |
|
47 |
|
48 `name` : str |
|
49 the command name, e.g. ``addalias`` |
|
50 `alias` : str |
|
51 the command's short alias, e.g. ``aa`` |
|
52 `func` : callable |
|
53 the function to handle the command |
|
54 `args` : str |
|
55 argument placeholders, e.g. ``aliasaddress`` |
|
56 `descr` : str |
|
57 short description of the command |
|
58 """ |
|
59 self.name = name |
|
60 self.alias = alias |
|
61 self.func = func |
|
62 self.args = args |
|
63 self.descr = descr |
|
64 |
|
65 @property |
|
66 def usage(self): |
|
67 """the command's usage info.""" |
|
68 return u'%s %s %s' % (prog, self.name, self.args) |
|
69 |
|
70 |
|
71 class RunContext(object): |
|
72 """Contains all information necessary to run a subcommand.""" |
|
73 __slots__ = ('argc', 'args', 'cget', 'hdlr', 'scmd') |
|
74 plan_a_b = _(u'Plan A failed ... trying Plan B: %(subcommand)s %(object)s') |
|
75 |
|
76 def __init__(self, argv, handler, command): |
|
77 """Create a new RunContext""" |
|
78 self.argc = len(argv) |
|
79 self.args = [unicode(arg, ENCODING) for arg in argv] |
|
80 self.cget = handler.cfg_dget |
|
81 self.hdlr = handler |
|
82 self.scmd = command |
|
83 |
|
84 |
|
85 def alias_add(ctx): |
|
86 """create a new alias e-mail address""" |
|
87 if ctx.argc < 3: |
|
88 usage(EX_MISSING_ARGS, _(u'Missing alias address and destination.'), |
|
89 ctx.scmd) |
|
90 elif ctx.argc < 4: |
|
91 usage(EX_MISSING_ARGS, _(u'Missing destination address.'), ctx.scmd) |
|
92 ctx.hdlr.alias_add(ctx.args[2].lower(), *ctx.args[3:]) |
|
93 |
|
94 |
|
95 def alias_delete(ctx): |
|
96 """delete the specified alias e-mail address or one of its destinations""" |
|
97 if ctx.argc < 3: |
|
98 usage(EX_MISSING_ARGS, _(u'Missing alias address.'), ctx.scmd) |
|
99 elif ctx.argc < 4: |
|
100 ctx.hdlr.alias_delete(ctx.args[2].lower()) |
|
101 else: |
|
102 ctx.hdlr.alias_delete(ctx.args[2].lower(), ctx.args[3]) |
|
103 |
|
104 |
|
105 def alias_info(ctx): |
|
106 """show the destination(s) of the specified alias""" |
|
107 if ctx.argc < 3: |
|
108 usage(EX_MISSING_ARGS, _(u'Missing alias address.'), ctx.scmd) |
|
109 address = ctx.args[2].lower() |
|
110 try: |
|
111 _print_aliase_info(address, ctx.hdlr.alias_info(address)) |
|
112 except VMMError, err: |
|
113 if err.code is ACCOUNT_EXISTS: |
|
114 w_err(0, ctx.plan_a_b % {'subcommand': u'userinfo', |
|
115 'object': address}) |
|
116 ctx.args[1] = 'userinfo' |
|
117 user_info(ctx) |
|
118 elif err.code is RELOCATED_EXISTS: |
|
119 w_std(0, ctx.plan_a_b % {'subcommand': u'relocatedinfo', |
|
120 'object': address}) |
|
121 ctx.args[1] = 'relocatedinfo' |
|
122 relocated_info(ctx) |
|
123 else: |
|
124 raise |
|
125 |
|
126 |
|
127 def aliasdomain_add(ctx): |
|
128 """create a new alias for an existing domain""" |
|
129 if ctx.argc < 3: |
|
130 usage(EX_MISSING_ARGS, _(u'Missing alias domain name and destination ' |
|
131 u'domain name.'), ctx.scmd) |
|
132 elif ctx.argc < 4: |
|
133 usage(EX_MISSING_ARGS, _(u'Missing destination domain name.'), |
|
134 ctx.scmd) |
|
135 ctx.hdlr.aliasdomain_add(ctx.args[2].lower(), ctx.args[3].lower()) |
|
136 |
|
137 |
|
138 def aliasdomain_delete(ctx): |
|
139 """delete the specified alias domain""" |
|
140 if ctx.argc < 3: |
|
141 usage(EX_MISSING_ARGS, _(u'Missing alias domain name.'), ctx.scmd) |
|
142 ctx.hdlr.aliasdomain_delete(ctx.args[2].lower()) |
|
143 |
|
144 |
|
145 def aliasdomain_info(ctx): |
|
146 """show the destination of the given alias domain""" |
|
147 if ctx.argc < 3: |
|
148 usage(EX_MISSING_ARGS, _(u'Missing alias domain name.'), ctx.scmd) |
|
149 try: |
|
150 _print_aliasdomain_info(ctx.hdlr.aliasdomain_info(ctx.args[2].lower())) |
|
151 except VMMError, err: |
|
152 if err.code is ALIASDOMAIN_ISDOMAIN: |
|
153 w_err(0, ctx.plan_a_b % {'subcommand': u'domaininfo', |
|
154 'object': ctx.args[2].lower()}) |
|
155 ctx.args[1] = 'domaininfo' |
|
156 domain_info(ctx) |
|
157 else: |
|
158 raise |
|
159 |
|
160 |
|
161 def aliasdomain_switch(ctx): |
|
162 """assign the given alias domain to an other domain""" |
|
163 if ctx.argc < 3: |
|
164 usage(EX_MISSING_ARGS, _(u'Missing alias domain name and destination ' |
|
165 u'domain name.'), ctx.scmd) |
|
166 elif ctx.argc < 4: |
|
167 usage(EX_MISSING_ARGS, _(u'Missing destination domain name.'), |
|
168 ctx.scmd) |
|
169 ctx.hdlr.aliasdomain_switch(ctx.args[2].lower(), ctx.args[3].lower()) |
|
170 |
|
171 |
|
172 def configure(ctx): |
|
173 """start interactive configuration modus""" |
|
174 if ctx.argc < 3: |
|
175 ctx.hdlr.configure() |
|
176 else: |
|
177 ctx.hdlr.configure(ctx.args[2].lower()) |
|
178 |
|
179 |
|
180 def domain_add(ctx): |
|
181 """create a new domain""" |
|
182 if ctx.argc < 3: |
|
183 usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd) |
|
184 elif ctx.argc < 4: |
|
185 ctx.hdlr.domain_add(ctx.args[2].lower()) |
|
186 else: |
|
187 ctx.hdlr.domain_add(ctx.args[2].lower(), ctx.args[3]) |
|
188 |
|
189 |
|
190 def domain_delete(ctx): |
|
191 """delete the given domain and all its alias domains""" |
|
192 if ctx.argc < 3: |
|
193 usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd) |
|
194 elif ctx.argc < 4: |
|
195 ctx.hdlr.domain_delete(ctx.args[2].lower()) |
|
196 elif ctx.args[3].lower() == 'force': |
|
197 ctx.hdlr.domain_delete(ctx.args[2].lower(), True) |
|
198 else: |
|
199 usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % ctx.args[3], |
|
200 ctx.scmd) |
|
201 |
|
202 |
|
203 def domain_info(ctx): |
|
204 """display information about the given domain""" |
|
205 if ctx.argc < 3: |
|
206 usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd) |
|
207 if ctx.argc < 4: |
|
208 details = None |
|
209 else: |
|
210 details = ctx.args[3].lower() |
|
211 if details not in ('accounts', 'aliasdomains', 'aliases', 'full', |
|
212 'relocated'): |
|
213 usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % details, |
|
214 ctx.scmd) |
|
215 try: |
|
216 info = ctx.hdlr.domain_info(ctx.args[2].lower(), details) |
|
217 except VMMError, err: |
|
218 if err.code is DOMAIN_ALIAS_EXISTS: |
|
219 w_err(0, ctx.plan_a_b % {'subcommand': u'aliasdomaininfo', |
|
220 'object': ctx.args[2].lower()}) |
|
221 ctx.args[1] = 'aliasdomaininfo' |
|
222 aliasdomain_info(ctx) |
|
223 else: |
|
224 raise |
|
225 else: |
|
226 if not details: |
|
227 _print_info(ctx, info, _(u'Domain')) |
|
228 else: |
|
229 _print_info(ctx, info[0], _(u'Domain')) |
|
230 if details == u'accounts': |
|
231 _print_list(info[1], _(u'accounts')) |
|
232 elif details == u'aliasdomains': |
|
233 _print_list(info[1], _(u'alias domains')) |
|
234 elif details == u'aliases': |
|
235 _print_list(info[1], _(u'aliases')) |
|
236 elif details == u'relocated': |
|
237 _print_list(info[1], _(u'relocated users')) |
|
238 else: |
|
239 _print_list(info[1], _(u'alias domains')) |
|
240 _print_list(info[2], _(u'accounts')) |
|
241 _print_list(info[3], _(u'aliases')) |
|
242 _print_list(info[4], _(u'relocated users')) |
|
243 |
|
244 |
|
245 def domain_transport(ctx): |
|
246 """update the transport of the specified domain""" |
|
247 if ctx.argc < 3: |
|
248 usage(EX_MISSING_ARGS, _(u'Missing domain name and new transport.'), |
|
249 ctx.scmd) |
|
250 if ctx.argc < 4: |
|
251 usage(EX_MISSING_ARGS, _(u'Missing new transport.'), ctx.scmd) |
|
252 if ctx.argc < 5: |
|
253 ctx.hdlr.domain_transport(ctx.args[2].lower(), ctx.args[3]) |
|
254 else: |
|
255 force = ctx.args[4].lower() |
|
256 if force != 'force': |
|
257 usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % force, |
|
258 ctx.scmd) |
|
259 ctx.hdlr.domain_transport(ctx.args[2].lower(), ctx.args[3], force) |
|
260 |
|
261 |
|
262 def get_user(ctx): |
|
263 """get the address of the user with the given UID""" |
|
264 if ctx.argc < 3: |
|
265 usage(EX_MISSING_ARGS, _(u'Missing userid.'), ctx.scmd) |
|
266 _print_info(ctx, ctx.hdlr.user_by_uid(ctx.args[2]), _(u'Account')) |
|
267 |
|
268 |
|
269 def help_(ctx): |
|
270 """print help messgaes.""" |
|
271 if ctx.argc > 2: |
|
272 hlptpc = ctx.args[2].lower() |
|
273 if hlptpc in cmd_map: |
|
274 topic = hlptpc |
|
275 else: |
|
276 for scmd in cmd_map.itervalues(): |
|
277 if scmd.alias == hlptpc: |
|
278 topic = scmd.name |
|
279 break |
|
280 else: |
|
281 usage(INVALID_ARGUMENT, _(u"Unknown help topic: '%s'") % |
|
282 ctx.args[2], ctx.scmd) |
|
283 # FIXME |
|
284 w_err(1, "'help %s' not yet implemented." % topic, 'see also: vmm(1)') |
|
285 |
|
286 old_ii = txt_wrpr.initial_indent |
|
287 old_si = txt_wrpr.subsequent_indent |
|
288 txt_wrpr.initial_indent = ' ' |
|
289 # len(max(_overview.iterkeys(), key=len)) #Py25 |
|
290 txt_wrpr.subsequent_indent = 20 * ' ' |
|
291 order = cmd_map.keys() |
|
292 order.sort() |
|
293 |
|
294 w_std(_(u'List of available subcommands:') + '\n') |
|
295 for key in order: |
|
296 w_std('\n'.join(txt_wrpr.wrap('%-18s %s' % (key, cmd_map[key].descr)))) |
|
297 |
|
298 txt_wrpr.initial_indent = old_ii |
|
299 txt_wrpr.subsequent_indent = old_si |
|
300 txt_wrpr.initial_indent = '' |
|
301 |
|
302 |
|
303 def list_domains(ctx): |
|
304 """list all domains / search domains by pattern""" |
|
305 matching = ctx.argc > 2 |
|
306 if matching: |
|
307 gids, domains = ctx.hdlr.domain_list(ctx.args[2].lower()) |
|
308 else: |
|
309 gids, domains = ctx.hdlr.domain_list() |
|
310 _print_domain_list(gids, domains, matching) |
|
311 |
|
312 |
|
313 def relocated_add(ctx): |
|
314 """create a new record for a relocated user""" |
|
315 if ctx.argc < 3: |
|
316 usage(EX_MISSING_ARGS, |
|
317 _(u'Missing relocated address and destination.'), ctx.scmd) |
|
318 elif ctx.argc < 4: |
|
319 usage(EX_MISSING_ARGS, _(u'Missing destination address.'), ctx.scmd) |
|
320 ctx.hdlr.relocated_add(ctx.args[2].lower(), ctx.args[3]) |
|
321 |
|
322 |
|
323 def relocated_delete(ctx): |
|
324 """delete the record of the relocated user""" |
|
325 if ctx.argc < 3: |
|
326 usage(EX_MISSING_ARGS, _(u'Missing relocated address.'), ctx.scmd) |
|
327 ctx.hdlr.relocated_delete(ctx.args[2].lower()) |
|
328 |
|
329 |
|
330 def relocated_info(ctx): |
|
331 """print information about a relocated user""" |
|
332 if ctx.argc < 3: |
|
333 usage(EX_MISSING_ARGS, _(u'Missing relocated address.'), ctx.scmd) |
|
334 relocated = ctx.args[2].lower() |
|
335 try: |
|
336 _print_relocated_info(addr=relocated, |
|
337 dest=ctx.hdlr.relocated_info(relocated)) |
|
338 except VMMError, err: |
|
339 if err.code is ACCOUNT_EXISTS: |
|
340 w_err(0, ctx.plan_a_b % {'subcommand': u'userinfo', |
|
341 'object': relocated}) |
|
342 ctx.args[1] = 'userinfoi' |
|
343 user_info(ctx) |
|
344 elif err.code is ALIAS_EXISTS: |
|
345 w_err(0, ctx.plan_a_b % {'subcommand': u'aliasinfo', |
|
346 'object': relocated}) |
|
347 ctx.args[1] = 'aliasinfo' |
|
348 alias_info(ctx) |
|
349 else: |
|
350 raise |
|
351 |
|
352 |
|
353 def user_add(ctx): |
|
354 """create a new e-mail user with the given address""" |
|
355 if ctx.argc < 3: |
|
356 usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd) |
|
357 elif ctx.argc < 4: |
|
358 password = None |
|
359 else: |
|
360 password = ctx.args[3] |
|
361 ctx.hdlr.user_add(ctx.args[2].lower(), password) |
|
362 |
|
363 |
|
364 def user_delete(ctx): |
|
365 """delete the specified user""" |
|
366 if ctx.argc < 3: |
|
367 usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd) |
|
368 elif ctx.argc < 4: |
|
369 ctx.hdlr.user_delete(ctx.args[2].lower()) |
|
370 elif ctx.args[3].lower() == 'force': |
|
371 ctx.hdlr.user_delete(ctx.args[2].lower(), True) |
|
372 else: |
|
373 usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % ctx.args[3], |
|
374 ctx.scmd) |
|
375 |
|
376 |
|
377 def user_disable(ctx): |
|
378 """deactivate all/the given service(s) for a user""" |
|
379 if ctx.argc < 3: |
|
380 usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd) |
|
381 elif ctx.argc < 4: |
|
382 ctx.hdlr.user_disable(ctx.args[2].lower()) |
|
383 else: |
|
384 services = [service.lower() for service in ctx.args[3:]] |
|
385 unknown = [service for service in services if service not in SERVICES] |
|
386 if unknown: |
|
387 usage(INVALID_ARGUMENT, _(u"Invalid service arguments: %s") % |
|
388 ' '.join(unknown), ctx.scmd) |
|
389 ctx.hdlr.user_disable(ctx.args[2].lower(), services) |
|
390 |
|
391 |
|
392 def user_enable(ctx): |
|
393 """activate all or the given service(s) for a user""" |
|
394 if ctx.argc < 3: |
|
395 usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd) |
|
396 elif ctx.argc < 4: |
|
397 ctx.hdlr.user_enable(ctx.args[2].lower()) |
|
398 else: |
|
399 services = [service.lower() for service in ctx.args[3:]] |
|
400 unknown = [service for service in services if service not in SERVICES] |
|
401 if unknown: |
|
402 usage(INVALID_ARGUMENT, _(u"Invalid service arguments: %s") % |
|
403 ' '.join(unknown), ctx.scmd) |
|
404 ctx.hdlr.user_enable(ctx.args[2].lower(), services) |
|
405 |
|
406 |
|
407 def user_info(ctx): |
|
408 """display information about the given address""" |
|
409 if ctx.argc < 3: |
|
410 usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd) |
|
411 if ctx.argc < 4: |
|
412 details = None |
|
413 else: |
|
414 details = ctx.args[3].lower() |
|
415 if details not in ('aliases', 'du', 'full'): |
|
416 usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % details, |
|
417 ctx.scmd) |
|
418 try: |
|
419 info = ctx.hdlr.user_info(ctx.args[2].lower(), details) |
|
420 except VMMError, err: |
|
421 if err.code is ALIAS_EXISTS: |
|
422 w_err(0, ctx.plan_a_b % {'subcommand': u'aliasinfo', |
|
423 'object': ctx.args[2].lower()}) |
|
424 ctx.args[1] = 'aliasinfo' |
|
425 alias_info(ctx) |
|
426 elif err.code is RELOCATED_EXISTS: |
|
427 w_err(0, ctx.plan_a_b % {'subcommand': u'relocatedinfo', |
|
428 'object': ctx.args[2].lower()}) |
|
429 ctx.args[1] = 'relocatedinfo' |
|
430 relocated_info(ctx) |
|
431 else: |
|
432 raise |
|
433 else: |
|
434 if details in (None, 'du'): |
|
435 _print_info(ctx, info, _(u'Account')) |
|
436 else: |
|
437 _print_info(ctx, info[0], _(u'Account')) |
|
438 _print_list(info[1], _(u'alias addresses')) |
|
439 |
|
440 |
|
441 def user_name(ctx): |
|
442 """set or update the real name for an address""" |
|
443 if ctx.argc < 3: |
|
444 usage(EX_MISSING_ARGS, _(u'Missing e-mail address and user’s name.'), |
|
445 ctx.scmd) |
|
446 if ctx.argc < 4: |
|
447 usage(EX_MISSING_ARGS, _(u'Missing user’s name.'), ctx.scmd) |
|
448 ctx.hdlr.user_name(ctx.args[2].lower(), ctx.args[3]) |
|
449 |
|
450 |
|
451 def user_password(ctx): |
|
452 """update the password for the given address""" |
|
453 if ctx.argc < 3: |
|
454 usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd) |
|
455 elif ctx.argc < 4: |
|
456 password = None |
|
457 else: |
|
458 password = ctx.args[3] |
|
459 ctx.hdlr.user_password(ctx.args[2].lower(), password) |
|
460 |
|
461 |
|
462 def user_transport(ctx): |
|
463 """update the transport of the given address""" |
|
464 if ctx.argc < 3: |
|
465 usage(EX_MISSING_ARGS, _(u'Missing e-mail address and transport.'), |
|
466 ctx.scmd) |
|
467 if ctx.argc < 4: |
|
468 usage(EX_MISSING_ARGS, _(u'Missing transport.'), ctx.scmd) |
|
469 ctx.hdlr.user_transport(ctx.args[2].lower(), ctx.args[3]) |
|
470 |
|
471 |
|
472 def usage(errno, errmsg, subcommand=None): |
|
473 """print usage message for the given command or all commands. |
|
474 When errno > 0, sys,exit(errno) will interrupt the program. |
|
475 """ |
|
476 if subcommand and subcommand in cmd_map: |
|
477 w_err(errno, _(u"Error: %s") % errmsg, |
|
478 _(u"usage: ") + cmd_map[subcommand].usage) |
|
479 |
|
480 # TP: Please adjust translated words like the original text. |
|
481 # (It's a table header.) Extract from usage text: |
|
482 # usage: vmm subcommand arguments |
|
483 # short long |
|
484 # subcommand arguments |
|
485 # |
|
486 # da domainadd fqdn [transport] |
|
487 # dd domaindelete fqdn [force] |
|
488 u_head = _(u"""usage: %s subcommand arguments |
|
489 short long |
|
490 subcommand arguments\n""") % prog |
|
491 order = cmd_map.keys() |
|
492 order.sort() |
|
493 w_err(0, u_head) |
|
494 for key in order: |
|
495 scmd = cmd_map[key] |
|
496 w_err(0, ' %-5s %-19s %s' % (scmd.alias, scmd.name, scmd.args)) |
|
497 w_err(errno, '', _(u"Error: %s") % errmsg) |
|
498 |
|
499 |
|
500 def version(ctx): |
|
501 """Write version and copyright information to stdout.""" |
|
502 w_std('%s, %s %s (%s %s)\nPython %s %s %s\n\n%s\n%s %s' % (prog, |
|
503 # TP: The words 'from', 'version' and 'on' are used in |
|
504 # the version information, e.g.: |
|
505 # vmm, version 0.5.2 (from 09/09/09) |
|
506 # Python 2.5.4 on FreeBSD |
|
507 _(u'version'), __version__, _(u'from'), |
|
508 strftime(locale.nl_langinfo(locale.D_FMT), |
|
509 strptime(__date__, '%Y-%m-%d')).decode(ENCODING, 'replace'), |
|
510 os.sys.version.split()[0], _(u'on'), os.uname()[0], |
|
511 __copyright__, prog, |
|
512 _(u'is free software and comes with ABSOLUTELY NO WARRANTY.'))) |
|
513 |
|
514 cmd = Command |
|
515 cmd_map = { # {{{ |
|
516 # Account commands |
|
517 'getuser': cmd('getuser', 'gu', get_user, 'uid', |
|
518 _(u'get the address of the user with the given UID')), |
|
519 'useradd': cmd('useradd', 'ua', user_add, 'address [password]', |
|
520 _(u'create a new e-mail user with the given address')), |
|
521 'userdelete': cmd('userdelete', 'ud', user_delete, 'address [force]', |
|
522 _(u'delete the specified user')), |
|
523 'userdisable': cmd('userdisable', 'u0', user_disable, |
|
524 'address [service ...]', |
|
525 _(u'deactivate all/the given service(s) for a user')), |
|
526 'userenable': cmd('userenable', 'u1', user_enable, 'address [service ...]', |
|
527 _(u'activate all or the given service(s) for a user')), |
|
528 'userinfo': cmd('userinfo', 'ui', user_info, 'address [details]', |
|
529 _(u'display information about the given address')), |
|
530 'username': cmd('username', 'un', user_name, 'address name', |
|
531 _(u'set or update the real name for an address')), |
|
532 'userpassword': cmd('userpassword', 'up', user_password, |
|
533 'address [password]', |
|
534 _(u'update the password for the given address')), |
|
535 'usertransport': cmd('usertransport', 'ut', user_transport, |
|
536 'address transport', |
|
537 _(u'update the transport of the given address')), |
|
538 # Alias commands |
|
539 'aliasadd': cmd('aliasadd', 'aa', alias_add, 'address destination ...', |
|
540 _(u'create a new alias e-mail address')), |
|
541 'aliasdelete': cmd('aliasdelete', 'ad', alias_delete, |
|
542 'address [destination]', |
|
543 _(u'delete the specified alias e-mail address or one ' |
|
544 u'of its destinations')), |
|
545 'aliasinfo': cmd('aliasinfo', 'ai', alias_info, 'address', |
|
546 _(u'show the destination(s) of the specified alias')), |
|
547 # AliasDomain commands |
|
548 'aliasdomainadd': cmd('aliasdomainadd', 'ada', aliasdomain_add, |
|
549 'fqdn destination', |
|
550 _(u'create a new alias for an existing domain')), |
|
551 'aliasdomaindelete': cmd('aliasdomaindelete', 'add', aliasdomain_delete, |
|
552 'fqdn', _(u'delete the specified alias domain')), |
|
553 'aliasdomaininfo': cmd('aliasdomaininfo', 'adi', aliasdomain_info, 'fqdn', |
|
554 _(u'show the destination of the given alias domain')), |
|
555 'aliasdomainswitch': cmd('aliasdomainswitch', 'ads', aliasdomain_switch, |
|
556 'fqdn destination', |
|
557 _(u'assign the given alias domain to an other domain')), |
|
558 # Domain commands |
|
559 'domainadd': cmd('domainadd', 'da', domain_add, 'fqdn [transport]', |
|
560 _(u'create a new domain')), |
|
561 'domaindelete': cmd('domaindelete', 'dd', domain_delete, 'fqdn [force]', |
|
562 _(u'delete the given domain and all its alias domains')), |
|
563 'domaininfo': cmd('domaininfo', 'di', domain_info, 'fqdn [details]', |
|
564 _(u'display information about the given domain')), |
|
565 'domaintransport': cmd('domaintransport', 'dt', domain_transport, |
|
566 'fqdn transport [force]', |
|
567 _(u'update the transport of the specified domain')), |
|
568 'listdomains': cmd('listdomains', 'ld', list_domains, '[pattern]', |
|
569 _(u'list all domains / search domains by pattern')), |
|
570 # Relocated commands |
|
571 'relocatedadd': cmd('relocatedadd', 'ra', relocated_add, |
|
572 'address newaddress', |
|
573 _(u'create a new record for a relocated user')), |
|
574 'relocateddelete': cmd('relocateddelete', 'rd', relocated_delete, |
|
575 'address', |
|
576 _(u'delete the record of the relocated user')), |
|
577 'relocatedinfo': cmd('relocatedinfo', 'ri', relocated_info, 'address', |
|
578 _(u'print information about a relocated user')), |
|
579 # cli commands |
|
580 'configure': cmd('configure', 'cf', configure, '[section]', |
|
581 _(u'start interactive configuration modus')), |
|
582 'help': cmd('help', 'h', help_, '[subcommand]', |
|
583 _(u'show a help overview or help for the given subcommand')), |
|
584 'version': cmd('version', 'v', version, '', |
|
585 _(u'show version and copyright information')), |
|
586 } # }}} |
|
587 |
|
588 |
|
589 def _get_order(ctx): |
|
590 """returns a tuple with (key, 1||0) tuples. Used by functions, which |
|
591 get a dict from the handler.""" |
|
592 order = () |
|
593 if ctx.scmd == 'domaininfo': |
|
594 order = ((u'domainname', 0), (u'gid', 1), (u'transport', 0), |
|
595 (u'domaindir', 0), (u'aliasdomains', 0), (u'accounts', 0), |
|
596 (u'aliases', 0), (u'relocated', 0)) |
|
597 elif ctx.scmd == 'userinfo': |
|
598 dc12 = ctx.cget('misc.dovecot_version') >= 0x10200b02 |
|
599 sieve = (u'managesieve', u'sieve')[dc12] |
|
600 if ctx.argc == 4 and ctx.args[3] != u'aliases' or \ |
|
601 ctx.cget('account.disk_usage'): |
|
602 order = ((u'address', 0), (u'name', 0), (u'uid', 1), (u'gid', 1), |
|
603 (u'home', 0), (u'mail_location', 0), (u'disk usage', 0), |
|
604 (u'transport', 0), (u'smtp', 1), (u'pop3', 1), |
|
605 (u'imap', 1), (sieve, 1)) |
|
606 else: |
|
607 order = ((u'address', 0), (u'name', 0), (u'uid', 1), (u'gid', 1), |
|
608 (u'home', 0), (u'mail_location', 0), (u'transport', 0), |
|
609 (u'smtp', 1), (u'pop3', 1), (u'imap', 1), (sieve, 1)) |
|
610 elif ctx.scmd == 'getuser': |
|
611 order = ((u'uid', 1), (u'gid', 1), (u'address', 0)) |
|
612 return order |
|
613 |
|
614 |
|
615 def _print_info(ctx, info, title): |
|
616 """Print info dicts.""" |
|
617 # TP: used in e.g. 'Domain information' or 'Account information' |
|
618 msg = u'%s %s' % (title, _(u'information')) |
|
619 w_std(msg, u'-' * len(msg)) |
|
620 for key, upper in _get_order(ctx): |
|
621 if upper: |
|
622 w_std(u'\t%s: %s' % (key.upper().ljust(15, u'.'), info[key])) |
|
623 else: |
|
624 w_std(u'\t%s: %s' % (key.title().ljust(15, u'.'), info[key])) |
|
625 print |
|
626 |
|
627 |
|
628 def _print_list(alist, title): |
|
629 """Print a list.""" |
|
630 # TP: used in e.g. 'Available alias addresses' or 'Available accounts' |
|
631 msg = u'%s %s' % (_(u'Available'), title) |
|
632 w_std(msg, u'-' * len(msg)) |
|
633 if alist: |
|
634 if title != _(u'alias domains'): |
|
635 w_std(*(u'\t%s' % item for item in alist)) |
|
636 else: |
|
637 for domain in alist: |
|
638 if not domain.startswith('xn--'): |
|
639 w_std(u'\t%s' % domain) |
|
640 else: |
|
641 w_std(u'\t%s (%s)' % (domain, domain.decode('idna'))) |
|
642 print |
|
643 else: |
|
644 w_std(_(u'\tNone'), '') |
|
645 |
|
646 |
|
647 def _print_aliase_info(alias, destinations): |
|
648 """Print the alias address and all its destinations""" |
|
649 title = _(u'Alias information') |
|
650 w_std(title, u'-' * len(title)) |
|
651 w_std(_(u'\tMail for %s will be redirected to:') % alias) |
|
652 w_std(*(u'\t * %s' % dest for dest in destinations)) |
|
653 print |
|
654 |
|
655 |
|
656 def _print_relocated_info(**kwargs): |
|
657 """Print the old and new addresses of a relocated user.""" |
|
658 title = _(u'Relocated information') |
|
659 w_std(title, u'-' * len(title)) |
|
660 w_std(_(u"\tUser '%(addr)s' has moved to '%(dest)s'") % kwargs, '') |
|
661 |
|
662 |
|
663 def _format_domain(domain, main=True): |
|
664 """format (prefix/convert) the domain name.""" |
|
665 if domain.startswith('xn--'): |
|
666 domain = u'%s (%s)' % (domain, domain.decode('idna')) |
|
667 if main: |
|
668 return u'\t[+] %s' % domain |
|
669 return u'\t[-] %s' % domain |
|
670 |
|
671 |
|
672 def _print_domain_list(dids, domains, matching): |
|
673 """Print a list of (matching) domains/alias domains.""" |
|
674 if matching: |
|
675 title = _(u'Matching domains') |
|
676 else: |
|
677 title = _(u'Available domains') |
|
678 w_std(title, '-' * len(title)) |
|
679 if domains: |
|
680 for did in dids: |
|
681 if domains[did][0] is not None: |
|
682 w_std(_format_domain(domains[did][0])) |
|
683 if len(domains[did]) > 1: |
|
684 w_std(*(_format_domain(a, False) for a in domains[did][1:])) |
|
685 else: |
|
686 w_std(_('\tNone')) |
|
687 print |
|
688 |
|
689 |
|
690 def _print_aliasdomain_info(info): |
|
691 """Print alias domain information.""" |
|
692 title = _(u'Alias domain information') |
|
693 for key in ('alias', 'domain'): |
|
694 if info[key].startswith('xn--'): |
|
695 info[key] = u'%s (%s)' % (info[key], info[key].decode('idna')) |
|
696 w_std(title, '-' * len(title), |
|
697 _('\tThe alias domain %(alias)s belongs to:\n\t * %(domain)s') % |
|
698 info, '') |
|
699 |
|
700 del _ |