/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release
237.7.719 by Teddy Hogeborn
Use Python 3 by default
1
#!/usr/bin/python3 -bb
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
2
# -*- mode: python; coding: utf-8 -*-
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
3
#
237.2.207 by Teddy Hogeborn
Update copyright year to "2010" wherever appropriate.
4
# Mandos Monitor - Control and monitor the Mandos server
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
5
#
237.7.517 by Teddy Hogeborn
Update copyright year to 2019
6
# Copyright © 2009-2019 Teddy Hogeborn
7
# Copyright © 2009-2019 Björn Påhlsson
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
8
#
237.7.455 by Teddy Hogeborn
Alter copyright notices slightly. Actual license is unchanged!
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
237.2.207 by Teddy Hogeborn
Update copyright year to "2010" wherever appropriate.
13
# the Free Software Foundation, either version 3 of the License, or
14
# (at your option) any later version.
15
#
237.7.455 by Teddy Hogeborn
Alter copyright notices slightly. Actual license is unchanged!
16
#     Mandos is distributed in the hope that it will be useful, but
17
#     WITHOUT ANY WARRANTY; without even the implied warranty of
237.2.207 by Teddy Hogeborn
Update copyright year to "2010" wherever appropriate.
18
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
#     GNU General Public License for more details.
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
20
#
237.2.207 by Teddy Hogeborn
Update copyright year to "2010" wherever appropriate.
21
# You should have received a copy of the GNU General Public License
237.7.455 by Teddy Hogeborn
Alter copyright notices slightly. Actual license is unchanged!
22
# along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
23
#
237.11.2 by Teddy Hogeborn
Change "fukt.bsnet.se" to "recompile.se" throughout.
24
# Contact the authors at <mandos@recompile.se>.
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
25
#
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
26
237.7.36 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Save match tag for
27
from __future__ import (division, absolute_import, print_function,
28
                        unicode_literals)
237.7.165 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
29
try:
30
    from future_builtins import *
31
except ImportError:
32
    pass
237.7.127 by Teddy Hogeborn
* mandos: Use all new builtins.
33
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
34
import sys
237.2.172 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
35
import os
237.7.722 by Teddy Hogeborn
Address Python 3 bytes/str warnings
36
import warnings
237.2.172 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
37
import datetime
237.7.732 by Teddy Hogeborn
mandos-monitor: Formatting changes only
38
import locale
39
import logging
237.2.172 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
40
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
41
import urwid.curses_display
42
import urwid
43
44
from dbus.mainloop.glib import DBusGMainLoop
237.7.378 by Teddy Hogeborn
Server: Use python-gi instead of old python-gobject
45
from gi.repository import GLib
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
46
47
import dbus
48
237.23.7 by Teddy Hogeborn
Use the .major attribute on sys.version_info instead of using "[0]".
49
if sys.version_info.major == 2:
237.7.165 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
50
    str = unicode
51
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
52
log = logging.getLogger(os.path.basename(sys.argv[0]))
53
logging.basicConfig(level="NOTSET", # Show all messages
54
                    format="%(message)s") # Show basic log messages
55
56
logging.captureWarnings(True)   # Show warnings via the logging system
57
237.7.732 by Teddy Hogeborn
mandos-monitor: Formatting changes only
58
locale.setlocale(locale.LC_ALL, "")
237.2.174 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
59
237.7.732 by Teddy Hogeborn
mandos-monitor: Formatting changes only
60
logging.getLogger("dbus.proxies").setLevel(logging.CRITICAL)
24.1.153 by Björn Påhlsson
early commit to ease todays coding
61
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
62
# Some useful constants
237.7.732 by Teddy Hogeborn
mandos-monitor: Formatting changes only
63
domain = "se.recompile"
64
server_interface = domain + ".Mandos"
65
client_interface = domain + ".Mandos.Client"
393 by Teddy Hogeborn
* Makefile (version): Change to 1.8.9.
66
version = "1.8.9"
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
67
237.7.333 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
68
try:
69
    dbus.OBJECT_MANAGER_IFACE
70
except AttributeError:
71
    dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
72
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
73
237.2.180 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
74
def isoformat_to_datetime(iso):
75
    "Parse an ISO 8601 date string to a datetime.datetime()"
76
    if not iso:
77
        return None
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
78
    d, t = iso.split("T", 1)
79
    year, month, day = d.split("-", 2)
80
    hour, minute, second = t.split(":", 2)
237.2.180 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
81
    second, fraction = divmod(float(second), 1)
82
    return datetime.datetime(int(year),
83
                             int(month),
84
                             int(day),
85
                             int(hour),
86
                             int(minute),
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
87
                             int(second),            # Whole seconds
88
                             int(fraction*1000000))  # Microseconds
