/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
1
#!/usr/bin/python
2
# -*- mode: python; coding: utf-8 -*-
3
4
from __future__ import division, absolute_import, with_statement
5
6
import sys
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
7
import os
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
8
import signal
9
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
10
import datetime
11
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
12
import urwid.curses_display
13
import urwid
14
15
from dbus.mainloop.glib import DBusGMainLoop
16
import gobject
17
18
import dbus
19
20
import UserList
21
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
22
import locale
23
24
locale.setlocale(locale.LC_ALL, u'')
25
24.2.2 by teddy at bsnet
* mandos: Use logging.getLogger() as in the documentation.
26
import logging
27
logging.getLogger('dbus.proxies').setLevel(logging.CRITICAL)
28
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
29
# Some useful constants
30
domain = 'se.bsnet.fukt'
31
server_interface = domain + '.Mandos'
32
client_interface = domain + '.Mandos.Client'
24.2.2 by teddy at bsnet
* mandos: Use logging.getLogger() as in the documentation.
33
version = "1.0.15"
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
34
35
# Always run in monochrome mode
36
urwid.curses_display.curses.has_colors = lambda : False
37
38
# Urwid doesn't support blinking, but we want it.  Since we have no
39
# use for underline on its own, we make underline also always blink.
40
urwid.curses_display.curses.A_UNDERLINE |= (
41
    urwid.curses_display.curses.A_BLINK)
42
43
class MandosClientPropertyCache(object):
44
    """This wraps a Mandos Client D-Bus proxy object, caches the
45
    properties and calls a hook function when any of them are
46
    changed.
47
    """
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
48
    def __init__(self, proxy_object=None, *args, **kwargs):
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
49
        self.proxy = proxy_object # Mandos Client proxy object
50
        
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
51
        self.properties = dict()
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
52
        self.proxy.connect_to_signal(u"PropertyChanged",
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
53
                                     self.property_changed,
54
                                     client_interface,
55
                                     byte_arrays=True)
24.2.2 by teddy at bsnet
* mandos: Use logging.getLogger() as in the documentation.
56
        
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
57
        self.properties.update(
58
            self.proxy.GetAll(client_interface,
59
                              dbus_interface = dbus.PROPERTIES_IFACE))
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
60
        super(MandosClientPropertyCache, self).__init__(
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
61
            proxy_object=proxy_object, *args, **kwargs)
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
62
    
63
    def property_changed(self, property=None, value=None):
64
        """This is called whenever we get a PropertyChanged signal
65
        It updates the changed property in the "properties" dict.
66
        """
67
        # Update properties dict with new value
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
68
        self.properties[property] = value
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
69
70
71
class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
72
    """A Mandos Client which is visible on the screen.
73
    """
74
    
75
    def __init__(self, server_proxy_object=None, update_hook=None,
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
76
                 delete_hook=None, logger=None, *args, **kwargs):
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
77
        # Called on update
78
        self.update_hook = update_hook
79
        # Called on delete
80
        self.delete_hook = delete_hook
81
        # Mandos Server proxy object
82
        self.server_proxy_object = server_proxy_object
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
83
        # Logger
84
        self.logger = logger
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
85
        
86
        # The widget shown normally
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
87
        self._text_widget = urwid.Text(u"")
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
88
        # The widget shown when we have focus
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
89
        self._focus_text_widget = urwid.Text(u"")
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
90
        super(MandosClientWidget, self).__init__(
91
            update_hook=update_hook, delete_hook=delete_hook,
92
            *args, **kwargs)
93
        self.update()
94
        self.opened = False
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
95
        self.proxy.connect_to_signal(u"CheckerCompleted",
96
                                     self.checker_completed,
97
                                     client_interface,
98
                                     byte_arrays=True)
99
        self.proxy.connect_to_signal(u"CheckerStarted",
100
                                     self.checker_started,
101
                                     client_interface,
102
                                     byte_arrays=True)
103
        self.proxy.connect_to_signal(u"GotSecret",
104
                                     self.got_secret,
105
                                     client_interface,
106
                                     byte_arrays=True)
107
        self.proxy.connect_to_signal(u"Rejected",
108
                                     self.rejected,
109
                                     client_interface,
110
                                     byte_arrays=True)
111
    
112
    def checker_completed(self, exitstatus, condition, command):
113
        if exitstatus == 0:
114
            self.logger(u'Checker for client %s (command "%s")'
115
                        u' was successful'
116
                        % (self.properties[u"name"], command))
117
            return
118
        if os.WIFEXITED(condition):
