/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

* network-hooks.d/bridge: Use "/usr/sbin/brctl" explicitly.
* plugins.d/mandos-client.c (run_network_hooks): Raise priviliges in
                                                 child process.
  (main): Do not use getuid() to check if running setuid root.  Do not
          raise privileges for run_network_hooks.

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-2011 Teddy Hogeborn
 
7
# Copyright © 2009-2011 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.4.1"
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):
127
134
        self.last_checker_failed = False
128
135
        
129
136
        # The widget shown normally
130
 
        self._text_widget = urwid.Text(u"")
 
137
        self._text_widget = urwid.Text("")
131
138
        # The widget shown when we have focus
132
 
        self._focus_text_widget = urwid.Text(u"")
 
139
        self._focus_text_widget = urwid.Text("")
133
140
        super(MandosClientWidget, self).__init__(
134
141
            update_hook=update_hook, delete_hook=delete_hook,
135
142
            *args, **kwargs)
137
144
        self.opened = False
138
145
        
139
146
        last_checked_ok = isoformat_to_datetime(self.properties
140
 
                                                [u"LastCheckedOK"])
 
147
                                                ["LastCheckedOK"])
141
148
        if last_checked_ok is None:
142
149
            self.last_checker_failed = True
143
150
        else:
146
153
                                        > datetime.timedelta
147
154
                                        (milliseconds=
148
155
                                         self.properties
149
 
                                         [u"Interval"]))
 
156
                                         ["Interval"]))
150
157
        
151
158
        if self.last_checker_failed:
152
159
            self.using_timer(True)
154
161
        if self.need_approval:
155
162
            self.using_timer(True)
156
163
        
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)
 
164
        self.match_objects = (
 
165
            self.proxy.connect_to_signal("CheckerCompleted",
 
166
                                         self.checker_completed,
 
167
                                         client_interface,
 
168
                                         byte_arrays=True),
 
169
            self.proxy.connect_to_signal("CheckerStarted",
 
170
                                         self.checker_started,
 
171
                                         client_interface,
 
172
                                         byte_arrays=True),
 
173
            self.proxy.connect_to_signal("GotSecret",
 
174
                                         self.got_secret,
 
175
                                         client_interface,
 
176
                                         byte_arrays=True),
 
177
            self.proxy.connect_to_signal("NeedApproval",
 
178
                                         self.need_approval,
 
179
                                         client_interface,
 
180
                                         byte_arrays=True),
 
181
            self.proxy.connect_to_signal("Rejected",
 
182
                                         self.rejected,
 
183
                                         client_interface,
 
184
                                         byte_arrays=True))
 
185
        #self.logger('Created client %s' % (self.properties["Name"]))
177
186
    
178
187
    def property_changed(self, property=None, value=None):
179
188
        super(self, MandosClientWidget).property_changed(property,
180
189
                                                         value)
181
 
        if property == u"ApprovalPending":
 
190
        if property == "ApprovalPending":
182
191
            using_timer(bool(value))
183
192
        
184
193
    def using_timer(self, flag):
191
200
        else:
192
201
            self._update_timer_callback_lock -= 1
193
202
        if old == 0 and self._update_timer_callback_lock:
 
203
            # Will update the shown timer value every second
194
204
            self._update_timer_callback_tag = (gobject.timeout_add
195
205
                                               (1000,
196
206
                                                self.update_timer))
203
213
            if self.last_checker_failed:
204
214
                self.last_checker_failed = False
205
215
                self.using_timer(False)
206
 
            #self.logger(u'Checker for client %s (command "%s")'
207
 
            #            u' was successful'
208
 
            #            % (self.properties[u"Name"], command))
 
216
            #self.logger('Checker for client %s (command "%s")'
 
217
            #            ' was successful'
 
218
            #            % (self.properties["Name"], command))
209
219
            self.update()
210
220
            return
211
221
        # Checker failed
213
223
            self.last_checker_failed = True
214
224
            self.using_timer(True)
215
225
        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,
 
