130
102
self.logger = logger
132
104
self._update_timer_callback_tag = None
133
self._update_timer_callback_lock = 0
105
self.last_checker_failed = False
135
107
# The widget shown normally
136
self._text_widget = urwid.Text("")
108
self._text_widget = urwid.Text(u"")
137
109
# The widget shown when we have focus
138
self._focus_text_widget = urwid.Text("")
110
self._focus_text_widget = urwid.Text(u"")
139
111
super(MandosClientWidget, self).__init__(
140
112
update_hook=update_hook, delete_hook=delete_hook,
143
115
self.opened = False
116
self.proxy.connect_to_signal(u"CheckerCompleted",
117
self.checker_completed,
120
self.proxy.connect_to_signal(u"CheckerStarted",
121
self.checker_started,
124
self.proxy.connect_to_signal(u"GotSecret",
128
self.proxy.connect_to_signal(u"NeedApproval",
132
self.proxy.connect_to_signal(u"Rejected",
145
136
last_checked_ok = isoformat_to_datetime(self.properties
148
if self.properties ["LastCheckerStatus"] != 0:
149
self.using_timer(True)
151
if self.need_approval:
152
self.using_timer(True)
154
self.match_objects = (
155
self.proxy.connect_to_signal("CheckerCompleted",
156
self.checker_completed,
159
self.proxy.connect_to_signal("CheckerStarted",
160
self.checker_started,
163
self.proxy.connect_to_signal("GotSecret",
167
self.proxy.connect_to_signal("NeedApproval",
171
self.proxy.connect_to_signal("Rejected",
175
#self.logger('Created client {0}'
176
# .format(self.properties["Name"]))
178
def property_changed(self, property=None, value=None):
179
super(self, MandosClientWidget).property_changed(property,
181
if property == "ApprovalPending":
182
using_timer(bool(value))
183
if property == "LastCheckerStatus":
184
using_timer(value != 0)
185
#self.logger('Checker for client {0} (command "{1}") was '
186
# ' successful'.format(self.properties["Name"],
189
def using_timer(self, flag):
190
"""Call this method with True or False when timer should be
191
activated or deactivated.
193
old = self._update_timer_callback_lock
195
self._update_timer_callback_lock += 1
138
if last_checked_ok is None:
139
self.last_checker_failed = True
197
self._update_timer_callback_lock -= 1
198
if old == 0 and self._update_timer_callback_lock:
199
# Will update the shown timer value every second
141
self.last_checker_failed = ((datetime.datetime.utcnow()
147
if self.last_checker_failed:
200
148
self._update_timer_callback_tag = (gobject.timeout_add
202
150
self.update_timer))
203
elif old and self._update_timer_callback_lock == 0:
204
gobject.source_remove(self._update_timer_callback_tag)
205
self._update_timer_callback_tag = None
207
152
def checker_completed(self, exitstatus, condition, command):
208
153
if exitstatus == 0:
154
if self.last_checker_failed:
155
self.last_checker_failed = False
156
gobject.source_remove(self._update_timer_callback_tag)
157
self._update_timer_callback_tag = None
158
#self.logger(u'Checker for client %s (command "%s")'
160
# % (self.properties[u"Name"], command))
164
if not self.last_checker_failed:
165
self.last_checker_failed = True
166
self._update_timer_callback_tag = (gobject.timeout_add
212
169
if os.WIFEXITED(condition):
213
self.logger('Checker for client {0} (command "{1}")'
214
' failed with exit code {2}'
215
.format(self.properties["Name"], command,
216
os.WEXITSTATUS(condition)))
170
self.logger(u'Checker for client %s (command "%s")'
171
u' failed with exit code %s'
172
% (self.properties[u"Name"], command,
173
os.WEXITSTATUS(condition)))
217
174
elif os.WIFSIGNALED(condition):
218
self.logger('Checker for client {0} (command "{1}") was'
219
' killed by signal {2}'
220
.format(self.properties["Name"], command,
221
os.WTERMSIG(condition)))
175
self.logger(u'Checker for client %s (command "%s")'
176
u' was killed by signal %s'
177
% (self.properties[u"Name"], command,
178
os.WTERMSIG(condition)))
222
179
elif os.WCOREDUMP(condition):
223
self.logger('Checker for client {0} (command "{1}")'
225
.format(self.properties["Name"], command))
180
self.logger(u'Checker for client %s (command "%s")'
182
% (self.properties[u"Name"], command))
227
self.logger('Checker for client {0} completed'
229
.format(self.properties["Name"]))
184
self.logger(u'Checker for client %s completed'
232
188
def checker_started(self, command):
233
"""Server signals that a checker started. This could be useful
234
to log in the future. """
235
#self.logger('Client {0} started checker "{1}"'
236
# .format(self.properties["Name"],
189
#self.logger(u'Client %s started checker "%s"'
190
# % (self.properties[u"Name"], unicode(command)))
240
193
def got_secret(self):
241
self.logger('Client {0} received its secret'
242
.format(self.properties["Name"]))
194
self.last_checker_failed = False
195
self.logger(u'Client %s received its secret'
196
% self.properties[u"Name"])
244
198
def need_approval(self, timeout, default):
246
message = 'Client {0} needs approval within {1} seconds'
200
message = u'Client %s needs approval within %s seconds'
248
message = 'Client {0} will get its secret in {1} seconds'
249
self.logger(message.format(self.properties["Name"],
251
self.using_timer(True)
202
message = u'Client %s will get its secret in %s seconds'
204
% (self.properties[u"Name"], timeout/1000))
253
206
def rejected(self, reason):
254
self.logger('Client {0} was rejected; reason: {1}'
255
.format(self.properties["Name"], reason))
207
self.logger(u'Client %s was rejected; reason: %s'
208
% (self.properties[u"Name"], reason))
257
210
def selectable(self):
258
211
"""Make this a "selectable" widget.
259
212
This overrides the method from urwid.FlowWidget."""
262
def rows(self, maxcolrow, focus=False):
215
def rows(self, (maxcol,), focus=False):
263
216
"""How many rows this widget will occupy might depend on
264
217
whether we have focus or not.
265
218
This overrides the method from urwid.FlowWidget"""
266
return self.current_widget(focus).rows(maxcolrow, focus=focus)
219
return self.current_widget(focus).rows((maxcol,), focus=focus)
268
221
def current_widget(self, focus=False):
269
222
if focus or self.opened:
273
226
def update(self):
274
227
"Called when what is visible on the screen should be updated."
275
228
# How to add standout mode to a style
276
with_standout = { "normal": "standout",
277
"bold": "bold-standout",
279
"underline-blink-standout",
280
"bold-underline-blink":
281
"bold-underline-blink-standout",
229
with_standout = { u"normal": u"standout",
230
u"bold": u"bold-standout",
232
u"underline-blink-standout",
233
u"bold-underline-blink":
234
u"bold-underline-blink-standout",
284
237
# Rebuild focus and non-focus widgets using current properties
286
239
# Base part of a client. Name!
287
base = '{name}: '.format(name=self.properties["Name"])
288
if not self.properties["Enabled"]:
290
elif self.properties["ApprovalPending"]:
240
base = (u'%(name)s: '
241
% {u"name": self.properties[u"Name"]})
242
if not self.properties[u"Enabled"]:
243
message = u"DISABLED"
244
elif self.properties[u"ApprovalPending"]:
245
if self.properties[u"ApprovedByDefault"]:
246
message = u"Connection established to client. (d)eny?"
248
message = u"Seeks approval to send secret. (a)pprove?"
249
elif self.last_checker_failed:
291
250
timeout = datetime.timedelta(milliseconds
292
251
= self.properties
294
last_approval_request = isoformat_to_datetime(
295
self.properties["LastApprovalRequest"])
296
if last_approval_request is not None:
297
timer = timeout - (datetime.datetime.utcnow()
298
- last_approval_request)
300
timer = datetime.timedelta()
301
if self.properties["ApprovedByDefault"]:
302
message = "Approval in {0}. (d)eny?"
304
message = "Denial in {0}. (a)pprove?"
305
message = message.format(unicode(timer).rsplit(".", 1)[0])
306
elif self.properties["LastCheckerStatus"] != 0:
307
# When checker has failed, print a timer until client expires
308
expires = self.properties["Expires"]
310
timer = datetime.timedelta(0)
312
expires = datetime.datetime.strptime(expires,
313
'%Y-%m-%dT%H:%M:%S.%f')
314
timer = expires - datetime.datetime.utcnow()
315
message = ('A checker has failed! Time until client'
316
' gets disabled: {0}'
317
.format(unicode(timer).rsplit(".", 1)[0]))
253
last_ok = isoformat_to_datetime(
254
max((self.properties[u"LastCheckedOK"]
255
or self.properties[u"Created"]),
256
self.properties[u"LastEnabled"]))
257
timer = timeout - (datetime.datetime.utcnow() - last_ok)
258
message = (u'A checker has failed! Time until client'
260
% unicode(timer).rsplit(".", 1)[0])
320
self._text = "{0}{1}".format(base, message)
263
self._text = "%s%s" % (base, message)
322
265
if not urwid.supports_unicode():
323
266
self._text = self._text.encode("ascii", "replace")
324
textlist = [("normal", self._text)]
267
textlist = [(u"normal", self._text)]
325
268
self._text_widget.set_text(textlist)
326
269
self._focus_text_widget.set_text([(with_standout[text[0]],
336
279
self.update_hook()
338
281
def update_timer(self):
339
"""called by gobject. Will indefinitely loop until
340
gobject.source_remove() on tag is called"""
342
284
return True # Keep calling this
344
def delete(self, *args, **kwargs):
345
287
if self._update_timer_callback_tag is not None:
346
288
gobject.source_remove(self._update_timer_callback_tag)
347
289
self._update_timer_callback_tag = None
348
for match in self.match_objects:
350
self.match_objects = ()
351
290
if self.delete_hook is not None:
352
291
self.delete_hook(self)
353
return super(MandosClientWidget, self).delete(*args, **kwargs)
355
def render(self, maxcolrow, focus=False):
293
def render(self, (maxcol,), focus=False):
356
294
"""Render differently if we have focus.
357
295
This overrides the method from urwid.FlowWidget"""
358
return self.current_widget(focus).render(maxcolrow,
296
return self.current_widget(focus).render((maxcol,),
361
def keypress(self, maxcolrow, key):
299
def keypress(self, (maxcol,), key):
363
301
This overrides the method from urwid.FlowWidget"""
365
self.proxy.Enable(dbus_interface = client_interface,
368
self.proxy.Disable(dbus_interface = client_interface,
303
self.proxy.Enable(dbus_interface = client_interface)
305
self.proxy.Disable(dbus_interface = client_interface)
371
307
self.proxy.Approve(dbus.Boolean(True, variant_level=1),
372
dbus_interface = client_interface,
308
dbus_interface = client_interface)
375
310
self.proxy.Approve(dbus.Boolean(False, variant_level=1),
376
dbus_interface = client_interface,
378
elif key == "R" or key == "_" or key == "ctrl k":
311
dbus_interface = client_interface)
312
elif key == u"r" or key == u"_" or key == u"ctrl k":
379
313
self.server_proxy_object.RemoveClient(self.proxy
383
self.proxy.StartChecker(dbus_interface = client_interface,
386
self.proxy.StopChecker(dbus_interface = client_interface,
389
self.proxy.CheckedOK(dbus_interface = client_interface,
316
self.proxy.StartChecker(dbus_interface = client_interface)
318
self.proxy.StopChecker(dbus_interface = client_interface)
320
self.proxy.CheckedOK(dbus_interface = client_interface)
392
# elif key == "p" or key == "=":
322
# elif key == u"p" or key == "=":
393
323
# self.proxy.pause()
394
# elif key == "u" or key == ":":
324
# elif key == u"u" or key == ":":
395
325
# self.proxy.unpause()
326
# elif key == u"RET":
432
363
self.screen = urwid.curses_display.Screen()
434
365
self.screen.register_palette((
436
"default", "default", None),
438
"default", "default", "bold"),
440
"default", "default", "underline"),
442
"default", "default", "standout"),
443
("bold-underline-blink",
444
"default", "default", ("bold", "underline")),
446
"default", "default", ("bold", "standout")),
447
("underline-blink-standout",
448
"default", "default", ("underline", "standout")),
449
("bold-underline-blink-standout",
450
"default", "default", ("bold", "underline",
367
u"default", u"default", None),
369
u"default", u"default", u"bold"),
371
u"default", u"default", u"underline"),
373
u"default", u"default", u"standout"),
374
(u"bold-underline-blink",
375
u"default", u"default", (u"bold", u"underline")),
377
u"default", u"default", (u"bold", u"standout")),
378
(u"underline-blink-standout",
379
u"default", u"default", (u"underline", u"standout")),
380
(u"bold-underline-blink-standout",
381
u"default", u"default", (u"bold", u"underline",
454
385
if urwid.supports_unicode():
455
self.divider = "─" # \u2500
456
#self.divider = "━" # \u2501
386
self.divider = u"─" # \u2500
387
#self.divider = u"━" # \u2501
458
#self.divider = "-" # \u002d
459
self.divider = "_" # \u005f
389
#self.divider = u"-" # \u002d
390
self.divider = u"_" # \u005f
461
392
self.screen.start()
476
407
# This keeps track of whether self.uilist currently has
477
408
# self.logbox in it or not
478
409
self.log_visible = True
479
self.log_wrap = "any"
410
self.log_wrap = u"any"
482
self.log_message_raw(("bold",
483
"Mandos Monitor version " + version))
484
self.log_message_raw(("bold",
413
self.log_message_raw((u"bold",
414
u"Mandos Monitor version " + version))
415
self.log_message_raw((u"bold",
487
418
self.busname = domain + '.Mandos'
488
419
self.main_loop = gobject.MainLoop()
420
self.bus = dbus.SystemBus()
421
mandos_dbus_objc = self.bus.get_object(
422
self.busname, u"/", follow_name_owner_changes=True)
423
self.mandos_serv = dbus.Interface(mandos_dbus_objc,
427
mandos_clients = (self.mandos_serv
428
.GetAllClientsWithProperties())
429
except dbus.exceptions.DBusException:
430
mandos_clients = dbus.Dictionary()
433
.connect_to_signal(u"ClientRemoved",
434
self.find_and_remove_client,
435
dbus_interface=server_interface,
438
.connect_to_signal(u"ClientAdded",
440
dbus_interface=server_interface,
443
.connect_to_signal(u"ClientNotFound",
444
self.client_not_found,
445
dbus_interface=server_interface,
447
for path, client in mandos_clients.iteritems():
448
client_proxy_object = self.bus.get_object(self.busname,
450
self.add_client(MandosClientWidget(server_proxy_object
453
=client_proxy_object,
490
463
def client_not_found(self, fingerprint, address):
491
self.log_message("Client with address {0} and fingerprint"
492
" {1} could not be found"
493
.format(address, fingerprint))
464
self.log_message((u"Client with address %s and fingerprint %s"
465
u" could not be found" % (address,
495
468
def rebuild(self):
496
469
"""This rebuilds the User Interface.
597
569
"""Start the main loop and exit when it's done."""
598
self.bus = dbus.SystemBus()
599
mandos_dbus_objc = self.bus.get_object(
600
self.busname, "/", follow_name_owner_changes=True)
601
self.mandos_serv = dbus.Interface(mandos_dbus_objc,
605
mandos_clients = (self.mandos_serv
606
.GetAllClientsWithProperties())
607
except dbus.exceptions.DBusException:
608
mandos_clients = dbus.Dictionary()
611
.connect_to_signal("ClientRemoved",
612
self.find_and_remove_client,
613
dbus_interface=server_interface,
616
.connect_to_signal("ClientAdded",
618
dbus_interface=server_interface,
621
.connect_to_signal("ClientNotFound",
622
self.client_not_found,
623
dbus_interface=server_interface,
625
for path, client in mandos_clients.iteritems():
626
client_proxy_object = self.bus.get_object(self.busname,
628
self.add_client(MandosClientWidget(server_proxy_object
631
=client_proxy_object,
642
571
self._input_callback_tag = (gobject.io_add_watch
643
572
(sys.stdin.fileno(),
669
598
except KeyError: # :-)
672
if key == "q" or key == "Q":
601
if key == u"q" or key == u"Q":
675
elif key == "window resize":
604
elif key == u"window resize":
676
605
self.size = self.screen.get_cols_rows()
678
elif key == "\f": # Ctrl-L
607
elif key == u"\f": # Ctrl-L
680
elif key == "l" or key == "D":
609
elif key == u"l" or key == u"D":
681
610
self.toggle_log_display()
683
elif key == "w" or key == "i":
612
elif key == u"w" or key == u"i":
684
613
self.change_log_display()
686
elif key == "?" or key == "f1" or key == "esc":
615
elif key == u"?" or key == u"f1" or key == u"esc":
687
616
if not self.log_visible:
688
617
self.log_visible = True
690
self.log_message_raw(("bold",
694
"l: Log window toggle",
695
"TAB: Switch window",
697
self.log_message_raw(("bold",
703
"s: Start new checker",
619
self.log_message_raw((u"bold",
623
u"l: Log window toggle",
624
u"TAB: Switch window",
626
self.log_message_raw((u"bold",
632
u"s: Start new checker",
710
639
if self.topwidget.get_focus() is self.logbox:
711
640
self.topwidget.set_focus(0)
713
642
self.topwidget.set_focus(self.logbox)
715
#elif (key == "end" or key == "meta >" or key == "G"
644
#elif (key == u"end" or key == u"meta >" or key == u"G"
717
646
# pass # xxx end-of-buffer
718
#elif (key == "home" or key == "meta <" or key == "g"
647
#elif (key == u"home" or key == u"meta <" or key == u"g"
720
649
# pass # xxx beginning-of-buffer
721
#elif key == "ctrl e" or key == "$":
650
#elif key == u"ctrl e" or key == u"$":
722
651
# pass # xxx move-end-of-line
723
#elif key == "ctrl a" or key == "^":
652
#elif key == u"ctrl a" or key == u"^":
724
653
# pass # xxx move-beginning-of-line
725
#elif key == "ctrl b" or key == "meta (" or key == "h":
654
#elif key == u"ctrl b" or key == u"meta (" or key == u"h":
726
655
# pass # xxx left
727
#elif key == "ctrl f" or key == "meta )" or key == "l":
656
#elif key == u"ctrl f" or key == u"meta )" or key == u"l":
728
657
# pass # xxx right
730
659
# pass # scroll up log
732
661
# pass # scroll down log
733
662
elif self.topwidget.selectable():
734
663
self.topwidget.keypress(self.size, key)