89
237.2.180 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
90
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
91
class MandosClientPropertyCache(object):
92
    """This wraps a Mandos Client D-Bus proxy object, caches the
93
    properties and calls a hook function when any of them are
94
    changed.
95
    """
237.7.128 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
96
    def __init__(self, proxy_object=None, properties=None, **kwargs):
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
97
        self.proxy = proxy_object  # Mandos Client proxy object
237.7.128 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
98
        self.properties = dict() if properties is None else properties
237.7.36 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Save match tag for
99
        self.property_changed_match = (
237.7.280 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
100
            self.proxy.connect_to_signal("PropertiesChanged",
101
                                         self.properties_changed,
102
                                         dbus.PROPERTIES_IFACE,
237.7.36 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Save match tag for
103
                                         byte_arrays=True))
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
104
237.7.128 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
105
        if properties is None:
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
106
            self.properties.update(self.proxy.GetAll(
107
                client_interface,
108
                dbus_interface=dbus.PROPERTIES_IFACE))
109
237.7.128 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
110
        super(MandosClientPropertyCache, self).__init__(**kwargs)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
111
237.7.280 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
112
    def properties_changed(self, interface, properties, invalidated):
113
        """This is called whenever we get a PropertiesChanged signal
114
        It updates the changed properties in the "properties" dict.
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
115
        """
116
        # Update properties dict with new value
237.7.333 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
117
        if interface == client_interface:
118
            self.properties.update(properties)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
119
237.7.128 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
120
    def delete(self):
237.7.36 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Save match tag for
121
        self.property_changed_match.remove()
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
122
123
124
class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
125
    """A Mandos Client which is visible on the screen.
126
    """
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
127
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
128
    def __init__(self, server_proxy_object=None, update_hook=None,
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
129
                 delete_hook=None, **kwargs):
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
130
        # Called on update
131
        self.update_hook = update_hook
132
        # Called on delete
133
        self.delete_hook = delete_hook
134
        # Mandos Server proxy object
135
        self.server_proxy_object = server_proxy_object
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
136
237.2.180 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
137
        self._update_timer_callback_tag = None
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
138
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
139
        # The widget shown normally
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
140
        self._text_widget = urwid.Text("")
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
141
        # The widget shown when we have focus
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
142
        self._focus_text_widget = urwid.Text("")
237.7.128 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
143
        super(MandosClientWidget, self).__init__(**kwargs)
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
144
        self.update()
145
        self.opened = False
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
146
237.7.36 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Save match tag for
147
        self.match_objects = (
148
            self.proxy.connect_to_signal("CheckerCompleted",
149
                                         self.checker_completed,
150
                                         client_interface,
151
                                         byte_arrays=True),
152
            self.proxy.connect_to_signal("CheckerStarted",
153
                                         self.checker_started,
154
                                         client_interface,
155
                                         byte_arrays=True),
156
            self.proxy.connect_to_signal("GotSecret",
157
                                         self.got_secret,
158
                                         client_interface,
159
                                         byte_arrays=True),
160
            self.proxy.connect_to_signal("NeedApproval",
161
                                         self.need_approval,
162
                                         client_interface,
163
                                         byte_arrays=True),
164
            self.proxy.connect_to_signal("Rejected",
165
                                         self.rejected,
166
                                         client_interface,
167
                                         byte_arrays=True))
237.7.732 by Teddy Hogeborn
mandos-monitor: Formatting changes only
168
        log.debug("Created client %s", self.properties["Name"])
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
169
237.2.205 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
170
    def using_timer(self, flag):
171
        """Call this method with True or False when timer should be
172
        activated or deactivated.
173
        """
237.7.129 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
174
        if flag and self._update_timer_callback_tag is None:
24.1.179 by Björn Påhlsson
New feature:
175
            # Will update the shown timer value every second
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
176
            self._update_timer_callback_tag = (
177
                GLib.timeout_add(1000,
178
                                 glib_safely(self.update_timer)))
237.7.129 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
179
        elif not (flag or self._update_timer_callback_tag is None):
237.7.378 by Teddy Hogeborn
Server: Use python-gi instead of old python-gobject
180
            GLib.source_remove(self._update_timer_callback_tag)
237.2.205 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
181
            self._update_timer_callback_tag = None
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
182
237.7.331 by Teddy Hogeborn
Revert change to D-Bus API.
183
    def checker_completed(self, exitstatus, condition, command):
237.2.172 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
184
        if exitstatus == 0:
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
185
            log.debug('Checker for client %s (command "%s")'
237.7.732 by Teddy Hogeborn
mandos-monitor: Formatting changes only
186
                      " succeeded", self.properties["Name"], command)
237.2.180 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
187
            self.update()
