/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos-monitor

  • Committer: Teddy Hogeborn
  • Date: 2011-07-16 00:29:19 UTC
  • Revision ID: teddy@fukt.bsnet.se-20110716002919-r77yikuiulj42o40
* initramfs-tools-script: Abort if plugin-runner is missing.  Removed
                          workaround for Debian bug #633582; the
                          workaround required getopt, which can not be
                          guaranteed.
* plugin-runner.c (main): Work around Debian bug #633582.
* plugins.d/mandos-client.c (main): - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/python
2
2
# -*- mode: python; coding: utf-8 -*-
 
3
 
4
# Mandos Monitor - Control and monitor the Mandos server
 
5
 
6
# Copyright © 2009-2011 Teddy Hogeborn
 
7
# Copyright © 2009-2011 Björn Påhlsson
 
8
 
9
# This program is free software: you can redistribute it and/or modify
 
10
# it under the terms of the GNU General Public License as published by
 
11
# the Free Software Foundation, either version 3 of the License, or
 
12
# (at your option) any later version.
 
13
#
 
14
#     This program is distributed in the hope that it will be useful,
 
15
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
17
#     GNU General Public License for more details.
 
18
 
19
# You should have received a copy of the GNU General Public License
 
20
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
21
 
22
# Contact the authors at <mandos@fukt.bsnet.se>.
 
23
3
24
 
4
 
from __future__ import division, absolute_import, with_statement
 
25
from __future__ import division, absolute_import, print_function, unicode_literals
5
26
 
6
27
import sys
7
28
import os
21
42
 
22
43
import locale
23
44
 
24
 
locale.setlocale(locale.LC_ALL, u'')
 
45
locale.setlocale(locale.LC_ALL, '')
25
46
 
26
47
import logging
27
48
logging.getLogger('dbus.proxies').setLevel(logging.CRITICAL)
30
51
domain = 'se.bsnet.fukt'
31
52
server_interface = domain + '.Mandos'
32
53
client_interface = domain + '.Mandos.Client'
33
 
version = "1.0.15"
 
54
version = "1.3.0"
34
55
 
35
56
# Always run in monochrome mode
36
57
urwid.curses_display.curses.has_colors = lambda : False
44
65
    "Parse an ISO 8601 date string to a datetime.datetime()"
45
66
    if not iso:
46
67
        return None
47
 
    d, t = iso.split(u"T", 1)
48
 
    year, month, day = d.split(u"-", 2)
49
 
    hour, minute, second = t.split(u":", 2)
 
68
    d, t = iso.split("T", 1)
 
69
    year, month, day = d.split("-", 2)
 
70
    hour, minute, second = t.split(":", 2)
50
71
    second, fraction = divmod(float(second), 1)
51
72
    return datetime.datetime(int(year),
52
73
                             int(month),
65
86
        self.proxy = proxy_object # Mandos Client proxy object
66
87
        
67
88
        self.properties = dict()
68
 
        self.proxy.connect_to_signal(u"PropertyChanged",
 
89
        self.proxy.connect_to_signal("PropertyChanged",
69
90
                                     self.property_changed,
70
91
                                     client_interface,
71
92
                                     byte_arrays=True)
106
127
        self.last_checker_failed = False
107
128
        
108
129
        # The widget shown normally
109
 
        self._text_widget = urwid.Text(u"")
 
130
        self._text_widget = urwid.Text("")
110
131
        # The widget shown when we have focus
111
 
        self._focus_text_widget = urwid.Text(u"")
 
132
        self._focus_text_widget = urwid.Text("")
112
133
        super(MandosClientWidget, self).__init__(
113
134
            update_hook=update_hook, delete_hook=delete_hook,
114
135
            *args, **kwargs)
116
137
        self.opened = False
117
138
        
118
139
        last_checked_ok = isoformat_to_datetime(self.properties
119
 
                                                [u"LastCheckedOK"])
 
140
                                                ["LastCheckedOK"])
120
141
        if last_checked_ok is None:
121
142
            self.last_checker_failed = True
122
143
        else:
125
146
                                        > datetime.timedelta
126
147
                                        (milliseconds=
127
148
                                         self.properties
128
 
                                         [u"Interval"]))
 