226
            self.logger('Checker for client %s (command "%s")'
 
227
                        ' failed with exit code %s'
 
228
                        % (self.properties["Name"], command,
219
229
                           os.WEXITSTATUS(condition)))
220
230
        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,
 
231
            self.logger('Checker for client %s (command "%s")'
 
232
                        ' was killed by signal %s'
 
233
                        % (self.properties["Name"], command,
224
234
                           os.WTERMSIG(condition)))
225
235
        elif os.WCOREDUMP(condition):
226
 
            self.logger(u'Checker for client %s (command "%s")'
227
 
                        u' dumped core'
228
 
                        % (self.properties[u"Name"], command))
 
236
            self.logger('Checker for client %s (command "%s")'
 
237
                        ' dumped core'
 
238
                        % (self.properties["Name"], command))
229
239
        else:
230
 
            self.logger(u'Checker for client %s completed'
231
 
                        u' mysteriously')
 
240
            self.logger('Checker for client %s completed'
 
241
                        ' mysteriously')
232
242
        self.update()
233
243
    
234
244
    def checker_started(self, command):
235
 
        #self.logger(u'Client %s started checker "%s"'
236
 
        #            % (self.properties[u"Name"], unicode(command)))
 
245
        #self.logger('Client %s started checker "%s"'
 
246
        #            % (self.properties["Name"], unicode(command)))
237
247
        pass
238
248
    
239
249
    def got_secret(self):
240
250
        self.last_checker_failed = False
241
 
        self.logger(u'Client %s received its secret'
242
 
                    % self.properties[u"Name"])
 
251
        self.logger('Client %s received its secret'
 
252
                    % self.properties["Name"])
243
253
    
244
254
    def need_approval(self, timeout, default):
245
255
        if not default:
246
 
            message = u'Client %s needs approval within %s seconds'
 
256
            message = 'Client %s needs approval within %s seconds'
247
257
        else:
248
 
            message = u'Client %s will get its secret in %s seconds'
 
258
            message = 'Client %s will get its secret in %s seconds'
249
259
        self.logger(message
250
 
                    % (self.properties[u"Name"], timeout/1000))
 
260
                    % (self.properties["Name"], timeout/1000))
251
261
        self.using_timer(True)
252
262
    
253
263
    def rejected(self, reason):
254
 
        self.logger(u'Client %s was rejected; reason: %s'
255
 
                    % (self.properties[u"Name"], reason))
 
264
        self.logger('Client %s was rejected; reason: %s'
 
265
                    % (self.properties["Name"], reason))
256
266
    
257
267
    def selectable(self):