237.2.172 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
188
            return
237.2.180 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
189
        # Checker failed
237.7.331 by Teddy Hogeborn
Revert change to D-Bus API.
190
        if os.WIFEXITED(condition):
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
191
            log.info('Checker for client %s (command "%s") failed'
237.7.732 by Teddy Hogeborn
mandos-monitor: Formatting changes only
192
                     " with exit code %d", self.properties["Name"],
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
193
                     command, os.WEXITSTATUS(condition))
237.7.331 by Teddy Hogeborn
Revert change to D-Bus API.
194
        elif os.WIFSIGNALED(condition):
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
195
            log.info('Checker for client %s (command "%s") was'
237.7.732 by Teddy Hogeborn
mandos-monitor: Formatting changes only
196
                     " killed by signal %d", self.properties["Name"],
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
197
                     command, os.WTERMSIG(condition))
237.2.180 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
198
        self.update()
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
199
237.2.172 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
200
    def checker_started(self, command):
237.7.261 by Teddy Hogeborn
mandos-monitor: New "verbose" mode to see less important log messages.
201
        """Server signals that a checker started."""
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
202
        log.debug('Client %s started checker "%s"',
203
                  self.properties["Name"], command)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
204
237.2.172 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
205
    def got_secret(self):
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
206
        log.info("Client %s received its secret",
207
                 self.properties["Name"])
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
208
24.1.153 by Björn Påhlsson
early commit to ease todays coding
209
    def need_approval(self, timeout, default):
210
        if not default:
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
211
            message = "Client %s needs approval within %f seconds"
24.1.153 by Björn Påhlsson
early commit to ease todays coding
212
        else:
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
213
            message = "Client %s will get its secret in %f seconds"
214
        log.info(message, self.properties["Name"], timeout/1000)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
215
24.1.153 by Björn Påhlsson
early commit to ease todays coding
216
    def rejected(self, reason):
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
217
        log.info("Client %s was rejected; reason: %s",
218
                 self.properties["Name"], reason)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
219
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
220
    def selectable(self):
221
        """Make this a "selectable" widget.
222
        This overrides the method from urwid.FlowWidget."""
223
        return True
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
224
237.8.1 by teddy at bsnet
* mandos-monitor: Use only unicode string literals.
225
    def rows(self, maxcolrow, focus=False):
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
226
        """How many rows this widget will occupy might depend on
227
        whether we have focus or not.
228
        This overrides the method from urwid.FlowWidget"""
237.8.1 by teddy at bsnet
* mandos-monitor: Use only unicode string literals.
229
        return self.current_widget(focus).rows(maxcolrow, focus=focus)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
230
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
231
    def current_widget(self, focus=False):
232
        if focus or self.opened:
233
            return self._focus_widget
234
        return self._widget
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
235
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
236
    def update(self):
237
        "Called when what is visible on the screen should be updated."
238
        # How to add standout mode to a style
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
239
        with_standout = {"normal": "standout",
240
                         "bold": "bold-standout",
241
                         "underline-blink":
242
                         "underline-blink-standout",
243
                         "bold-underline-blink":
244
                         "bold-underline-blink-standout",
245
                         }
246
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
247
        # Rebuild focus and non-focus widgets using current properties
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
248
24.1.154 by Björn Påhlsson
merge
249
        # Base part of a client. Name!
237.7.732 by Teddy Hogeborn
mandos-monitor: Formatting changes only
250
        base = "{name}: ".format(name=self.properties["Name"])
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
251
        if not self.properties["Enabled"]:
252
            message = "DISABLED"
237.7.129 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
253
            self.using_timer(False)
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
254
        elif self.properties["ApprovalPending"]:
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
255
            timeout = datetime.timedelta(
256
                milliseconds=self.properties["ApprovalDelay"])
237.2.205 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
257
            last_approval_request = isoformat_to_datetime(
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
258
                self.properties["LastApprovalRequest"])
237.2.205 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
259
            if last_approval_request is not None:
237.7.129 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
260
                timer = max(timeout - (datetime.datetime.utcnow()
261
                                       - last_approval_request),
262
                            datetime.timedelta())
237.2.205 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
263
            else:
264
                timer = datetime.timedelta()
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
265
            if self.properties["ApprovedByDefault"]:
237.23.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
266
                message = "Approval in {}. (d)eny?"
24.1.159 by Björn Påhlsson
added approval to mandos-ctl
267
            else:
237.23.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
268
                message = "Denial in {}. (a)pprove?"
237.7.165 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
269
            message = message.format(str(timer).rsplit(".", 1)[0])
237.7.129 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
270
            self.using_timer(True)
237.7.105 by Teddy Hogeborn
Use the new Client.LastCheckerStatus property.
271
        elif self.properties["LastCheckerStatus"] != 0:
