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