55
61
domain = 'se.recompile'
56
62
server_interface = domain + '.Mandos'
57
63
client_interface = domain + '.Mandos.Client'
60
# Always run in monochrome mode
61
urwid.curses_display.curses.has_colors = lambda : False
63
# Urwid doesn't support blinking, but we want it. Since we have no
64
# use for underline on its own, we make underline also always blink.
65
urwid.curses_display.curses.A_UNDERLINE |= (
66
urwid.curses_display.curses.A_BLINK)
68
66
def isoformat_to_datetime(iso):
69
67
"Parse an ISO 8601 date string to a datetime.datetime()"
86
84
properties and calls a hook function when any of them are
89
def __init__(self, proxy_object=None, *args, **kwargs):
87
def __init__(self, proxy_object=None, properties=None, **kwargs):
90
88
self.proxy = proxy_object # Mandos Client proxy object
92
self.properties = dict()
89
self.properties = dict() if properties is None else properties
93
90
self.property_changed_match = (
94
91
self.proxy.connect_to_signal("PropertyChanged",
95
self.property_changed,
92
self._property_changed,
99
self.properties.update(
100
self.proxy.GetAll(client_interface,
101
dbus_interface = dbus.PROPERTIES_IFACE))
103
#XXX This breaks good super behaviour
104
# super(MandosClientPropertyCache, self).__init__(
96
if properties is None:
97
self.properties.update(
98
self.proxy.GetAll(client_interface,
100
= dbus.PROPERTIES_IFACE))
102
super(MandosClientPropertyCache, self).__init__(**kwargs)
104
def _property_changed(self, property, value):
105
"""Helper which takes positional arguments"""
106
return self.property_changed(property=property, value=value)
107
108
def property_changed(self, property=None, value=None):
108
109
"""This is called whenever we get a PropertyChanged signal
111
112
# Update properties dict with new value
112
113
self.properties[property] = value
114
def delete(self, *args, **kwargs):
115
116
self.property_changed_match.remove()
116
super(MandosClientPropertyCache, self).__init__(
120
119
class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
124
123
def __init__(self, server_proxy_object=None, update_hook=None,
125
delete_hook=None, logger=None, *args, **kwargs):
124
delete_hook=None, logger=None, **kwargs):
126
125
# Called on update
127
126
self.update_hook = update_hook
128
127
# Called on delete
133
132
self.logger = logger
135
134
self._update_timer_callback_tag = None
136
self._update_timer_callback_lock = 0
138
136
# The widget shown normally
139
137
self._text_widget = urwid.Text("")
140
138
# The widget shown when we have focus
141
139
self._focus_text_widget = urwid.Text("")
142
super(MandosClientWidget, self).__init__(
143
update_hook=update_hook, delete_hook=delete_hook,
140
super(MandosClientWidget, self).__init__(**kwargs)
146
142
self.opened = False
148
last_checked_ok = isoformat_to_datetime(self.properties
151
if self.properties ["LastCheckerStatus"] != 0:
152
self.using_timer(True)
154
if self.need_approval:
155
self.using_timer(True)
157
144
self.match_objects = (
158
145
self.proxy.connect_to_signal("CheckerCompleted",
159
146
self.checker_completed,
178
165
#self.logger('Created client {0}'
179
166
# .format(self.properties["Name"]))
181
def property_changed(self, property=None, value=None):
182
super(self, MandosClientWidget).property_changed(property,
184
if property == "ApprovalPending":
185
using_timer(bool(value))
186
if property == "LastCheckerStatus":
187
using_timer(value != 0)
188
#self.logger('Checker for client {0} (command "{1}") was '
189
# ' successful'.format(self.properties["Name"],
192
168
def using_timer(self, flag):
193
169
"""Call this method with True or False when timer should be
194
170
activated or deactivated.
196
old = self._update_timer_callback_lock
198
self._update_timer_callback_lock += 1
200
self._update_timer_callback_lock -= 1
201
if old == 0 and self._update_timer_callback_lock:
172
if flag and self._update_timer_callback_tag is None:
202
173
# Will update the shown timer value every second
203
174
self._update_timer_callback_tag = (gobject.timeout_add
205
176
self.update_timer))
206
elif old and self._update_timer_callback_lock == 0:
177
elif not (flag or self._update_timer_callback_tag is None):
207
178
gobject.source_remove(self._update_timer_callback_tag)
208
179
self._update_timer_callback_tag = None
283
253
"bold-underline-blink":
284
254
"bold-underline-blink-standout",
287
257
# Rebuild focus and non-focus widgets using current properties
289
259
# Base part of a client. Name!
290
260
base = '{name}: '.format(name=self.properties["Name"])
291
261
if not self.properties["Enabled"]:
292
262
message = "DISABLED"
263
self.using_timer(False)
293
264
elif self.properties["ApprovalPending"]:
294
265
timeout = datetime.timedelta(milliseconds
295
266
= self.properties
297
268
last_approval_request = isoformat_to_datetime(
298
269
self.properties["LastApprovalRequest"])
299
270
if last_approval_request is not None:
300
timer = timeout - (datetime.datetime.utcnow()
301
- last_approval_request)
271
timer = max(timeout - (datetime.datetime.utcnow()
272
- last_approval_request),
273
datetime.timedelta())
303
275
timer = datetime.timedelta()
304
276
if self.properties["ApprovedByDefault"]:
305
277
message = "Approval in {0}. (d)eny?"
307
279
message = "Denial in {0}. (a)pprove?"
308
message = message.format(unicode(timer).rsplit(".", 1)[0])
280
message = message.format(str(timer).rsplit(".", 1)[0])
281
self.using_timer(True)
309
282
elif self.properties["LastCheckerStatus"] != 0:
310
283
# When checker has failed, show timer until client expires
311
284
expires = self.properties["Expires"]
315
288
expires = (datetime.datetime.strptime
316
289
(expires, '%Y-%m-%dT%H:%M:%S.%f'))
317
timer = expires - datetime.datetime.utcnow()
290
timer = max(expires - datetime.datetime.utcnow(),
291
datetime.timedelta())
318
292
message = ('A checker has failed! Time until client'
319
293
' gets disabled: {0}'
320
.format(unicode(timer).rsplit(".", 1)[0]))
294
.format(str(timer).rsplit(".", 1)[0]))
295
self.using_timer(True)
322
297
message = "enabled"
298
self.using_timer(False)
323
299
self._text = "{0}{1}".format(base, message)
325
301
if not urwid.supports_unicode():
326
302
self._text = self._text.encode("ascii", "replace")
327
303
textlist = [("normal", self._text)]
345
321
return True # Keep calling this
347
def delete(self, *args, **kwargs):
323
def delete(self, **kwargs):
348
324
if self._update_timer_callback_tag is not None:
349
325
gobject.source_remove(self._update_timer_callback_tag)
350
326
self._update_timer_callback_tag = None
353
329
self.match_objects = ()
354
330
if self.delete_hook is not None:
355
331
self.delete_hook(self)
356
return super(MandosClientWidget, self).delete(*args, **kwargs)
332
return super(MandosClientWidget, self).delete(**kwargs)
358
334
def render(self, maxcolrow, focus=False):
359
335
"""Render differently if we have focus.
404
def property_changed(self, property=None, value=None,
380
def property_changed(self, property=None, **kwargs):
406
381
"""Call self.update() if old value is not new value.
407
382
This overrides the method from MandosClientPropertyCache"""
408
property_name = unicode(property)
383
property_name = str(property)
409
384
old_value = self.properties.get(property_name)
410
385
super(MandosClientWidget, self).property_changed(
411
property=property, value=value, *args, **kwargs)
386
property=property, **kwargs)
412
387
if self.properties.get(property_name) != old_value:
418
393
"down" key presses, thus not allowing any containing widgets to
419
394
use them as an excuse to shift focus away from this widget.
421
def keypress(self, maxcolrow, key):
422
ret = super(ConstrainedListBox, self).keypress(maxcolrow, key)
396
def keypress(self, *args, **kwargs):
397
ret = super(ConstrainedListBox, self).keypress(*args, **kwargs)
423
398
if ret in ("up", "down"):
439
414
"default", "default", None),
441
"default", "default", "bold"),
416
"bold", "default", "bold"),
442
417
("underline-blink",
443
"default", "default", "underline"),
418
"underline,blink", "default", "underline,blink"),
445
"default", "default", "standout"),
420
"standout", "default", "standout"),
446
421
("bold-underline-blink",
447
"default", "default", ("bold", "underline")),
422
"bold,underline,blink", "default", "bold,underline,blink"),
448
423
("bold-standout",
449
"default", "default", ("bold", "standout")),
424
"bold,standout", "default", "bold,standout"),
450
425
("underline-blink-standout",
451
"default", "default", ("underline", "standout")),
426
"underline,blink,standout", "default",
427
"underline,blink,standout"),
452
428
("bold-underline-blink-standout",
453
"default", "default", ("bold", "underline",
429
"bold,underline,blink,standout", "default",
430
"bold,underline,blink,standout"),
457
433
if urwid.supports_unicode():
608
580
mandos_clients = (self.mandos_serv
609
581
.GetAllClientsWithProperties())
582
if not mandos_clients:
583
self.log_message_raw(("bold", "Note: Server has no clients."))
610
584
except dbus.exceptions.DBusException:
585
self.log_message_raw(("bold", "Note: No Mandos server running."))
611
586
mandos_clients = dbus.Dictionary()
613
588
(self.mandos_serv
625
600
self.client_not_found,
626
601
dbus_interface=server_interface,
627
602
byte_arrays=True))
628
for path, client in mandos_clients.iteritems():
603
for path, client in mandos_clients.items():
629
604
client_proxy_object = self.bus.get_object(self.busname,
631
606
self.add_client(MandosClientWidget(server_proxy_object