149
                                         ["Interval"]))
129
150
        
130
151
        if self.last_checker_failed:
131
152
            self.using_timer(True)
133
154
        if self.need_approval:
134
155
            self.using_timer(True)
135
156
        
136
 
        self.proxy.connect_to_signal(u"CheckerCompleted",
 
157
        self.proxy.connect_to_signal("CheckerCompleted",
137
158
                                     self.checker_completed,
138
159
                                     client_interface,
139
160
                                     byte_arrays=True)
140
 
        self.proxy.connect_to_signal(u"CheckerStarted",
 
161
        self.proxy.connect_to_signal("CheckerStarted",
141
162
                                     self.checker_started,
142
163
                                     client_interface,
143
164
                                     byte_arrays=True)
144
 
        self.proxy.connect_to_signal(u"GotSecret",
 
165
        self.proxy.connect_to_signal("GotSecret",
145
166
                                     self.got_secret,
146
167
                                     client_interface,
147
168
                                     byte_arrays=True)
148
 
        self.proxy.connect_to_signal(u"NeedApproval",
 
169
        self.proxy.connect_to_signal("NeedApproval",
149
170
                                     self.need_approval,
150
171
                                     client_interface,
151
172
                                     byte_arrays=True)
152
 
        self.proxy.connect_to_signal(u"Rejected",
 
173
        self.proxy.connect_to_signal("Rejected",
153
174
                                     self.rejected,
154
175
                                     client_interface,
155
176
                                     byte_arrays=True)
157
178
    def property_changed(self, property=None, value=None):
158
179
        super(self, MandosClientWidget).property_changed(property,
159
180
                                                         value)
160
 
        if property == u"ApprovalPending":
 
181
        if property == "ApprovalPending":
161
182
            using_timer(bool(value))
162
183
        
163
184
    def using_timer(self, flag):
182
203
            if self.last_checker_failed:
183
204
                self.last_checker_failed = False
184
205
                self.using_timer(False)
185
 
            #self.logger(u'Checker for client %s (command "%s")'
186
 
            #            u' was successful'
187
 
            #            % (self.properties[u"Name"], command))
 
206
            #self.logger('Checker for client %s (command "%s")'
 
207
            #            ' was successful'
 
208
            #            % (self.properties["Name"], command))
188
209
            self.update()
189
210
            return
190
211
        # Checker failed
192
213
            self.last_checker_failed = True
193
214
            self.using_timer(True)
194
215
        if os.WIFEXITED(condition):
195
 
            self.logger(u'Checker for client %s (command "%s")'
196
 
                        u' failed with exit code %s'
197
 
                        % (self.properties[u"Name"], command,
 
216
            self.logger('Checker for client %s (command "%s")'
 
217
                        ' failed with exit code %s'
 
218
                        % (self.properties["Name"], command,
198
219
                           os.WEXITSTATUS(condition)))
199
220
        elif os.WIFSIGNALED(condition):
200
 
            self.logger(u'Checker for client %s (command "%s")'
201
 
                        u' was killed by signal %s'
202
 
                        % (self.properties[u"Name"], command,
 
221
            self.logger('Checker for client %s (command "%s")'
 
222
                        ' was killed by signal %s'
 
223
                        % (self.properties["Name"], command,
203
224
                           os.WTERMSIG(condition)))
204
225
        elif os.WCOREDUMP(condition):
205
 
            self.logger(u'Checker for client %s (command "%s")'
206
 
                        u' dumped core'
207
 
                        % (self.properties[u"Name"], command))
 
226
            self.logger('Checker for client %s (command "%s")'
 
227
                        ' dumped core'
 
228
                        % (self.properties["Name"], command))
208
229
        else:
209
 
            self.logger(u'Checker for client %s completed'
210
 
                        u' mysteriously')
 
230
            self.logger('Checker for client %s completed'
 
231
                        ' mysteriously')
211
232
        self.update()
212
233
    
213
234
    def checker_started(self, command):
214
 
        #self.logger(u'Client %s started checker "%s"'
215
 
        #            % (self.properties[u"Name"], unicode(command)))
 
235
        #self.logger('Client %s started checker "%s"'
 
236
        #            % (self.properties["Name"], unicode(command)))
216
237
        pass
217
238
    
218
239
    def got_secret(self):
219
240
        self.last_checker_failed = False
220
 
        self.logger(u'Client %s received its secret'
221
 
                    % self.properties[u"Name"])
 
241
        self.logger('Client %s received its secret'
 
242
                    % self.properties["Name"])
222
243
    
223
244
    def need_approval(self, timeout, default):
224
245
        if not default:
225
 
            message = u'Client %s needs approval within %s seconds'
 
246
            message = 'Client %s needs approval within %s seconds'
226
247
        else:
227
 
            message = u'Client %s will get its secret in %s seconds'
 
248
            message = 'Client %s will get its secret in %s seconds'
228
249
        self.logger(message
229
 
                    % (self.properties[u"Name"], timeout/1000))
 
250
                    % (self.properties["Name"], timeout/1000))
230
251
        self.using_timer(True)
231
252
    
232
253
    def rejected(self, reason):
233
 
        self.logger(u'Client %s was rejected; reason: %s'
234
 
                    % (self.properties[u"Name"], reason))
 
254
        self.logger('Client %s was rejected; reason: %s'
 
255
                    % (self.properties["Name"], reason))
235
256
    
236
257
    def selectable(self):
237
258
        """Make this a "selectable" widget.
238
259
        This overrides the method from urwid.FlowWidget."""
239
260
        return True
240
261
    
241
 
    def rows(self, (maxcol,), focus=False):
 
262
    def rows(self, maxcolrow, focus=False):
242
263
        """How many rows this widget will occupy might depend on
243
264
        whether we have focus or not.
244
265
        This overrides the method from urwid.FlowWidget"""
245
 
        return self.current_widget(focus).rows((maxcol,), focus=focus)
 
266
        return self.current_widget(focus).rows(maxcolrow, focus=focus)
246
267
    
247
268
    def current_widget(self, focus=False):
248
269
        if focus or self.opened:
252
273
    def update(self):
253
274
        "Called when what is visible on the screen should be updated."
254
275
        # How to add standout mode to a style
255
 
        with_standout = { u"normal": u"standout",
256
 
                          u"bold": u"bold-standout",
257
 
                          u"underline-blink":
258
 
                              u"underline-blink-standout",
259
 
                          u"bold-underline-blink":
260
 
                              u"bold-underline-blink-standout",
 
276
        with_standout = { "normal": "standout",
 
277
                          "bold": "bold-standout",
 
278
                          "underline-blink":
 
279
                              "underline-blink-standout",
 
280
                          "bold-underline-blink":
 
281
                              "bold-underline-blink-standout",
261
282
                          }
262
283
 
263
284
        # Rebuild focus and non-focus widgets using current properties
264
285
 
265
286
        # Base part of a client. Name!
266
 
        base = (u'%(name)s: '
267
 
                      % {u"name": self.properties[u"Name"]})
268
 
        if not self.properties[u"Enabled"]:
269
 
            message = u"DISABLED"
270
 
        elif self.properties[u"ApprovalPending"]:
 
287
        base = ('%(name)s: '
 
288
                      % {"name": self.properties["Name"]})
 
289
        if not self.properties["Enabled"]:
 
290
            message = "DISABLED"
 
291
        elif self.properties["ApprovalPending"]:
271
292
            timeout = datetime.timedelta(milliseconds
272
293
                                         = self.properties
273
 
                                         [u"ApprovalDelay"])
 
294
                                         ["ApprovalDelay"])
274
295
            last_approval_request = isoformat_to_datetime(
275
 
                self.properties[u"LastApprovalRequest"])
 
296
                self.properties["LastApprovalRequest"])
276
297
            if last_approval_request is not None:
277
298
                timer = timeout - (datetime.datetime.utcnow()
278
299
                                   - last_approval_request)
279
300
            else:
280
301
                timer = datetime.timedelta()
281
 
            if self.properties[u"ApprovedByDefault"]:
282
 
                message = u"Approval in %s. (d)eny?"
 
302
            if self.properties["ApprovedByDefault"]:
 
303
                message = "Approval in %s. (d)eny?"
283
304
            else:
284
 
                message = u"Denial in %s. (a)pprove?"
 
305
                message = "Denial in %s. (a)pprove?"
285
306
            message = message % unicode(timer).rsplit(".", 1)[0]
286
307
        elif self.last_checker_failed:
287
308
            timeout = datetime.timedelta(milliseconds
288
309
                                         = self.properties
289
 
                                         [u"Timeout"])
 
310
                                         ["Timeout"])
290
311
            last_ok = isoformat_to_datetime(
291
 
                max((self.properties[u"LastCheckedOK"]
292
 
                     or self.properties[u"Created"]),
293
 
                    self.properties[u"LastEnabled"]))
 
312
                max((self.properties["LastCheckedOK"]
 
313
                     or self.properties["Created"]),
 
314
                    self.properties["LastEnabled"]))
294
315
            timer = timeout - (datetime.datetime.utcnow() - last_ok)
295
 
            message = (u'A checker has failed! Time until client'
296
 
                       u' gets disabled: %s'
 
316
            message = ('A checker has failed! Time until client'
 
317
                       ' gets disabled: %s'
297
318
                           % unicode(timer).rsplit(".", 1)[0])
298
319
        else:
299
 
            message = u"enabled"
 
320
            message = "enabled"
300
321
        self._text = "%s%s" % (base, message)
301
322
            
302
323
        if not urwid.supports_unicode():
303
324
            self._text = self._text.encode("ascii", "replace")
304
 
        textlist = [(u"normal", self._text)]
 
325
        textlist = [("normal", self._text)]
305
326
        self._text_widget.set_text(textlist)
306
327
        self._focus_text_widget.set_text([(with_standout[text[0]],
307
328
                                           text[1])
327
348
        if self.delete_hook is not None:
328
349
            self.delete_hook(self)
329
350
    
330
 
    def render(self, (maxcol,), focus=False):
 
351
    def render(self, maxcolrow, focus=False):
331
352
        """Render differently if we have focus.
332
353
        This overrides the method from urwid.FlowWidget"""
333
 
        return self.current_widget(focus).render((maxcol,),
 
354
        return self.current_widget(focus).render(maxcolrow,
334
355
                                                 focus=focus)
335
356
    
336
 
    def keypress(self, (maxcol,), key):
 
357
    def keypress(self, maxcolrow, key):
337
358
        """Handle keys.
338
359
        This overrides the method from urwid.FlowWidget"""
339
 
        if key == u"+":
340
 
            self.proxy.Enable(dbus_interface = client_interface)
341
 
        elif key == u"-":
342
 
            self.proxy.Disable(dbus_interface = client_interface)
343
 
        elif key == u"a":
 
360
        if key == "+":
 
361
            self.proxy.Enable(dbus_interface = client_interface,
 
362
                              ignore_reply=True)
 
363
        elif key == "-":
 
364
            self.proxy.Disable(dbus_interface = client_interface,
 
365
                               ignore_reply=True)
 
366
        elif key == "a":
344
367
            self.proxy.Approve(dbus.Boolean(True, variant_level=1),
345
 
                               dbus_interface = client_interface)
346
 
        elif key == u"d":
 
368
                               dbus_interface = client_interface,
 
369
                               ignore_reply=True)
 
370
        elif key == "d":
347
371
            self.proxy.Approve(dbus.Boolean(False, variant_level=1),
348
 
                                  dbus_interface = client_interface)
349
 
        elif key == u"r" or key == u"_" or key == u"ctrl k":
 
372
                                  dbus_interface = client_interface,
 
373
                               ignore_reply=True)
 
374
        elif key == "R" or key == "_" or key == "ctrl k":
350
375
            self.server_proxy_object.RemoveClient(self.proxy
351
 
                                                  .object_path)
352
 
        elif key == u"s":
353
 
            self.proxy.StartChecker(dbus_interface = client_interface)
354
 
        elif key == u"S":
355
 
            self.proxy.StopChecker(dbus_interface = client_interface)
356
 
        elif key == u"C":
357
 
            self.proxy.CheckedOK(dbus_interface = client_interface)
 
376
                                                  .object_path,
 
377
                                                  ignore_reply=True)
 
378
        elif key == "s":
 
379
            self.proxy.StartChecker(dbus_interface = client_interface,
 
380
                                    ignore_reply=True)
 
381
        elif key == "S":
 
382
            self.proxy.StopChecker(dbus_interface = client_interface,
 
383
                                   ignore_reply=True)
 
384
        elif key == "C":
 
385
            self.proxy.CheckedOK(dbus_interface = client_interface,
 
386
                                 ignore_reply=True)
358
387
        # xxx
359
 
#         elif key == u"p" or key == "=":
 
388
#         elif key == "p" or key == "=":
360
389
#             self.proxy.pause()
361
 
#         elif key == u"u" or key == ":":
 
390
#         elif key == "u" or key == ":":
362
391
#             self.proxy.unpause()
363
 
#         elif key == u"RET":
 
392
#         elif key == "RET":
364
393
#             self.open()
365
394
        else:
366
395
            return key
382
411
    "down" key presses, thus not allowing any containing widgets to
383
412
    use them as an excuse to shift focus away from this widget.
384
413
    """
385
 
    def keypress(self, (maxcol, maxrow), key):
386
 
        ret = super(ConstrainedListBox, self).keypress((maxcol,
387
 
                                                        maxrow), key)
388
 
        if ret in (u"up", u"down"):
 
414
    def keypress(self, maxcolrow, key):
 
415
        ret = super(ConstrainedListBox, self).keypress(maxcolrow, key)
 
416
        if ret in ("up", "down"):
389
417
            return
390
418
        return ret
391
419
 
400
428
        self.screen = urwid.curses_display.Screen()
401
429
        
402
430
        self.screen.register_palette((
403
 
                (u"normal",
404
 
                 u"default", u"default", None),
405
 
                (u"bold",
406
 
                 u"default", u"default", u"bold"),
407
 
                (u"underline-blink",
408
 
                 u"default", u"default", u"underline"),
409
 
                (u"standout",
410
 
                 u"default", u"default", u"standout"),
411
 
                (u"bold-underline-blink",
412
 
                 u"default", u"default", (u"bold", u"underline")),
413
 
                (u"bold-standout",
414
 
                 u"default", u"default", (u"bold", u"standout")),
415
 
                (u"underline-blink-standout",
416
 
                 u"default", u"default", (u"underline", u"standout")),
417
 
                (u"bold-underline-blink-standout",
418
 
                 u"default", u"default", (u"bold", u"underline",
419
 
                                          u"standout")),
 
431
                ("normal",
 
432
                 "default", "default", None),
 
433
                ("bold",
 
434
                 "default", "default", "bold"),
 
435
                ("underline-blink",
 
436
                 "default", "default", "underline"),
 
437
                ("standout",
 
438
                 "default", "default", "standout"),
 
439
                ("bold-underline-blink",
 
440
                 "default", "default", ("bold", "underline")),
 
441
                ("bold-standout",
 
442
                 "default", "default", ("bold", "standout")),
 
443
                ("underline-blink-standout",
 
444
                 "default", "default", ("underline", "standout")),
 
445
                ("bold-underline-blink-standout",
 
446
                 "default", "default", ("bold", "underline",
 
447
                                          "standout")),
420
448
                ))
421
449
        
422
450
        if urwid.supports_unicode():
423
 
            self.divider = u"─" # \u2500
424
 
            #self.divider = u"━" # \u2501
 
451
            self.divider = "─" # \u2500
 
452
            #self.divider = "━" # \u2501
425
453
        else:
426
 
            #self.divider = u"-" # \u002d
427
 
            self.divider = u"_" # \u005f
 
454
            #self.divider = "-" # \u002d
 
455
            self.divider = "_" # \u005f
428
456
        
429
457
        self.screen.start()
430
458
        
444
472
        # This keeps track of whether self.uilist currently has
445
473
        # self.logbox in it or not
446
474
        self.log_visible = True
447
 
        self.log_wrap = u"any"
 
475
        self.log_wrap = "any"
448
476
        
449
477
        self.rebuild()
450
 
        self.log_message_raw((u"bold",
451
 
                              u"Mandos Monitor version " + version))
452
 
        self.log_message_raw((u"bold",
453
 
                              u"q: Quit  ?: Help"))
 
478
        self.log_message_raw(("bold",
 
479
                              "Mandos Monitor version " + version))
 
480
        self.log_message_raw(("bold",
 
481
                              "q: Quit  ?: Help"))
454
482
        
455
483
        self.busname = domain + '.Mandos'
456
484
        self.main_loop = gobject.MainLoop()
457
485
        self.bus = dbus.SystemBus()
458
486
        mandos_dbus_objc = self.bus.get_object(
459
 
            self.busname, u"/", follow_name_owner_changes=True)
 
487
            self.busname, "/", follow_name_owner_changes=True)
460
488
        self.mandos_serv = dbus.Interface(mandos_dbus_objc,
461
489
                                          dbus_interface
462
490
                                          = server_interface)
467
495
            mandos_clients = dbus.Dictionary()
468
496
        
469
497
        (self.mandos_serv
470
 
         .connect_to_signal(u"ClientRemoved",
 
498
         .connect_to_signal("ClientRemoved",
471
499
                            self.find_and_remove_client,
472
500
                            dbus_interface=server_interface,
473
501
                            byte_arrays=True))
474
502
        (self.mandos_serv
475
 
         .connect_to_signal(u"ClientAdded",
 
503
         .connect_to_signal("ClientAdded",
476
504
                            self.add_new_client,
477
505
                            dbus_interface=server_interface,
478
506
                            byte_arrays=True))
479
507
        (self.mandos_serv
480
 
         .connect_to_signal(u"ClientNotFound",
 
508
         .connect_to_signal("ClientNotFound",
481
509
                            self.client_not_found,
482
510
                            dbus_interface=server_interface,
483
511
                            byte_arrays=True))
498
526
                            path=path)
499
527
    
500
528
    def client_not_found(self, fingerprint, address):
501
 
        self.log_message((u"Client with address %s and fingerprint %s"
502
 
                          u" could not be found" % (address,
 
529
        self.log_message(("Client with address %s and fingerprint %s"
 
530
                          " could not be found" % (address,
503
531
                                                    fingerprint)))
504
532
    
505
533
    def rebuild(self):
521
549
    
522
550
    def log_message(self, message):
523
551
        timestamp = datetime.datetime.now().isoformat()
524
 
        self.log_message_raw(timestamp + u": " + message)
 
552
        self.log_message_raw(timestamp + ": " + message)
525
553
    
526
554
    def log_message_raw(self, markup):
527
555
        """Add a log message to the log buffer."""
530
558
            and len(self.log) > self.max_log_length):
531
559
            del self.log[0:len(self.log)-self.max_log_length-1]
532
560
        self.logbox.set_focus(len(self.logbox.body.contents),
533
 
                              coming_from=u"above")
 
561
                              coming_from="above")
534
562
        self.refresh()
535
563
    
536
564
    def toggle_log_display(self):
537
565
        """Toggle visibility of the log buffer."""
538
566
        self.log_visible = not self.log_visible
539
567
        self.rebuild()
540
 
        #self.log_message(u"Log visibility changed to: "
 
568
        #self.log_message("Log visibility changed to: "
541
569
        #                 + unicode(self.log_visible))
542
570
    
543
571
    def change_log_display(self):
544
572
        """Change type of log display.
545
573
        Currently, this toggles wrapping of text lines."""
546
 
        if self.log_wrap == u"clip":
547
 
            self.log_wrap = u"any"
 
574
        if self.log_wrap == "clip":
 
575
            self.log_wrap = "any"
548
576
        else:
549
 
            self.log_wrap = u"clip"
 
577
            self.log_wrap = "clip"
550
578
        for textwidget in self.log:
551
579
            textwidget.set_wrap_mode(self.log_wrap)
552
 
        #self.log_message(u"Wrap mode: " + self.log_wrap)
 
580
        #self.log_message("Wrap mode: " + self.log_wrap)
553
581
    
554
582
    def find_and_remove_client(self, path, name):
555
583
        """Find an client from its object path and remove it.
582
610
        if path is None:
583
611
            path = client.proxy.object_path
584
612
        self.clients_dict[path] = client
585
 
        self.clients.sort(None, lambda c: c.properties[u"Name"])
 
613
        self.clients.sort(None, lambda c: c.properties["Name"])
586
614
        self.refresh()
587
615
    
588
616
    def remove_client(self, client, path=None):
619
647
    
620
648
    def process_input(self, source, condition):
621
649
        keys = self.screen.get_input()
622
 
        translations = { u"ctrl n": u"down",      # Emacs
623
 
                         u"ctrl p": u"up",        # Emacs
624
 
                         u"ctrl v": u"page down", # Emacs
625
 
                         u"meta v": u"page up",   # Emacs
626
 
                         u" ": u"page down",      # less
627
 
                         u"f": u"page down",      # less
628
 
                         u"b": u"page up",        # less
629
 
                         u"j": u"down",           # vi
630
 
                         u"k": u"up",             # vi
 
650
        translations = { "ctrl n": "down",      # Emacs
 
651
                         "ctrl p": "up",        # Emacs
 
652
                         "ctrl v": "page down", # Emacs
 
653
                         "meta v": "page up",   # Emacs
 
654
                         " ": "page down",      # less
 
655
                         "f": "page down",      # less
 
656
                         "b": "page up",        # less
 
657
                         "j": "down",           # vi
 
658
                         "k": "up",             # vi
631
659
                         }
632
660
        for key in keys:
633
661
            try:
635
663
            except KeyError:    # :-)
636
664
                pass
637
665
            
638
 
            if key == u"q" or key == u"Q":
 
666
            if key == "q" or key == "Q":
639
667
                self.stop()
640
668
                break
641
 
            elif key == u"window resize":
 
669
            elif key == "window resize":
642
670
                self.size = self.screen.get_cols_rows()
643
671
                self.refresh()
644
 
            elif key == u"\f":  # Ctrl-L
 
672
            elif key == "\f":  # Ctrl-L
645
673
                self.refresh()
646
 
            elif key == u"l" or key == u"D":
 
674
            elif key == "l" or key == "D":
647
675
                self.toggle_log_display()
648
676
                self.refresh()
649
 
            elif key == u"w" or key == u"i":
 
677
            elif key == "w" or key == "i":
650
678
                self.change_log_display()
651
679
                self.refresh()
652
 
            elif key == u"?" or key == u"f1" or key == u"esc":
 
680
            elif key == "?" or key == "f1" or key == "esc":
653
681
                if not self.log_visible:
654
682
                    self.log_visible = True
655
683
                    self.rebuild()
656
 
                self.log_message_raw((u"bold",
657
 
                                      u"  ".
658
 
                                      join((u"q: Quit",
659
 
                                            u"?: Help",
660
 
                                            u"l: Log window toggle",
661
 
                                            u"TAB: Switch window",
662
 
                                            u"w: Wrap (log)"))))
663
 
                self.log_message_raw((u"bold",
664
 
                                      u"  "
665
 
                                      .join((u"Clients:",
666
 
                                             u"+: Enable",
667
 
                                             u"-: Disable",
668
 
                                             u"r: Remove",
669
 
                                             u"s: Start new checker",
670
 
                                             u"S: Stop checker",
671
 
                                             u"C: Checker OK",
672
 
                                             u"a: Approve",
673
 
                                             u"d: Deny"))))
 
684
                self.log_message_raw(("bold",
 
685
                                      "  ".
 
686
                                      join(("q: Quit",
 
687
                                            "?: Help",
 
688
                                            "l: Log window toggle",
 
689
                                            "TAB: Switch window",
 
690
                                            "w: Wrap (log)"))))
 
691
                self.log_message_raw(("bold",
 
692
                                      "  "
 
693
                                      .join(("Clients:",
 
694
                                             "+: Enable",
 
695
                                             "-: Disable",
 
696
                                             "R: Remove",
 
697
                                             "s: Start new checker",
 
698
                                             "S: Stop checker",
 
699
                                             "C: Checker OK",
 
700
                                             "a: Approve",
 
701
                                             "d: Deny"))))
674
702
                self.refresh()
675
 
            elif key == u"tab":
 
703
            elif key == "tab":
676
704
                if self.topwidget.get_focus() is self.logbox:
677
705
                    self.topwidget.set_focus(0)
678
706
                else:
679
707
                    self.topwidget.set_focus(self.logbox)
680
708
                self.refresh()
681
 
            #elif (key == u"end" or key == u"meta >" or key == u"G"
682
 
            #      or key == u">"):
 
709
            #elif (key == "end" or key == "meta >" or key == "G"
 
710
            #      or key == ">"):
683
711
            #    pass            # xxx end-of-buffer
684
 
            #elif (key == u"home" or key == u"meta <" or key == u"g"
685
 
            #      or key == u"<"):
 
712
            #elif (key == "home" or key == "meta <" or key == "g"
 
713
            #      or key == "<"):
686
714
            #    pass            # xxx beginning-of-buffer
687
 
            #elif key == u"ctrl e" or key == u"$":
 
715
            #elif key == "ctrl e" or key == "$":
688
716
            #    pass            # xxx move-end-of-line
689
 
            #elif key == u"ctrl a" or key == u"^":
 
717
            #elif key == "ctrl a" or key == "^":
690
718
            #    pass            # xxx move-beginning-of-line
691
 
            #elif key == u"ctrl b" or key == u"meta (" or key == u"h":
 
719
            #elif key == "ctrl b" or key == "meta (" or key == "h":
692
720
            #    pass            # xxx left
693
 
            #elif key == u"ctrl f" or key == u"meta )" or key == u"l":
 
721
            #elif key == "ctrl f" or key == "meta )" or key == "l":
694
722
            #    pass            # xxx right
695
 
            #elif key == u"a":
 
723
            #elif key == "a":
696
724
            #    pass            # scroll up log
697
 
            #elif key == u"z":
 
725
            #elif key == "z":
698
726
            #    pass            # scroll down log
699
727
            elif self.topwidget.selectable():
700
728
                self.topwidget.keypress(self.size, key)