/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos-monitor

  • Committer: Teddy Hogeborn
  • Date: 2016-07-03 03:32:28 UTC
  • Revision ID: teddy@recompile.se-20160703033228-cafpnnfs53kdg52g
Change all http:// URLs to https:// wherever possible.

* INSTALL (Prerequisites/Libraries/Mandos Server): Change "http://" to
  "https://" wherever possible.
* Makefile (FORTIFY): - '' -
* mandos.xml (SEE ALSO): - '' -
* plugin-runner.c (main): - '' -
* plugins.d/mandos-client.c (start_mandos_communication): - '' -
* plugins.d/mandos-client.xml (SEE ALSO): - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python3 -bb
 
1
#!/usr/bin/python
2
2
# -*- mode: python; coding: utf-8 -*-
3
 
#
 
3
4
4
# Mandos Monitor - Control and monitor the Mandos server
5
 
#
6
 
# Copyright © 2009-2019 Teddy Hogeborn
7
 
# Copyright © 2009-2019 Björn Påhlsson
8
 
#
9
 
# This file is part of Mandos.
10
 
#
11
 
# Mandos is free software: you can redistribute it and/or modify it
12
 
# under the terms of the GNU General Public License as published by
 
5
 
6
# Copyright © 2009-2016 Teddy Hogeborn
 
7
# Copyright © 2009-2016 Björn Påhlsson
 
8
 
9
# This program is free software: you can redistribute it and/or modify
 
10
# it under the terms of the GNU General Public License as published by
13
11
# the Free Software Foundation, either version 3 of the License, or
14
12
# (at your option) any later version.
15
13
#
16
 
#     Mandos is distributed in the hope that it will be useful, but
17
 
#     WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
#     This program is distributed in the hope that it will be useful,
 
15
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
18
16
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
17
#     GNU General Public License for more details.
20
 
#
 
18
21
19
# You should have received a copy of the GNU General Public License
22
 
# along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
23
 
#
 
20
# along with this program.  If not, see
 
21
# <http://www.gnu.org/licenses/>.
 
22
24
23
# Contact the authors at <mandos@recompile.se>.
25
 
#
 
24
26
25
 
27
26
from __future__ import (division, absolute_import, print_function,
28
27
                        unicode_literals)
33
32
 
34
33
import sys
35
34
import os
36
 
import warnings
 
35
 
37
36
import datetime
38
37
 
39
38
import urwid.curses_display
46
45
 
47
46
import locale
48
47
 
49
 
import logging
50
 
 
51
48
if sys.version_info.major == 2:
52
49
    str = unicode
53
50
 
54
51
locale.setlocale(locale.LC_ALL, '')
55
52
 
 
53
import logging
56
54
logging.getLogger('dbus.proxies').setLevel(logging.CRITICAL)
57
55
 
58
56
# Some useful constants
59
57
domain = 'se.recompile'
60
58
server_interface = domain + '.Mandos'
61
59
client_interface = domain + '.Mandos.Client'
62
 
version = "1.8.8"
 
60
version = "1.7.10"
63
61
 
64
62
try:
65
63
    dbus.OBJECT_MANAGER_IFACE
66
64
except AttributeError:
67
65
    dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
68
66
 
69
 
 
70
67
def isoformat_to_datetime(iso):
71
68
    "Parse an ISO 8601 date string to a datetime.datetime()"
72
69
    if not iso:
80
77
                             int(day),
81
78
                             int(hour),
82
79
                             int(minute),
83
 
                             int(second),            # Whole seconds
84
 
                             int(fraction*1000000))  # Microseconds
85
 
 
 
80
                             int(second),           # Whole seconds
 
81
                             int(fraction*1000000)) # Microseconds
86
82
 
87
83
class MandosClientPropertyCache(object):
88
84
    """This wraps a Mandos Client D-Bus proxy object, caches the
90
86
    changed.
91
87
    """
92
88
    def __init__(self, proxy_object=None, properties=None, **kwargs):