258
268
        """Make this a "selectable" widget.
273
283
    def update(self):
274
284
        "Called when what is visible on the screen should be updated."
275
285
        # 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",
 
286
        with_standout = { "normal": "standout",
 
287
                          "bold": "bold-standout",
 
288
                          "underline-blink":
 
289
                              "underline-blink-standout",
 
290
                          "bold-underline-blink":
 
291
                              "bold-underline-blink-standout",
282
292
                          }
283
293
 
284
294
        # Rebuild focus and non-focus widgets using current properties
285
295
 
286
296
        # 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"]:
 
297
        base = ('%(name)s: '
 
298
                      % {"name": self.properties["Name"]})
 
299
        if not self.properties["Enabled"]:
 
300
            message = "DISABLED"
 
301
        elif self.properties["ApprovalPending"]:
292
302
            timeout = datetime.timedelta(milliseconds
293
303
                                         = self.properties
294
 
                                         [u"ApprovalDelay"])
 
304
                                         ["ApprovalDelay"])
295
305
            last_approval_request = isoformat_to_datetime(
296
 
                self.properties[u"LastApprovalRequest"])
 
306
                self.properties["LastApprovalRequest"])
297
307
            if last_approval_request is not None:
298
308
                timer = timeout - (datetime.datetime.utcnow()
299
309
                                   - last_approval_request)
300
310
            else:
301
311
                timer = datetime.timedelta()
302
 
            if self.properties[u"ApprovedByDefault"]:
303
 
                message = u"Approval in %s. (d)eny?"
 
312
            if self.properties["ApprovedByDefault"]:
 
313
                message = "Approval in %s. (d)eny?"
304
314
            else:
305
 
                message = u"Denial in %s. (a)pprove?"
 
315
                message = "Denial in %s. (a)pprove?"
306
316
            message = message % unicode(timer).rsplit(".", 1)[0]
307
317
        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'
 
318
            # When checker has failed, print a timer until client expires
 
319
            expires = self.properties["Expires"]
 
320
            if expires == "":
 
321
                timer = datetime.timedelta(0)
 
322
            else:
 
323
                expires = datetime.datetime.strptime(expires,
 
324
                                                     '%Y-%m-%dT%H:%M:%S.%f')
 
325
                timer = expires - datetime.datetime.utcnow()
 
326
            message = ('A checker has failed! Time until client'
 
327
                       ' gets disabled: %s'
318
328
                           % unicode(timer).rsplit(".", 1)[0])
319
329
        else:
320
 
            message = u"enabled"
321
 
        self._text = u"%s%s" % (base, message)
 
330
            message = "enabled"
 
331
        self._text = "%s%s" % (base, message)
322
332
            
323
333
        if not urwid.supports_unicode():
324
 
            self._text = self._text.encode(u"ascii", u"replace")
325
 
        textlist = [(u"normal", self._text)]
 
334
            self._text = self._text.encode("ascii", "replace")
 
335
        textlist = [("normal", self._text)]
326
336
        self._text_widget.set_text(textlist)
327
337
        self._focus_text_widget.set_text([(with_standout[text[0]],
328
338
                                           text[1])
331
341
                                          for text in textlist])
332
342
        self._widget = self._text_widget
333
343
        self._focus_widget = urwid.AttrWrap(self._focus_text_widget,
334
 
                                            u"standout")
 
344
                                            "standout")
335
345
        # Run update hook, if any
336
346
        if self.update_hook is not None:
337
347
            self.update_hook()
338
348
    
339
349
    def update_timer(self):
340
 
        "called by gobject"
 
350
        """called by gobject. Will indefinitely loop until
 
351
        gobject.source_remove() on tag is called"""
341
352
        self.update()
342
353
        return True             # Keep calling this
343
354
    
344
 
    def delete(self):
 
355
    def delete(self, *args, **kwargs):
345
356
        if self._update_timer_callback_tag is not None:
346
357
            gobject.source_remove(self._update_timer_callback_tag)
347
358
            self._update_timer_callback_tag = None
 
359
        for match in self.match_objects:
 
360
            match.remove()
 
361
        self.match_objects = ()
348
362
        if self.delete_hook is not None:
349
363
            self.delete_hook(self)
 
364
        return super(MandosClientWidget, self).delete(*args, **kwargs)
350
365
    
351
366
    def render(self, maxcolrow, focus=False):
352
367
        """Render differently if we have focus.
357
372
    def keypress(self, maxcolrow, key):
358
373
        """Handle keys.
359
374
        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":
 
375
        if key == "+":
 
376
            self.proxy.Enable(dbus_interface = client_interface,
 
377
                              ignore_reply=True)
 
378
        elif key == "-":
 
379
            self.proxy.Disable(dbus_interface = client_interface,
 
380
                               ignore_reply=True)
 
381
        elif key == "a":
365
382
            self.proxy.Approve(dbus.Boolean(True, variant_level=1),
366
 
                               dbus_interface = client_interface)
367
 
        elif key == u"d":
 
383
                               dbus_interface = client_interface,
 
384
                               ignore_reply=True)
 
385
        elif key == "d":
368
386
            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":
 
387
                                  dbus_interface = client_interface,
 
388
                               ignore_reply=True)
 
389
        elif key == "R" or key == "_" or key == "ctrl k":
371
390
            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)
 
391
                                                  .object_path,
 
392
                                                  ignore_reply=True)
 
393
        elif key == "s":
 
394
            self.proxy.StartChecker(dbus_interface = client_interface,
 
395
                                    ignore_reply=True)
 
396
        elif key == "S":
 
397
            self.proxy.StopChecker(dbus_interface = client_interface,
 
398
                                   ignore_reply=True)
 
399
        elif key == "C":
 
400
            self.proxy.CheckedOK(dbus_interface = client_interface,
 
401
                                 ignore_reply=True)
379
402
        # xxx
380
 
#         elif key == u"p" or key == "=":
 
403
#         elif key == "p" or key == "=":
381
404
#             self.proxy.pause()
382
 
#         elif key == u"u" or key == ":":
 
405
#         elif key == "u" or key == ":":
383
406
#             self.proxy.unpause()
384
 
#         elif key == u"RET":
 
407
#         elif key == "RET":
385
408
#             self.open()
386
409
        else:
387
410
            return key
405
428
    """
