/mandos/release

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