93
 
        self.proxy = proxy_object  # Mandos Client proxy object
 
89
        self.proxy = proxy_object # Mandos Client proxy object
94
90
        self.properties = dict() if properties is None else properties
95
91
        self.property_changed_match = (
96
92
            self.proxy.connect_to_signal("PropertiesChanged",
97
93
                                         self.properties_changed,
98
94
                                         dbus.PROPERTIES_IFACE,
99
95
                                         byte_arrays=True))
100
 
 
 
96
        
101
97
        if properties is None:
102
 
            self.properties.update(self.proxy.GetAll(
103
 
                client_interface,
104
 
                dbus_interface=dbus.PROPERTIES_IFACE))
105
 
 
 
98
            self.properties.update(
 
99
                self.proxy.GetAll(client_interface,
 
100
                                  dbus_interface
 
101
                                  = dbus.PROPERTIES_IFACE))
 
102
        
106
103
        super(MandosClientPropertyCache, self).__init__(**kwargs)
107
 
 
 
104
    
108
105
    def properties_changed(self, interface, properties, invalidated):
109
106
        """This is called whenever we get a PropertiesChanged signal
110
107
        It updates the changed properties in the "properties" dict.
112
109
        # Update properties dict with new value
113
110
        if interface == client_interface:
114
111
            self.properties.update(properties)
115
 
 
 
112
    
116
113
    def delete(self):
117
114
        self.property_changed_match.remove()
118
115
 
120
117
class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
121
118
    """A Mandos Client which is visible on the screen.
122
119
    """
123
 
 
 
120
    
124
121
    def __init__(self, server_proxy_object=None, update_hook=None,
125
122
                 delete_hook=None, logger=None, **kwargs):
126
123
        # Called on update
131
128
        self.server_proxy_object = server_proxy_object
132
129
        # Logger
133
130
        self.logger = logger
134
 
 
 
131
        
135
132
        self._update_timer_callback_tag = None
136
 
 
 
133
        
137
134
        # The widget shown normally
138
135
        self._text_widget = urwid.Text("")
139
136
        # The widget shown when we have focus
141
138
        super(MandosClientWidget, self).__init__(**kwargs)
142
139
        self.update()
143
140
        self.opened = False
144
 
 
 
141
        
145
142
        self.match_objects = (
146
143
            self.proxy.connect_to_signal("CheckerCompleted",
147
144
                                         self.checker_completed,
165
162
                                         byte_arrays=True))
166
163
        self.logger('Created client {}'
167
164
                    .format(self.properties["Name"]), level=0)
168
 
 
 
165
    
169
166
    def using_timer(self, flag):
170
167
        """Call this method with True or False when timer should be
171
168
        activated or deactivated.
178
175
        elif not (flag or self._update_timer_callback_tag is None):
179
176
            GLib.source_remove(self._update_timer_callback_tag)
180
177
            self._update_timer_callback_tag = None
181
 
 
 
178
    
182
179
    def checker_completed(self, exitstatus, condition, command):
183
180
        if exitstatus == 0:
184
181
            self.logger('Checker for client {} (command "{}")'
198
195
                        .format(self.properties["Name"], command,
199
196
                                os.WTERMSIG(condition)))
200
197
        self.update()
201
 
 
 
198
    
202
199
    def checker_started(self, command):
203
200
        """Server signals that a checker started."""
204
201
        self.logger('Client {} started checker "{}"'
205
202
                    .format(self.properties["Name"],
206
203
                            command), level=0)
207
 
 
 
204
    
208
205
    def got_secret(self):
209
206
        self.logger('Client {} received its secret'
210
207
                    .format(self.properties["Name"]))
211
 
 
 
208
    
212
209
    def need_approval(self, timeout, default):
213
210
        if not default:
214
211
            message = 'Client {} needs approval within {} seconds'
216
213
            message = 'Client {} will get its secret in {} seconds'
217
214
        self.logger(message.format(self.properties["Name"],
218
215
                                   timeout/1000))
