/mandos/release

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