171
 
        except dbus.exceptions.DBusException, error:
 
 
172
        except dbus.exceptions.DBusException as error:
 
172
173
            logger.critical("DBusException: %s", error)
 
175
176
        self.rename_count += 1
 
176
177
    def remove(self):
 
177
178
        """Derived from the Avahi example code"""
 
 
179
        if self.entry_group_state_changed_match is not None:
 
 
180
            self.entry_group_state_changed_match.remove()
 
 
181
            self.entry_group_state_changed_match = None
 
178
182
        if self.group is not None:
 
179
183
            self.group.Reset()
 
181
185
        """Derived from the Avahi example code"""
 
182
187
        if self.group is None:
 
183
188
            self.group = dbus.Interface(
 
184
189
                self.bus.get_object(avahi.DBUS_NAME,
 
185
190
                                    self.server.EntryGroupNew()),
 
186
191
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
 
187
 
            self.group.connect_to_signal('StateChanged',
 
189
 
                                         .entry_group_state_changed)
 
 
192
        self.entry_group_state_changed_match = (
 
 
193
            self.group.connect_to_signal(
 
 
194
                'StateChanged', self .entry_group_state_changed))
 
190
195
        logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
 
191
196
                     self.name, self.type)
 
192
197
        self.group.AddService(
 
 
215
220
    def cleanup(self):
 
216
221
        """Derived from the Avahi example code"""
 
217
222
        if self.group is not None:
 
 
225
            except (dbus.exceptions.UnknownMethodException,
 
 
226
                    dbus.exceptions.DBusException) as e:
 
219
228
            self.group = None
 
220
 
    def server_state_changed(self, state):
 
 
230
    def server_state_changed(self, state, error=None):
 
221
231
        """Derived from the Avahi example code"""
 
222
232
        logger.debug("Avahi server state change: %i", state)
 
223
 
        if state == avahi.SERVER_COLLISION:
 
224
 
            logger.error("Zeroconf server name collision")
 
 
233
        bad_states = { avahi.SERVER_INVALID:
 
 
234
                           "Zeroconf server invalid",
 
 
235
                       avahi.SERVER_REGISTERING: None,
 
 
236
                       avahi.SERVER_COLLISION:
 
 
237
                           "Zeroconf server name collision",
 
 
238
                       avahi.SERVER_FAILURE:
 
 
239
                           "Zeroconf server failure" }
 
 
240
        if state in bad_states:
 
 
241
            if bad_states[state] is not None:
 
 
243
                    logger.error(bad_states[state])
 
 
245
                    logger.error(bad_states[state] + ": %r", error)
 
226
247
        elif state == avahi.SERVER_RUNNING:
 
 
251
                logger.debug("Unknown state: %r", state)
 
 
253
                logger.debug("Unknown state: %r: %r", state, error)
 
228
254
    def activate(self):
 
229
255
        """Derived from the Avahi example code"""
 
230
256
        if self.server is None:
 
231
257
            self.server = dbus.Interface(
 
232
258
                self.bus.get_object(avahi.DBUS_NAME,
 
233
 
                                    avahi.DBUS_PATH_SERVER),
 
 
259
                                    avahi.DBUS_PATH_SERVER,
 
 
260
                                    follow_name_owner_changes=True),
 
234
261
                avahi.DBUS_INTERFACE_SERVER)
 
235
262
        self.server.connect_to_signal("StateChanged",
 
236
263
                                 self.server_state_changed)
 
237
264
        self.server_state_changed(self.server.GetState())
 
 
267
def _timedelta_to_milliseconds(td):
 
 
268
    "Convert a datetime.timedelta() to milliseconds"
 
 
