/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 at recompile
  • Date: 2012-02-25 03:49:47 UTC
  • mto: This revision was merged to the branch mainline in revision 561.
  • Revision ID: teddy@recompile.se-20120225034947-qdjgp2h92bpote0d
* mandos: Use os.devnull instead of os.path.devnull.  Fix some white
          space.
  (PGPEngine.encrypt, PGPEngine.decrypt): Bug fix: open /dev/null for
                                          writing, not reading.

Show diffs side-by-side

added added

removed removed

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