219
 
 
 
216
    
220
217
    def rejected(self, reason):
221
218
        self.logger('Client {} was rejected; reason: {}'
222
219
                    .format(self.properties["Name"], reason))
223
 
 
 
220
    
224
221
    def selectable(self):
225
222
        """Make this a "selectable" widget.
226
223
        This overrides the method from urwid.FlowWidget."""
227
224
        return True
228
 
 
 
225
    
229
226
    def rows(self, maxcolrow, focus=False):
230
227
        """How many rows this widget will occupy might depend on
231
228
        whether we have focus or not.
232
229
        This overrides the method from urwid.FlowWidget"""
233
230
        return self.current_widget(focus).rows(maxcolrow, focus=focus)
234
 
 
 
231
    
235
232
    def current_widget(self, focus=False):
236
233
        if focus or self.opened:
237
234
            return self._focus_widget
238
235
        return self._widget
239
 
 
 
236
    
240
237
    def update(self):
241
238
        "Called when what is visible on the screen should be updated."
242
239
        # How to add standout mode to a style
243
 
        with_standout = {"normal": "standout",
244
 
                         "bold": "bold-standout",
245
 
                         "underline-blink":
246
 
                         "underline-blink-standout",
247
 
                         "bold-underline-blink":
248
 
                         "bold-underline-blink-standout",
249
 
                         }
250
 
 
 
240
        with_standout = { "normal": "standout",
 
241
                          "bold": "bold-standout",
 
242
                          "underline-blink":
 
243
                              "underline-blink-standout",
 
244
                          "bold-underline-blink":
 
245
                              "bold-underline-blink-standout",
 
246
                          }
 
247
        
251
248
        # Rebuild focus and non-focus widgets using current properties
252
 
 
 
249
        
253
250
        # Base part of a client. Name!
254
251
        base = '{name}: '.format(name=self.properties["Name"])
255
252
        if not self.properties["Enabled"]:
256
253
            message = "DISABLED"
257
254
            self.using_timer(False)
258
255
        elif self.properties["ApprovalPending"]:
259
 
            timeout = datetime.timedelta(
260
 
                milliseconds=self.properties["ApprovalDelay"])
 
256
            timeout = datetime.timedelta(milliseconds
 
257
                                         = self.properties
 
258
                                         ["ApprovalDelay"])
261
259
            last_approval_request = isoformat_to_datetime(
262
260
                self.properties["LastApprovalRequest"])
263
261
            if last_approval_request is not None:
290
288
            message = "enabled"
291
289
            self.using_timer(False)
292
290
        self._text = "{}{}".format(base, message)
293
 
 
 
291
        
294
292
        if not urwid.supports_unicode():
295
293
            self._text = self._text.encode("ascii", "replace")
296
294
        textlist = [("normal", self._text)]
306
304
        # Run update hook, if any
307
305
        if self.update_hook is not None:
308
306
            self.update_hook()
309
 
 
 
307
    
310
308
    def update_timer(self):
311
309
        """called by GLib. Will indefinitely loop until
312
310
        GLib.source_remove() on tag is called
313
311
        """
314
312
        self.update()
315
313
        return True             # Keep calling this
316
 
 
 
314
    
317
315
    def delete(self, **kwargs):
318
316
        if self._update_timer_callback_tag is not None:
319
317
            GLib.source_remove(self._update_timer_callback_tag)
324
322
        if self.delete_hook is not None:
325
323
            self.delete_hook(self)
326
324
        return super(MandosClientWidget, self).delete(**kwargs)
327
 
 
 
325
    
328
326
    def render(self, maxcolrow, focus=False):
329
327
        """Render differently if we have focus.
330
328
        This overrides the method from urwid.FlowWidget"""
331
329
        return self.current_widget(focus).render(maxcolrow,
332
330
                                                 focus=focus)
333
 
 
 
331
    
334
332
    def keypress(self, maxcolrow, key):