119
            self.logger(u'Checker for client %s (command "%s")'
120
                        u' failed with exit code %s'
121
                        % (self.properties[u"name"], command,
122
                           os.WEXITSTATUS(condition)))
123
            return
124
        if os.WIFSIGNALED(condition):
125
            self.logger(u'Checker for client %s (command "%s")'
126
                        u' was killed by signal %s'
127
                        % (self.properties[u"name"], command,
128
                           os.WTERMSIG(condition)))
129
            return
130
        if os.WCOREDUMP(condition):
131
            self.logger(u'Checker for client %s (command "%s")'
132
                        u' dumped core'
133
                        % (self.properties[u"name"], command))
134
        self.logger(u'Checker for client %s completed mysteriously')
135
    
136
    def checker_started(self, command):
137
        self.logger(u'Client %s started checker "%s"'
138
                    % (self.properties[u"name"], unicode(command)))
139
    
140
    def got_secret(self):
141
        self.logger(u'Client %s received its secret'
142
                    % self.properties[u"name"])
143
    
144
    def rejected(self):
145
        self.logger(u'Client %s was rejected'
146
                    % self.properties[u"name"])
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
147
    
148
    def selectable(self):
149
        """Make this a "selectable" widget.
150
        This overrides the method from urwid.FlowWidget."""
151
        return True
152
    
153
    def rows(self, (maxcol,), focus=False):
154
        """How many rows this widget will occupy might depend on
155
        whether we have focus or not.
156
        This overrides the method from urwid.FlowWidget"""
157
        return self.current_widget(focus).rows((maxcol,), focus=focus)
158
    
159
    def current_widget(self, focus=False):
160
        if focus or self.opened:
161
            return self._focus_widget
162
        return self._widget
163
    
164
    def update(self):
165
        "Called when what is visible on the screen should be updated."
166
        # How to add standout mode to a style
167
        with_standout = { u"normal": u"standout",
168
                          u"bold": u"bold-standout",
169
                          u"underline-blink":
170
                              u"underline-blink-standout",
171
                          u"bold-underline-blink":
172
                              u"bold-underline-blink-standout",
173
                          }
174
        
175
        # Rebuild focus and non-focus widgets using current properties
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
176
        self._text = (u'%(name)s: %(enabled)s'
177
                      % { u"name": self.properties[u"name"],
178
                          u"enabled":
179
                              (u"enabled"
180
                               if self.properties[u"enabled"]
181
                               else u"DISABLED")})
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
182
        if not urwid.supports_unicode():
183
            self._text = self._text.encode("ascii", "replace")
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
184
        textlist = [(u"normal", self._text)]
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
185
        self._text_widget.set_text(textlist)
186
        self._focus_text_widget.set_text([(with_standout[text[0]],
187
                                           text[1])
188
                                          if isinstance(text, tuple)
189
                                          else text
190
                                          for text in textlist])
191
        self._widget = self._text_widget
192
        self._focus_widget = urwid.AttrWrap(self._focus_text_widget,
193
                                            "standout")
194
        # Run update hook, if any
195
        if self.update_hook is not None:
196
            self.update_hook()
197
    
198
    def delete(self):
199
        if self.delete_hook is not None:
200
            self.delete_hook(self)
201
    
202
    def render(self, (maxcol,), focus=False):
203
        """Render differently if we have focus.
204
        This overrides the method from urwid.FlowWidget"""
205
        return self.current_widget(focus).render((maxcol,),
206
                                                 focus=focus)
207
    
208
    def keypress(self, (maxcol,), key):
209
        """Handle keys.
210
        This overrides the method from urwid.FlowWidget"""
211
        if key == u"e" or key == u"+":
212
            self.proxy.Enable()
213
        elif key == u"d" or key == u"-":
214
            self.proxy.Disable()
408 by Teddy Hogeborn
* debian/rules: Only set BROKEN_PIE if binutils is a specific range of
215
        elif key == u"r" or key == u"_" or key == u"ctrl k":
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
216
            self.server_proxy_object.RemoveClient(self.proxy
217
                                                  .object_path)
218
        elif key == u"s":
219
            self.proxy.StartChecker()
407 by Teddy Hogeborn
* mandos-monitor (MandosClientWidget): Change "StopChecker" key to "S"
220
        elif key == u"S":
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
221
            self.proxy.StopChecker()
407 by Teddy Hogeborn
* mandos-monitor (MandosClientWidget): Change "StopChecker" key to "S"
222
        elif key == u"C":
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
223
            self.proxy.CheckedOK()
224
        # xxx
225
#         elif key == u"p" or key == "=":
226
#             self.proxy.pause()
227
#         elif key == u"u" or key == ":":
228
#             self.proxy.unpause()
229
#         elif key == u"RET":
230
#             self.open()
231
        else:
