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.recompile'
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):
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.
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
self._text = "%s%s" % (base, message)
321
self._text = u"%s%s" % (base, message)
333
323
if not urwid.supports_unicode():
334
self._text = self._text.encode("ascii", "replace")
335
textlist = [("normal", self._text)]
324
self._text = self._text.encode(u"ascii", u"replace")
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]],
341
331
for text in textlist])
342
332
self._widget = self._text_widget
343
333
self._focus_widget = urwid.AttrWrap(self._focus_text_widget,
345
335
# Run update hook, if any
346
336
if self.update_hook is not None:
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
351
def render(self, maxcolrow, focus=False):
367
352
"""Render differently if we have focus.
372
357
def keypress(self, maxcolrow, 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":
443
420
self.screen = urwid.curses_display.Screen()
445
422
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",
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",
465
442
if urwid.supports_unicode():
466
self.divider = "─" # \u2500
467
#self.divider = "━" # \u2501
443
self.divider = u"─" # \u2500
444
#self.divider = u"━" # \u2501
469
#self.divider = "-" # \u002d
470
self.divider = "_" # \u005f
446
#self.divider = u"-" # \u002d
447
self.divider = u"_" # \u005f
472
449
self.screen.start()
487
464
# This keeps track of whether self.uilist currently has
488
465
# self.logbox in it or not
489
466
self.log_visible = True
490
self.log_wrap = "any"
467
self.log_wrap = u"any"
493
self.log_message_raw(("bold",
494
"Mandos Monitor version " + version))
495
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",
498
475
self.busname = domain + '.Mandos'
499
476
self.main_loop = gobject.MainLoop()
500
477
self.bus = dbus.SystemBus()
501
478
mandos_dbus_objc = self.bus.get_object(
502
self.busname, "/", follow_name_owner_changes=True)
479
self.busname, u"/", follow_name_owner_changes=True)
503
480
self.mandos_serv = dbus.Interface(mandos_dbus_objc,
505
482
= server_interface)
510
487
mandos_clients = dbus.Dictionary()
512
489
(self.mandos_serv
513
.connect_to_signal("ClientRemoved",
490
.connect_to_signal(u"ClientRemoved",
514
491
self.find_and_remove_client,
515
492
dbus_interface=server_interface,
516
493
byte_arrays=True))
517
494
(self.mandos_serv
518
.connect_to_signal("ClientAdded",
495
.connect_to_signal(u"ClientAdded",
519
496
self.add_new_client,
520
497
dbus_interface=server_interface,
521
498
byte_arrays=True))
522
499
(self.mandos_serv
523
.connect_to_signal("ClientNotFound",
500
.connect_to_signal(u"ClientNotFound",
524
501
self.client_not_found,
525
502
dbus_interface=server_interface,
526
503
byte_arrays=True))
573
550
and len(self.log) > self.max_log_length):
574
551
del self.log[0:len(self.log)-self.max_log_length-1]
575
552
self.logbox.set_focus(len(self.logbox.body.contents),
553
coming_from=u"above")
579
556
def toggle_log_display(self):
580
557
"""Toggle visibility of the log buffer."""
581
558
self.log_visible = not self.log_visible
583
#self.log_message("Log visibility changed to: "
560
#self.log_message(u"Log visibility changed to: "
584
561
# + unicode(self.log_visible))
586
563
def change_log_display(self):
587
564
"""Change type of log display.
588
565
Currently, this toggles wrapping of text lines."""
589
if self.log_wrap == "clip":
590
self.log_wrap = "any"
566
if self.log_wrap == u"clip":
567
self.log_wrap = u"any"
592
self.log_wrap = "clip"
569
self.log_wrap = u"clip"
593
570
for textwidget in self.log:
594
571
textwidget.set_wrap_mode(self.log_wrap)
595
#self.log_message("Wrap mode: " + self.log_wrap)
572
#self.log_message(u"Wrap mode: " + self.log_wrap)
597
574
def find_and_remove_client(self, path, name):
598
"""Find a client by its object path and remove it.
575
"""Find an client from its object path and remove it.
600
577
This is connected to the ClientRemoved signal from the
601
578
Mandos server object."""
665
640
def process_input(self, source, condition):
666
641
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
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
680
655
except KeyError: # :-)
683
if key == "q" or key == "Q":
658
if key == u"q" or key == u"Q":
686
elif key == "window resize":
661
elif key == u"window resize":
687
662
self.size = self.screen.get_cols_rows()
689
elif key == "\f": # Ctrl-L
664
elif key == u"\f": # Ctrl-L
691
elif key == "l" or key == "D":
666
elif key == u"l" or key == u"D":
692
667
self.toggle_log_display()
694
elif key == "w" or key == "i":
669
elif key == u"w" or key == u"i":
695
670
self.change_log_display()
697
elif key == "?" or key == "f1" or key == "esc":
672
elif key == u"?" or key == u"f1" or key == u"esc":
698
673
if not self.log_visible:
699
674
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",
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",
721
696
if self.topwidget.get_focus() is self.logbox:
722
697
self.topwidget.set_focus(0)
724
699
self.topwidget.set_focus(self.logbox)
726
#elif (key == "end" or key == "meta >" or key == "G"
701
#elif (key == u"end" or key == u"meta >" or key == u"G"
728
703
# pass # xxx end-of-buffer
729
#elif (key == "home" or key == "meta <" or key == "g"
704
#elif (key == u"home" or key == u"meta <" or key == u"g"
731
706
# pass # xxx beginning-of-buffer
732
#elif key == "ctrl e" or key == "$":
707
#elif key == u"ctrl e" or key == u"$":
733
708
# pass # xxx move-end-of-line
734
#elif key == "ctrl a" or key == "^":
709
#elif key == u"ctrl a" or key == u"^":
735
710
# pass # xxx move-beginning-of-line
736
#elif key == "ctrl b" or key == "meta (" or key == "h":
711
#elif key == u"ctrl b" or key == u"meta (" or key == u"h":
737
712
# pass # xxx left
738
#elif key == "ctrl f" or key == "meta )" or key == "l":
713
#elif key == u"ctrl f" or key == u"meta )" or key == u"l":
739
714
# pass # xxx right
741
716
# pass # scroll up log
743
718
# pass # scroll down log
744
719
elif self.topwidget.selectable():
745
720
self.topwidget.keypress(self.size, key)