406
429
    def keypress(self, maxcolrow, key):
407
430
        ret = super(ConstrainedListBox, self).keypress(maxcolrow, key)
408
 
        if ret in (u"up", u"down"):
 
431
        if ret in ("up", "down"):
409
432
            return
410
433
        return ret
411
434
 
420
443
        self.screen = urwid.curses_display.Screen()
421
444
        
422
445
        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")),
 
446
                ("normal",
 
447
                 "default", "default", None),
 
448
                ("bold",
 
449
                 "default", "default", "bold"),
 
450
                ("underline-blink",
 
451
                 "default", "default", "underline"),
 
452
                ("standout",
 
453
                 "default", "default", "standout"),
 
454
                ("bold-underline-blink",
 
455
                 "default", "default", ("bold", "underline")),
 
456
                ("bold-standout",
 
457
                 "default", "default", ("bold", "standout")),
 
458
                ("underline-blink-standout",
 
459
                 "default", "default", ("underline", "standout")),
 
460
                ("bold-underline-blink-standout",
 
461
                 "default", "default", ("bold", "underline",
 
462
                                          "standout")),
440
463
                ))
441
464
        
442
465
        if urwid.supports_unicode():
443
 
            self.divider = u"─" # \u2500
444
 
            #self.divider = u"━" # \u2501
 
466
            self.divider = "─" # \u2500
 
467
            #self.divider = "━" # \u2501
445
468
        else:
446
 
            #self.divider = u"-" # \u002d
447
 
            self.divider = u"_" # \u005f
 
469
            #self.divider = "-" # \u002d
 
470
            self.divider = "_" # \u005f
448
471
        
449
472
        self.screen.start()
450
473
        
464
487
        # This keeps track of whether self.uilist currently has
465
488
        # self.logbox in it or not
466
489
        self.log_visible = True
467
 
        self.log_wrap = u"any"
 
490
        self.log_wrap = "any"
468
491
        
469
492
        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"))
 
493
        self.log_message_raw(("bold",
 
494
                              "Mandos Monitor version " + version))
 
495
        self.log_message_raw(("bold",
 
496
                              "q: Quit  ?: Help"))
474
497
        
475
498
        self.busname = domain + '.Mandos'
476
499
        self.main_loop = gobject.MainLoop()
477
500
        self.bus = dbus.SystemBus()
478
501
        mandos_dbus_objc = self.bus.get_object(
479
 
            self.busname, u"/", follow_name_owner_changes=True)
 
502
            self.busname, "/", follow_name_owner_changes=True)
480
503
        self.mandos_serv = dbus.Interface(mandos_dbus_objc,
481
504
                                          dbus_interface
482
505
                                          = server_interface)
487
510
            mandos_clients = dbus.Dictionary()
488
511
        