232
            return key
233
    
234
    def property_changed(self, property=None, value=None,
235
                         *args, **kwargs):
236
        """Call self.update() if old value is not new value.
237
        This overrides the method from MandosClientPropertyCache"""
238
        property_name = unicode(property)
239
        old_value = self.properties.get(property_name)
240
        super(MandosClientWidget, self).property_changed(
241
            property=property, value=value, *args, **kwargs)
242
        if self.properties.get(property_name) != old_value:
243
            self.update()
244
245
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
246
class ConstrainedListBox(urwid.ListBox):
247
    """Like a normal urwid.ListBox, but will consume all "up" or
248
    "down" key presses, thus not allowing any containing widgets to
249
    use them as an excuse to shift focus away from this widget.
250
    """
251
    def keypress(self, (maxcol, maxrow), key):
252
        ret = super(ConstrainedListBox, self).keypress((maxcol, maxrow), key)
253
        if ret in (u"up", u"down"):
254
            return
255
        return ret
256
257
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
258
class UserInterface(object):
259
    """This is the entire user interface - the whole screen
260
    with boxes, lists of client widgets, etc.
261
    """
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
262
    def __init__(self, max_log_length=1000):
263
        DBusGMainLoop(set_as_default=True)
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
264
        
265
        self.screen = urwid.curses_display.Screen()
266
        
267
        self.screen.register_palette((
268
                (u"normal",
269
                 u"default", u"default", None),
270
                (u"bold",
271
                 u"default", u"default", u"bold"),
272
                (u"underline-blink",
273
                 u"default", u"default", u"underline"),
274
                (u"standout",
275
                 u"default", u"default", u"standout"),
276
                (u"bold-underline-blink",
277
                 u"default", u"default", (u"bold", u"underline")),
278
                (u"bold-standout",
279
                 u"default", u"default", (u"bold", u"standout")),
280
                (u"underline-blink-standout",
281
                 u"default", u"default", (u"underline", u"standout")),
282
                (u"bold-underline-blink-standout",
283
                 u"default", u"default", (u"bold", u"underline",
284
                                          u"standout")),
285
                ))
286
        
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
287
        if urwid.supports_unicode():
408 by Teddy Hogeborn
* debian/rules: Only set BROKEN_PIE if binutils is a specific range of
288
            self.divider = u"─" # \u2500
289
            #self.divider = u"━" # \u2501
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
290
        else:
291
            #self.divider = u"-" # \u002d
292
            self.divider = u"_" # \u005f
293
        
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
294
        self.screen.start()
295
        
296
        self.size = self.screen.get_cols_rows()
297
        
298
        self.clients = urwid.SimpleListWalker([])
299
        self.clients_dict = {}
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
300
        
301
        # We will add Text widgets to this list
302
        self.log = []
303
        self.max_log_length = max_log_length
304
        
305
        # We keep a reference to the log widget so we can remove it
306
        # from the ListWalker without it getting destroyed
307
        self.logbox = ConstrainedListBox(self.log)
308
        
309
        # This keeps track of whether self.uilist currently has
310
        # self.logbox in it or not
311
        self.log_visible = True
312
        self.log_wrap = u"any"
313
        
314
        self.rebuild()
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
315
        self.log_message_raw((u"bold",
316
                              u"Mandos Monitor version " + version))
317
        self.log_message_raw((u"bold",
318
                              u"q: Quit  ?: Help"))
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
319
        
320
        self.busname = domain + '.Mandos'
321
        self.main_loop = gobject.MainLoop()
322
        self.bus = dbus.SystemBus()
323
        mandos_dbus_objc = self.bus.get_object(
324
            self.busname, u"/", follow_name_owner_changes=True)
325
        self.mandos_serv = dbus.Interface(mandos_dbus_objc,
326
                                          dbus_interface
327
                                          = server_interface)
328
        try:
329
            mandos_clients = (self.mandos_serv
330
                              .GetAllClientsWithProperties())
331
        except dbus.exceptions.DBusException:
332
            mandos_clients = dbus.Dictionary()
333
        
334
        (self.mandos_serv
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
335
         .connect_to_signal(u"ClientRemoved",
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
336
                            self.find_and_remove_client,
337
                            dbus_interface=server_interface,
338
                            byte_arrays=True))