335
333
        """Handle keys.
336
334
        This overrides the method from urwid.FlowWidget"""
337
335
        if key == "+":
338
336
            self.proxy.Set(client_interface, "Enabled",
339
 
                           dbus.Boolean(True), ignore_reply=True,
340
 
                           dbus_interface=dbus.PROPERTIES_IFACE)
 
337
                           dbus.Boolean(True), ignore_reply = True,
 
338
                           dbus_interface = dbus.PROPERTIES_IFACE)
341
339
        elif key == "-":
342
340
            self.proxy.Set(client_interface, "Enabled", False,
343
 
                           ignore_reply=True,
344
 
                           dbus_interface=dbus.PROPERTIES_IFACE)
 
341
                           ignore_reply = True,
 
342
                           dbus_interface = dbus.PROPERTIES_IFACE)
345
343
        elif key == "a":
346
344
            self.proxy.Approve(dbus.Boolean(True, variant_level=1),
347
 
                               dbus_interface=client_interface,
 
345
                               dbus_interface = client_interface,
348
346
                               ignore_reply=True)
349
347
        elif key == "d":
350
348
            self.proxy.Approve(dbus.Boolean(False, variant_level=1),
351
 
                               dbus_interface=client_interface,
 
349
                                  dbus_interface = client_interface,
352
350
                               ignore_reply=True)
353
351
        elif key == "R" or key == "_" or key == "ctrl k":
354
352
            self.server_proxy_object.RemoveClient(self.proxy
356
354
                                                  ignore_reply=True)
357
355
        elif key == "s":
358
356
            self.proxy.Set(client_interface, "CheckerRunning",
359
 
                           dbus.Boolean(True), ignore_reply=True,
360
 
                           dbus_interface=dbus.PROPERTIES_IFACE)
 
357
                           dbus.Boolean(True), ignore_reply = True,
 
358
                           dbus_interface = dbus.PROPERTIES_IFACE)
361
359
        elif key == "S":
362
360
            self.proxy.Set(client_interface, "CheckerRunning",
363
 
                           dbus.Boolean(False), ignore_reply=True,
364
 
                           dbus_interface=dbus.PROPERTIES_IFACE)
 
361
                           dbus.Boolean(False), ignore_reply = True,
 
362
                           dbus_interface = dbus.PROPERTIES_IFACE)
365
363
        elif key == "C":
