|
1 # -*- coding: UTF-8 -*- |
|
2 # Copyright (c) 2010 - 2012, Pascal Volk |
|
3 # See COPYING for distribution information. |
|
4 """ |
|
5 VirtualMailManager.cli |
|
6 ~~~~~~~~~~~~~~~~~~~~~~ |
|
7 |
|
8 VirtualMailManager's command line interface. |
|
9 """ |
|
10 |
|
11 import os |
|
12 from array import array |
|
13 from fcntl import ioctl |
|
14 from getpass import getpass |
|
15 from termios import TIOCGWINSZ |
|
16 |
|
17 from VirtualMailManager import ENCODING |
|
18 from VirtualMailManager.constants import VMM_TOO_MANY_FAILURES |
|
19 from VirtualMailManager.errors import VMMError |
|
20 |
|
21 |
|
22 __all__ = ('prog', 'get_winsize', 'read_pass', 'w_err', 'w_std') |
|
23 |
|
24 _ = lambda msg: msg |
|
25 _std_write = os.sys.stdout.write |
|
26 _err_write = os.sys.stderr.write |
|
27 prog = os.path.basename(os.sys.argv[0]) |
|
28 |
|
29 |
|
30 def w_std(*args): |
|
31 """Writes a line for each arg of *args*, encoded in the current |
|
32 ENCODING, to stdout. |
|
33 """ |
|
34 _std_write('\n'.join(a.encode(ENCODING, 'replace') for a in args) + '\n') |
|
35 |
|
36 |
|
37 def w_err(code, *args): |
|
38 """Writes a line for each arg of *args*, encoded in the current |
|
39 ENCODING, to stderr. |
|
40 This function optionally interrupts the program execution if *code* |
|
41 does not equal to 0. *code* will be used as the system exit status. |
|
42 """ |
|
43 _err_write('\n'.join(a.encode(ENCODING, 'replace') for a in args) + '\n') |
|
44 if code: |
|
45 os.sys.exit(code) |
|
46 |
|
47 |
|
48 def get_winsize(): |
|
49 """Returns a tuple of integers ``(ws_row, ws_col)`` with the height and |
|
50 width of the terminal.""" |
|
51 fd = None |
|
52 for dev in (os.sys.stdout, os.sys.stderr, os.sys.stdin): |
|
53 if hasattr(dev, 'fileno') and os.isatty(dev.fileno()): |
|
54 fd = dev.fileno() |
|
55 break |
|
56 if fd is None: # everything seems to be redirected |
|
57 # fall back to environment or assume some common defaults |
|
58 ws_row, ws_col = 24, 80 |
|
59 try: |
|
60 ws_col = int(os.environ.get('COLUMNS', 80)) |
|
61 ws_row = int(os.environ.get('LINES', 24)) |
|
62 except ValueError: |
|
63 pass |
|
64 return ws_row, ws_col |
|
65 #"struct winsize" with the ``unsigned short int``s ws_{row,col,{x,y}pixel} |
|
66 ws = array('H', (0, 0, 0, 0)) |
|
67 ioctl(fd, TIOCGWINSZ, ws, True) |
|
68 ws_row, ws_col = ws[:2] |
|
69 return ws_row, ws_col |
|
70 |
|
71 |
|
72 def read_pass(): |
|
73 """Interactive 'password chat', returns the password in plain format. |
|
74 |
|
75 Throws a VMMError after the third failure. |
|
76 """ |
|
77 # TP: Please preserve the trailing space. |
|
78 readp_msg0 = _(u'Enter new password: ').encode(ENCODING, 'replace') |
|
79 # TP: Please preserve the trailing space. |
|
80 readp_msg1 = _(u'Retype new password: ').encode(ENCODING, 'replace') |
|
81 mismatched = True |
|
82 failures = 0 |
|
83 while mismatched: |
|
84 if failures > 2: |
|
85 raise VMMError(_(u'Too many failures - try again later.'), |
|
86 VMM_TOO_MANY_FAILURES) |
|
87 clear0 = getpass(prompt=readp_msg0) |
|
88 clear1 = getpass(prompt=readp_msg1) |
|
89 if clear0 != clear1: |
|
90 failures += 1 |
|
91 w_err(0, _(u'Sorry, passwords do not match.')) |
|
92 continue |
|
93 if not clear0: |
|
94 failures += 1 |
|
95 w_err(0, _(u'Sorry, empty passwords are not permitted.')) |
|
96 continue |
|
97 mismatched = False |
|
98 return clear0 |
|
99 |
|
100 del _ |