237.7.120 by Teddy Hogeborn
* mandos-ctl: Break long lines.
272
            # When checker has failed, show timer until client expires
24.1.179 by Björn Påhlsson
New feature:
273
            expires = self.properties["Expires"]
274
            if expires == "":
275
                timer = datetime.timedelta(0)
276
            else:
237.7.120 by Teddy Hogeborn
* mandos-ctl: Break long lines.
277
                expires = (datetime.datetime.strptime
237.7.732 by Teddy Hogeborn
mandos-monitor: Formatting changes only
278
                           (expires, "%Y-%m-%dT%H:%M:%S.%f"))
237.7.129 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
279
                timer = max(expires - datetime.datetime.utcnow(),
280
                            datetime.timedelta())
237.7.732 by Teddy Hogeborn
mandos-monitor: Formatting changes only
281
            message = ("A checker has failed! Time until client"
282
                       " gets disabled: {}"
237.7.165 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
283
                       .format(str(timer).rsplit(".", 1)[0]))
237.7.129 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
284
            self.using_timer(True)
24.1.154 by Björn Påhlsson
merge
285
        else:
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
286
            message = "enabled"
237.7.129 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
287
            self.using_timer(False)
237.23.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
288
        self._text = "{}{}".format(base, message)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
289
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
290
        if not urwid.supports_unicode():
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
291
            self._text = self._text.encode("ascii", "replace")
292
        textlist = [("normal", self._text)]
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
293
        self._text_widget.set_text(textlist)
294
        self._focus_text_widget.set_text([(with_standout[text[0]],
295
                                           text[1])
296
                                          if isinstance(text, tuple)
297
                                          else text
298
                                          for text in textlist])
299
        self._widget = self._text_widget
300
        self._focus_widget = urwid.AttrWrap(self._focus_text_widget,
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
301
                                            "standout")
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
302
        # Run update hook, if any
303
        if self.update_hook is not None:
304
            self.update_hook()
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
305
237.2.180 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
306
    def update_timer(self):
237.7.378 by Teddy Hogeborn
Server: Use python-gi instead of old python-gobject
307
        """called by GLib. Will indefinitely loop until
308
        GLib.source_remove() on tag is called
309
        """
237.2.180 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
310
        self.update()
311
        return True             # Keep calling this
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
312
237.7.128 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
313
    def delete(self, **kwargs):
237.2.180 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
314
        if self._update_timer_callback_tag is not None:
237.7.378 by Teddy Hogeborn
Server: Use python-gi instead of old python-gobject
315
            GLib.source_remove(self._update_timer_callback_tag)
237.2.180 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
316
            self._update_timer_callback_tag = None
237.7.36 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Save match tag for
317
        for match in self.match_objects:
318
            match.remove()
319
        self.match_objects = ()
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
320
        if self.delete_hook is not None:
321
            self.delete_hook(self)
237.7.128 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
322
        return super(MandosClientWidget, self).delete(**kwargs)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
323
237.8.1 by teddy at bsnet
* mandos-monitor: Use only unicode string literals.
324
    def render(self, maxcolrow, focus=False):
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
325
        """Render differently if we have focus.
326
        This overrides the method from urwid.FlowWidget"""
237.8.1 by teddy at bsnet
* mandos-monitor: Use only unicode string literals.
327
        return self.current_widget(focus).render(maxcolrow,
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
328
                                                 focus=focus)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
329
237.8.1 by teddy at bsnet
* mandos-monitor: Use only unicode string literals.
330
    def keypress(self, maxcolrow, key):
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
331
        """Handle keys.
332
        This overrides the method from urwid.FlowWidget"""
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
333
        if key == "+":
237.7.329 by Teddy Hogeborn
Deprecate some D-Bus methods in favor of D-Bus properties.
334
            self.proxy.Set(client_interface, "Enabled",
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
335
                           dbus.Boolean(True), ignore_reply=True,
336
                           dbus_interface=dbus.PROPERTIES_IFACE)
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
337
        elif key == "-":
237.7.329 by Teddy Hogeborn
Deprecate some D-Bus methods in favor of D-Bus properties.
338
            self.proxy.Set(client_interface, "Enabled", False,
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
339
                           ignore_reply=True,
340
                           dbus_interface=dbus.PROPERTIES_IFACE)
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
341
        elif key == "a":
24.1.154 by Björn Påhlsson
merge
342
            self.proxy.Approve(dbus.Boolean(True, variant_level=1),
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
343
                               dbus_interface=client_interface,
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
344
                               ignore_reply=True)
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
345
        elif key == "d":
