51
49
if sys.version_info.major == 2:
 
54
 
locale.setlocale(locale.LC_ALL, '')
 
56
 
logging.getLogger('dbus.proxies').setLevel(logging.CRITICAL)
 
 
54
# Show warnings by default
 
 
55
if not sys.warnoptions:
 
 
56
    warnings.simplefilter("default")
 
 
58
log = logging.getLogger(os.path.basename(sys.argv[0]))
 
 
59
logging.basicConfig(level="NOTSET", # Show all messages
 
 
60
                    format="%(message)s") # Show basic log messages
 
 
62
logging.captureWarnings(True)   # Show warnings via the logging system
 
 
64
locale.setlocale(locale.LC_ALL, "")
 
 
66
logging.getLogger("dbus.proxies").setLevel(logging.CRITICAL)
 
 
67
logging.getLogger("urwid").setLevel(logging.INFO)
 
58
69
# Some useful constants
 
59
 
domain = 'se.recompile'
 
60
 
server_interface = domain + '.Mandos'
 
61
 
client_interface = domain + '.Mandos.Client'
 
 
70
domain = "se.recompile"
 
 
71
server_interface = domain + ".Mandos"
 
 
72
client_interface = domain + ".Mandos.Client"
 
65
76
    dbus.OBJECT_MANAGER_IFACE
 
 
117
128
        self.property_changed_match.remove()
 
120
 
class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
 
 
131
class MandosClientWidget(MandosClientPropertyCache, urwid.Widget):
 
121
132
    """A Mandos Client which is visible on the screen.
 
 
135
    _sizing = frozenset(["flow"])
 
124
137
    def __init__(self, server_proxy_object=None, update_hook=None,
 
125
 
                 delete_hook=None, logger=None, **kwargs):
 
 
138
                 delete_hook=None, **kwargs):
 
126
139
        # Called on update
 
127
140
        self.update_hook = update_hook
 
128
141
        # Called on delete
 
129
142
        self.delete_hook = delete_hook
 
130
143
        # Mandos Server proxy object
 
131
144
        self.server_proxy_object = server_proxy_object
 
135
146
        self._update_timer_callback_tag = None
 
 
173
183
        if flag and self._update_timer_callback_tag is None:
 
174
184
            # Will update the shown timer value every second
 
175
 
            self._update_timer_callback_tag = (GLib.timeout_add
 
 
185
            self._update_timer_callback_tag = (
 
 
186
                GLib.timeout_add(1000,
 
 
187
                                 glib_safely(self.update_timer)))
 
178
188
        elif not (flag or self._update_timer_callback_tag is None):
 
179
189
            GLib.source_remove(self._update_timer_callback_tag)
 
180
190
            self._update_timer_callback_tag = None
 
182
192
    def checker_completed(self, exitstatus, condition, command):
 
183
193
        if exitstatus == 0:
 
184
 
            self.logger('Checker for client {} (command "{}")'
 
185
 
                        ' succeeded'.format(self.properties["Name"],
 
 
194
            log.debug('Checker for client %s (command "%s")'
 
 
195
                      " succeeded", self.properties["Name"], command)
 
190
199
        if os.WIFEXITED(condition):
 
191
 
            self.logger('Checker for client {} (command "{}") failed'
 
193
 
                        .format(self.properties["Name"], command,
 
194
 
                                os.WEXITSTATUS(condition)))
 
 
200
            log.info('Checker for client %s (command "%s") failed'
 
 
201
                     " with exit code %d", self.properties["Name"],
 
 
202
                     command, os.WEXITSTATUS(condition))
 
195
203
        elif os.WIFSIGNALED(condition):
 
196
 
            self.logger('Checker for client {} (command "{}") was'
 
197
 
                        ' killed by signal {}'
 
198
 
                        .format(self.properties["Name"], command,
 
199
 
                                os.WTERMSIG(condition)))
 
 
204
            log.info('Checker for client %s (command "%s") was'
 
 
205
                     " killed by signal %d", self.properties["Name"],
 
 
206
                     command, os.WTERMSIG(condition))
 
202
209
    def checker_started(self, command):
 
203
210
        """Server signals that a checker started."""
 
204
 
        self.logger('Client {} started checker "{}"'
 
205
 
                    .format(self.properties["Name"],
 
 
211
        log.debug('Client %s started checker "%s"',
 
 
212
                  self.properties["Name"], command)
 
208
214
    def got_secret(self):
 
209
 
        self.logger('Client {} received its secret'
 
210
 
                    .format(self.properties["Name"]))
 
 
215
        log.info("Client %s received its secret",
 
 
216
                 self.properties["Name"])
 
212
218
    def need_approval(self, timeout, default):
 
214
 
            message = 'Client {} needs approval within {} seconds'
 
 
220
            message = "Client %s needs approval within %f seconds"
 
216
 
            message = 'Client {} will get its secret in {} seconds'
 
217
 
        self.logger(message.format(self.properties["Name"],
 
 
222
            message = "Client %s will get its secret in %f seconds"
 
 
223
        log.info(message, self.properties["Name"], timeout/1000)
 
220
225
    def rejected(self, reason):
 
221
 
        self.logger('Client {} was rejected; reason: {}'
 
222
 
                    .format(self.properties["Name"], reason))
 
 
226
        log.info("Client %s was rejected; reason: %s",
 
 
227
                 self.properties["Name"], reason)
 
224
229
    def selectable(self):
 
225
230
        """Make this a "selectable" widget.
 
226
 
        This overrides the method from urwid.FlowWidget."""
 
 
231
        This overrides the method from urwid.Widget."""
 
