130
86
        self.logger = logger
 
132
 
        self._update_timer_callback_tag = None
 
133
 
        self._update_timer_callback_lock = 0
 
134
 
        self.last_checker_failed = False
 
136
88
        # The widget shown normally
 
137
 
        self._text_widget = urwid.Text("")
 
 
89
        self._text_widget = urwid.Text(u"")
 
138
90
        # The widget shown when we have focus
 
139
 
        self._focus_text_widget = urwid.Text("")
 
 
91
        self._focus_text_widget = urwid.Text(u"")
 
140
92
        super(MandosClientWidget, self).__init__(
 
141
93
            update_hook=update_hook, delete_hook=delete_hook,
 
144
96
        self.opened = False
 
146
 
        last_checked_ok = isoformat_to_datetime(self.properties
 
148
 
        if last_checked_ok is None:
 
149
 
            self.last_checker_failed = True
 
151
 
            self.last_checker_failed = ((datetime.datetime.utcnow()
 
158
 
        if self.last_checker_failed:
 
159
 
            self.using_timer(True)
 
161
 
        if self.need_approval:
 
162
 
            self.using_timer(True)
 
164
 
        self.match_objects = (
 
165
 
            self.proxy.connect_to_signal("CheckerCompleted",
 
166
 
                                         self.checker_completed,
 
169
 
            self.proxy.connect_to_signal("CheckerStarted",
 
170
 
                                         self.checker_started,
 
173
 
            self.proxy.connect_to_signal("GotSecret",
 
177
 
            self.proxy.connect_to_signal("NeedApproval",
 
181
 
            self.proxy.connect_to_signal("Rejected",
 
185
 
        #self.logger('Created client %s' % (self.properties["Name"]))
 
187
 
    def property_changed(self, property=None, value=None):
 
188
 
        super(self, MandosClientWidget).property_changed(property,
 
190
 
        if property == "ApprovalPending":
 
191
 
            using_timer(bool(value))
 
193
 
    def using_timer(self, flag):
 
194
 
        """Call this method with True or False when timer should be
 
195
 
        activated or deactivated.
 
197
 
        old = self._update_timer_callback_lock
 
199
 
            self._update_timer_callback_lock += 1
 
201
 
            self._update_timer_callback_lock -= 1
 
202
 
        if old == 0 and self._update_timer_callback_lock:
 
203
 
            # Will update the shown timer value every second
 
204
 
            self._update_timer_callback_tag = (gobject.timeout_add
 
207
 
        elif old and self._update_timer_callback_lock == 0:
 
208
 
            gobject.source_remove(self._update_timer_callback_tag)
 
209
 
            self._update_timer_callback_tag = None
 
 
97
        self.proxy.connect_to_signal(u"CheckerCompleted",
 
 
98
                                     self.checker_completed,
 
 
101
        self.proxy.connect_to_signal(u"CheckerStarted",
 
 
102
                                     self.checker_started,
 
 
105
        self.proxy.connect_to_signal(u"GotSecret",
 
 
109
        self.proxy.connect_to_signal(u"NeedApproval",
 
 
113
        self.proxy.connect_to_signal(u"Rejected",
 
211
118
    def checker_completed(self, exitstatus, condition, command):
 
212
119
        if exitstatus == 0:
 
213
 
            if self.last_checker_failed:
 
214
 
                self.last_checker_failed = False
 
215
 
                self.using_timer(False)
 
216
 
            #self.logger('Checker for client %s (command "%s")'
 
218
 
            #            % (self.properties["Name"], command))
 
 
120
            #self.logger(u'Checker for client %s (command "%s")'
 
 
122
            #            % (self.properties[u"name"], command))
 
222
 
        if not self.last_checker_failed:
 
223
 
            self.last_checker_failed = True
 
224
 
            self.using_timer(True)
 
225
124
        if os.WIFEXITED(condition):
 
226
 
            self.logger('Checker for client %s (command "%s")'
 
227
 
                        ' failed with exit code %s'
 
228
 
                        % (self.properties["Name"], command,
 
 
125
            self.logger(u'Checker for client %s (command "%s")'
 
 
126
                        u' failed with exit code %s'
 
 
127
                        % (self.properties[u"name"], command,
 
229
128
                           os.WEXITSTATUS(condition)))
 
230
 
        elif os.WIFSIGNALED(condition):
 
231
 
            self.logger('Checker for client %s (command "%s")'
 
232
 
                        ' was killed by signal %s'
 
233
 
                        % (self.properties["Name"], command,
 
 
130
        if os.WIFSIGNALED(condition):
 
 
131
            self.logger(u'Checker for client %s (command "%s")'
 
 
132
                        u' was killed by signal %s'
 
 
133
                        % (self.properties[u"name"], command,
 
234
134
                           os.WTERMSIG(condition)))
 
235
 
        elif os.WCOREDUMP(condition):
 
236
 
            self.logger('Checker for client %s (command "%s")'
 
238
 
                        % (self.properties["Name"], command))
 
240
 
            self.logger('Checker for client %s completed'
 
 
136
        if os.WCOREDUMP(condition):
 
 
137
            self.logger(u'Checker for client %s (command "%s")'
 
 
139
                        % (self.properties[u"name"], command))
 
 
140
        self.logger(u'Checker for client %s completed mysteriously')
 
244
142
    def checker_started(self, command):
 
245
 
        #self.logger('Client %s started checker "%s"'
 
246
 
        #            % (self.properties["Name"], unicode(command)))
 
 
143
        #self.logger(u'Client %s started checker "%s"'
 
 
144
        #            % (self.properties[u"name"], unicode(command)))
 
249
147
    def got_secret(self):
 
250
 
        self.last_checker_failed = False
 
251
 
        self.logger('Client %s received its secret'
 
252
 
                    % self.properties["Name"])
 
 
148
        self.logger(u'Client %s received its secret'
 
 
149
                    % self.properties[u"name"])
 
254
151
    def need_approval(self, timeout, default):
 
256
 
            message = 'Client %s needs approval within %s seconds'
 
 
153
            message = u'Client %s needs approval within %s seconds'
 
258
 
            message = 'Client %s will get its secret in %s seconds'
 
 
155
            message = u'Client %s will get its secret in %s seconds'
 
259
156
        self.logger(message
 
260
 
                    % (self.properties["Name"], timeout/1000))
 
261
 
        self.using_timer(True)
 
 
157
                    % (self.properties[u"name"], timeout/1000))
 
263
159
    def rejected(self, reason):
 
264
 
        self.logger('Client %s was rejected; reason: %s'
 
265
 
                    % (self.properties["Name"], reason))
 
 
160
        self.logger(u'Client %s was rejected; reason: %s'
 
 
161
                    % (self.properties[u"name"], reason))
 
267
163
    def selectable(self):
 
268
164
        """Make this a "selectable" widget.
 
269
165
        This overrides the method from urwid.FlowWidget."""
 
272
 
    def rows(self, maxcolrow, focus=False):
 
 
168
    def rows(self, (maxcol,), focus=False):
 
273
169
        """How many rows this widget will occupy might depend on
 
274
170
        whether we have focus or not.
 
275
171
        This overrides the method from urwid.FlowWidget"""
 
276
 
        return self.current_widget(focus).rows(maxcolrow, focus=focus)
 
 
172
        return self.current_widget(focus).rows((maxcol,), focus=focus)
 
278
174
    def current_widget(self, focus=False):
 
279
175
        if focus or self.opened:
 
 
283
179
    def update(self):
 
284
180
        "Called when what is visible on the screen should be updated."
 
285
181
        # How to add standout mode to a style
 
286
 
        with_standout = { "normal": "standout",
 
287
 
                          "bold": "bold-standout",
 
289
 
                              "underline-blink-standout",
 
290
 
                          "bold-underline-blink":
 
291
 
                              "bold-underline-blink-standout",
 
 
182
        with_standout = { u"normal": u"standout",
 
 
183
                          u"bold": u"bold-standout",
 
 
185
                              u"underline-blink-standout",
 
 
186
                          u"bold-underline-blink":
 
 
187
                              u"bold-underline-blink-standout",
 
294
190
        # Rebuild focus and non-focus widgets using current properties
 
296
192
        # Base part of a client. Name!
 
298
 
                      % {"name": self.properties["Name"]})
 
299
 
        if not self.properties["Enabled"]:
 
301
 
        elif self.properties["ApprovalPending"]:
 
302
 
            timeout = datetime.timedelta(milliseconds
 
305
 
            last_approval_request = isoformat_to_datetime(
 
306
 
                self.properties["LastApprovalRequest"])
 
307
 
            if last_approval_request is not None:
 
308
 
                timer = timeout - (datetime.datetime.utcnow()
 
309
 
                                   - last_approval_request)
 
311
 
                timer = datetime.timedelta()
 
312
 
            if self.properties["ApprovedByDefault"]:
 
313
 
                message = "Approval in %s. (d)eny?"
 
315
 
                message = "Denial in %s. (a)pprove?"
 
316
 
            message = message % unicode(timer).rsplit(".", 1)[0]
 
317
 
        elif self.last_checker_failed:
 
318
 
            # When checker has failed, print a timer until client expires
 
319
 
            expires = self.properties["Expires"]
 
321
 
                timer = datetime.timedelta(0)
 
323
 
                expires = datetime.datetime.strptime(expires,
 
324
 
                                                     '%Y-%m-%dT%H:%M:%S.%f')
 
325
 
                timer = expires - datetime.datetime.utcnow()
 
326
 
            message = ('A checker has failed! Time until client'
 
328
 
                           % unicode(timer).rsplit(".", 1)[0])
 
 
193
        self._text = (u'%(name)s: '
 
 
194
                      % {u"name": self.properties[u"name"]})
 
 
196
        if self.properties[u"approved_pending"]:
 
 
197
            if self.properties[u"approved_by_default"]:
 
 
198
                self._text += u"Connection established to client. (d)eny?"
 
 
200
                self._text += u"Seeks approval to send secret. (a)pprove?"
 
331
 
        self._text = "%s%s" % (base, message)
 
 
202
            self._text += (u'%(enabled)s'
 
 
205
                                if self.properties[u"enabled"]
 
333
207
        if not urwid.supports_unicode():
 
334
208
            self._text = self._text.encode("ascii", "replace")
 
335
 
        textlist = [("normal", self._text)]
 
 
209
        textlist = [(u"normal", self._text)]
 
336
210
        self._text_widget.set_text(textlist)
 
337
211
        self._focus_text_widget.set_text([(with_standout[text[0]],
 
 
346
220
        if self.update_hook is not None:
 
347
221
            self.update_hook()
 
349
 
    def update_timer(self):
 
350
 
        """called by gobject. Will indefinitely loop until
 
351
 
        gobject.source_remove() on tag is called"""
 
353
 
        return True             # Keep calling this
 
355
 
    def delete(self, *args, **kwargs):
 
356
 
        if self._update_timer_callback_tag is not None:
 
357
 
            gobject.source_remove(self._update_timer_callback_tag)
 
358
 
            self._update_timer_callback_tag = None
 
359
 
        for match in self.match_objects:
 
361
 
        self.match_objects = ()
 
362
224
        if self.delete_hook is not None:
 
363
225
            self.delete_hook(self)
 
364
 
        return super(MandosClientWidget, self).delete(*args, **kwargs)
 
366
 
    def render(self, maxcolrow, focus=False):
 
 
227
    def render(self, (maxcol,), focus=False):
 
367
228
        """Render differently if we have focus.
 
368
229
        This overrides the method from urwid.FlowWidget"""
 
369
 
        return self.current_widget(focus).render(maxcolrow,
 
 
230
        return self.current_widget(focus).render((maxcol,),
 
372
 
    def keypress(self, maxcolrow, key):
 
 
233
    def keypress(self, (maxcol,), key):
 
374
235
        This overrides the method from urwid.FlowWidget"""
 
376
 
            self.proxy.Enable(dbus_interface = client_interface,
 
379
 
            self.proxy.Disable(dbus_interface = client_interface,
 
 
237
            self.proxy.Enable(dbus_interface = client_interface)
 
 
239
            self.proxy.Disable(dbus_interface = client_interface)
 
382
241
            self.proxy.Approve(dbus.Boolean(True, variant_level=1),
 
383
 
                               dbus_interface = client_interface,
 
 
242
                               dbus_interface = client_interface)
 
386
244
            self.proxy.Approve(dbus.Boolean(False, variant_level=1),
 
387
 
                                  dbus_interface = client_interface,
 
389
 
        elif key == "R" or key == "_" or key == "ctrl k":
 
 
245
                                  dbus_interface = client_interface)
 
 
246
        elif key == u"r" or key == u"_" or key == u"ctrl k":
 
390
247
            self.server_proxy_object.RemoveClient(self.proxy
 
394
 
            self.proxy.StartChecker(dbus_interface = client_interface,
 
397
 
            self.proxy.StopChecker(dbus_interface = client_interface,
 
400
 
            self.proxy.CheckedOK(dbus_interface = client_interface,
 
 
250
            self.proxy.StartChecker(dbus_interface = client_interface)
 
 
252
            self.proxy.StopChecker(dbus_interface = client_interface)
 
 
254
            self.proxy.CheckedOK(dbus_interface = client_interface)
 
403
 
#         elif key == "p" or key == "=":
 
 
256
#         elif key == u"p" or key == "=":
 
404
257
#             self.proxy.pause()
 
405
 
#         elif key == "u" or key == ":":
 
 
258
#         elif key == u"u" or key == ":":
 
406
259
#             self.proxy.unpause()
 
 
260
#         elif key == u"RET":
 
 
263
#            self.proxy.Approve(True)
 
 
265
#            self.proxy.Approve(False)
 
 
443
300
        self.screen = urwid.curses_display.Screen()
 
445
302
        self.screen.register_palette((
 
447
 
                 "default", "default", None),
 
449
 
                 "default", "default", "bold"),
 
451
 
                 "default", "default", "underline"),
 
453
 
                 "default", "default", "standout"),
 
454
 
                ("bold-underline-blink",
 
455
 
                 "default", "default", ("bold", "underline")),
 
457
 
                 "default", "default", ("bold", "standout")),
 
458
 
                ("underline-blink-standout",
 
459
 
                 "default", "default", ("underline", "standout")),
 
460
 
                ("bold-underline-blink-standout",
 
461
 
                 "default", "default", ("bold", "underline",
 
 
304
                 u"default", u"default", None),
 
 
306
                 u"default", u"default", u"bold"),
 
 
308
                 u"default", u"default", u"underline"),
 
 
310
                 u"default", u"default", u"standout"),
 
 
311
                (u"bold-underline-blink",
 
 
312
                 u"default", u"default", (u"bold", u"underline")),
 
 
314
                 u"default", u"default", (u"bold", u"standout")),
 
 
315
                (u"underline-blink-standout",
 
 
316
                 u"default", u"default", (u"underline", u"standout")),
 
 
317
                (u"bold-underline-blink-standout",
 
 
318
                 u"default", u"default", (u"bold", u"underline",
 
465
322
        if urwid.supports_unicode():
 
466
 
            self.divider = "─" # \u2500
 
467
 
            #self.divider = "━" # \u2501
 
 
323
            self.divider = u"─" # \u2500
 
 
324
            #self.divider = u"━" # \u2501
 
469
 
            #self.divider = "-" # \u002d
 
470
 
            self.divider = "_" # \u005f
 
 
326
            #self.divider = u"-" # \u002d
 
 
327
            self.divider = u"_" # \u005f
 
472
329
        self.screen.start()
 
 
573
427
            and len(self.log) > self.max_log_length):
 
574
428
            del self.log[0:len(self.log)-self.max_log_length-1]
 
575
429
        self.logbox.set_focus(len(self.logbox.body.contents),
 
 
430
                              coming_from=u"above")
 
579
433
    def toggle_log_display(self):
 
580
434
        """Toggle visibility of the log buffer."""
 
581
435
        self.log_visible = not self.log_visible
 
583
 
        #self.log_message("Log visibility changed to: "
 
584
 
        #                 + unicode(self.log_visible))
 
 
437
        self.log_message(u"Log visibility changed to: "
 
 
438
                         + unicode(self.log_visible))
 
586
440
    def change_log_display(self):
 
587
441
        """Change type of log display.
 
588
442
        Currently, this toggles wrapping of text lines."""
 
589
 
        if self.log_wrap == "clip":
 
590
 
            self.log_wrap = "any"
 
 
443
        if self.log_wrap == u"clip":
 
 
444
            self.log_wrap = u"any"
 
592
 
            self.log_wrap = "clip"
 
 
446
            self.log_wrap = u"clip"
 
593
447
        for textwidget in self.log:
 
594
448
            textwidget.set_wrap_mode(self.log_wrap)
 
595
 
        #self.log_message("Wrap mode: " + self.log_wrap)
 
 
449
        self.log_message(u"Wrap mode: " + self.log_wrap)
 
597
451
    def find_and_remove_client(self, path, name):
 
598
 
        """Find a client by its object path and remove it.
 
 
452
        """Find an client from its object path and remove it.
 
600
454
        This is connected to the ClientRemoved signal from the
 
601
455
        Mandos server object."""
 
 
680
532
            except KeyError:    # :-)
 
683
 
            if key == "q" or key == "Q":
 
 
535
            if key == u"q" or key == u"Q":
 
686
 
            elif key == "window resize":
 
 
538
            elif key == u"window resize":
 
687
539
                self.size = self.screen.get_cols_rows()
 
689
 
            elif key == "\f":  # Ctrl-L
 
 
541
            elif key == u"\f":  # Ctrl-L
 
691
 
            elif key == "l" or key == "D":
 
 
543
            elif key == u"l" or key == u"D":
 
692
544
                self.toggle_log_display()
 
694
 
            elif key == "w" or key == "i":
 
 
546
            elif key == u"w" or key == u"i":
 
695
547
                self.change_log_display()
 
697
 
            elif key == "?" or key == "f1" or key == "esc":
 
 
549
            elif key == u"?" or key == u"f1" or key == u"esc":
 
698
550
                if not self.log_visible:
 
699
551
                    self.log_visible = True
 
701
 
                self.log_message_raw(("bold",
 
705
 
                                            "l: Log window toggle",
 
706
 
                                            "TAB: Switch window",
 
708
 
                self.log_message_raw(("bold",
 
714
 
                                             "s: Start new checker",
 
 
553
                self.log_message_raw((u"bold",
 
 
557
                                            u"l: Log window toggle",
 
 
558
                                            u"TAB: Switch window",
 
 
560
                self.log_message_raw((u"bold",
 
 
566
                                             u"s: Start new checker",
 
721
573
                if self.topwidget.get_focus() is self.logbox:
 
722
574
                    self.topwidget.set_focus(0)
 
724
576
                    self.topwidget.set_focus(self.logbox)
 
726
 
            #elif (key == "end" or key == "meta >" or key == "G"
 
 
578
            #elif (key == u"end" or key == u"meta >" or key == u"G"
 
728
580
            #    pass            # xxx end-of-buffer
 
729
 
            #elif (key == "home" or key == "meta <" or key == "g"
 
 
581
            #elif (key == u"home" or key == u"meta <" or key == u"g"
 
731
583
            #    pass            # xxx beginning-of-buffer
 
732
 
            #elif key == "ctrl e" or key == "$":
 
 
584
            #elif key == u"ctrl e" or key == u"$":
 
733
585
            #    pass            # xxx move-end-of-line
 
734
 
            #elif key == "ctrl a" or key == "^":
 
 
586
            #elif key == u"ctrl a" or key == u"^":
 
735
587
            #    pass            # xxx move-beginning-of-line
 
736
 
            #elif key == "ctrl b" or key == "meta (" or key == "h":
 
 
588
            #elif key == u"ctrl b" or key == u"meta (" or key == u"h":
 
737
589
            #    pass            # xxx left
 
738
 
            #elif key == "ctrl f" or key == "meta )" or key == "l":
 
 
590
            #elif key == u"ctrl f" or key == u"meta )" or key == u"l":
 
739
591
            #    pass            # xxx right
 
741
593
            #    pass            # scroll up log
 
743
595
            #    pass            # scroll down log
 
744
596
            elif self.topwidget.selectable():
 
745
597
                self.topwidget.keypress(self.size, key)