24.1.154 by Björn Påhlsson
merge
346
            self.proxy.Approve(dbus.Boolean(False, variant_level=1),
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
347
                               dbus_interface=client_interface,
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
348
                               ignore_reply=True)
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
349
        elif key == "R" or key == "_" or key == "ctrl k":
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
350
            self.server_proxy_object.RemoveClient(self.proxy
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
351
                                                  .object_path,
352
                                                  ignore_reply=True)
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
353
        elif key == "s":
237.7.329 by Teddy Hogeborn
Deprecate some D-Bus methods in favor of D-Bus properties.
354
            self.proxy.Set(client_interface, "CheckerRunning",
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
355
                           dbus.Boolean(True), ignore_reply=True,
356
                           dbus_interface=dbus.PROPERTIES_IFACE)
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
357
        elif key == "S":
237.7.329 by Teddy Hogeborn
Deprecate some D-Bus methods in favor of D-Bus properties.
358
            self.proxy.Set(client_interface, "CheckerRunning",
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
359
                           dbus.Boolean(False), ignore_reply=True,
360
                           dbus_interface=dbus.PROPERTIES_IFACE)
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
361
        elif key == "C":
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
362
            self.proxy.CheckedOK(dbus_interface=client_interface,
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
363
                                 ignore_reply=True)
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
364
        # xxx
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
365
#         elif key == "p" or key == "=":
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
366
#             self.proxy.pause()
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
367
#         elif key == "u" or key == ":":
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
368
#             self.proxy.unpause()
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
369
#         elif key == "RET":
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
370
#             self.open()
371
        else:
372
            return key
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
373
237.7.280 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
374
    def properties_changed(self, interface, properties, invalidated):
375
        """Call self.update() if any properties changed.
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
376
        This overrides the method from MandosClientPropertyCache"""
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
377
        old_values = {key: self.properties.get(key)
378
                      for key in properties.keys()}
237.7.280 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
379
        super(MandosClientWidget, self).properties_changed(
380
            interface, properties, invalidated)
381
        if any(old_values[key] != self.properties.get(key)
382
               for key in old_values):
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
383
            self.update()
384
385
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
386
def glib_safely(func, retval=True):
387
    def safe_func(*args, **kwargs):
388
        try:
389
            return func(*args, **kwargs)
390
        except Exception:
391
            log.exception("")
392
            return retval
393
    return safe_func
394
395
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
396
class ConstrainedListBox(urwid.ListBox):
397
    """Like a normal urwid.ListBox, but will consume all "up" or
398
    "down" key presses, thus not allowing any containing widgets to
399
    use them as an excuse to shift focus away from this widget.
400
    """
237.7.128 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
401
    def keypress(self, *args, **kwargs):
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
402
        ret = (super(ConstrainedListBox, self)
403
               .keypress(*args, **kwargs))
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
404
        if ret in ("up", "down"):
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
405
            return
406
        return ret
407
408
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
409
class UserInterface(object):
410
    """This is the entire user interface - the whole screen
411
    with boxes, lists of client widgets, etc.
412
    """
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
413
    def __init__(self, max_log_length=1000):
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
414
        DBusGMainLoop(set_as_default=True)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
415
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
416
        self.screen = urwid.curses_display.Screen()
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
417
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
418
        self.screen.register_palette((
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
419
                ("normal",
420
                 "default", "default", None),
421
                ("bold",
237.7.165 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
422
                 "bold", "default", "bold"),
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
423
                ("underline-blink",
237.7.165 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
424
                 "underline,blink", "default", "underline,blink"),
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
425
                ("standout",
237.7.165 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
426
                 "standout", "default", "standout"),
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
427
                ("bold-underline-blink",
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
428
                 "bold,underline,blink", "default",
429
                 "bold,underline,blink"),
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
430
                ("bold-standout",
237.7.165 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
431
                 "bold,standout", "default", "bold,standout"),
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
432
                ("underline-blink-standout",
237.7.165 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
433
                 "underline,blink,standout", "default",
434
                 "underline,blink,standout"),
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
435
                ("bold-underline-blink-standout",
237.7.165 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
436
                 "bold,underline,blink,standout", "default",
437
                 "bold,underline,blink,standout"),
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
438
                ))
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
439
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
440
        if urwid.supports_unicode():
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
441
            self.divider = "─"  # \u2500
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
442
        else:
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
443
            self.divider = "_"  # \u005f
444
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
445
        self.screen.start()
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
446
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
447
        self.size = self.screen.get_cols_rows()
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
448
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
449
        self.clients = urwid.SimpleListWalker([])
450
        self.clients_dict = {}
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
451
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
452
        # We will add Text widgets to this list