366
 
            self.proxy.CheckedOK(dbus_interface=client_interface,
 
364
            self.proxy.CheckedOK(dbus_interface = client_interface,
367
365
                                 ignore_reply=True)
368
366
        # xxx
369
367
#         elif key == "p" or key == "=":
374
372
#             self.open()
375
373
        else:
376
374
            return key
377
 
 
 
375
    
378
376
    def properties_changed(self, interface, properties, invalidated):
379
377
        """Call self.update() if any properties changed.
380
378
        This overrides the method from MandosClientPropertyCache"""
381
 
        old_values = {key: self.properties.get(key)
382
 
                      for key in properties.keys()}
 
379
        old_values = { key: self.properties.get(key)
 
380
                       for key in properties.keys() }
383
381
        super(MandosClientWidget, self).properties_changed(
384
382
            interface, properties, invalidated)
385
383
        if any(old_values[key] != self.properties.get(key)
393
391
    use them as an excuse to shift focus away from this widget.
394
392
    """
395
393
    def keypress(self, *args, **kwargs):
396
 
        ret = (super(ConstrainedListBox, self)
397
 
               .keypress(*args, **kwargs))
 
394
        ret = super(ConstrainedListBox, self).keypress(*args, **kwargs)
398
395
        if ret in ("up", "down"):
399
396
            return
400
397
        return ret
406
403
    """
407
404
    def __init__(self, max_log_length=1000, log_level=1):
408
405
        DBusGMainLoop(set_as_default=True)
409
 
 
 
406
        
410
407
        self.screen = urwid.curses_display.Screen()
411
 
 
 
408
        
412
409
        self.screen.register_palette((
413
410
                ("normal",
414
411
                 "default", "default", None),
419
416
                ("standout",
420
417
                 "standout", "default", "standout"),
421
418
                ("bold-underline-blink",
422
 
                 "bold,underline,blink", "default",
423
 
                 "bold,underline,blink"),
 
419
                 "bold,underline,blink", "default", "bold,underline,blink"),
424
420
                ("bold-standout",
425
421
                 "bold,standout", "default", "bold,standout"),
426
422
                ("underline-blink-standout",
430
426
                 "bold,underline,blink,standout", "default",
431
427
                 "bold,underline,blink,standout"),
432
428
                ))
433
 
 
 
429
        
434
430
        if urwid.supports_unicode():
435
 
            self.divider = "─"  # \u2500
 
431
            self.divider = "─" # \u2500
 
432
            #self.divider = "━" # \u2501
436
433
        else:
437
 
            self.divider = "_"  # \u005f
438
 
 
 
434
            #self.divider = "-" # \u002d
 
435
            self.divider = "_" # \u005f
 
436
        
439
437
        self.screen.start()
440
 
 
 
438
        
441
439
        self.size = self.screen.get_cols_rows()
442
 
 
 
440
        
443
441
        self.clients = urwid.SimpleListWalker([])
444
442
        self.clients_dict = {}
445
 
 
 
443
        
446
444
        # We will add Text widgets to this list
447
 
        self.log = urwid.SimpleListWalker([])
 
445
        self.log = []
448
446
        self.max_log_length = max_log_length
449
 
 
 
447
        
450
448
        self.log_level = log_level
451
 
 
 
449
        
452
450
        # We keep a reference to the log widget so we can remove it
453
451
        # from the ListWalker without it getting destroyed
454
452
        self.logbox = ConstrainedListBox(self.log)
455
 
 
 
453
        
456
454
        # This keeps track of whether self.uilist currently has
457
455
        # self.logbox in it or not
458
456
        self.log_visible = True
459
457
        self.log_wrap = "any"
460
 
 
 
458
        
461
459
        self.rebuild()
462
460
        self.log_message_raw(("bold",
463
461
                              "Mandos Monitor version " + version))
464
462
        self.log_message_raw(("bold",
465
463
                              "q: Quit  ?: Help"))
466
 
 
 
464
        
467
465
        self.busname = domain + '.Mandos'
468
466
        self.main_loop = GLib.MainLoop()
469
 
 
470
 
    def client_not_found(self, key_id, address):
471
 
        self.log_message("Client with address {} and key ID {} could"
472
 
                         " not be found".format(address, key_id))
473
 
 
 
467
    
 
468
    def client_not_found(self, fingerprint, address):
 
469
        self.log_message("Client with address {} and fingerprint {}"
 
470
                         " could not be found"
 
471
                         .format(address, fingerprint))
 
472
    
474
473
    def rebuild(self):
475
474
        """This rebuilds the User Interface.
476
475
        Call this when the widget layout needs to change"""
477
476
        self.uilist = []
478
 
        # self.uilist.append(urwid.ListBox(self.clients))
 
477
        #self.uilist.append(urwid.ListBox(self.clients))
479
478
        self.uilist.append(urwid.Frame(ConstrainedListBox(self.
480
479
                                                          clients),
481
 
                                       # header=urwid.Divider(),
 
480
                                       #header=urwid.Divider(),
482
481
                                       header=None,
483
 
                                       footer=urwid.Divider(
484
 
                                           div_char=self.divider)))
 
482
                                       footer=
 
483
                                       urwid.Divider(div_char=
 
484
                                                     self.divider)))
485
485
        if self.log_visible:
486
486
            self.uilist.append(self.logbox)
487
487
        self.topwidget = urwid.Pile(self.uilist)
488
 
 
 
488
    
489
489
    def log_message(self, message, level=1):
490
490
        """Log message formatted with timestamp"""
491
491
        if level < self.log_level:
