/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: Björn Påhlsson
  • Date: 2010-09-07 20:22:16 UTC
  • mto: (237.4.3 mandos-release)
  • mto: This revision was merged to the branch mainline in revision 421.
  • Revision ID: belorn@fukt.bsnet.se-20100907202216-vypcn3nsd02mqz4s
mandos-monitor: removed milisecondsseconds from countdown.

Show diffs side-by-side

added added

removed removed

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