45
locale.setlocale(locale.LC_ALL, u'')
46
locale.setlocale(locale.LC_ALL, '')
48
logging.getLogger(u'dbus.proxies').setLevel(logging.CRITICAL)
49
logging.getLogger('dbus.proxies').setLevel(logging.CRITICAL)
50
51
# Some useful constants
51
domain = u'se.bsnet.fukt'
52
server_interface = domain + u'.Mandos'
53
client_interface = domain + u'.Mandos.Client'
52
domain = 'se.recompile'
53
server_interface = domain + '.Mandos'
54
client_interface = domain + '.Mandos.Client'
56
57
# Always run in monochrome mode
57
58
urwid.curses_display.curses.has_colors = lambda : False
65
66
"Parse an ISO 8601 date string to a datetime.datetime()"
68
d, t = iso.split(u"T", 1)
69
year, month, day = d.split(u"-", 2)
70
hour, minute, second = t.split(u":", 2)
69
d, t = iso.split("T", 1)
70
year, month, day = d.split("-", 2)
71
hour, minute, second = t.split(":", 2)
71
72
second, fraction = divmod(float(second), 1)
72
73
return datetime.datetime(int(year),
86
87
self.proxy = proxy_object # Mandos Client proxy object
88
89
self.properties = dict()
89
self.proxy.connect_to_signal(u"PropertyChanged",
90
self.property_changed,
90
self.property_changed_match = (
91
self.proxy.connect_to_signal("PropertyChanged",
92
self.property_changed,
94
96
self.properties.update(
95
97
self.proxy.GetAll(client_interface,
96
98
dbus_interface = dbus.PROPERTIES_IFACE))
98
#XXX This break good super behaviour!
100
#XXX This breaks good super behaviour
99
101
# super(MandosClientPropertyCache, self).__init__(
100
102
# *args, **kwargs)
127
134
self.last_checker_failed = False
129
136
# The widget shown normally
130
self._text_widget = urwid.Text(u"")
137
self._text_widget = urwid.Text("")
131
138
# The widget shown when we have focus
132
self._focus_text_widget = urwid.Text(u"")
139
self._focus_text_widget = urwid.Text("")
133
140
super(MandosClientWidget, self).__init__(
134
141
update_hook=update_hook, delete_hook=delete_hook,
154
161
if self.need_approval:
155
162
self.using_timer(True)
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",
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"]))
178
187
def property_changed(self, property=None, value=None):
179
188
super(self, MandosClientWidget).property_changed(property,
181
if property == u"ApprovalPending":
190
if property == "ApprovalPending":
182
191
using_timer(bool(value))
184
193
def using_timer(self, flag):
213
223
self.last_checker_failed = True
214
224
self.using_timer(True)
215
225
if os.WIFEXITED(condition):
216
self.logger(u'Checker for client %s (command "%s")'
217
u' failed with exit code %s'
218
% (self.properties[u"Name"], command,
226
self.logger('Checker for client %s (command "%s")'
227
' failed with exit code %s'
228
% (self.properties["Name"], command,
219
229
os.WEXITSTATUS(condition)))
220
230
elif os.WIFSIGNALED(condition):
221
self.logger(u'Checker for client %s (command "%s")'
222
u' was killed by signal %s'
223
% (self.properties[u"Name"], command,
231
self.logger('Checker for client %s (command "%s")'
232
' was killed by signal %s'
233
% (self.properties["Name"], command,
224
234
os.WTERMSIG(condition)))
225
235
elif os.WCOREDUMP(condition):
226
self.logger(u'Checker for client %s (command "%s")'
228
% (self.properties[u"Name"], command))
236
self.logger('Checker for client %s (command "%s")'
238
% (self.properties["Name"], command))
230
self.logger(u'Checker for client %s completed'
240
self.logger('Checker for client %s completed'
234
244
def checker_started(self, command):
235
#self.logger(u'Client %s started checker "%s"'
236
# % (self.properties[u"Name"], unicode(command)))
245
#self.logger('Client %s started checker "%s"'
246
# % (self.properties["Name"], unicode(command)))
239
249
def got_secret(self):
240
250
self.last_checker_failed = False
241
self.logger(u'Client %s received its secret'
242
% self.properties[u"Name"])
251
self.logger('Client %s received its secret'
252
% self.properties["Name"])
244
254
def need_approval(self, timeout, default):
246
message = u'Client %s needs approval within %s seconds'
256
message = 'Client %s needs approval within %s seconds'
248
message = u'Client %s will get its secret in %s seconds'
258
message = 'Client %s will get its secret in %s seconds'
249
259
self.logger(message
250
% (self.properties[u"Name"], timeout/1000))
260
% (self.properties["Name"], timeout/1000))
251
261
self.using_timer(True)
253
263
def rejected(self, reason):
254
self.logger(u'Client %s was rejected; reason: %s'
255
% (self.properties[u"Name"], reason))
264
self.logger('Client %s was rejected; reason: %s'
265
% (self.properties["Name"], reason))
257
267
def selectable(self):
258
268
"""Make this a "selectable" widget.
273
283
def update(self):
274
284
"Called when what is visible on the screen should be updated."
275
285
# How to add standout mode to a style
276
with_standout = { u"normal": u"standout",
277
u"bold": u"bold-standout",
279
u"underline-blink-standout",
280
u"bold-underline-blink":
281
u"bold-underline-blink-standout",
286
with_standout = { "normal": "standout",
287
"bold": "bold-standout",
289
"underline-blink-standout",
290
"bold-underline-blink":
291
"bold-underline-blink-standout",
284
294
# Rebuild focus and non-focus widgets using current properties
286
296
# Base part of a client. Name!
287
base = (u'%(name)s: '
288
% {u"name": self.properties[u"Name"]})
289
if not self.properties[u"Enabled"]:
290
message = u"DISABLED"
291
elif self.properties[u"ApprovalPending"]:
298
% {"name": self.properties["Name"]})
299
if not self.properties["Enabled"]:
301
elif self.properties["ApprovalPending"]:
292
302
timeout = datetime.timedelta(milliseconds
293
303
= self.properties
295
305
last_approval_request = isoformat_to_datetime(
296
self.properties[u"LastApprovalRequest"])
306
self.properties["LastApprovalRequest"])
297
307
if last_approval_request is not None:
298
308
timer = timeout - (datetime.datetime.utcnow()
299
309
- last_approval_request)
301
311
timer = datetime.timedelta()
302
if self.properties[u"ApprovedByDefault"]:
303
message = u"Approval in %s. (d)eny?"
312
if self.properties["ApprovedByDefault"]:
313
message = "Approval in %s. (d)eny?"
305
message = u"Denial in %s. (a)pprove?"
315
message = "Denial in %s. (a)pprove?"
306
316
message = message % unicode(timer).rsplit(".", 1)[0]
307
317
elif self.last_checker_failed:
308
timeout = datetime.timedelta(milliseconds
311
last_ok = isoformat_to_datetime(
312
max((self.properties[u"LastCheckedOK"]
313
or self.properties[u"Created"]),
314
self.properties[u"LastEnabled"]))
315
timer = timeout - (datetime.datetime.utcnow() - last_ok)
316
message = (u'A checker has failed! Time until client'
317
u' gets disabled: %s'
318
# When checker has failed, print a timer until client expires
319
expires = self.properties["Expires"]
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'
318
328
% unicode(timer).rsplit(".", 1)[0])
321
self._text = u"%s%s" % (base, message)
331
self._text = "%s%s" % (base, message)
323
333
if not urwid.supports_unicode():
324
self._text = self._text.encode(u"ascii", u"replace")
325
textlist = [(u"normal", self._text)]
334
self._text = self._text.encode("ascii", "replace")
335
textlist = [("normal", self._text)]
326
336
self._text_widget.set_text(textlist)
327
337
self._focus_text_widget.set_text([(with_standout[text[0]],
331
341
for text in textlist])
332
342
self._widget = self._text_widget
333
343
self._focus_widget = urwid.AttrWrap(self._focus_text_widget,
335
345
# Run update hook, if any
336
346
if self.update_hook is not None:
337
347
self.update_hook()
339
349
def update_timer(self):
350
"""called by gobject. Will indefinitely loop until
351
gobject.source_remove() on tag is called"""
342
353
return True # Keep calling this
355
def delete(self, *args, **kwargs):
345
356
if self._update_timer_callback_tag is not None:
346
357
gobject.source_remove(self._update_timer_callback_tag)
347
358
self._update_timer_callback_tag = None
359
for match in self.match_objects:
361
self.match_objects = ()
348
362
if self.delete_hook is not None:
349
363
self.delete_hook(self)
364
return super(MandosClientWidget, self).delete(*args, **kwargs)
351
366
def render(self, maxcolrow, focus=False):
352
367
"""Render differently if we have focus.
357
372
def keypress(self, maxcolrow, key):
359
374
This overrides the method from urwid.FlowWidget"""
361
self.proxy.Enable(dbus_interface = client_interface)
363
self.proxy.Disable(dbus_interface = client_interface)
376
self.proxy.Enable(dbus_interface = client_interface,
379
self.proxy.Disable(dbus_interface = client_interface,
365
382
self.proxy.Approve(dbus.Boolean(True, variant_level=1),
366
dbus_interface = client_interface)
383
dbus_interface = client_interface,
368
386
self.proxy.Approve(dbus.Boolean(False, variant_level=1),
369
dbus_interface = client_interface)
370
elif key == u"R" or key == u"_" or key == u"ctrl k":
387
dbus_interface = client_interface,
389
elif key == "R" or key == "_" or key == "ctrl k":
371
390
self.server_proxy_object.RemoveClient(self.proxy
374
self.proxy.StartChecker(dbus_interface = client_interface)
376
self.proxy.StopChecker(dbus_interface = client_interface)
378
self.proxy.CheckedOK(dbus_interface = client_interface)
394
self.proxy.StartChecker(dbus_interface = client_interface,
397
self.proxy.StopChecker(dbus_interface = client_interface,
400
self.proxy.CheckedOK(dbus_interface = client_interface,
380
# elif key == u"p" or key == "=":
403
# elif key == "p" or key == "=":
381
404
# self.proxy.pause()
382
# elif key == u"u" or key == ":":
405
# elif key == "u" or key == ":":
383
406
# self.proxy.unpause()
384
# elif key == u"RET":
420
443
self.screen = urwid.curses_display.Screen()
422
445
self.screen.register_palette((
424
u"default", u"default", None),
426
u"default", u"default", u"bold"),
428
u"default", u"default", u"underline"),
430
u"default", u"default", u"standout"),
431
(u"bold-underline-blink",
432
u"default", u"default", (u"bold", u"underline")),
434
u"default", u"default", (u"bold", u"standout")),
435
(u"underline-blink-standout",
436
u"default", u"default", (u"underline", u"standout")),
437
(u"bold-underline-blink-standout",
438
u"default", u"default", (u"bold", u"underline",
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",
442
465
if urwid.supports_unicode():
443
self.divider = u"─" # \u2500
444
#self.divider = u"━" # \u2501
466
self.divider = "─" # \u2500
467
#self.divider = "━" # \u2501
446
#self.divider = u"-" # \u002d
447
self.divider = u"_" # \u005f
469
#self.divider = "-" # \u002d
470
self.divider = "_" # \u005f
449
472
self.screen.start()
464
487
# This keeps track of whether self.uilist currently has
465
488
# self.logbox in it or not
466
489
self.log_visible = True
467
self.log_wrap = u"any"
490
self.log_wrap = "any"
470
self.log_message_raw((u"bold",
471
u"Mandos Monitor version " + version))
472
self.log_message_raw((u"bold",
493
self.log_message_raw(("bold",
494
"Mandos Monitor version " + version))
495
self.log_message_raw(("bold",
475
498
self.busname = domain + '.Mandos'
476
499
self.main_loop = gobject.MainLoop()
477
500
self.bus = dbus.SystemBus()
478
501
mandos_dbus_objc = self.bus.get_object(
479
self.busname, u"/", follow_name_owner_changes=True)
502
self.busname, "/", follow_name_owner_changes=True)
480
503
self.mandos_serv = dbus.Interface(mandos_dbus_objc,
482
505
= server_interface)
487
510
mandos_clients = dbus.Dictionary()
489
512
(self.mandos_serv
490
.connect_to_signal(u"ClientRemoved",
513
.connect_to_signal("ClientRemoved",
491
514
self.find_and_remove_client,
492
515
dbus_interface=server_interface,
493
516
byte_arrays=True))
494
517
(self.mandos_serv
495
.connect_to_signal(u"ClientAdded",
518
.connect_to_signal("ClientAdded",
496
519
self.add_new_client,
497
520
dbus_interface=server_interface,
498
521
byte_arrays=True))
499
522
(self.mandos_serv
500
.connect_to_signal(u"ClientNotFound",
523
.connect_to_signal("ClientNotFound",
501
524
self.client_not_found,
502
525
dbus_interface=server_interface,
503
526
byte_arrays=True))
550
573
and len(self.log) > self.max_log_length):
551
574
del self.log[0:len(self.log)-self.max_log_length-1]
552
575
self.logbox.set_focus(len(self.logbox.body.contents),
553
coming_from=u"above")
556
579
def toggle_log_display(self):
557
580
"""Toggle visibility of the log buffer."""
558
581
self.log_visible = not self.log_visible
560
#self.log_message(u"Log visibility changed to: "
583
#self.log_message("Log visibility changed to: "
561
584
# + unicode(self.log_visible))
563
586
def change_log_display(self):
564
587
"""Change type of log display.
565
588
Currently, this toggles wrapping of text lines."""
566
if self.log_wrap == u"clip":
567
self.log_wrap = u"any"
589
if self.log_wrap == "clip":
590
self.log_wrap = "any"
569
self.log_wrap = u"clip"
592
self.log_wrap = "clip"
570
593
for textwidget in self.log:
571
594
textwidget.set_wrap_mode(self.log_wrap)
572
#self.log_message(u"Wrap mode: " + self.log_wrap)
595
#self.log_message("Wrap mode: " + self.log_wrap)
574
597
def find_and_remove_client(self, path, name):
575
"""Find an client from its object path and remove it.
598
"""Find a client by its object path and remove it.
577
600
This is connected to the ClientRemoved signal from the
578
601
Mandos server object."""
640
665
def process_input(self, source, condition):
641
666
keys = self.screen.get_input()
642
translations = { u"ctrl n": u"down", # Emacs
643
u"ctrl p": u"up", # Emacs
644
u"ctrl v": u"page down", # Emacs
645
u"meta v": u"page up", # Emacs
646
u" ": u"page down", # less
647
u"f": u"page down", # less
648
u"b": u"page up", # less
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
655
680
except KeyError: # :-)
658
if key == u"q" or key == u"Q":
683
if key == "q" or key == "Q":
661
elif key == u"window resize":
686
elif key == "window resize":
662
687
self.size = self.screen.get_cols_rows()
664
elif key == u"\f": # Ctrl-L
689
elif key == "\f": # Ctrl-L
666
elif key == u"l" or key == u"D":
691
elif key == "l" or key == "D":
667
692
self.toggle_log_display()
669
elif key == u"w" or key == u"i":
694
elif key == "w" or key == "i":
670
695
self.change_log_display()
672
elif key == u"?" or key == u"f1" or key == u"esc":
697
elif key == "?" or key == "f1" or key == "esc":
673
698
if not self.log_visible:
674
699
self.log_visible = True
676
self.log_message_raw((u"bold",
680
u"l: Log window toggle",
681
u"TAB: Switch window",
683
self.log_message_raw((u"bold",
689
u"s: Start new checker",
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",
696
721
if self.topwidget.get_focus() is self.logbox:
697
722
self.topwidget.set_focus(0)
699
724
self.topwidget.set_focus(self.logbox)
701
#elif (key == u"end" or key == u"meta >" or key == u"G"
726
#elif (key == "end" or key == "meta >" or key == "G"
703
728
# pass # xxx end-of-buffer
704
#elif (key == u"home" or key == u"meta <" or key == u"g"
729
#elif (key == "home" or key == "meta <" or key == "g"
706
731
# pass # xxx beginning-of-buffer
707
#elif key == u"ctrl e" or key == u"$":
732
#elif key == "ctrl e" or key == "$":
708
733
# pass # xxx move-end-of-line
709
#elif key == u"ctrl a" or key == u"^":
734
#elif key == "ctrl a" or key == "^":
710
735
# pass # xxx move-beginning-of-line
711
#elif key == u"ctrl b" or key == u"meta (" or key == u"h":
736
#elif key == "ctrl b" or key == "meta (" or key == "h":
712
737
# pass # xxx left
713
#elif key == u"ctrl f" or key == u"meta )" or key == u"l":
738
#elif key == "ctrl f" or key == "meta )" or key == "l":
714
739
# pass # xxx right
716
741
# pass # scroll up log
718
743
# pass # scroll down log
719
744
elif self.topwidget.selectable():
720
745
self.topwidget.keypress(self.size, key)