489
512
        (self.mandos_serv
490
 
         .connect_to_signal(u"ClientRemoved",
 
513
         .connect_to_signal("ClientRemoved",
491
514
                            self.find_and_remove_client,
492
515
                            dbus_interface=server_interface,
493
516
                            byte_arrays=True))
494
517
        (self.mandos_serv
495
 
         .connect_to_signal(u"ClientAdded",
 
518
         .connect_to_signal("ClientAdded",
496
519
                            self.add_new_client,
497
520
                            dbus_interface=server_interface,
498
521
                            byte_arrays=True))
499
522
        (self.mandos_serv
500
 
         .connect_to_signal(u"ClientNotFound",
 
523
         .connect_to_signal("ClientNotFound",
501
524
                            self.client_not_found,
502
525
                            dbus_interface=server_interface,
503
526
                            byte_arrays=True))
518
541
                            path=path)
519
542
    
520
543
    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,
 
544
        self.log_message(("Client with address %s and fingerprint %s"
 
545
                          " could not be found" % (address,
523
546
                                                    fingerprint)))
524
547
    
525
548
    def rebuild(self):
541
564
    
542
565
    def log_message(self, message):
543
566
        timestamp = datetime.datetime.now().isoformat()
544
 
        self.log_message_raw(timestamp + u": " + message)
 
567
        self.log_message_raw(timestamp + ": " + message)
545
568
    
546
569
    def log_message_raw(self, markup):
547
570
        """Add a log message to the log buffer."""
550
573
            and len(self.log) > self.max_log_length):
551
574
            del self.log[0:len(self.log)-self.max_log_length-1]
552
575
        self.logbox.set_focus(len(self.logbox.body.contents),
553
 
                              coming_from=u"above")
 
576
                              coming_from="above")
554
577
        self.refresh()
555
578
    
556
579
    def toggle_log_display(self):
557
580
        """Toggle visibility of the log buffer."""
558
581
        self.log_visible = not self.log_visible
559
582
        self.rebuild()
560
 
        #self.log_message(u"Log visibility changed to: "
 
583
        #self.log_message("Log visibility changed to: "
561
584
        #                 + unicode(self.log_visible))
562
585
    
563
586
    def change_log_display(self):
564
587
        """Change type of log display.
565
588
        Currently, this toggles wrapping of text lines."""
566
 
        if self.log_wrap == u"clip":
567
 
            self.log_wrap = u"any"
 
589
        if self.log_wrap == "clip":
 
590
            self.log_wrap = "any"
568
591
        else:
569
 
            self.log_wrap = u"clip"
 
592
            self.log_wrap = "clip"
570
593
        for textwidget in self.log:
571
594
            textwidget.set_wrap_mode(self.log_wrap)
572
 
        #self.log_message(u"Wrap mode: " + self.log_wrap)
 
595
        #self.log_message("Wrap mode: " + self.log_wrap)
573
596
    
574
597
    def find_and_remove_client(self, path, name):
575
 
        """Find an client from its object path and remove it.
 
598
        """Find a client by its object path and remove it.
576
599
        
577
600
        This is connected to the ClientRemoved signal from the