229
234
    def rows(self, maxcolrow, focus=False):
 
230
235
        """How many rows this widget will occupy might depend on
 
231
236
        whether we have focus or not.
 
232
 
        This overrides the method from urwid.FlowWidget"""
 
 
237
        This overrides the method from urwid.Widget"""
 
233
238
        return self.current_widget(focus).rows(maxcolrow, focus=focus)
 
235
240
    def current_widget(self, focus=False):
 
 
328
333
    def render(self, maxcolrow, focus=False):
 
329
334
        """Render differently if we have focus.
 
330
 
        This overrides the method from urwid.FlowWidget"""
 
 
335
        This overrides the method from urwid.Widget"""
 
331
336
        return self.current_widget(focus).render(maxcolrow,
 
334
339
    def keypress(self, maxcolrow, key):
 
336
 
        This overrides the method from urwid.FlowWidget"""
 
 
341
        This overrides the method from urwid.Widget"""
 
338
343
            self.proxy.Set(client_interface, "Enabled",
 
339
344
                           dbus.Boolean(True), ignore_reply=True,
 
 
458
471
        self.log_visible = True
 
459
472
        self.log_wrap = "any"
 
 
474
        self.loghandler = UILogHandler(self)
 
462
 
        self.log_message_raw(("bold",
 
463
 
                              "Mandos Monitor version " + version))
 
464
 
        self.log_message_raw(("bold",
 
 
477
        self.add_log_line(("bold",
 
 
478
                           "Mandos Monitor version " + version))
 
 
479
        self.add_log_line(("bold", "q: Quit  ?: Help"))
 
467
 
        self.busname = domain + '.Mandos'
 
 
481
        self.busname = domain + ".Mandos"
 
468
482
        self.main_loop = GLib.MainLoop()
 
470
 
    def client_not_found(self, fingerprint, address):
 
471
 
        self.log_message("Client with address {} and fingerprint {}"
 
472
 
                         " could not be found"
 
473
 
                         .format(address, fingerprint))
 
 
484
    def client_not_found(self, key_id, address):
 
 
485
        log.info("Client with address %s and key ID %s could"
 
 
486
                 " not be found", address, key_id)
 
475
488
    def rebuild(self):
 
476
489
        """This rebuilds the User Interface.
 
 
487
500
            self.uilist.append(self.logbox)
 
488
501
        self.topwidget = urwid.Pile(self.uilist)
 
490
 
    def log_message(self, message, level=1):
 
491
 
        """Log message formatted with timestamp"""
 
492
 
        if level < self.log_level:
 
494
 
        timestamp = datetime.datetime.now().isoformat()
 
495
 
        self.log_message_raw("{}: {}".format(timestamp, message),
 
498
 
    def log_message_raw(self, markup, level=1):
 
499
 
        """Add a log message to the log buffer."""
 
500
 
        if level < self.log_level:
 
 
503
    def add_log_line(self, markup):
 
502
504
        self.log.append(urwid.Text(markup, wrap=self.log_wrap))
 
503
505
        if self.max_log_length:
 
504
506
            if len(self.log) > self.max_log_length:
 
505
 
                del self.log[0:len(self.log)-self.max_log_length-1]
 
 
507
                del self.log[0:(len(self.log) - self.max_log_length)]
 
506
508
        self.logbox.set_focus(len(self.logbox.body.contents)-1,
 
507
509
                              coming_from="above")
 
 
623
624
                proxy_object=client_proxy_object,
 
624
625
                properties=client,
 
625
626
                update_hook=self.refresh,
 
626
 
                delete_hook=self.remove_client,
 
627
 
                logger=self.log_message),
 
 
627
                delete_hook=self.remove_client),
 
631
 
        self._input_callback_tag = (GLib.io_add_watch
 
 
631
        self._input_callback_tag = (
 
 
633
                GLib.IOChannel.unix_new(sys.stdin.fileno()),
 
 
634
                GLib.PRIORITY_DEFAULT, GLib.IO_IN,
 
 
635
                glib_safely(self.process_input)))
 
635
636
        self.main_loop.run()
 
636
637
        # Main loop has finished, we should close everything now
 
637
638
        GLib.source_remove(self._input_callback_tag)
 
 
639
        with warnings.catch_warnings():
 
 
640
            warnings.simplefilter("ignore", BytesWarning)
 
641
644
        self.main_loop.quit()
 
 
645
        log.removeHandler(self.loghandler)
 
 
646
        log.propagate = self.orig_log_propagate
 
643
648
    def process_input(self, source, condition):
 
644
649
        keys = self.screen.get_input()
 
 
 
742
class UILogHandler(logging.Handler):
 
 
743
    def __init__(self, ui, *args, **kwargs):
 
 
745
        super(UILogHandler, self).__init__(*args, **kwargs)
 
 
747
            logging.Formatter("%(asctime)s: %(message)s"))
 
 
748
    def emit(self, record):
 
 
749
        msg = self.format(record)
 
 
750
        if record.levelno > logging.INFO:
 
 
752
        self.ui.add_log_line(msg)
 
738
755
ui = UserInterface()
 
741
758
except KeyboardInterrupt:
 
743
 
except Exception as e:
 
744
 
    ui.log_message(str(e))
 
 
759
    with warnings.catch_warnings():
 
 
760
        warnings.filterwarnings("ignore", "", BytesWarning)
 
 
763
    with warnings.catch_warnings():
 
 
764
        warnings.filterwarnings("ignore", "", BytesWarning)