493
493
        timestamp = datetime.datetime.now().isoformat()
494
494
        self.log_message_raw("{}: {}".format(timestamp, message),
495
495
                             level=level)
496
 
 
 
496
    
497
497
    def log_message_raw(self, markup, level=1):
498
498
        """Add a log message to the log buffer."""
499
499
        if level < self.log_level:
500
500
            return
501
501
        self.log.append(urwid.Text(markup, wrap=self.log_wrap))
502
 
        if self.max_log_length:
503
 
            if len(self.log) > self.max_log_length:
504
 
                del self.log[0:len(self.log)-self.max_log_length-1]
505
 
        self.logbox.set_focus(len(self.logbox.body.contents)-1,
 
502
        if (self.max_log_length
 
503
            and len(self.log) > self.max_log_length):
 
504
            del self.log[0:len(self.log)-self.max_log_length-1]
 
505
        self.logbox.set_focus(len(self.logbox.body.contents),
506
506
                              coming_from="above")
507
507
        self.refresh()
508
 
 
 
508
    
509
509
    def toggle_log_display(self):
510
510
        """Toggle visibility of the log buffer."""
511
511
        self.log_visible = not self.log_visible
512
512
        self.rebuild()
513
513
        self.log_message("Log visibility changed to: {}"
514
514
                         .format(self.log_visible), level=0)
515
 
 
 
515
    
516
516
    def change_log_display(self):
517
517
        """Change type of log display.
518
518
        Currently, this toggles wrapping of text lines."""
524
524
            textwidget.set_wrap_mode(self.log_wrap)
525
525
        self.log_message("Wrap mode: {}".format(self.log_wrap),
526
526
                         level=0)
527
 
 
 
527
    
528
528
    def find_and_remove_client(self, path, interfaces):
529
529
        """Find a client by its object path and remove it.
530
 
 
 
530
        
531
531
        This is connected to the InterfacesRemoved signal from the
532
532
        Mandos server object."""
533
533
        if client_interface not in interfaces:
541
541
                             .format(path))
542
542
            return
543
543
        client.delete()
544
 
 
 
544
    
545
545
    def add_new_client(self, path, ifs_and_props):
546
546
        """Find a client by its object path and remove it.
547
 
 
 
547
        
548
548
        This is connected to the InterfacesAdded signal from the
549
549
        Mandos server object.
550
550
        """
552
552
            # Not a Mandos client object; ignore
553
553
            return
554
554
        client_proxy_object = self.bus.get_object(self.busname, path)