578
601
        Mandos server object."""
580
603
            client = self.clients_dict[path]
581
604
        except KeyError:
582
605
            # not found?
 
606
            self.log_message("Unknown client %r (%r) removed", name,
 
607
                             path)
583
608
            return
584
 
        self.remove_client(client, path)
 
609
        client.delete()
585
610
    
586
611
    def add_new_client(self, path):
587
612
        client_proxy_object = self.bus.get_object(self.busname, path)
602
627
        if path is None:
603
628
            path = client.proxy.object_path
604
629
        self.clients_dict[path] = client
605
 
        self.clients.sort(None, lambda c: c.properties[u"Name"])
 
630
        self.clients.sort(None, lambda c: c.properties["Name"])
606
631
        self.refresh()
607
632
    
608
633
    def remove_client(self, client, path=None):
639
664
    
640
665
    def process_input(self, source, condition):
641
666
        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
 
667
        translations = { "ctrl n": "down",      # Emacs
 
668
                         "ctrl p": "up",        # Emacs
 
669
                         "ctrl v": "page down", # Emacs
 
670
                         "meta v": "page up",   # Emacs
 
671
                         " ": "page down",      # less
 
672
                         "f": "page down",      # less
 
673
                         "b": "page up",        # less
 
674
                         "j": "down",           # vi
 
675
                         "k": "up",             # vi
651
676
                         }
652
677
        for key in keys:
653
678
            try:
655
680
            except KeyError:    # :-)
656
681
                pass
657
682
            
658
 
            if key == u"q" or key == u"Q":
 
683
            if key == "q" or key == "Q":
659
684
                self.stop()
660
685
                break
661
 
            elif key == u"window resize":
 
686
            elif key == "window resize":
662
687
                self.size = self.screen.get_cols_rows()
663
688
                self.refresh()
664
 
            elif key == u"\f":  # Ctrl-L
 
689
            elif key == "\f":  # Ctrl-L
665
690
                self.refresh()
666
 
            elif key == u"l" or key == u"D":
 
691
            elif key == "l" or key == "D":
667
692
                self.toggle_log_display()
668
693
                self.refresh()
669
 
            elif key == u"w" or key == u"i":
 
694
            elif key == "w" or key == "i":
670
695
                self.change_log_display()
671
696
                self.refresh()
672
 
            elif key == u"?" or key == u"f1" or key == u"esc":
 
697
            elif key == "?" or key == "f1" or key == "esc":
673
698
                if not self.log_visible:
674
699
                    self.log_visible = True
675
700
                    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"))))
 
701
                self.log_message_raw(("bold",
 
702
                                      "  ".
 
703
                                      join(("q: Quit",
 
704
                                            "?: Help",
 
705
                                            "l: Log window toggle",
 
706
                                            "TAB: Switch window",
 
707
                                            "w: Wrap (log)"))))
 
708
                self.log_message_raw(("bold",
 
709
                                      "  "
 
710
                                      .join(("Clients:",
 
711
                                             "+: Enable",
 
712
                                             "-: Disable",
 
713
                                             "R: Remove",
 
714
                                             "s: Start new checker",
 
715
                                             "S: Stop checker",
 
716
                                             "C: Checker OK",
 
717
                                             "a: Approve",
 
718
                                             "d: Deny"))))
694
719
                self.refresh()
695
 
            elif key == u"tab":
 
720
            elif key == "tab":
696
721
                if self.topwidget.get_focus() is self.logbox:
697
722
                    self.topwidget.set_focus(0)
698
723
                else:
699
724
                    self.topwidget.set_focus(self.logbox)
700
725
                self.refresh()
701
 
            #elif (key == u"end" or key == u"meta >" or key == u"G"
702
 
            #      or key == u">"):
 
726
            #elif (key == "end" or key == "meta >" or key == "G"
 
727
            #      or key == ">"):
703
728
            #    pass            # xxx end-of-buffer
704
 
            #elif (key == u"home" or key == u"meta <" or key == u"g"
705
 
            #      or key == u"<"):
 
729
            #elif (key == "home" or key == "meta <" or key == "g"
 
730
            #      or key == "<"):
706
731
            #    pass            # xxx beginning-of-buffer
707
 
            #elif key == u"ctrl e" or key == u"$":
 
732
            #elif key == "ctrl e" or key == "$":
708
733
            #    pass            # xxx move-end-of-line
709
 
            #elif key == u"ctrl a" or key == u"^":
 
734
            #elif key == "ctrl a" or key == "^":
710
735
            #    pass            # xxx move-beginning-of-line
711
 
            #elif key == u"ctrl b" or key == u"meta (" or key == u"h":
 
736
            #elif key == "ctrl b" or key == "meta (" or key == "h":
712
737
            #    pass            # xxx left
713
 
            #elif key == u"ctrl f" or key == u"meta )" or key == u"l":
 
738
            #elif key == "ctrl f" or key == "meta )" or key == "l":
714
739
            #    pass            # xxx right
715
 
            #elif key == u"a":
 
740
            #elif key == "a":
716
741
            #    pass            # scroll up log
717
 
            #elif key == u"z":
 
742
            #elif key == "z":
718
743
            #    pass            # scroll down log
719
744
            elif self.topwidget.selectable():
720
745
                self.topwidget.keypress(self.size, key)