339
        (self.mandos_serv
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
340
         .connect_to_signal(u"ClientAdded",
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
341
                            self.add_new_client,
342
                            dbus_interface=server_interface,
343
                            byte_arrays=True))
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
344
        (self.mandos_serv
345
         .connect_to_signal(u"ClientNotFound",
346
                            self.client_not_found,
347
                            dbus_interface=server_interface,
348
                            byte_arrays=True))
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
349
        for path, client in mandos_clients.iteritems():
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
350
            client_proxy_object = self.bus.get_object(self.busname,
351
                                                      path)
352
            self.add_client(MandosClientWidget(server_proxy_object
353
                                               =self.mandos_serv,
354
                                               proxy_object
355
                                               =client_proxy_object,
356
                                               properties=client,
357
                                               update_hook
358
                                               =self.refresh,
359
                                               delete_hook
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
360
                                               =self.remove_client,
361
                                               logger
362
                                               =self.log_message),
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
363
                            path=path)
364
    
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
365
    def client_not_found(self, fingerprint, address):
366
        self.log_message((u"Client with address %s and fingerprint %s"
367
                          u" could not be found" % (address,
368
                                                    fingerprint)))
369
    
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
370
    def rebuild(self):
371
        """This rebuilds the User Interface.
372
        Call this when the widget layout needs to change"""
373
        self.uilist = []
374
        #self.uilist.append(urwid.ListBox(self.clients))
375
        self.uilist.append(urwid.Frame(ConstrainedListBox(self.clients),
376
                                       #header=urwid.Divider(),
377
                                       header=None,
378
                                       footer=urwid.Divider(div_char=self.divider)))
379
        if self.log_visible:
380
            self.uilist.append(self.logbox)
381
            pass
382
        self.topwidget = urwid.Pile(self.uilist)
383
    
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
384
    def log_message(self, message):
385
        timestamp = datetime.datetime.now().isoformat()
386
        self.log_message_raw(timestamp + u": " + message)
387
    
388
    def log_message_raw(self, markup):
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
389
        """Add a log message to the log buffer."""
390
        self.log.append(urwid.Text(markup, wrap=self.log_wrap))
391
        if (self.max_log_length
392
            and len(self.log) > self.max_log_length):
393
            del self.log[0:len(self.log)-self.max_log_length-1]
408 by Teddy Hogeborn
* debian/rules: Only set BROKEN_PIE if binutils is a specific range of
394
        self.logbox.set_focus(len(self.logbox.body.contents),
395
                              coming_from=u"above")
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
396
        self.refresh()
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
397
    
398
    def toggle_log_display(self):
399
        """Toggle visibility of the log buffer."""
400
        self.log_visible = not self.log_visible
401
        self.rebuild()
402
        self.log_message(u"Log visibility changed to: "
403
                         + unicode(self.log_visible))
404
    
405
    def change_log_display(self):
406
        """Change type of log display.
407
        Currently, this toggles wrapping of text lines."""
408
        if self.log_wrap == u"clip":
409
            self.log_wrap = u"any"
410
        else:
411
            self.log_wrap = u"clip"
412
        for textwidget in self.log:
413
            textwidget.set_wrap_mode(self.log_wrap)
414
        self.log_message(u"Wrap mode: " + self.log_wrap)
415
    
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
416
    def find_and_remove_client(self, path, name):
417
        """Find an client from its object path and remove it.
418
        
419
        This is connected to the ClientRemoved signal from the
420
        Mandos server object."""
421
        try:
422
            client = self.clients_dict[path]
423
        except KeyError:
424
            # not found?
425
            return
426
        self.remove_client(client, path)
427
    
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
428
    def add_new_client(self, path):
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
429
        client_proxy_object = self.bus.get_object(self.busname, path)
430
        self.add_client(MandosClientWidget(server_proxy_object
431
                                           =self.mandos_serv,
432
                                           proxy_object
433
                                           =client_proxy_object,
434
                                           update_hook
435
                                           =self.refresh,
436
                                           delete_hook
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
437
                                           =self.remove_client,
438
                                           logger
439
                                           =self.log_message),
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
440
                        path=path)
441
    
442
    def add_client(self, client, path=None):
443
        self.clients.append(client)
444
        if path is None:
445
            path = client.proxy.object_path
446
        self.clients_dict[path] = client
447
        self.clients.sort(None, lambda c: c.properties[u"name"])
448
        self.refresh()
449
    
450
    def remove_client(self, client, path=None):
451
        self.clients.remove(client)
452
        if path is None:
453
            path = client.proxy.object_path
454
        del self.clients_dict[path]
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
455
        if not self.clients_dict:
456
            # Work around bug in Urwid 0.9.8.3 - if a SimpleListWalker
457
            # is completely emptied, we need to recreate it.
458
            self.clients = urwid.SimpleListWalker([])
459
            self.rebuild()
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
460
        self.refresh()