237.7.516 by Teddy Hogeborn
Do minor fix to make mandos-monitor work with new python-urwid
453
        self.log = urwid.SimpleListWalker([])
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
454
        self.max_log_length = max_log_length
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
455
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
456
        # We keep a reference to the log widget so we can remove it
457
        # from the ListWalker without it getting destroyed
458
        self.logbox = ConstrainedListBox(self.log)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
459
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
460
        # This keeps track of whether self.uilist currently has
461
        # self.logbox in it or not
462
        self.log_visible = True
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
463
        self.log_wrap = "any"
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
464
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
465
        self.loghandler = UILogHandler(self)
466
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
467
        self.rebuild()
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
468
        self.add_log_line(("bold",
469
                           "Mandos Monitor version " + version))
470
        self.add_log_line(("bold", "q: Quit  ?: Help"))
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
471
237.7.732 by Teddy Hogeborn
mandos-monitor: Formatting changes only
472
        self.busname = domain + ".Mandos"
237.7.378 by Teddy Hogeborn
Server: Use python-gi instead of old python-gobject
473
        self.main_loop = GLib.MainLoop()
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
474
237.7.723 by Teddy Hogeborn
mandos-monitor: Update message from "fingerprint" to "key ID"
475
    def client_not_found(self, key_id, address):
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
476
        log.info("Client with address %s and key ID %s could"
477
                 " not be found", address, key_id)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
478
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
479
    def rebuild(self):
480
        """This rebuilds the User Interface.
481
        Call this when the widget layout needs to change"""
482
        self.uilist = []
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
483
        # self.uilist.append(urwid.ListBox(self.clients))
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
484
        self.uilist.append(urwid.Frame(ConstrainedListBox(self.
485
                                                          clients),
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
486
                                       # header=urwid.Divider(),
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
487
                                       header=None,
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
488
                                       footer=urwid.Divider(
489
                                           div_char=self.divider)))
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
490
        if self.log_visible:
491
            self.uilist.append(self.logbox)
492
        self.topwidget = urwid.Pile(self.uilist)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
493
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
494
    def add_log_line(self, markup):
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
495
        self.log.append(urwid.Text(markup, wrap=self.log_wrap))
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
496
        if self.max_log_length:
497
            if len(self.log) > self.max_log_length:
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
498
                del self.log[0:(len(self.log) - self.max_log_length)]
237.7.516 by Teddy Hogeborn
Do minor fix to make mandos-monitor work with new python-urwid
499
        self.logbox.set_focus(len(self.logbox.body.contents)-1,
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
500
                              coming_from="above")
237.2.172 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
501
        self.refresh()
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
502
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
503
    def toggle_log_display(self):
504
        """Toggle visibility of the log buffer."""
505
        self.log_visible = not self.log_visible
506
        self.rebuild()
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
507
        log.debug("Log visibility changed to: %s", self.log_visible)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
508
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
509
    def change_log_display(self):
510
        """Change type of log display.
511
        Currently, this toggles wrapping of text lines."""
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
512
        if self.log_wrap == "clip":
513
            self.log_wrap = "any"
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
514
        else:
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
515
            self.log_wrap = "clip"
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
516
        for textwidget in self.log:
517
            textwidget.set_wrap_mode(self.log_wrap)
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
518
        log.debug("Wrap mode: %s", self.log_wrap)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
519
237.7.333 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
520
    def find_and_remove_client(self, path, interfaces):
237.7.36 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Save match tag for
521
        """Find a client by its object path and remove it.
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
522
237.7.333 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
523
        This is connected to the InterfacesRemoved signal from the
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
524
        Mandos server object."""
237.7.333 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
525
        if client_interface not in interfaces:
526
            # Not a Mandos client object; ignore
527
            return
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
528
        try:
529
            client = self.clients_dict[path]
530
        except KeyError:
531
            # not found?
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
532
            log.warning("Unknown client %s removed", path)
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
533
            return
237.7.36 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Save match tag for
534
        client.delete()
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
535
237.7.333 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
536
    def add_new_client(self, path, ifs_and_props):
537
        """Find a client by its object path and remove it.
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
538
237.7.333 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
539
        This is connected to the InterfacesAdded signal from the
540
        Mandos server object.
541
        """
542
        if client_interface not in ifs_and_props:
543
            # Not a Mandos client object; ignore
544
            return
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
545
        client_proxy_object = self.bus.get_object(self.busname, path)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
546
        self.add_client(MandosClientWidget(
547
            server_proxy_object=self.mandos_serv,
548
            proxy_object=client_proxy_object,
549
            update_hook=self.refresh,
550
            delete_hook=self.remove_client,
551
            properties=dict(ifs_and_props[client_interface])),
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
552
                        path=path)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
553
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
554
    def add_client(self, client, path=None):
555
        self.clients.append(client)
556
        if path is None:
