66
65
"Parse an ISO 8601 date string to a datetime.datetime()"
69
d, t = iso.split("T", 1)
70
year, month, day = d.split("-", 2)
71
hour, minute, second = t.split(":", 2)
68
d, t = iso.split(u"T", 1)
69
year, month, day = d.split(u"-", 2)
70
hour, minute, second = t.split(u":", 2)
72
71
second, fraction = divmod(float(second), 1)
73
72
return datetime.datetime(int(year),
87
86
self.proxy = proxy_object # Mandos Client proxy object
89
88
self.properties = dict()
90
self.property_changed_match = (
91
self.proxy.connect_to_signal("PropertyChanged",
92
self.property_changed,
89
self.proxy.connect_to_signal(u"PropertyChanged",
90
self.property_changed,
96
94
self.properties.update(
97
95
self.proxy.GetAll(client_interface,
98
96
dbus_interface = dbus.PROPERTIES_IFACE))
100
#XXX This breaks good super behaviour
98
#XXX This break good super behaviour!
101
99
# super(MandosClientPropertyCache, self).__init__(
102
100
# *args, **kwargs)
134
127
self.last_checker_failed = False
136
129
# The widget shown normally
137
self._text_widget = urwid.Text("")
130
self._text_widget = urwid.Text(u"")
138
131
# The widget shown when we have focus
139
self._focus_text_widget = urwid.Text("")
132
self._focus_text_widget = urwid.Text(u"")
140
133
super(MandosClientWidget, self).__init__(
141
134
update_hook=update_hook, delete_hook=delete_hook,
161
154
if self.need_approval:
162
155
self.using_timer(True)
164
self.match_objects = (
165
self.proxy.connect_to_signal("CheckerCompleted",
166
self.checker_completed,
169
self.proxy.connect_to_signal("CheckerStarted",
170
self.checker_started,
173
self.proxy.connect_to_signal("GotSecret",
177
self.proxy.connect_to_signal("NeedApproval",
181
self.proxy.connect_to_signal("Rejected",
185
#self.logger('Created client %s' % (self.properties["Name"]))
157
self.proxy.connect_to_signal(u"CheckerCompleted",
158
self.checker_completed,
161
self.proxy.connect_to_signal(u"CheckerStarted",
162
self.checker_started,
165
self.proxy.connect_to_signal(u"GotSecret",
169
self.proxy.connect_to_signal(u"NeedApproval",
173
self.proxy.connect_to_signal(u"Rejected",
187
178
def property_changed(self, property=None, value=None):
188
179
super(self, MandosClientWidget).property_changed(property,
190
if property == "ApprovalPending":
181
if property == u"ApprovalPending":
191
182
using_timer(bool(value))
193
184
def using_timer(self, flag):
222
213
self.last_checker_failed = True
223
214
self.using_timer(True)
224
215
if os.WIFEXITED(condition):
225
self.logger('Checker for client %s (command "%s")'
226
' failed with exit code %s'
227
% (self.properties["Name"], command,
216
self.logger(u'Checker for client %s (command "%s")'
217
u' failed with exit code %s'
218
% (self.properties[u"Name"], command,
228
219
os.WEXITSTATUS(condition)))
229
220
elif os.WIFSIGNALED(condition):
230
self.logger('Checker for client %s (command "%s")'
231
' was killed by signal %s'
232
% (self.properties["Name"], command,
221
self.logger(u'Checker for client %s (command "%s")'
222
u' was killed by signal %s'
223
% (self.properties[u"Name"], command,
233
224
os.WTERMSIG(condition)))
234
225
elif os.WCOREDUMP(condition):
235
self.logger('Checker for client %s (command "%s")'
237
% (self.properties["Name"], command))
226
self.logger(u'Checker for client %s (command "%s")'
228
% (self.properties[u"Name"], command))
239
self.logger('Checker for client %s completed'
230
self.logger(u'Checker for client %s completed'
243
234
def checker_started(self, command):
244
#self.logger('Client %s started checker "%s"'
245
# % (self.properties["Name"], unicode(command)))
235
#self.logger(u'Client %s started checker "%s"'
236
# % (self.properties[u"Name"], unicode(command)))
248
239
def got_secret(self):
249
240
self.last_checker_failed = False
250
self.logger('Client %s received its secret'
251
% self.properties["Name"])
241
self.logger(u'Client %s received its secret'
242
% self.properties[u"Name"])
253
244
def need_approval(self, timeout, default):
255
message = 'Client %s needs approval within %s seconds'
246
message = u'Client %s needs approval within %s seconds'
257
message = 'Client %s will get its secret in %s seconds'
248
message = u'Client %s will get its secret in %s seconds'
258
249
self.logger(message
259
% (self.properties["Name"], timeout/1000))
250
% (self.properties[u"Name"], timeout/1000))
260
251
self.using_timer(True)
262
253
def rejected(self, reason):
263
self.logger('Client %s was rejected; reason: %s'
264
% (self.properties["Name"], reason))
254
self.logger(u'Client %s was rejected; reason: %s'
255
% (self.properties[u"Name"], reason))
266
257
def selectable(self):
267
258
"""Make this a "selectable" widget.
268
259
This overrides the method from urwid.FlowWidget."""
271
def rows(self, maxcolrow, focus=False):
262
def rows(self, (maxcol,), focus=False):
272
263
"""How many rows this widget will occupy might depend on
273
264
whether we have focus or not.
274
265
This overrides the method from urwid.FlowWidget"""
275
return self.current_widget(focus).rows(maxcolrow, focus=focus)
266
return self.current_widget(focus).rows((maxcol,), focus=focus)
277
268
def current_widget(self, focus=False):
278
269
if focus or self.opened:
282
273
def update(self):
283
274
"Called when what is visible on the screen should be updated."
284
275
# How to add standout mode to a style
285
with_standout = { "normal": "standout",
286
"bold": "bold-standout",
288
"underline-blink-standout",
289
"bold-underline-blink":
290
"bold-underline-blink-standout",
276
with_standout = { u"normal": u"standout",
277
u"bold": u"bold-standout",
279
u"underline-blink-standout",
280
u"bold-underline-blink":
281
u"bold-underline-blink-standout",
293
284
# Rebuild focus and non-focus widgets using current properties
295
286
# Base part of a client. Name!
297
% {"name": self.properties["Name"]})
298
if not self.properties["Enabled"]:
300
elif self.properties["ApprovalPending"]:
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"]:
301
292
timeout = datetime.timedelta(milliseconds
302
293
= self.properties
304
295
last_approval_request = isoformat_to_datetime(
305
self.properties["LastApprovalRequest"])
296
self.properties[u"LastApprovalRequest"])
306
297
if last_approval_request is not None:
307
298
timer = timeout - (datetime.datetime.utcnow()
308
299
- last_approval_request)
310
301
timer = datetime.timedelta()
311
if self.properties["ApprovedByDefault"]:
312
message = "Approval in %s. (d)eny?"
302
if self.properties[u"ApprovedByDefault"]:
303
message = u"Approval in %s. (d)eny?"
314
message = "Denial in %s. (a)pprove?"
305
message = u"Denial in %s. (a)pprove?"
315
306
message = message % unicode(timer).rsplit(".", 1)[0]
316
307
elif self.last_checker_failed:
317
308
timeout = datetime.timedelta(milliseconds
318
309
= self.properties
320
311
last_ok = isoformat_to_datetime(
321
max((self.properties["LastCheckedOK"]
322
or self.properties["Created"]),
323
self.properties["LastEnabled"]))
312
max((self.properties[u"LastCheckedOK"]
313
or self.properties[u"Created"]),
314
self.properties[u"LastEnabled"]))
324
315
timer = timeout - (datetime.datetime.utcnow() - last_ok)
325
message = ('A checker has failed! Time until client'
316
message = (u'A checker has failed! Time until client'
317
u' gets disabled: %s'
327
318
% unicode(timer).rsplit(".", 1)[0])
330
321
self._text = "%s%s" % (base, message)
332
323
if not urwid.supports_unicode():
333
324
self._text = self._text.encode("ascii", "replace")
334
textlist = [("normal", self._text)]
325
textlist = [(u"normal", self._text)]
335
326
self._text_widget.set_text(textlist)
336
327
self._focus_text_widget.set_text([(with_standout[text[0]],
351
342
return True # Keep calling this
353
def delete(self, *args, **kwargs):
354
345
if self._update_timer_callback_tag is not None:
355
346
gobject.source_remove(self._update_timer_callback_tag)
356
347
self._update_timer_callback_tag = None
357
for match in self.match_objects:
359
self.match_objects = ()
360
348
if self.delete_hook is not None:
361
349
self.delete_hook(self)
362
return super(MandosClientWidget, self).delete(*args, **kwargs)
364
def render(self, maxcolrow, focus=False):
351
def render(self, (maxcol,), focus=False):
365
352
"""Render differently if we have focus.
366
353
This overrides the method from urwid.FlowWidget"""
367
return self.current_widget(focus).render(maxcolrow,
354
return self.current_widget(focus).render((maxcol,),
370
def keypress(self, maxcolrow, key):
357
def keypress(self, (maxcol,), key):
372
359
This overrides the method from urwid.FlowWidget"""
374
self.proxy.Enable(dbus_interface = client_interface,
377
self.proxy.Disable(dbus_interface = client_interface,
361
self.proxy.Enable(dbus_interface = client_interface)
363
self.proxy.Disable(dbus_interface = client_interface)
380
365
self.proxy.Approve(dbus.Boolean(True, variant_level=1),
381
dbus_interface = client_interface,
366
dbus_interface = client_interface)
384
368
self.proxy.Approve(dbus.Boolean(False, variant_level=1),
385
dbus_interface = client_interface,
387
elif key == "R" or key == "_" or key == "ctrl k":
369
dbus_interface = client_interface)
370
elif key == u"r" or key == u"_" or key == u"ctrl k":
388
371
self.server_proxy_object.RemoveClient(self.proxy
392
self.proxy.StartChecker(dbus_interface = client_interface,
395
self.proxy.StopChecker(dbus_interface = client_interface,
398
self.proxy.CheckedOK(dbus_interface = client_interface,
374
self.proxy.StartChecker(dbus_interface = client_interface)
376
self.proxy.StopChecker(dbus_interface = client_interface)
378
self.proxy.CheckedOK(dbus_interface = client_interface)
401
# elif key == "p" or key == "=":
380
# elif key == u"p" or key == "=":
402
381
# self.proxy.pause()
403
# elif key == "u" or key == ":":
382
# elif key == u"u" or key == ":":
404
383
# self.proxy.unpause()
384
# elif key == u"RET":
424
403
"down" key presses, thus not allowing any containing widgets to
425
404
use them as an excuse to shift focus away from this widget.
427
def keypress(self, maxcolrow, key):
428
ret = super(ConstrainedListBox, self).keypress(maxcolrow, key)
429
if ret in ("up", "down"):
406
def keypress(self, (maxcol, maxrow), key):
407
ret = super(ConstrainedListBox, self).keypress((maxcol,
409
if ret in (u"up", u"down"):
441
421
self.screen = urwid.curses_display.Screen()
443
423
self.screen.register_palette((
445
"default", "default", None),
447
"default", "default", "bold"),
449
"default", "default", "underline"),
451
"default", "default", "standout"),
452
("bold-underline-blink",
453
"default", "default", ("bold", "underline")),
455
"default", "default", ("bold", "standout")),
456
("underline-blink-standout",
457
"default", "default", ("underline", "standout")),
458
("bold-underline-blink-standout",
459
"default", "default", ("bold", "underline",
425
u"default", u"default", None),
427
u"default", u"default", u"bold"),
429
u"default", u"default", u"underline"),
431
u"default", u"default", u"standout"),
432
(u"bold-underline-blink",
433
u"default", u"default", (u"bold", u"underline")),
435
u"default", u"default", (u"bold", u"standout")),
436
(u"underline-blink-standout",
437
u"default", u"default", (u"underline", u"standout")),
438
(u"bold-underline-blink-standout",
439
u"default", u"default", (u"bold", u"underline",
463
443
if urwid.supports_unicode():
464
self.divider = "─" # \u2500
465
#self.divider = "━" # \u2501
444
self.divider = u"─" # \u2500
445
#self.divider = u"━" # \u2501
467
#self.divider = "-" # \u002d
468
self.divider = "_" # \u005f
447
#self.divider = u"-" # \u002d
448
self.divider = u"_" # \u005f
470
450
self.screen.start()
485
465
# This keeps track of whether self.uilist currently has
486
466
# self.logbox in it or not
487
467
self.log_visible = True
488
self.log_wrap = "any"
468
self.log_wrap = u"any"
491
self.log_message_raw(("bold",
492
"Mandos Monitor version " + version))
493
self.log_message_raw(("bold",
471
self.log_message_raw((u"bold",
472
u"Mandos Monitor version " + version))
473
self.log_message_raw((u"bold",
496
476
self.busname = domain + '.Mandos'
497
477
self.main_loop = gobject.MainLoop()
498
478
self.bus = dbus.SystemBus()
499
479
mandos_dbus_objc = self.bus.get_object(
500
self.busname, "/", follow_name_owner_changes=True)
480
self.busname, u"/", follow_name_owner_changes=True)
501
481
self.mandos_serv = dbus.Interface(mandos_dbus_objc,
503
483
= server_interface)
508
488
mandos_clients = dbus.Dictionary()
510
490
(self.mandos_serv
511
.connect_to_signal("ClientRemoved",
491
.connect_to_signal(u"ClientRemoved",
512
492
self.find_and_remove_client,
513
493
dbus_interface=server_interface,
514
494
byte_arrays=True))
515
495
(self.mandos_serv
516
.connect_to_signal("ClientAdded",
496
.connect_to_signal(u"ClientAdded",
517
497
self.add_new_client,
518
498
dbus_interface=server_interface,
519
499
byte_arrays=True))
520
500
(self.mandos_serv
521
.connect_to_signal("ClientNotFound",
501
.connect_to_signal(u"ClientNotFound",
522
502
self.client_not_found,
523
503
dbus_interface=server_interface,
524
504
byte_arrays=True))
571
551
and len(self.log) > self.max_log_length):
572
552
del self.log[0:len(self.log)-self.max_log_length-1]
573
553
self.logbox.set_focus(len(self.logbox.body.contents),
554
coming_from=u"above")
577
557
def toggle_log_display(self):
578
558
"""Toggle visibility of the log buffer."""
579
559
self.log_visible = not self.log_visible
581
#self.log_message("Log visibility changed to: "
561
#self.log_message(u"Log visibility changed to: "
582
562
# + unicode(self.log_visible))
584
564
def change_log_display(self):
585
565
"""Change type of log display.
586
566
Currently, this toggles wrapping of text lines."""
587
if self.log_wrap == "clip":
588
self.log_wrap = "any"
567
if self.log_wrap == u"clip":
568
self.log_wrap = u"any"
590
self.log_wrap = "clip"
570
self.log_wrap = u"clip"
591
571
for textwidget in self.log:
592
572
textwidget.set_wrap_mode(self.log_wrap)
593
#self.log_message("Wrap mode: " + self.log_wrap)
573
#self.log_message(u"Wrap mode: " + self.log_wrap)
595
575
def find_and_remove_client(self, path, name):
596
"""Find a client by its object path and remove it.
576
"""Find an client from its object path and remove it.
598
578
This is connected to the ClientRemoved signal from the
599
579
Mandos server object."""
663
641
def process_input(self, source, condition):
664
642
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
643
translations = { u"ctrl n": u"down", # Emacs
644
u"ctrl p": u"up", # Emacs
645
u"ctrl v": u"page down", # Emacs
646
u"meta v": u"page up", # Emacs
647
u" ": u"page down", # less
648
u"f": u"page down", # less
649
u"b": u"page up", # less
678
656
except KeyError: # :-)
681
if key == "q" or key == "Q":
659
if key == u"q" or key == u"Q":
684
elif key == "window resize":
662
elif key == u"window resize":
685
663
self.size = self.screen.get_cols_rows()
687
elif key == "\f": # Ctrl-L
665
elif key == u"\f": # Ctrl-L
689
elif key == "l" or key == "D":
667
elif key == u"l" or key == u"D":
690
668
self.toggle_log_display()
692
elif key == "w" or key == "i":
670
elif key == u"w" or key == u"i":
693
671
self.change_log_display()
695
elif key == "?" or key == "f1" or key == "esc":
673
elif key == u"?" or key == u"f1" or key == u"esc":
696
674
if not self.log_visible:
697
675
self.log_visible = True
699
self.log_message_raw(("bold",
703
"l: Log window toggle",
704
"TAB: Switch window",
706
self.log_message_raw(("bold",
712
"s: Start new checker",
677
self.log_message_raw((u"bold",
681
u"l: Log window toggle",
682
u"TAB: Switch window",
684
self.log_message_raw((u"bold",
690
u"s: Start new checker",
719
697
if self.topwidget.get_focus() is self.logbox:
720
698
self.topwidget.set_focus(0)
722
700
self.topwidget.set_focus(self.logbox)
724
#elif (key == "end" or key == "meta >" or key == "G"
702
#elif (key == u"end" or key == u"meta >" or key == u"G"
726
704
# pass # xxx end-of-buffer
727
#elif (key == "home" or key == "meta <" or key == "g"
705
#elif (key == u"home" or key == u"meta <" or key == u"g"
729
707
# pass # xxx beginning-of-buffer
730
#elif key == "ctrl e" or key == "$":
708
#elif key == u"ctrl e" or key == u"$":
731
709
# pass # xxx move-end-of-line
732
#elif key == "ctrl a" or key == "^":
710
#elif key == u"ctrl a" or key == u"^":
733
711
# pass # xxx move-beginning-of-line
734
#elif key == "ctrl b" or key == "meta (" or key == "h":
712
#elif key == u"ctrl b" or key == u"meta (" or key == u"h":
735
713
# pass # xxx left
736
#elif key == "ctrl f" or key == "meta )" or key == "l":
714
#elif key == u"ctrl f" or key == u"meta )" or key == u"l":
737
715
# pass # xxx right
739
717
# pass # scroll up log
741
719
# pass # scroll down log
742
720
elif self.topwidget.selectable():
743
721
self.topwidget.keypress(self.size, key)