46
locale.setlocale(locale.LC_ALL, '')
45
locale.setlocale(locale.LC_ALL, u'')
49
logging.getLogger('dbus.proxies').setLevel(logging.CRITICAL)
48
logging.getLogger(u'dbus.proxies').setLevel(logging.CRITICAL)
51
50
# Some useful constants
52
domain = 'se.bsnet.fukt'
53
server_interface = domain + '.Mandos'
54
client_interface = domain + '.Mandos.Client'
51
domain = u'se.bsnet.fukt'
52
server_interface = domain + u'.Mandos'
53
client_interface = domain + u'.Mandos.Client'
57
56
# Always run in monochrome mode
58
57
urwid.curses_display.curses.has_colors = lambda : False
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.
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
self._text = "%s%s" % (base, message)
321
self._text = u"%s%s" % (base, message)
332
323
if not urwid.supports_unicode():
333
self._text = self._text.encode("ascii", "replace")
334
textlist = [("normal", self._text)]
324
self._text = self._text.encode(u"ascii", u"replace")
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
351
def render(self, maxcolrow, focus=False):
365
352
"""Render differently if we have focus.
370
357
def keypress(self, maxcolrow, 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":
441
420
self.screen = urwid.curses_display.Screen()
443
422
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",
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",
463
442
if urwid.supports_unicode():
464
self.divider = "─" # \u2500
465
#self.divider = "━" # \u2501
443
self.divider = u"─" # \u2500
444
#self.divider = u"━" # \u2501
467
#self.divider = "-" # \u002d
468
self.divider = "_" # \u005f
446
#self.divider = u"-" # \u002d
447
self.divider = u"_" # \u005f
470
449
self.screen.start()
485
464
# This keeps track of whether self.uilist currently has
486
465
# self.logbox in it or not
487
466
self.log_visible = True
488
self.log_wrap = "any"
467
self.log_wrap = u"any"
491
self.log_message_raw(("bold",
492
"Mandos Monitor version " + version))
493
self.log_message_raw(("bold",
470
self.log_message_raw((u"bold",
471
u"Mandos Monitor version " + version))
472
self.log_message_raw((u"bold",
496
475
self.busname = domain + '.Mandos'
497
476
self.main_loop = gobject.MainLoop()
498
477
self.bus = dbus.SystemBus()
499
478
mandos_dbus_objc = self.bus.get_object(
500
self.busname, "/", follow_name_owner_changes=True)
479
self.busname, u"/", follow_name_owner_changes=True)
501
480
self.mandos_serv = dbus.Interface(mandos_dbus_objc,
503
482
= server_interface)
508
487
mandos_clients = dbus.Dictionary()
510
489
(self.mandos_serv
511
.connect_to_signal("ClientRemoved",
490
.connect_to_signal(u"ClientRemoved",
512
491
self.find_and_remove_client,
513
492
dbus_interface=server_interface,
514
493
byte_arrays=True))
515
494
(self.mandos_serv
516
.connect_to_signal("ClientAdded",
495
.connect_to_signal(u"ClientAdded",
517
496
self.add_new_client,
518
497
dbus_interface=server_interface,
519
498
byte_arrays=True))
520
499
(self.mandos_serv
521
.connect_to_signal("ClientNotFound",
500
.connect_to_signal(u"ClientNotFound",
522
501
self.client_not_found,
523
502
dbus_interface=server_interface,
524
503
byte_arrays=True))
571
550
and len(self.log) > self.max_log_length):
572
551
del self.log[0:len(self.log)-self.max_log_length-1]
573
552
self.logbox.set_focus(len(self.logbox.body.contents),
553
coming_from=u"above")
577
556
def toggle_log_display(self):
578
557
"""Toggle visibility of the log buffer."""
579
558
self.log_visible = not self.log_visible
581
#self.log_message("Log visibility changed to: "
560
#self.log_message(u"Log visibility changed to: "
582
561
# + unicode(self.log_visible))
584
563
def change_log_display(self):
585
564
"""Change type of log display.
586
565
Currently, this toggles wrapping of text lines."""
587
if self.log_wrap == "clip":
588
self.log_wrap = "any"
566
if self.log_wrap == u"clip":
567
self.log_wrap = u"any"
590
self.log_wrap = "clip"
569
self.log_wrap = u"clip"
591
570
for textwidget in self.log:
592
571
textwidget.set_wrap_mode(self.log_wrap)
593
#self.log_message("Wrap mode: " + self.log_wrap)
572
#self.log_message(u"Wrap mode: " + self.log_wrap)
595
574
def find_and_remove_client(self, path, name):
596
"""Find a client by its object path and remove it.
575
"""Find an client from its object path and remove it.
598
577
This is connected to the ClientRemoved signal from the
599
578
Mandos server object."""
663
640
def process_input(self, source, condition):
664
641
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
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
678
655
except KeyError: # :-)
681
if key == "q" or key == "Q":
658
if key == u"q" or key == u"Q":
684
elif key == "window resize":
661
elif key == u"window resize":
685
662
self.size = self.screen.get_cols_rows()
687
elif key == "\f": # Ctrl-L
664
elif key == u"\f": # Ctrl-L
689
elif key == "l" or key == "D":
666
elif key == u"l" or key == u"D":
690
667
self.toggle_log_display()
692
elif key == "w" or key == "i":
669
elif key == u"w" or key == u"i":
693
670
self.change_log_display()
695
elif key == "?" or key == "f1" or key == "esc":
672
elif key == u"?" or key == u"f1" or key == u"esc":
696
673
if not self.log_visible:
697
674
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",
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",
719
696
if self.topwidget.get_focus() is self.logbox:
720
697
self.topwidget.set_focus(0)
722
699
self.topwidget.set_focus(self.logbox)
724
#elif (key == "end" or key == "meta >" or key == "G"
701
#elif (key == u"end" or key == u"meta >" or key == u"G"
726
703
# pass # xxx end-of-buffer
727
#elif (key == "home" or key == "meta <" or key == "g"
704
#elif (key == u"home" or key == u"meta <" or key == u"g"
729
706
# pass # xxx beginning-of-buffer
730
#elif key == "ctrl e" or key == "$":
707
#elif key == u"ctrl e" or key == u"$":
731
708
# pass # xxx move-end-of-line
732
#elif key == "ctrl a" or key == "^":
709
#elif key == u"ctrl a" or key == u"^":
733
710
# pass # xxx move-beginning-of-line
734
#elif key == "ctrl b" or key == "meta (" or key == "h":
711
#elif key == u"ctrl b" or key == u"meta (" or key == u"h":
735
712
# pass # xxx left
736
#elif key == "ctrl f" or key == "meta )" or key == "l":
713
#elif key == u"ctrl f" or key == u"meta )" or key == u"l":
737
714
# pass # xxx right
739
716
# pass # scroll up log
741
718
# pass # scroll down log
742
719
elif self.topwidget.selectable():
743
720
self.topwidget.keypress(self.size, key)