555
 
        self.add_client(MandosClientWidget(
556
 
            server_proxy_object=self.mandos_serv,
557
 
            proxy_object=client_proxy_object,
558
 
            update_hook=self.refresh,
559
 
            delete_hook=self.remove_client,
560
 
            logger=self.log_message,
561
 
            properties=dict(ifs_and_props[client_interface])),
 
555
        self.add_client(MandosClientWidget(server_proxy_object
 
556
                                           =self.mandos_serv,
 
557
                                           proxy_object
 
558
                                           =client_proxy_object,
 
559
                                           update_hook
 
560
                                           =self.refresh,
 
561
                                           delete_hook
 
562
                                           =self.remove_client,
 
563
                                           logger
 
564
                                           =self.log_message,
 
565
                                           properties
 
566
                                           = dict(ifs_and_props[
 
567
                                               client_interface])),
562
568
                        path=path)
563
 
 
 
569
    
564
570
    def add_client(self, client, path=None):
565
571
        self.clients.append(client)
566
572
        if path is None:
568
574
        self.clients_dict[path] = client
569
575
        self.clients.sort(key=lambda c: c.properties["Name"])
570
576
        self.refresh()
571
 
 
 
577
    
572
578
    def remove_client(self, client, path=None):
573
579
        self.clients.remove(client)
574
580
        if path is None:
575
581
            path = client.proxy.object_path
576
582
        del self.clients_dict[path]
577
583
        self.refresh()
578
 
 
 
584
    
579
585
    def refresh(self):
580
586
        """Redraw the screen"""
581
587
        canvas = self.topwidget.render(self.size, focus=True)
582
588
        self.screen.draw_screen(self.size, canvas)
583
 
 
 
589
    
584
590
    def run(self):
585
591
        """Start the main loop and exit when it's done."""
586
592
        self.bus = dbus.SystemBus()
587
593
        mandos_dbus_objc = self.bus.get_object(
588
594
            self.busname, "/", follow_name_owner_changes=True)
589
 
        self.mandos_serv = dbus.Interface(
590
 
            mandos_dbus_objc, dbus_interface=server_interface)
 
595
        self.mandos_serv = dbus.Interface(mandos_dbus_objc,
 
596
                                          dbus_interface
 
597
                                          = server_interface)
591
598
        try:
592
599
            mandos_clients = (self.mandos_serv
593
600
                              .GetAllClientsWithProperties())
594
601
            if not mandos_clients:
595
 
                self.log_message_raw(("bold",
596
 
                                      "Note: Server has no clients."))
 
602
                self.log_message_raw(("bold", "Note: Server has no clients."))
597
603
        except dbus.exceptions.DBusException:
598
 
            self.log_message_raw(("bold",
599
 
                                  "Note: No Mandos server running."))
 
604
            self.log_message_raw(("bold", "Note: No Mandos server running."))
600
605
            mandos_clients = dbus.Dictionary()
601
 
 
 
606
        
602
607
        (self.mandos_serv
603
608
         .connect_to_signal("InterfacesRemoved",
604
609
                            self.find_and_remove_client,
605
 
                            dbus_interface=dbus.OBJECT_MANAGER_IFACE,
 
610
                            dbus_interface
 
611
                            = dbus.OBJECT_MANAGER_IFACE,
606
612
                            byte_arrays=True))
607
613
        (self.mandos_serv
608
614
         .connect_to_signal("InterfacesAdded",
609
615
                            self.add_new_client,
610
 
                            dbus_interface=dbus.OBJECT_MANAGER_IFACE,
 
616
                            dbus_interface
 
617
                            = dbus.OBJECT_MANAGER_IFACE,
611
618
                            byte_arrays=True))
612
619
        (self.mandos_serv
613
620
         .connect_to_signal("ClientNotFound",
617
624
        for path, client in mandos_clients.items():
618
625
            client_proxy_object = self.bus.get_object(self.busname,
619
626
                                                      path)
620
 
            self.add_client(MandosClientWidget(
621
 
                server_proxy_object=self.mandos_serv,
622
 
                proxy_object=client_proxy_object,
623
 
                properties=client,
624
 
                update_hook=self.refresh,
625
 
                delete_hook=self.remove_client,
626
 
                logger=self.log_message),
 
627
            self.add_client(MandosClientWidget(server_proxy_object
 
628
                                               =self.mandos_serv,
 
629
                                               proxy_object
 
630
                                               =client_proxy_object,
 
631
                                               properties=client,
 
632
                                               update_hook
 
633
                                               =self.refresh,
 
634
                                               delete_hook
 
635
                                               =self.remove_client,
 
636
                                               logger
 
637
                                               =self.log_message),
627
638
                            path=path)
628
 
 
 
639
        
629
640
        self.refresh()
630
 
        self._input_callback_tag = (
631
 
            GLib.io_add_watch(
632
 
                GLib.IOChannel.unix_new(sys.stdin.fileno()),
633
 
                GLib.PRIORITY_DEFAULT, GLib.IO_IN,
634
 
                self.process_input))
 
641
        self._input_callback_tag = (GLib.io_add_watch
 
642
                                    (sys.stdin.fileno(),
 
643
                                     GLib.IO_IN,
 
644
                                     self.process_input))
635
645
        self.main_loop.run()
636
646
        # Main loop has finished, we should close everything now
637
647
        GLib.source_remove(self._input_callback_tag)
638
 
        with warnings.catch_warnings():
639
 
            warnings.simplefilter("ignore", BytesWarning)
640
 
            self.screen.stop()
641
 
 
 
648
        self.screen.stop()
 
649
    
642
650
    def stop(self):
643
651
        self.main_loop.quit()
644
 
 
 
652
    
645
653
    def process_input(self, source, condition):
646
654
        keys = self.screen.get_input()
647
 
        translations = {"ctrl n": "down",       # Emacs
648
 
                        "ctrl p": "up",         # Emacs
649
 
                        "ctrl v": "page down",  # Emacs
650
 
                        "meta v": "page up",    # Emacs
651
 
                        " ": "page down",       # less
652
 
                        "f": "page down",       # less
653
 
                        "b": "page up",         # less
654
 
                        "j": "down",            # vi
655
 
                        "k": "up",              # vi
656
 
                        }
 
655
        translations = { "ctrl n": "down",      # Emacs
 
656
                         "ctrl p": "up",        # Emacs
 
657
                         "ctrl v": "page down", # Emacs
 
658
                         "meta v": "page up",   # Emacs
 
659
                         " ": "page down",      # less
 
660
                         "f": "page down",      # less
 
661
                         "b": "page up",        # less
 
662
                         "j": "down",           # vi
 
663
                         "k": "up",             # vi
 
664
                         }
657
665
        for key in keys:
658
666
            try:
659
667
                key = translations[key]
660
668
            except KeyError:    # :-)
661
669
                pass
662
 
 
 
670
            
663
671
            if key == "q" or key == "Q":
664
672
                self.stop()
665
673
                break
713
721
                else:
714
722
                    self.log_level = 0
715
723
                    self.log_message("Verbose mode: On")
716
 
            # elif (key == "end" or key == "meta >" or key == "G"
717
 
            #       or key == ">"):
718
 
            #     pass            # xxx end-of-buffer
719
 
            # elif (key == "home" or key == "meta <" or key == "g"
720
 
            #       or key == "<"):
721
 
            #     pass            # xxx beginning-of-buffer
722
 
            # elif key == "ctrl e" or key == "$":
723
 
            #     pass            # xxx move-end-of-line
724
 
            # elif key == "ctrl a" or key == "^":
725
 
            #     pass            # xxx move-beginning-of-line
726
 
            # elif key == "ctrl b" or key == "meta (" or key == "h":
727
 
            #     pass            # xxx left
728
 
            # elif key == "ctrl f" or key == "meta )" or key == "l":
729
 
            #     pass            # xxx right
730
 
            # elif key == "a":
731
 
            #     pass            # scroll up log
732
 
            # elif key == "z":
733
 
            #     pass            # scroll down log
 
724
            #elif (key == "end" or key == "meta >" or key == "G"
 
725
            #      or key == ">"):
 
726
            #    pass            # xxx end-of-buffer
 
727
            #elif (key == "home" or key == "meta <" or key == "g"
 
728
            #      or key == "<"):
 
729
            #    pass            # xxx beginning-of-buffer
 
730
            #elif key == "ctrl e" or key == "$":
 
731
            #    pass            # xxx move-end-of-line
 
732
            #elif key == "ctrl a" or key == "^":
 
733
            #    pass            # xxx move-beginning-of-line
 
734
            #elif key == "ctrl b" or key == "meta (" or key == "h":
 
735
            #    pass            # xxx left
 
736
            #elif key == "ctrl f" or key == "meta )" or key == "l":
 
737
            #    pass            # xxx right
 
738
            #elif key == "a":
 
739
            #    pass            # scroll up log
 
740
            #elif key == "z":
 
741
            #    pass            # scroll down log
734
742
            elif self.topwidget.selectable():
735
743
                self.topwidget.keypress(self.size, key)
736
744
                self.refresh()
737
745
        return True
738
746
 
739
 
 
740
747
ui = UserInterface()
741
748
try:
742
749
    ui.run()