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):
223
213
self.last_checker_failed = True
224
214
self.using_timer(True)
225
215
if os.WIFEXITED(condition):
226
self.logger('Checker for client %s (command "%s")'
227
' failed with exit code %s'
228
% (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,
229
219
os.WEXITSTATUS(condition)))
230
220
elif os.WIFSIGNALED(condition):
231
self.logger('Checker for client %s (command "%s")'
232
' was killed by signal %s'
233
% (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,
234
224
os.WTERMSIG(condition)))
235
225
elif os.WCOREDUMP(condition):
236
self.logger('Checker for client %s (command "%s")'
238
% (self.properties["Name"], command))
226
self.logger(u'Checker for client %s (command "%s")'
228
% (self.properties[u"Name"], command))
240
self.logger('Checker for client %s completed'
230
self.logger(u'Checker for client %s completed'
244
234
def checker_started(self, command):
245
#self.logger('Client %s started checker "%s"'
246
# % (self.properties["Name"], unicode(command)))
235
#self.logger(u'Client %s started checker "%s"'
236
# % (self.properties[u"Name"], unicode(command)))
249
239
def got_secret(self):
250
240
self.last_checker_failed = False
251
self.logger('Client %s received its secret'
252
% self.properties["Name"])
241
self.logger(u'Client %s received its secret'
242
% self.properties[u"Name"])
254
244
def need_approval(self, timeout, default):
256
message = 'Client %s needs approval within %s seconds'
246
message = u'Client %s needs approval within %s seconds'
258
message = 'Client %s will get its secret in %s seconds'
248
message = u'Client %s will get its secret in %s seconds'
259
249
self.logger(message
260
% (self.properties["Name"], timeout/1000))
250
% (self.properties[u"Name"], timeout/1000))
261
251
self.using_timer(True)
263
253
def rejected(self, reason):
264
self.logger('Client %s was rejected; reason: %s'
265
% (self.properties["Name"], reason))
254
self.logger(u'Client %s was rejected; reason: %s'
255
% (self.properties[u"Name"], reason))
267
257
def selectable(self):
268
258
"""Make this a "selectable" widget.
269
259
This overrides the method from urwid.FlowWidget."""
272
def rows(self, maxcolrow, focus=False):
262
def rows(self, (maxcol,), focus=False):
273
263
"""How many rows this widget will occupy might depend on
274
264
whether we have focus or not.
275
265
This overrides the method from urwid.FlowWidget"""
276
return self.current_widget(focus).rows(maxcolrow, focus=focus)
266
return self.current_widget(focus).rows((maxcol,), focus=focus)
278
268
def current_widget(self, focus=False):
279
269
if focus or self.opened:
283
273
def update(self):
284
274
"Called when what is visible on the screen should be updated."
285
275
# How to add standout mode to a style
286
with_standout = { "normal": "standout",
287
"bold": "bold-standout",
289
"underline-blink-standout",
290
"bold-underline-blink":
291
"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",
294
284
# Rebuild focus and non-focus widgets using current properties
296
286
# Base part of a client. Name!
298
% {"name": self.properties["Name"]})
299
if not self.properties["Enabled"]:
301
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"]:
302
292
timeout = datetime.timedelta(milliseconds
303
293
= self.properties
305
295
last_approval_request = isoformat_to_datetime(
306
self.properties["LastApprovalRequest"])
296
self.properties[u"LastApprovalRequest"])
307
297
if last_approval_request is not None:
308
298
timer = timeout - (datetime.datetime.utcnow()
309
299
- last_approval_request)
311
301
timer = datetime.timedelta()
312
if self.properties["ApprovedByDefault"]:
313
message = "Approval in %s. (d)eny?"
302
if self.properties[u"ApprovedByDefault"]:
303
message = u"Approval in %s. (d)eny?"
315
message = "Denial in %s. (a)pprove?"
305
message = u"Denial in %s. (a)pprove?"
316
306
message = message % unicode(timer).rsplit(".", 1)[0]
317
307
elif self.last_checker_failed:
318
# When checker has failed, print a timer until client expires
319
expires = self.properties["Expires"]
321
timer = datetime.timedelta(0)
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'
308
timeout = datetime.timedelta(milliseconds
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'
328
318
% unicode(timer).rsplit(".", 1)[0])
331
321
self._text = "%s%s" % (base, message)
333
323
if not urwid.supports_unicode():
334
324
self._text = self._text.encode("ascii", "replace")
335
textlist = [("normal", self._text)]
325
textlist = [(u"normal", self._text)]
336
326
self._text_widget.set_text(textlist)
337
327
self._focus_text_widget.set_text([(with_standout[text[0]],
347
337
self.update_hook()
349
339
def update_timer(self):
350
"""called by gobject. Will indefinitely loop until
351
gobject.source_remove() on tag is called"""
353
342
return True # Keep calling this
355
def delete(self, *args, **kwargs):
356
345
if self._update_timer_callback_tag is not None:
357
346
gobject.source_remove(self._update_timer_callback_tag)
358
347
self._update_timer_callback_tag = None
359
for match in self.match_objects:
361
self.match_objects = ()
362
348
if self.delete_hook is not None:
363
349
self.delete_hook(self)
364
return super(MandosClientWidget, self).delete(*args, **kwargs)
366
def render(self, maxcolrow, focus=False):
351
def render(self, (maxcol,), focus=False):
367
352
"""Render differently if we have focus.
368
353
This overrides the method from urwid.FlowWidget"""
369
return self.current_widget(focus).render(maxcolrow,
354
return self.current_widget(focus).render((maxcol,),
372
def keypress(self, maxcolrow, key):
357
def keypress(self, (maxcol,), key):
374
359
This overrides the method from urwid.FlowWidget"""
376
self.proxy.Enable(dbus_interface = client_interface,
379
self.proxy.Disable(dbus_interface = client_interface,
361
self.proxy.Enable(dbus_interface = client_interface)
363
self.proxy.Disable(dbus_interface = client_interface)
382
365
self.proxy.Approve(dbus.Boolean(True, variant_level=1),
383
dbus_interface = client_interface,
366
dbus_interface = client_interface)
386
368
self.proxy.Approve(dbus.Boolean(False, variant_level=1),
387
dbus_interface = client_interface,
389
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":
390
371
self.server_proxy_object.RemoveClient(self.proxy
394
self.proxy.StartChecker(dbus_interface = client_interface,
397
self.proxy.StopChecker(dbus_interface = client_interface,
400
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)
403
# elif key == "p" or key == "=":
380
# elif key == u"p" or key == "=":
404
381
# self.proxy.pause()
405
# elif key == "u" or key == ":":
382
# elif key == u"u" or key == ":":
406
383
# self.proxy.unpause()
384
# elif key == u"RET":
426
403
"down" key presses, thus not allowing any containing widgets to
427
404
use them as an excuse to shift focus away from this widget.
429
def keypress(self, maxcolrow, key):
430
ret = super(ConstrainedListBox, self).keypress(maxcolrow, key)
431
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"):
443
421
self.screen = urwid.curses_display.Screen()
445
423
self.screen.register_palette((
447
"default", "default", None),
449
"default", "default", "bold"),
451
"default", "default", "underline"),
453
"default", "default", "standout"),
454
("bold-underline-blink",
455
"default", "default", ("bold", "underline")),
457
"default", "default", ("bold", "standout")),
458
("underline-blink-standout",
459
"default", "default", ("underline", "standout")),
460
("bold-underline-blink-standout",
461
"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",
465
443
if urwid.supports_unicode():
466
self.divider = "─" # \u2500
467
#self.divider = "━" # \u2501
444
self.divider = u"─" # \u2500
445
#self.divider = u"━" # \u2501
469
#self.divider = "-" # \u002d
470
self.divider = "_" # \u005f
447
#self.divider = u"-" # \u002d
448
self.divider = u"_" # \u005f
472
450
self.screen.start()
487
465
# This keeps track of whether self.uilist currently has
488
466
# self.logbox in it or not
489
467
self.log_visible = True
490
self.log_wrap = "any"
468
self.log_wrap = u"any"
493
self.log_message_raw(("bold",
494
"Mandos Monitor version " + version))
495
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",
498
476
self.busname = domain + '.Mandos'
499
477
self.main_loop = gobject.MainLoop()
500
478
self.bus = dbus.SystemBus()
501
479
mandos_dbus_objc = self.bus.get_object(
502
self.busname, "/", follow_name_owner_changes=True)
480
self.busname, u"/", follow_name_owner_changes=True)
503
481
self.mandos_serv = dbus.Interface(mandos_dbus_objc,
505
483
= server_interface)
510
488
mandos_clients = dbus.Dictionary()
512
490
(self.mandos_serv
513
.connect_to_signal("ClientRemoved",
491
.connect_to_signal(u"ClientRemoved",
514
492
self.find_and_remove_client,
515
493
dbus_interface=server_interface,
516
494
byte_arrays=True))
517
495
(self.mandos_serv
518
.connect_to_signal("ClientAdded",
496
.connect_to_signal(u"ClientAdded",
519
497
self.add_new_client,
520
498
dbus_interface=server_interface,
521
499
byte_arrays=True))
522
500
(self.mandos_serv
523
.connect_to_signal("ClientNotFound",
501
.connect_to_signal(u"ClientNotFound",
524
502
self.client_not_found,
525
503
dbus_interface=server_interface,
526
504
byte_arrays=True))
573
551
and len(self.log) > self.max_log_length):
574
552
del self.log[0:len(self.log)-self.max_log_length-1]
575
553
self.logbox.set_focus(len(self.logbox.body.contents),
554
coming_from=u"above")
579
557
def toggle_log_display(self):
580
558
"""Toggle visibility of the log buffer."""
581
559
self.log_visible = not self.log_visible
583
#self.log_message("Log visibility changed to: "
561
#self.log_message(u"Log visibility changed to: "
584
562
# + unicode(self.log_visible))
586
564
def change_log_display(self):
587
565
"""Change type of log display.
588
566
Currently, this toggles wrapping of text lines."""
589
if self.log_wrap == "clip":
590
self.log_wrap = "any"
567
if self.log_wrap == u"clip":
568
self.log_wrap = u"any"
592
self.log_wrap = "clip"
570
self.log_wrap = u"clip"
593
571
for textwidget in self.log:
594
572
textwidget.set_wrap_mode(self.log_wrap)
595
#self.log_message("Wrap mode: " + self.log_wrap)
573
#self.log_message(u"Wrap mode: " + self.log_wrap)
597
575
def find_and_remove_client(self, path, name):
598
"""Find a client by its object path and remove it.
576
"""Find an client from its object path and remove it.
600
578
This is connected to the ClientRemoved signal from the
601
579
Mandos server object."""
665
641
def process_input(self, source, condition):
666
642
keys = self.screen.get_input()
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
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
680
656
except KeyError: # :-)
683
if key == "q" or key == "Q":
659
if key == u"q" or key == u"Q":
686
elif key == "window resize":
662
elif key == u"window resize":
687
663
self.size = self.screen.get_cols_rows()
689
elif key == "\f": # Ctrl-L
665
elif key == u"\f": # Ctrl-L
691
elif key == "l" or key == "D":
667
elif key == u"l" or key == u"D":
692
668
self.toggle_log_display()
694
elif key == "w" or key == "i":
670
elif key == u"w" or key == u"i":
695
671
self.change_log_display()
697
elif key == "?" or key == "f1" or key == "esc":
673
elif key == u"?" or key == u"f1" or key == u"esc":
698
674
if not self.log_visible:
699
675
self.log_visible = True
701
self.log_message_raw(("bold",
705
"l: Log window toggle",
706
"TAB: Switch window",
708
self.log_message_raw(("bold",
714
"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",
721
697
if self.topwidget.get_focus() is self.logbox:
722
698
self.topwidget.set_focus(0)
724
700
self.topwidget.set_focus(self.logbox)
726
#elif (key == "end" or key == "meta >" or key == "G"
702
#elif (key == u"end" or key == u"meta >" or key == u"G"
728
704
# pass # xxx end-of-buffer
729
#elif (key == "home" or key == "meta <" or key == "g"
705
#elif (key == u"home" or key == u"meta <" or key == u"g"
731
707
# pass # xxx beginning-of-buffer
732
#elif key == "ctrl e" or key == "$":
708
#elif key == u"ctrl e" or key == u"$":
733
709
# pass # xxx move-end-of-line
734
#elif key == "ctrl a" or key == "^":
710
#elif key == u"ctrl a" or key == u"^":
735
711
# pass # xxx move-beginning-of-line
736
#elif key == "ctrl b" or key == "meta (" or key == "h":
712
#elif key == u"ctrl b" or key == u"meta (" or key == u"h":
737
713
# pass # xxx left
738
#elif key == "ctrl f" or key == "meta )" or key == "l":
714
#elif key == u"ctrl f" or key == u"meta )" or key == u"l":
739
715
# pass # xxx right
741
717
# pass # scroll up log
743
719
# pass # scroll down log
744
720
elif self.topwidget.selectable():
745
721
self.topwidget.keypress(self.size, key)