/mandos/trunk

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