269
    return ((td.days * 24 * 60 * 60 * 1000)
 
 
270
            + (td.seconds * 1000)
 
 
271
            + (td.microseconds // 1000))
 
240
273
class Client(object):
 
241
274
    """A representation of a client host served by this server.
 
 
270
303
    secret:     bytestring; sent verbatim (over TLS) to client
 
271
304
    timeout:    datetime.timedelta(); How long from last_checked_ok
 
272
305
                                      until this client is disabled
 
 
306
    extended_timeout:   extra long timeout when password has been sent
 
273
307
    runtime_expansions: Allowed attributes for runtime expansion.
 
 
308
    expires:    datetime.datetime(); time (UTC) when a client will be
 
276
312
    runtime_expansions = ("approval_delay", "approval_duration",
 
277
313
                          "created", "enabled", "fingerprint",
 
278
314
                          "host", "interval", "last_checked_ok",
 
279
315
                          "last_enabled", "name", "timeout")
 
282
 
    def _timedelta_to_milliseconds(td):
 
283
 
        "Convert a datetime.timedelta() to milliseconds"
 
284
 
        return ((td.days * 24 * 60 * 60 * 1000)
 
285
 
                + (td.seconds * 1000)
 
286
 
                + (td.microseconds // 1000))
 
288
317
    def timeout_milliseconds(self):
 
289
318
        "Return the 'timeout' attribute in milliseconds"
 
290
 
        return self._timedelta_to_milliseconds(self.timeout)
 
 
319
        return _timedelta_to_milliseconds(self.timeout)
 
 
321
    def extended_timeout_milliseconds(self):
 
 
322
        "Return the 'extended_timeout' attribute in milliseconds"
 
 
323
        return _timedelta_to_milliseconds(self.extended_timeout)    
 
292
325
    def interval_milliseconds(self):
 
293
326
        "Return the 'interval' attribute in milliseconds"
 
294
 
        return self._timedelta_to_milliseconds(self.interval)
 
 
327
        return _timedelta_to_milliseconds(self.interval)
 
296
329
    def approval_delay_milliseconds(self):
 
297
 
        return self._timedelta_to_milliseconds(self.approval_delay)
 
 
330
        return _timedelta_to_milliseconds(self.approval_delay)
 
299
332
    def __init__(self, name = None, disable_hook=None, config=None):
 
300
333
        """Note: the 'checker' key in 'config' sets the
 
 
704
744
            xmlstring = document.toxml("utf-8")
 
705
745
            document.unlink()
 
706
746
        except (AttributeError, xml.dom.DOMException,
 
707
 
                xml.parsers.expat.ExpatError), error:
 
 
747
                xml.parsers.expat.ExpatError) as error:
 
708
748
            logger.error("Failed to override Introspection method",
 
 
753
def datetime_to_dbus (dt, variant_level=0):
 
 
754
    """Convert a UTC datetime.datetime() to a D-Bus type."""
 
 
756
        return dbus.String("", variant_level = variant_level)
 
 
757
    return dbus.String(dt.isoformat(),
 
 
758
                       variant_level=variant_level)
 
713
760
class ClientDBus(Client, DBusObjectWithProperties):
 
714
761
    """A Client class using D-Bus
 
 
737
784
        DBusObjectWithProperties.__init__(self, self.bus,
 
738
785
                                          self.dbus_object_path)
 
740
 
    def _get_approvals_pending(self):
 
741
 
        return self._approvals_pending
 
742
 
    def _set_approvals_pending(self, value):
 
743
 
        old_value = self._approvals_pending
 
744
 
        self._approvals_pending = value
 
746
 
        if (hasattr(self, "dbus_object_path")
 
747
 
            and bval is not bool(old_value)):
 
748
 
            dbus_bool = dbus.Boolean(bval, variant_level=1)
 
749
 
            self.PropertyChanged(dbus.String("ApprovalPending"),
 
752
 
    approvals_pending = property(_get_approvals_pending,
 
753
 
                                 _set_approvals_pending)
 
754
 
    del _get_approvals_pending, _set_approvals_pending
 
757
 
    def _datetime_to_dbus(dt, variant_level=0):
 
758
 
        """Convert a UTC datetime.datetime() to a D-Bus type."""
 
759
 
        return dbus.String(dt.isoformat(),
 
760
 
                           variant_level=variant_level)
 
763
 
        oldstate = getattr(self, "enabled", False)
 
764
 
        r = Client.enable(self)
 
765
 
        if oldstate != self.enabled:
 
767
 
            self.PropertyChanged(dbus.String("Enabled"),
 
768
 
                                 dbus.Boolean(True, variant_level=1))
 
769
 
            self.PropertyChanged(
 
770
 
                dbus.String("LastEnabled"),
 
771
 
                self._datetime_to_dbus(self.last_enabled,
 
775
 
    def disable(self, quiet = False):
 
776
 
        oldstate = getattr(self, "enabled", False)
 
777
 
        r = Client.disable(self, quiet=quiet)
 
778
 
        if not quiet and oldstate != self.enabled:
 
780
 
            self.PropertyChanged(dbus.String("Enabled"),
 
781
 
                                 dbus.Boolean(False, variant_level=1))
 
 
787
    def notifychangeproperty(transform_func,
 
 
788
                             dbus_name, type_func=lambda x: x,
 
 
790
        """ Modify a variable so that its a property that announce its
 
 
792
        transform_fun: Function that takes a value and transform it to
 
 
794
        dbus_name: DBus name of the variable
 
 
795
        type_func: Function that transform the value before sending it
 
 
797
        variant_level: DBus variant level. default: 1
 
 
800
        def setter(self, value):
 
 
801
            old_value = real_value[0]
 
 
802
            real_value[0] = value
 
 
803
            if hasattr(self, "dbus_object_path"):
 
 
804
                if type_func(old_value) != type_func(real_value[0]):
 
 
805
                    dbus_value = transform_func(type_func(real_value[0]),
 
 
807
                    self.PropertyChanged(dbus.String(dbus_name),
 
 
810
        return property(lambda self: real_value[0], setter)
 
 
813
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
 
 
814
    approvals_pending = notifychangeproperty(dbus.Boolean,
 
 
817
    enabled = notifychangeproperty(dbus.Boolean, "Enabled")
 
 
818
    last_enabled = notifychangeproperty(datetime_to_dbus,
 
 
820
    checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
 
 
821
                                   type_func = lambda checker: checker is not None)
 
 
822
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
 
 
824
    last_approval_request = notifychangeproperty(datetime_to_dbus,
 
 
825
                                                 "LastApprovalRequest")
 
 
826
    approved_by_default = notifychangeproperty(dbus.Boolean,
 
 
828
    approval_delay = notifychangeproperty(dbus.UInt16, "ApprovalDelay",
 
 
829
                                          type_func = _timedelta_to_milliseconds)
 
 
830
    approval_duration = notifychangeproperty(dbus.UInt16, "ApprovalDuration",
 
 
831
                                             type_func = _timedelta_to_milliseconds)
 
 
832
    host = notifychangeproperty(dbus.String, "Host")
 
 
833
    timeout = notifychangeproperty(dbus.UInt16, "Timeout",
 
 
834
                                   type_func = _timedelta_to_milliseconds)
 
 
835
    extended_timeout = notifychangeproperty(dbus.UInt16, "ExtendedTimeout",
 
 
836
                                            type_func = _timedelta_to_milliseconds)
 
 
837
    interval = notifychangeproperty(dbus.UInt16, "Interval",
 
 
838
                                    type_func = _timedelta_to_milliseconds)
 
 
839
    checker_command = notifychangeproperty(dbus.String, "Checker")
 
 
841
    del notifychangeproperty
 
784
843
    def __del__(self, *args, **kwargs):
 
 
812
868
        return Client.checker_callback(self, pid, condition, command,
 
815
 
    def checked_ok(self, *args, **kwargs):
 
816
 
        r = Client.checked_ok(self, *args, **kwargs)
 
818
 
        self.PropertyChanged(
 
819
 
            dbus.String("LastCheckedOK"),
 
820
 
            (self._datetime_to_dbus(self.last_checked_ok,
 
824
 
    def need_approval(self, *args, **kwargs):
 
825
 
        r = Client.need_approval(self, *args, **kwargs)
 
827
 
        self.PropertyChanged(
 
828
 
            dbus.String("LastApprovalRequest"),
 
829
 
            (self._datetime_to_dbus(self.last_approval_request,
 
833
871
    def start_checker(self, *args, **kwargs):
 
834
872
        old_checker = self.checker
 
835
873
        if self.checker is not None:
 
 
842
880
            and old_checker_pid != self.checker.pid):
 
843
881
            # Emit D-Bus signal
 
844
882
            self.CheckerStarted(self.current_checker_command)
 
845
 
            self.PropertyChanged(
 
846
 
                dbus.String("CheckerRunning"),
 
847
 
                dbus.Boolean(True, variant_level=1))
 
850
 
    def stop_checker(self, *args, **kwargs):
 
851
 
        old_checker = getattr(self, "checker", None)
 
852
 
        r = Client.stop_checker(self, *args, **kwargs)
 
853
 
        if (old_checker is not None
 
854
 
            and getattr(self, "checker", None) is None):
 
855
 
            self.PropertyChanged(dbus.String("CheckerRunning"),
 
856
 
                                 dbus.Boolean(False, variant_level=1))
 
859
885
    def _reset_approved(self):
 
860
886
        self._approved = None
 
 
972
995
        if value is None:       # get
 
973
996
            return dbus.UInt64(self.approval_delay_milliseconds())
 
974
997
        self.approval_delay = datetime.timedelta(0, 0, 0, value)
 
976
 
        self.PropertyChanged(dbus.String("ApprovalDelay"),
 
977
 
                             dbus.UInt64(value, variant_level=1))
 
979
999
    # ApprovalDuration - property
 
980
1000
    @dbus_service_property(_interface, signature="t",
 
981
1001
                           access="readwrite")
 
982
1002
    def ApprovalDuration_dbus_property(self, value=None):
 
983
1003
        if value is None:       # get
 
984
 
            return dbus.UInt64(self._timedelta_to_milliseconds(
 
 
1004
            return dbus.UInt64(_timedelta_to_milliseconds(
 
985
1005
                    self.approval_duration))
 
986
1006
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
 
988
 
        self.PropertyChanged(dbus.String("ApprovalDuration"),
 
989
 
                             dbus.UInt64(value, variant_level=1))
 
991
1008
    # Name - property
 
992
1009
    @dbus_service_property(_interface, signature="s", access="read")
 
 
1005
1022
        if value is None:       # get
 
1006
1023
            return dbus.String(self.host)
 
1007
1024
        self.host = value
 
1009
 
        self.PropertyChanged(dbus.String("Host"),
 
1010
 
                             dbus.String(value, variant_level=1))
 
1012
1026
    # Created - property
 
1013
1027
    @dbus_service_property(_interface, signature="s", access="read")
 
1014
1028
    def Created_dbus_property(self):
 
1015
 
        return dbus.String(self._datetime_to_dbus(self.created))
 
 
1029
        return dbus.String(datetime_to_dbus(self.created))
 
1017
1031
    # LastEnabled - property
 
1018
1032
    @dbus_service_property(_interface, signature="s", access="read")
 
1019
1033
    def LastEnabled_dbus_property(self):
 
1020
 
        if self.last_enabled is None:
 
1021
 
            return dbus.String("")
 
1022
 
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
 
 
1034
        return datetime_to_dbus(self.last_enabled)
 
1024
1036
    # Enabled - property
 
1025
1037
    @dbus_service_property(_interface, signature="b",
 
 
1039
1051
        if value is not None:
 
1040
1052
            self.checked_ok()
 
1042
 
        if self.last_checked_ok is None:
 
1043
 
            return dbus.String("")
 
1044
 
        return dbus.String(self._datetime_to_dbus(self
 
 
1054
        return datetime_to_dbus(self.last_checked_ok)
 
 
1056
    # Expires - property
 
 
1057
    @dbus_service_property(_interface, signature="s", access="read")
 
 
1058
    def Expires_dbus_property(self):
 
 
1059
        return datetime_to_dbus(self.expires)
 
1047
1061
    # LastApprovalRequest - property
 
1048
1062
    @dbus_service_property(_interface, signature="s", access="read")
 
1049
1063
    def LastApprovalRequest_dbus_property(self):
 
1050
 
        if self.last_approval_request is None:
 
1051
 
            return dbus.String("")
 
1052
 
        return dbus.String(self.
 
1053
 
                           _datetime_to_dbus(self
 
1054
 
                                             .last_approval_request))
 
 
1064
        return datetime_to_dbus(self.last_approval_request)
 
1056
1066
    # Timeout - property
 
1057
1067
    @dbus_service_property(_interface, signature="t",
 
 
1078
1086
            # The timeout has passed
 
 
1089
            self.expires = (datetime.datetime.utcnow()
 
 
1090
                            + datetime.timedelta(milliseconds = time_to_die))
 
1081
1091
            self.disable_initiator_tag = (gobject.timeout_add
 
1082
1092
                                          (time_to_die, self.disable))
 
 
1094
    # ExtendedTimeout - property
 
 
1095
    @dbus_service_property(_interface, signature="t",
 
 
1097
    def ExtendedTimeout_dbus_property(self, value=None):
 
 
1098
        if value is None:       # get
 
 
1099
            return dbus.UInt64(self.extended_timeout_milliseconds())
 
 
1100
        self.extended_timeout = datetime.timedelta(0, 0, 0, value)
 
1084
1102
    # Interval - property
 
1085
1103
    @dbus_service_property(_interface, signature="t",
 
1086
1104
                           access="readwrite")