461
    
462
    def refresh(self):
463
        """Redraw the screen"""
464
        canvas = self.topwidget.render(self.size, focus=True)
465
        self.screen.draw_screen(self.size, canvas)
466
    
467
    def run(self):
468
        """Start the main loop and exit when it's done."""
469
        self.refresh()
470
        self._input_callback_tag = (gobject.io_add_watch
471
                                    (sys.stdin.fileno(),
472
                                     gobject.IO_IN,
473
                                     self.process_input))
474
        self.main_loop.run()
475
        # Main loop has finished, we should close everything now
476
        gobject.source_remove(self._input_callback_tag)
477
        self.screen.stop()
478
    
479
    def stop(self):
480
        self.main_loop.quit()
481
    
482
    def process_input(self, source, condition):
483
        keys = self.screen.get_input()
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
484
        translations = { u"ctrl n": u"down",      # Emacs
485
                         u"ctrl p": u"up",        # Emacs
486
                         u"ctrl v": u"page down", # Emacs
487
                         u"meta v": u"page up",   # Emacs
488
                         u" ": u"page down",      # less
489
                         u"f": u"page down",      # less
490
                         u"b": u"page up",        # less
491
                         u"j": u"down",           # vi
492
                         u"k": u"up",             # vi
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
493
                         }
494
        for key in keys:
495
            try:
496
                key = translations[key]
497
            except KeyError:    # :-)
498
                pass
499
            
500
            if key == u"q" or key == u"Q":
501
                self.stop()
502
                break
503
            elif key == u"window resize":
504
                self.size = self.screen.get_cols_rows()
505
                self.refresh()
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
506
            elif key == u"\f":  # Ctrl-L
507
                self.refresh()
508
            elif key == u"l" or key == u"D":
509
                self.toggle_log_display()
510
                self.refresh()
511
            elif key == u"w" or key == u"i":
512
                self.change_log_display()
513
                self.refresh()
408 by Teddy Hogeborn
* debian/rules: Only set BROKEN_PIE if binutils is a specific range of
514
            elif key == u"?" or key == u"f1" or key == u"esc":
407 by Teddy Hogeborn
* mandos-monitor (MandosClientWidget): Change "StopChecker" key to "S"
515
                if not self.log_visible:
516
                    self.log_visible = True
517
                    self.rebuild()
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
518
                self.log_message_raw((u"bold",
519
                                      u"  ".
520
                                      join((u"q: Quit",
521
                                            u"?: Help",
522
                                            u"l: Log window toggle",
523
                                            u"TAB: Switch window",
524
                                            u"w: Wrap (log)"))))
525
                self.log_message_raw((u"bold",
526
                                      u"  "
527
                                      .join((u"Clients:",
528
                                             u"e: Enable",
529
                                             u"d: Disable",
530
                                             u"r: Remove",
531
                                             u"s: Start new checker",
532
                                             u"S: Stop checker",
533
                                             u"C: Checker OK"))))
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
534
                self.refresh()
535
            elif key == u"tab":
536
                if self.topwidget.get_focus() is self.logbox:
537
                    self.topwidget.set_focus(0)
538
                else:
539
                    self.topwidget.set_focus(self.logbox)
540
                self.refresh()
407 by Teddy Hogeborn
* mandos-monitor (MandosClientWidget): Change "StopChecker" key to "S"
541
            #elif (key == u"end" or key == u"meta >" or key == u"G"
542
            #      or key == u">"):
543
            #    pass            # xxx end-of-buffer
544
            #elif (key == u"home" or key == u"meta <" or key == u"g"
545
            #      or key == u"<"):
546
            #    pass            # xxx beginning-of-buffer
547
            #elif key == u"ctrl e" or key == u"$":
548
            #    pass            # xxx move-end-of-line
549
            #elif key == u"ctrl a" or key == u"^":
550
            #    pass            # xxx move-beginning-of-line
551
            #elif key == u"ctrl b" or key == u"meta (" or key == u"h":
552
            #    pass            # xxx left
553
            #elif key == u"ctrl f" or key == u"meta )" or key == u"l":
554
            #    pass            # xxx right
555
            #elif key == u"a":
556
            #    pass            # scroll up log
557
            #elif key == u"z":
558
            #    pass            # scroll down log
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
559
            elif self.topwidget.selectable():
560
                self.topwidget.keypress(self.size, key)
561
                self.refresh()
562
        return True
563
564
ui = UserInterface()
565
try:
566
    ui.run()
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
567
except Exception, e:
568
    ui.log_message(unicode(e))
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
569
    ui.screen.stop()
570
    raise