557
            path = client.proxy.object_path
558
        self.clients_dict[path] = client
237.7.165 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
559
        self.clients.sort(key=lambda c: c.properties["Name"])
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
560
        self.refresh()
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
561
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
562
    def remove_client(self, client, path=None):
563
        self.clients.remove(client)
564
        if path is None:
565
            path = client.proxy.object_path
566
        del self.clients_dict[path]
567
        self.refresh()
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
568
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
569
    def refresh(self):
570
        """Redraw the screen"""
571
        canvas = self.topwidget.render(self.size, focus=True)
572
        self.screen.draw_screen(self.size, canvas)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
573
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
574
    def run(self):
575
        """Start the main loop and exit when it's done."""
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
576
        log.addHandler(self.loghandler)
577
        self.orig_log_propagate = log.propagate
578
        log.propagate = False
579
        self.orig_log_level = log.level
580
        log.setLevel("INFO")
237.7.85 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
581
        self.bus = dbus.SystemBus()
582
        mandos_dbus_objc = self.bus.get_object(
583
            self.busname, "/", follow_name_owner_changes=True)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
584
        self.mandos_serv = dbus.Interface(
585
            mandos_dbus_objc, dbus_interface=server_interface)
237.7.85 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
586
        try:
587
            mandos_clients = (self.mandos_serv
588
                              .GetAllClientsWithProperties())
237.7.160 by Björn Påhlsson
added note messages when mandos-monitor starts without running server,
589
            if not mandos_clients:
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
590
                log.warning("Note: Server has no clients.")
237.7.85 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
591
        except dbus.exceptions.DBusException:
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
592
            log.warning("Note: No Mandos server running.")
237.7.85 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
593
            mandos_clients = dbus.Dictionary()
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
594
237.7.85 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
595
        (self.mandos_serv
237.7.333 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
596
         .connect_to_signal("InterfacesRemoved",
237.7.85 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
597
                            self.find_and_remove_client,
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
598
                            dbus_interface=dbus.OBJECT_MANAGER_IFACE,
237.7.85 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
599
                            byte_arrays=True))
600
        (self.mandos_serv
237.7.333 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
601
         .connect_to_signal("InterfacesAdded",
237.7.85 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
602
                            self.add_new_client,
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
603
                            dbus_interface=dbus.OBJECT_MANAGER_IFACE,
237.7.85 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
604
                            byte_arrays=True))
605
        (self.mandos_serv
606
         .connect_to_signal("ClientNotFound",
607
                            self.client_not_found,
608
                            dbus_interface=server_interface,
609
                            byte_arrays=True))
237.7.165 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
610
        for path, client in mandos_clients.items():
237.7.85 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
611
            client_proxy_object = self.bus.get_object(self.busname,
612
                                                      path)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
613
            self.add_client(MandosClientWidget(
614
                server_proxy_object=self.mandos_serv,
615
                proxy_object=client_proxy_object,
616
                properties=client,
617
                update_hook=self.refresh,
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
618
                delete_hook=self.remove_client),
237.7.85 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
619
                            path=path)
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
620
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
621
        self.refresh()
237.7.717 by Teddy Hogeborn
mandos-monitor: Use new GLib.io_add_watch() call signature
622
        self._input_callback_tag = (
623
            GLib.io_add_watch(
624
                GLib.IOChannel.unix_new(sys.stdin.fileno()),
625
                GLib.PRIORITY_DEFAULT, GLib.IO_IN,
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
626
                glib_safely(self.process_input)))
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
627
        self.main_loop.run()
628
        # Main loop has finished, we should close everything now
237.7.378 by Teddy Hogeborn
Server: Use python-gi instead of old python-gobject
629
        GLib.source_remove(self._input_callback_tag)
237.7.722 by Teddy Hogeborn
Address Python 3 bytes/str warnings
630
        with warnings.catch_warnings():
631
            warnings.simplefilter("ignore", BytesWarning)
632
            self.screen.stop()
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
633
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
634
    def stop(self):
635
        self.main_loop.quit()
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
636
        log.removeHandler(self.loghandler)
637
        log.propagate = self.orig_log_propagate
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
638
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
639
    def process_input(self, source, condition):
640
        keys = self.screen.get_input()
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
641
        translations = {"ctrl n": "down",       # Emacs
642
                        "ctrl p": "up",         # Emacs
643
                        "ctrl v": "page down",  # Emacs
644
                        "meta v": "page up",    # Emacs
645
                        " ": "page down",       # less
646
                        "f": "page down",       # less
647
                        "b": "page up",         # less
648
                        "j": "down",            # vi
649
                        "k": "up",              # vi
650
                        }
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
651
        for key in keys:
652
            try:
653
                key = translations[key]
