15
41
from dbus.mainloop.glib import DBusGMainLoop
42
from gi.repository import GLib
24
locale.setlocale(locale.LC_ALL, u'')
50
if sys.version_info.major == 2:
53
locale.setlocale(locale.LC_ALL, '')
55
logging.getLogger('dbus.proxies').setLevel(logging.CRITICAL)
26
57
# Some useful constants
27
domain = 'se.bsnet.fukt'
58
domain = 'se.recompile'
28
59
server_interface = domain + '.Mandos'
29
60
client_interface = domain + '.Mandos.Client'
32
# Always run in monochrome mode
33
urwid.curses_display.curses.has_colors = lambda : False
35
# Urwid doesn't support blinking, but we want it. Since we have no
36
# use for underline on its own, we make underline also always blink.
37
urwid.curses_display.curses.A_UNDERLINE |= (
38
urwid.curses_display.curses.A_BLINK)
64
dbus.OBJECT_MANAGER_IFACE
65
except AttributeError:
66
dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
40
69
def isoformat_to_datetime(iso):
41
70
"Parse an ISO 8601 date string to a datetime.datetime()"
44
d, t = iso.split(u"T", 1)
45
year, month, day = d.split(u"-", 2)
46
hour, minute, second = t.split(u":", 2)
73
d, t = iso.split("T", 1)
74
year, month, day = d.split("-", 2)
75
hour, minute, second = t.split(":", 2)
47
76
second, fraction = divmod(float(second), 1)
48
77
return datetime.datetime(int(year),
53
int(second), # Whole seconds
54
int(fraction*1000000)) # Microseconds
82
int(second), # Whole seconds
83
int(fraction*1000000)) # Microseconds
56
86
class MandosClientPropertyCache(object):
57
87
"""This wraps a Mandos Client D-Bus proxy object, caches the
58
88
properties and calls a hook function when any of them are
61
def __init__(self, proxy_object=None, *args, **kwargs):
62
self.proxy = proxy_object # Mandos Client proxy object
64
self.properties = dict()
65
self.proxy.connect_to_signal(u"PropertyChanged",
66
self.property_changed,
70
self.properties.update(
71
self.proxy.GetAll(client_interface,
72
dbus_interface = dbus.PROPERTIES_IFACE))
73
super(MandosClientPropertyCache, self).__init__(
74
proxy_object=proxy_object, *args, **kwargs)
76
def property_changed(self, property=None, value=None):
77
"""This is called whenever we get a PropertyChanged signal
78
It updates the changed property in the "properties" dict.
91
def __init__(self, proxy_object=None, properties=None, **kwargs):
92
self.proxy = proxy_object # Mandos Client proxy object
93
self.properties = dict() if properties is None else properties
94
self.property_changed_match = (
95
self.proxy.connect_to_signal("PropertiesChanged",
96
self.properties_changed,
97
dbus.PROPERTIES_IFACE,
100
if properties is None:
101
self.properties.update(self.proxy.GetAll(
103
dbus_interface=dbus.PROPERTIES_IFACE))
105
super(MandosClientPropertyCache, self).__init__(**kwargs)
107
def properties_changed(self, interface, properties, invalidated):
108
"""This is called whenever we get a PropertiesChanged signal
109
It updates the changed properties in the "properties" dict.
80
111
# Update properties dict with new value
81
self.properties[property] = value
112
if interface == client_interface:
113
self.properties.update(properties)
116
self.property_changed_match.remove()
84
119
class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
85
120
"""A Mandos Client which is visible on the screen.
88
123
def __init__(self, server_proxy_object=None, update_hook=None,
89
delete_hook=None, logger=None, *args, **kwargs):
124
delete_hook=None, logger=None, **kwargs):
90
125
# Called on update
91
126
self.update_hook = update_hook
92
127
# Called on delete
95
130
self.server_proxy_object = server_proxy_object
97
132
self.logger = logger
99
134
self._update_timer_callback_tag = None
100
self.last_checker_failed = False
102
136
# The widget shown normally
103
self._text_widget = urwid.Text(u"")
137
self._text_widget = urwid.Text("")
104
138
# The widget shown when we have focus
105
self._focus_text_widget = urwid.Text(u"")
106
super(MandosClientWidget, self).__init__(
107
update_hook=update_hook, delete_hook=delete_hook,
139
self._focus_text_widget = urwid.Text("")
140
super(MandosClientWidget, self).__init__(**kwargs)
110
142
self.opened = False
111
self.proxy.connect_to_signal(u"CheckerCompleted",
112
self.checker_completed,
115
self.proxy.connect_to_signal(u"CheckerStarted",
116
self.checker_started,
119
self.proxy.connect_to_signal(u"GotSecret",
123
self.proxy.connect_to_signal(u"Rejected",
127
last_checked_ok = isoformat_to_datetime(self.properties
129
if last_checked_ok is None:
130
self.last_checker_failed = True
132
self.last_checker_failed = ((datetime.datetime.utcnow()
136
self.properties["interval"]))
137
if self.last_checker_failed:
138
self._update_timer_callback_tag = (gobject.timeout_add
144
self.match_objects = (
145
self.proxy.connect_to_signal("CheckerCompleted",
146
self.checker_completed,
149
self.proxy.connect_to_signal("CheckerStarted",
150
self.checker_started,
153
self.proxy.connect_to_signal("GotSecret",
157
self.proxy.connect_to_signal("NeedApproval",
161
self.proxy.connect_to_signal("Rejected",
165
self.logger('Created client {}'
166
.format(self.properties["Name"]), level=0)
168
def using_timer(self, flag):
169
"""Call this method with True or False when timer should be
170
activated or deactivated.
172
if flag and self._update_timer_callback_tag is None:
173
# Will update the shown timer value every second
174
self._update_timer_callback_tag = (GLib.timeout_add
140
176
self.update_timer))
177
elif not (flag or self._update_timer_callback_tag is None):
178
GLib.source_remove(self._update_timer_callback_tag)
179
self._update_timer_callback_tag = None
142
181
def checker_completed(self, exitstatus, condition, command):
143
182
if exitstatus == 0:
144
if self.last_checker_failed:
145
self.last_checker_failed = False
146
gobject.source_remove(self._update_timer_callback_tag)
147
self._update_timer_callback_tag = None
148
self.logger(u'Checker for client %s (command "%s")'
150
% (self.properties[u"name"], command))
183
self.logger('Checker for client {} (command "{}")'
184
' succeeded'.format(self.properties["Name"],
154
if not self.last_checker_failed:
155
self.last_checker_failed = True
156
self._update_timer_callback_tag = (gobject.timeout_add
159
189
if os.WIFEXITED(condition):
160
self.logger(u'Checker for client %s (command "%s")'
161
u' failed with exit code %s'
162
% (self.properties[u"name"], command,
163
os.WEXITSTATUS(condition)))
190
self.logger('Checker for client {} (command "{}") failed'
192
.format(self.properties["Name"], command,
193
os.WEXITSTATUS(condition)))
164
194
elif os.WIFSIGNALED(condition):
165
self.logger(u'Checker for client %s (command "%s")'
166
u' was killed by signal %s'
167
% (self.properties[u"name"], command,
168
os.WTERMSIG(condition)))
169
elif os.WCOREDUMP(condition):
170
self.logger(u'Checker for client %s (command "%s")'
172
% (self.properties[u"name"], command))
174
self.logger(u'Checker for client %s completed mysteriously')
195
self.logger('Checker for client {} (command "{}") was'
196
' killed by signal {}'
197
.format(self.properties["Name"], command,
198
os.WTERMSIG(condition)))
177
201
def checker_started(self, command):
178
self.logger(u'Client %s started checker "%s"'
179
% (self.properties[u"name"], unicode(command)))
202
"""Server signals that a checker started."""
203
self.logger('Client {} started checker "{}"'
204
.format(self.properties["Name"],
181
207
def got_secret(self):
182
self.logger(u'Client %s received its secret'
183
% self.properties[u"name"])
186
self.logger(u'Client %s was rejected'
187
% self.properties[u"name"])
208
self.logger('Client {} received its secret'
209
.format(self.properties["Name"]))
211
def need_approval(self, timeout, default):
213
message = 'Client {} needs approval within {} seconds'
215
message = 'Client {} will get its secret in {} seconds'
216
self.logger(message.format(self.properties["Name"],
219
def rejected(self, reason):
220
self.logger('Client {} was rejected; reason: {}'
221
.format(self.properties["Name"], reason))
189
223
def selectable(self):
190
224
"""Make this a "selectable" widget.
191
225
This overrides the method from urwid.FlowWidget."""
194
def rows(self, (maxcol,), focus=False):
228
def rows(self, maxcolrow, focus=False):
195
229
"""How many rows this widget will occupy might depend on
196
230
whether we have focus or not.
197
231
This overrides the method from urwid.FlowWidget"""
198
return self.current_widget(focus).rows((maxcol,), focus=focus)
232
return self.current_widget(focus).rows(maxcolrow, focus=focus)
200
234
def current_widget(self, focus=False):
201
235
if focus or self.opened:
202
236
return self._focus_widget
203
237
return self._widget
205
239
def update(self):
206
240
"Called when what is visible on the screen should be updated."
207
241
# How to add standout mode to a style
208
with_standout = { u"normal": u"standout",
209
u"bold": u"bold-standout",
211
u"underline-blink-standout",
212
u"bold-underline-blink":
213
u"bold-underline-blink-standout",
242
with_standout = {"normal": "standout",
243
"bold": "bold-standout",
245
"underline-blink-standout",
246
"bold-underline-blink":
247
"bold-underline-blink-standout",
216
250
# Rebuild focus and non-focus widgets using current properties
217
self._text = (u'%(name)s: %(enabled)s%(timer)s'
218
% { u"name": self.properties[u"name"],
221
if self.properties[u"enabled"]
223
u"timer": (unicode(datetime.timedelta
229
- isoformat_to_datetime
230
(max((self.properties
235
self.properties[u"last_enabled"]))))
236
if (self.last_checker_failed
252
# Base part of a client. Name!
253
base = '{name}: '.format(name=self.properties["Name"])
254
if not self.properties["Enabled"]:
256
self.using_timer(False)
257
elif self.properties["ApprovalPending"]:
258
timeout = datetime.timedelta(
259
milliseconds=self.properties["ApprovalDelay"])
260
last_approval_request = isoformat_to_datetime(
261
self.properties["LastApprovalRequest"])
262
if last_approval_request is not None:
263
timer = max(timeout - (datetime.datetime.utcnow()
264
- last_approval_request),
265
datetime.timedelta())
267
timer = datetime.timedelta()
268
if self.properties["ApprovedByDefault"]:
269
message = "Approval in {}. (d)eny?"
271
message = "Denial in {}. (a)pprove?"
272
message = message.format(str(timer).rsplit(".", 1)[0])
273
self.using_timer(True)
274
elif self.properties["LastCheckerStatus"] != 0:
275
# When checker has failed, show timer until client expires
276
expires = self.properties["Expires"]
278
timer = datetime.timedelta(0)
280
expires = (datetime.datetime.strptime
281
(expires, '%Y-%m-%dT%H:%M:%S.%f'))
282
timer = max(expires - datetime.datetime.utcnow(),
283
datetime.timedelta())
284
message = ('A checker has failed! Time until client'
286
.format(str(timer).rsplit(".", 1)[0]))
287
self.using_timer(True)
290
self.using_timer(False)
291
self._text = "{}{}".format(base, message)
240
293
if not urwid.supports_unicode():
241
294
self._text = self._text.encode("ascii", "replace")
242
textlist = [(u"normal", self._text)]
295
textlist = [("normal", self._text)]
243
296
self._text_widget.set_text(textlist)
244
297
self._focus_text_widget.set_text([(with_standout[text[0]],
252
305
# Run update hook, if any
253
306
if self.update_hook is not None:
254
307
self.update_hook()
256
309
def update_timer(self):
310
"""called by GLib. Will indefinitely loop until
311
GLib.source_remove() on tag is called
259
314
return True # Keep calling this
316
def delete(self, **kwargs):
262
317
if self._update_timer_callback_tag is not None:
263
gobject.source_remove(self._update_timer_callback_tag)
318
GLib.source_remove(self._update_timer_callback_tag)
264
319
self._update_timer_callback_tag = None
320
for match in self.match_objects:
322
self.match_objects = ()
265
323
if self.delete_hook is not None:
266
324
self.delete_hook(self)
268
def render(self, (maxcol,), focus=False):
325
return super(MandosClientWidget, self).delete(**kwargs)
327
def render(self, maxcolrow, focus=False):
269
328
"""Render differently if we have focus.
270
329
This overrides the method from urwid.FlowWidget"""
271
return self.current_widget(focus).render((maxcol,),
330
return self.current_widget(focus).render(maxcolrow,
274
def keypress(self, (maxcol,), key):
333
def keypress(self, maxcolrow, key):
276
335
This overrides the method from urwid.FlowWidget"""
277
if key == u"e" or key == u"+":
279
elif key == u"d" or key == u"-":
281
elif key == u"r" or key == u"_" or key == u"ctrl k":
337
self.proxy.Set(client_interface, "Enabled",
338
dbus.Boolean(True), ignore_reply=True,
339
dbus_interface=dbus.PROPERTIES_IFACE)
341
self.proxy.Set(client_interface, "Enabled", False,
343
dbus_interface=dbus.PROPERTIES_IFACE)
345
self.proxy.Approve(dbus.Boolean(True, variant_level=1),
346
dbus_interface=client_interface,
349
self.proxy.Approve(dbus.Boolean(False, variant_level=1),
350
dbus_interface=client_interface,
352
elif key == "R" or key == "_" or key == "ctrl k":
282
353
self.server_proxy_object.RemoveClient(self.proxy
285
self.proxy.StartChecker()
287
self.proxy.StopChecker()
289
self.proxy.CheckedOK()
357
self.proxy.Set(client_interface, "CheckerRunning",
358
dbus.Boolean(True), ignore_reply=True,
359
dbus_interface=dbus.PROPERTIES_IFACE)
361
self.proxy.Set(client_interface, "CheckerRunning",
362
dbus.Boolean(False), ignore_reply=True,
363
dbus_interface=dbus.PROPERTIES_IFACE)
365
self.proxy.CheckedOK(dbus_interface=client_interface,
291
# elif key == u"p" or key == "=":
368
# elif key == "p" or key == "=":
292
369
# self.proxy.pause()
293
# elif key == u"u" or key == ":":
370
# elif key == "u" or key == ":":
294
371
# self.proxy.unpause()
295
# elif key == u"RET":
300
def property_changed(self, property=None, value=None,
302
"""Call self.update() if old value is not new value.
377
def properties_changed(self, interface, properties, invalidated):
378
"""Call self.update() if any properties changed.
303
379
This overrides the method from MandosClientPropertyCache"""
304
property_name = unicode(property)
305
old_value = self.properties.get(property_name)
306
super(MandosClientWidget, self).property_changed(
307
property=property, value=value, *args, **kwargs)
308
if self.properties.get(property_name) != old_value:
380
old_values = {key: self.properties.get(key)
381
for key in properties.keys()}
382
super(MandosClientWidget, self).properties_changed(
383
interface, properties, invalidated)
384
if any(old_values[key] != self.properties.get(key)
385
for key in old_values):
325
403
"""This is the entire user interface - the whole screen
326
404
with boxes, lists of client widgets, etc.
328
def __init__(self, max_log_length=1000):
406
def __init__(self, max_log_length=1000, log_level=1):
329
407
DBusGMainLoop(set_as_default=True)
331
409
self.screen = urwid.curses_display.Screen()
333
411
self.screen.register_palette((
335
u"default", u"default", None),
337
u"default", u"default", u"bold"),
339
u"default", u"default", u"underline"),
341
u"default", u"default", u"standout"),
342
(u"bold-underline-blink",
343
u"default", u"default", (u"bold", u"underline")),
345
u"default", u"default", (u"bold", u"standout")),
346
(u"underline-blink-standout",
347
u"default", u"default", (u"underline", u"standout")),
348
(u"bold-underline-blink-standout",
349
u"default", u"default", (u"bold", u"underline",
413
"default", "default", None),
415
"bold", "default", "bold"),
417
"underline,blink", "default", "underline,blink"),
419
"standout", "default", "standout"),
420
("bold-underline-blink",
421
"bold,underline,blink", "default",
422
"bold,underline,blink"),
424
"bold,standout", "default", "bold,standout"),
425
("underline-blink-standout",
426
"underline,blink,standout", "default",
427
"underline,blink,standout"),
428
("bold-underline-blink-standout",
429
"bold,underline,blink,standout", "default",
430
"bold,underline,blink,standout"),
353
433
if urwid.supports_unicode():
354
self.divider = u"─" # \u2500
355
#self.divider = u"━" # \u2501
434
self.divider = "─" # \u2500
357
#self.divider = u"-" # \u002d
358
self.divider = u"_" # \u005f
436
self.divider = "_" # \u005f
360
438
self.screen.start()
362
440
self.size = self.screen.get_cols_rows()
364
442
self.clients = urwid.SimpleListWalker([])
365
443
self.clients_dict = {}
367
445
# We will add Text widgets to this list
369
447
self.max_log_length = max_log_length
449
self.log_level = log_level
371
451
# We keep a reference to the log widget so we can remove it
372
452
# from the ListWalker without it getting destroyed
373
453
self.logbox = ConstrainedListBox(self.log)
375
455
# This keeps track of whether self.uilist currently has
376
456
# self.logbox in it or not
377
457
self.log_visible = True
378
self.log_wrap = u"any"
458
self.log_wrap = "any"
381
self.log_message_raw((u"bold",
382
u"Mandos Monitor version " + version))
383
self.log_message_raw((u"bold",
461
self.log_message_raw(("bold",
462
"Mandos Monitor version " + version))
463
self.log_message_raw(("bold",
386
466
self.busname = domain + '.Mandos'
387
self.main_loop = gobject.MainLoop()
388
self.bus = dbus.SystemBus()
389
mandos_dbus_objc = self.bus.get_object(
390
self.busname, u"/", follow_name_owner_changes=True)
391
self.mandos_serv = dbus.Interface(mandos_dbus_objc,
395
mandos_clients = (self.mandos_serv
396
.GetAllClientsWithProperties())
397
except dbus.exceptions.DBusException:
398
mandos_clients = dbus.Dictionary()
401
.connect_to_signal(u"ClientRemoved",
402
self.find_and_remove_client,
403
dbus_interface=server_interface,
406
.connect_to_signal(u"ClientAdded",
408
dbus_interface=server_interface,
411
.connect_to_signal(u"ClientNotFound",
412
self.client_not_found,
413
dbus_interface=server_interface,
415
for path, client in mandos_clients.iteritems():
416
client_proxy_object = self.bus.get_object(self.busname,
418
self.add_client(MandosClientWidget(server_proxy_object
421
=client_proxy_object,
467
self.main_loop = GLib.MainLoop()
431
469
def client_not_found(self, fingerprint, address):
432
self.log_message((u"Client with address %s and fingerprint %s"
433
u" could not be found" % (address,
470
self.log_message("Client with address {} and fingerprint {}"
471
" could not be found"
472
.format(address, fingerprint))
436
474
def rebuild(self):
437
475
"""This rebuilds the User Interface.
438
476
Call this when the widget layout needs to change"""
440
#self.uilist.append(urwid.ListBox(self.clients))
441
self.uilist.append(urwid.Frame(ConstrainedListBox(self.clients),
442
#header=urwid.Divider(),
478
# self.uilist.append(urwid.ListBox(self.clients))
479
self.uilist.append(urwid.Frame(ConstrainedListBox(self.
481
# header=urwid.Divider(),
444
footer=urwid.Divider(div_char=self.divider)))
483
footer=urwid.Divider(
484
div_char=self.divider)))
445
485
if self.log_visible:
446
486
self.uilist.append(self.logbox)
448
487
self.topwidget = urwid.Pile(self.uilist)
450
def log_message(self, message):
489
def log_message(self, message, level=1):
490
"""Log message formatted with timestamp"""
491
if level < self.log_level:
451
493
timestamp = datetime.datetime.now().isoformat()
452
self.log_message_raw(timestamp + u": " + message)
454
def log_message_raw(self, markup):
494
self.log_message_raw("{}: {}".format(timestamp, message),
497
def log_message_raw(self, markup, level=1):
455
498
"""Add a log message to the log buffer."""
499
if level < self.log_level:
456
501
self.log.append(urwid.Text(markup, wrap=self.log_wrap))
457
if (self.max_log_length
458
and len(self.log) > self.max_log_length):
459
del self.log[0:len(self.log)-self.max_log_length-1]
502
if self.max_log_length:
503
if len(self.log) > self.max_log_length:
504
del self.log[0:len(self.log)-self.max_log_length-1]
460
505
self.logbox.set_focus(len(self.logbox.body.contents),
461
coming_from=u"above")
464
509
def toggle_log_display(self):
465
510
"""Toggle visibility of the log buffer."""
466
511
self.log_visible = not self.log_visible
468
self.log_message(u"Log visibility changed to: "
469
+ unicode(self.log_visible))
513
self.log_message("Log visibility changed to: {}"
514
.format(self.log_visible), level=0)
471
516
def change_log_display(self):
472
517
"""Change type of log display.
473
518
Currently, this toggles wrapping of text lines."""
474
if self.log_wrap == u"clip":
475
self.log_wrap = u"any"
519
if self.log_wrap == "clip":
520
self.log_wrap = "any"
477
self.log_wrap = u"clip"
522
self.log_wrap = "clip"
478
523
for textwidget in self.log:
479
524
textwidget.set_wrap_mode(self.log_wrap)
480
self.log_message(u"Wrap mode: " + self.log_wrap)
482
def find_and_remove_client(self, path, name):
483
"""Find an client from its object path and remove it.
485
This is connected to the ClientRemoved signal from the
525
self.log_message("Wrap mode: {}".format(self.log_wrap),
528
def find_and_remove_client(self, path, interfaces):
529
"""Find a client by its object path and remove it.
531
This is connected to the InterfacesRemoved signal from the
486
532
Mandos server object."""
533
if client_interface not in interfaces:
534
# Not a Mandos client object; ignore
488
537
client = self.clients_dict[path]
492
self.remove_client(client, path)
494
def add_new_client(self, path):
540
self.log_message("Unknown client {!r} removed"
545
def add_new_client(self, path, ifs_and_props):
546
"""Find a client by its object path and remove it.
548
This is connected to the InterfacesAdded signal from the
549
Mandos server object.
551
if client_interface not in ifs_and_props:
552
# Not a Mandos client object; ignore
495
554
client_proxy_object = self.bus.get_object(self.busname, path)
496
self.add_client(MandosClientWidget(server_proxy_object
499
=client_proxy_object,
555
self.add_client(MandosClientWidget(
556
server_proxy_object=self.mandos_serv,
557
proxy_object=client_proxy_object,
558
update_hook=self.refresh,
559
delete_hook=self.remove_client,
560
logger=self.log_message,
561
properties=dict(ifs_and_props[client_interface])),
508
564
def add_client(self, client, path=None):
509
565
self.clients.append(client)
511
567
path = client.proxy.object_path
512
568
self.clients_dict[path] = client
513
self.clients.sort(None, lambda c: c.properties[u"name"])
569
self.clients.sort(key=lambda c: c.properties["Name"])
516
572
def remove_client(self, client, path=None):
517
573
self.clients.remove(client)
519
575
path = client.proxy.object_path
520
576
del self.clients_dict[path]
521
if not self.clients_dict:
522
# Work around bug in Urwid 0.9.8.3 - if a SimpleListWalker
523
# is completely emptied, we need to recreate it.
524
self.clients = urwid.SimpleListWalker([])
528
579
def refresh(self):
529
580
"""Redraw the screen"""
530
581
canvas = self.topwidget.render(self.size, focus=True)
531
582
self.screen.draw_screen(self.size, canvas)
534
585
"""Start the main loop and exit when it's done."""
586
self.bus = dbus.SystemBus()
587
mandos_dbus_objc = self.bus.get_object(
588
self.busname, "/", follow_name_owner_changes=True)
589
self.mandos_serv = dbus.Interface(
590
mandos_dbus_objc, dbus_interface=server_interface)
592
mandos_clients = (self.mandos_serv
593
.GetAllClientsWithProperties())
594
if not mandos_clients:
595
self.log_message_raw(("bold",
596
"Note: Server has no clients."))
597
except dbus.exceptions.DBusException:
598
self.log_message_raw(("bold",
599
"Note: No Mandos server running."))
600
mandos_clients = dbus.Dictionary()
603
.connect_to_signal("InterfacesRemoved",
604
self.find_and_remove_client,
605
dbus_interface=dbus.OBJECT_MANAGER_IFACE,
608
.connect_to_signal("InterfacesAdded",
610
dbus_interface=dbus.OBJECT_MANAGER_IFACE,
613
.connect_to_signal("ClientNotFound",
614
self.client_not_found,
615
dbus_interface=server_interface,
617
for path, client in mandos_clients.items():
618
client_proxy_object = self.bus.get_object(self.busname,
620
self.add_client(MandosClientWidget(
621
server_proxy_object=self.mandos_serv,
622
proxy_object=client_proxy_object,
624
update_hook=self.refresh,
625
delete_hook=self.remove_client,
626
logger=self.log_message),
536
self._input_callback_tag = (gobject.io_add_watch
630
self._input_callback_tag = (GLib.io_add_watch
537
631
(sys.stdin.fileno(),
539
633
self.process_input))
540
634
self.main_loop.run()
541
635
# Main loop has finished, we should close everything now
542
gobject.source_remove(self._input_callback_tag)
636
GLib.source_remove(self._input_callback_tag)
543
637
self.screen.stop()
546
640
self.main_loop.quit()
548
642
def process_input(self, source, condition):
549
643
keys = self.screen.get_input()
550
translations = { u"ctrl n": u"down", # Emacs
551
u"ctrl p": u"up", # Emacs
552
u"ctrl v": u"page down", # Emacs
553
u"meta v": u"page up", # Emacs
554
u" ": u"page down", # less
555
u"f": u"page down", # less
556
u"b": u"page up", # less
644
translations = {"ctrl n": "down", # Emacs
645
"ctrl p": "up", # Emacs
646
"ctrl v": "page down", # Emacs
647
"meta v": "page up", # Emacs
648
" ": "page down", # less
649
"f": "page down", # less
650
"b": "page up", # less
562
656
key = translations[key]
563
657
except KeyError: # :-)
566
if key == u"q" or key == u"Q":
660
if key == "q" or key == "Q":
569
elif key == u"window resize":
663
elif key == "window resize":
570
664
self.size = self.screen.get_cols_rows()
572
elif key == u"\f": # Ctrl-L
666
elif key == "ctrl l":
574
elif key == u"l" or key == u"D":
669
elif key == "l" or key == "D":
575
670
self.toggle_log_display()
577
elif key == u"w" or key == u"i":
672
elif key == "w" or key == "i":
578
673
self.change_log_display()
580
elif key == u"?" or key == u"f1" or key == u"esc":
675
elif key == "?" or key == "f1" or key == "esc":
581
676
if not self.log_visible:
582
677
self.log_visible = True
584
self.log_message_raw((u"bold",
588
u"l: Log window toggle",
589
u"TAB: Switch window",
591
self.log_message_raw((u"bold",
597
u"s: Start new checker",
679
self.log_message_raw(("bold",
683
"l: Log window toggle",
684
"TAB: Switch window",
685
"w: Wrap (log lines)",
686
"v: Toggle verbose log",
688
self.log_message_raw(("bold",
694
"s: Start new checker",
602
701
if self.topwidget.get_focus() is self.logbox:
603
702
self.topwidget.set_focus(0)
605
704
self.topwidget.set_focus(self.logbox)
607
#elif (key == u"end" or key == u"meta >" or key == u"G"
609
# pass # xxx end-of-buffer
610
#elif (key == u"home" or key == u"meta <" or key == u"g"
612
# pass # xxx beginning-of-buffer
613
#elif key == u"ctrl e" or key == u"$":
614
# pass # xxx move-end-of-line
615
#elif key == u"ctrl a" or key == u"^":
616
# pass # xxx move-beginning-of-line
617
#elif key == u"ctrl b" or key == u"meta (" or key == u"h":
619
#elif key == u"ctrl f" or key == u"meta )" or key == u"l":
622
# pass # scroll up log
624
# pass # scroll down log
707
if self.log_level == 0:
709
self.log_message("Verbose mode: Off")
712
self.log_message("Verbose mode: On")
713
# elif (key == "end" or key == "meta >" or key == "G"
715
# pass # xxx end-of-buffer
716
# elif (key == "home" or key == "meta <" or key == "g"
718
# pass # xxx beginning-of-buffer
719
# elif key == "ctrl e" or key == "$":
720
# pass # xxx move-end-of-line
721
# elif key == "ctrl a" or key == "^":
722
# pass # xxx move-beginning-of-line
723
# elif key == "ctrl b" or key == "meta (" or key == "h":
725
# elif key == "ctrl f" or key == "meta )" or key == "l":
728
# pass # scroll up log
730
# pass # scroll down log
625
731
elif self.topwidget.selectable():
626
732
self.topwidget.keypress(self.size, key)