/mandos/release

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