654
            except KeyError:    # :-)
655
                pass
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
656
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
657
            if key == "q" or key == "Q":
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
658
                self.stop()
659
                break
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
660
            elif key == "window resize":
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
661
                self.size = self.screen.get_cols_rows()
662
                self.refresh()
237.7.270 by Teddy Hogeborn
mandos-monitor: Bug fix: Make Ctrl-L actually redraw the screen.
663
            elif key == "ctrl l":
664
                self.screen.clear()
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
665
                self.refresh()
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
666
            elif key == "l" or key == "D":
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
667
                self.toggle_log_display()
668
                self.refresh()
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
669
            elif key == "w" or key == "i":
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
670
                self.change_log_display()
671
                self.refresh()
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
672
            elif key == "?" or key == "f1" or key == "esc":
237.2.170 by Teddy Hogeborn
* mandos-monitor (MandosClientWidget): Change "StopChecker" key to "S"
673
                if not self.log_visible:
674
                    self.log_visible = True
675
                    self.rebuild()
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
676
                self.add_log_line(("bold",
677
                                   "  ".join(("q: Quit",
678
                                              "?: Help",
679
                                              "l: Log window toggle",
680
                                              "TAB: Switch window",
681
                                              "w: Wrap (log lines)",
682
                                              "v: Toggle verbose log",
683
                                   ))))
684
                self.add_log_line(("bold",
685
                                   "  ".join(("Clients:",
686
                                              "+: Enable",
687
                                              "-: Disable",
688
                                              "R: Remove",
689
                                              "s: Start new checker",
690
                                              "S: Stop checker",
691
                                              "C: Checker OK",
692
                                              "a: Approve",
693
                                              "d: Deny",
694
                                   ))))
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
695
                self.refresh()
237.8.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
696
            elif key == "tab":
237.2.168 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
697
                if self.topwidget.get_focus() is self.logbox:
698
                    self.topwidget.set_focus(0)
699
                else:
700
                    self.topwidget.set_focus(self.logbox)
701
                self.refresh()
237.7.261 by Teddy Hogeborn
mandos-monitor: New "verbose" mode to see less important log messages.
702
            elif key == "v":
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
703
                if log.level < logging.INFO:
704
                    log.setLevel(logging.INFO)
705
                    log.info("Verbose mode: Off")
237.7.261 by Teddy Hogeborn
mandos-monitor: New "verbose" mode to see less important log messages.
706
                else:
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
707
                    log.setLevel(logging.NOTSET)
708
                    log.info("Verbose mode: On")
237.7.421 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
709
            # elif (key == "end" or key == "meta >" or key == "G"
710
            #       or key == ">"):
711
            #     pass            # xxx end-of-buffer
712
            # elif (key == "home" or key == "meta <" or key == "g"
713
            #       or key == "<"):
714
            #     pass            # xxx beginning-of-buffer
715
            # elif key == "ctrl e" or key == "$":
716
            #     pass            # xxx move-end-of-line
717
            # elif key == "ctrl a" or key == "^":
718
            #     pass            # xxx move-beginning-of-line
719
            # elif key == "ctrl b" or key == "meta (" or key == "h":
720
            #     pass            # xxx left
721
            # elif key == "ctrl f" or key == "meta )" or key == "l":
722
            #     pass            # xxx right
723
            # elif key == "a":
724
            #     pass            # scroll up log
725
            # elif key == "z":
726
            #     pass            # scroll down log
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
727
            elif self.topwidget.selectable():
728
                self.topwidget.keypress(self.size, key)
729
                self.refresh()
730
        return True
731
237.7.436 by Teddy Hogeborn
Add more PEP8 compliance (as per the "pycodestyle" tool).
732
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
733
class UILogHandler(logging.Handler):
734
    def __init__(self, ui, *args, **kwargs):
735
        self.ui = ui
736
        super(UILogHandler, self).__init__(*args, **kwargs)
737
        self.setFormatter(
738
            logging.Formatter("%(asctime)s: %(message)s"))
739
    def emit(self, record):
740
        msg = self.format(record)
741
        if record.levelno > logging.INFO:
742
            msg = ("bold", msg)
743
        self.ui.add_log_line(msg)
744
745
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
746
ui = UserInterface()
747
try:
748
    ui.run()
24.1.159 by Björn Påhlsson
added approval to mandos-ctl
749
except KeyboardInterrupt:
237.7.731 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
750
    with warnings.catch_warnings():
751
        warnings.filterwarnings("ignore", "", BytesWarning)
752
        ui.screen.stop()
753
except Exception:
754
    with warnings.catch_warnings():
755
        warnings.filterwarnings("ignore", "", BytesWarning)
756
        ui.screen.stop()
237.2.167 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
757
    raise