477
487
            return now < (self.last_checked_ok + self.timeout)
 
480
 
class ClientDBus(Client, dbus.service.Object):
 
 
490
def dbus_service_property(dbus_interface, signature=u"v",
 
 
491
                          access=u"readwrite", byte_arrays=False):
 
 
492
    """Decorators for marking methods of a DBusObjectWithProperties to
 
 
493
    become properties on the D-Bus.
 
 
495
    The decorated method will be called with no arguments by "Get"
 
 
496
    and with one argument by "Set".
 
 
498
    The parameters, where they are supported, are the same as
 
 
499
    dbus.service.method, except there is only "signature", since the
 
 
500
    type from Get() and the type sent to Set() is the same.
 
 
503
        func._dbus_is_property = True
 
 
504
        func._dbus_interface = dbus_interface
 
 
505
        func._dbus_signature = signature
 
 
506
        func._dbus_access = access
 
 
507
        func._dbus_name = func.__name__
 
 
508
        if func._dbus_name.endswith(u"_dbus_property"):
 
 
509
            func._dbus_name = func._dbus_name[:-14]
 
 
510
        func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
 
 
515
class DBusPropertyException(dbus.exceptions.DBusException):
 
 
516
    """A base class for D-Bus property-related exceptions
 
 
518
    def __unicode__(self):
 
 
519
        return unicode(str(self))
 
 
522
class DBusPropertyAccessException(DBusPropertyException):
 
 
523
    """A property's access permissions disallows an operation.
 
 
528
class DBusPropertyNotFound(DBusPropertyException):
 
 
529
    """An attempt was made to access a non-existing property.
 
 
534
class DBusObjectWithProperties(dbus.service.Object):
 
 
535
    """A D-Bus object with properties.
 
 
537
    Classes inheriting from this can use the dbus_service_property
 
 
538
    decorator to expose methods as D-Bus properties.  It exposes the
 
 
539
    standard Get(), Set(), and GetAll() methods on the D-Bus.
 
 
543
    def _is_dbus_property(obj):
 
 
544
        return getattr(obj, u"_dbus_is_property", False)
 
 
546
    def _get_all_dbus_properties(self):
 
 
547
        """Returns a generator of (name, attribute) pairs
 
 
549
        return ((prop._dbus_name, prop)
 
 
551
                inspect.getmembers(self, self._is_dbus_property))
 
 
553
    def _get_dbus_property(self, interface_name, property_name):
 
 
554
        """Returns a bound method if one exists which is a D-Bus
 
 
555
        property with the specified name and interface.
 
 
557
        for name in (property_name,
 
 
558
                     property_name + u"_dbus_property"):
 
 
559
            prop = getattr(self, name, None)
 
 
561
                or not self._is_dbus_property(prop)
 
 
562
                or prop._dbus_name != property_name
 
 
563
                or (interface_name and prop._dbus_interface
 
 
564
                    and interface_name != prop._dbus_interface)):
 
 
568
        raise DBusPropertyNotFound(self.dbus_object_path + u":"
 
 
569
                                   + interface_name + u"."
 
 
572
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
 
 
574
    def Get(self, interface_name, property_name):
 
 
575
        """Standard D-Bus property Get() method, see D-Bus standard.
 
 
577
        prop = self._get_dbus_property(interface_name, property_name)
 
 
578
        if prop._dbus_access == u"write":
 
 
579
            raise DBusPropertyAccessException(property_name)
 
 
581
        if not hasattr(value, u"variant_level"):
 
 
583
        return type(value)(value, variant_level=value.variant_level+1)
 
 
585
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
 
 
586
    def Set(self, interface_name, property_name, value):
 
 
587
        """Standard D-Bus property Set() method, see D-Bus standard.
 
 
589
        prop = self._get_dbus_property(interface_name, property_name)
 
 
590
        if prop._dbus_access == u"read":
 
 
591
            raise DBusPropertyAccessException(property_name)
 
 
592
        if prop._dbus_get_args_options[u"byte_arrays"]:
 
 
593
            value = dbus.ByteArray(''.join(unichr(byte)
 
 
597
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
 
 
598
                         out_signature=u"a{sv}")
 
 
599
    def GetAll(self, interface_name):
 
 
600
        """Standard D-Bus property GetAll() method, see D-Bus
 
 
603
        Note: Will not include properties with access="write".
 
 
606
        for name, prop in self._get_all_dbus_properties():
 
 
608
                and interface_name != prop._dbus_interface):
 
 
609
                # Interface non-empty but did not match
 
 
611
            # Ignore write-only properties
 
 
612
            if prop._dbus_access == u"write":
 
 
615
            if not hasattr(value, u"variant_level"):
 
 
618
            all[name] = type(value)(value, variant_level=
 
 
619
                                    value.variant_level+1)
 
 
620
        return dbus.Dictionary(all, signature=u"sv")
 
 
622
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
 
 
624
                         path_keyword='object_path',
 
 
625
                         connection_keyword='connection')
 
 
626
    def Introspect(self, object_path, connection):
 
 
627
        """Standard D-Bus method, overloaded to insert property tags.
 
 
629
        xmlstring = dbus.service.Object.Introspect(self, object_path,
 
 
632
            document = xml.dom.minidom.parseString(xmlstring)
 
 
633
            def make_tag(document, name, prop):
 
 
634
                e = document.createElement(u"property")
 
 
635
                e.setAttribute(u"name", name)
 
 
636
                e.setAttribute(u"type", prop._dbus_signature)
 
 
637
                e.setAttribute(u"access", prop._dbus_access)
 
 
639
            for if_tag in document.getElementsByTagName(u"interface"):
 
 
640
                for tag in (make_tag(document, name, prop)
 
 
642
                            in self._get_all_dbus_properties()
 
 
643
                            if prop._dbus_interface
 
 
644
                            == if_tag.getAttribute(u"name")):
 
 
645
                    if_tag.appendChild(tag)
 
 
646
                # Add the names to the return values for the
 
 
647
                # "org.freedesktop.DBus.Properties" methods
 
 
648
                if (if_tag.getAttribute(u"name")
 
 
649
                    == u"org.freedesktop.DBus.Properties"):
 
 
650
                    for cn in if_tag.getElementsByTagName(u"method"):
 
 
651
                        if cn.getAttribute(u"name") == u"Get":
 
 
652
                            for arg in cn.getElementsByTagName(u"arg"):
 
 
653
                                if (arg.getAttribute(u"direction")
 
 
655
                                    arg.setAttribute(u"name", u"value")
 
 
656
                        elif cn.getAttribute(u"name") == u"GetAll":
 
 
657
                            for arg in cn.getElementsByTagName(u"arg"):
 
 
658
                                if (arg.getAttribute(u"direction")
 
 
660
                                    arg.setAttribute(u"name", u"props")
 
 
661
            xmlstring = document.toxml(u"utf-8")
 
 
663
        except (AttributeError, xml.dom.DOMException,
 
 
664
                xml.parsers.expat.ExpatError), error:
 
 
665
            logger.error(u"Failed to override Introspection method",
 
 
670
class ClientDBus(Client, DBusObjectWithProperties):
 
481
671
    """A Client class using D-Bus
 
 
614
 
    # GetAllProperties - method
 
615
 
    @dbus.service.method(_interface, out_signature=u"a{sv}")
 
616
 
    def GetAllProperties(self):
 
618
 
        return dbus.Dictionary({
 
619
 
                dbus.String(u"name"):
 
620
 
                    dbus.String(self.name, variant_level=1),
 
621
 
                dbus.String(u"fingerprint"):
 
622
 
                    dbus.String(self.fingerprint, variant_level=1),
 
623
 
                dbus.String(u"host"):
 
624
 
                    dbus.String(self.host, variant_level=1),
 
625
 
                dbus.String(u"created"):
 
626
 
                    self._datetime_to_dbus(self.created,
 
628
 
                dbus.String(u"last_enabled"):
 
629
 
                    (self._datetime_to_dbus(self.last_enabled,
 
631
 
                     if self.last_enabled is not None
 
632
 
                     else dbus.Boolean(False, variant_level=1)),
 
633
 
                dbus.String(u"enabled"):
 
634
 
                    dbus.Boolean(self.enabled, variant_level=1),
 
635
 
                dbus.String(u"last_checked_ok"):
 
636
 
                    (self._datetime_to_dbus(self.last_checked_ok,
 
638
 
                     if self.last_checked_ok is not None
 
639
 
                     else dbus.Boolean (False, variant_level=1)),
 
640
 
                dbus.String(u"timeout"):
 
641
 
                    dbus.UInt64(self.timeout_milliseconds(),
 
643
 
                dbus.String(u"interval"):
 
644
 
                    dbus.UInt64(self.interval_milliseconds(),
 
646
 
                dbus.String(u"checker"):
 
647
 
                    dbus.String(self.checker_command,
 
649
 
                dbus.String(u"checker_running"):
 
650
 
                    dbus.Boolean(self.checker is not None,
 
652
 
                dbus.String(u"object_path"):
 
653
 
                    dbus.ObjectPath(self.dbus_object_path,
 
657
 
    # IsStillValid - method
 
658
 
    @dbus.service.method(_interface, out_signature=u"b")
 
659
 
    def IsStillValid(self):
 
660
 
        return self.still_valid()
 
662
804
    # PropertyChanged - signal
 
663
805
    @dbus.service.signal(_interface, signature=u"sv")
 
664
806
    def PropertyChanged(self, property, value):
 
668
 
    # ReceivedSecret - signal
 
669
811
    @dbus.service.signal(_interface)
 
670
 
    def ReceivedSecret(self):
 
 
680
 
    # SetChecker - method
 
681
 
    @dbus.service.method(_interface, in_signature=u"s")
 
682
 
    def SetChecker(self, checker):
 
683
 
        "D-Bus setter method"
 
684
 
        self.checker_command = checker
 
686
 
        self.PropertyChanged(dbus.String(u"checker"),
 
687
 
                             dbus.String(self.checker_command,
 
691
 
    @dbus.service.method(_interface, in_signature=u"s")
 
692
 
    def SetHost(self, host):
 
693
 
        "D-Bus setter method"
 
696
 
        self.PropertyChanged(dbus.String(u"host"),
 
697
 
                             dbus.String(self.host, variant_level=1))
 
699
 
    # SetInterval - method
 
700
 
    @dbus.service.method(_interface, in_signature=u"t")
 
701
 
    def SetInterval(self, milliseconds):
 
702
 
        self.interval = datetime.timedelta(0, 0, 0, milliseconds)
 
704
 
        self.PropertyChanged(dbus.String(u"interval"),
 
705
 
                             (dbus.UInt64(self.interval_milliseconds(),
 
709
 
    @dbus.service.method(_interface, in_signature=u"ay",
 
711
 
    def SetSecret(self, secret):
 
712
 
        "D-Bus setter method"
 
713
 
        self.secret = str(secret)
 
715
 
    # SetTimeout - method
 
716
 
    @dbus.service.method(_interface, in_signature=u"t")
 
717
 
    def SetTimeout(self, milliseconds):
 
718
 
        self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
 
720
 
        self.PropertyChanged(dbus.String(u"timeout"),
 
721
 
                             (dbus.UInt64(self.timeout_milliseconds(),
 
724
822
    # Enable - method
 
725
823
    @dbus.service.method(_interface)
 
726
824
    def Enable(self):
 
 
744
842
    def StopChecker(self):
 
745
843
        self.stop_checker()
 
 
846
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
 
847
    def name_dbus_property(self):
 
 
848
        return dbus.String(self.name)
 
 
850
    # fingerprint - property
 
 
851
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
 
852
    def fingerprint_dbus_property(self):
 
 
853
        return dbus.String(self.fingerprint)
 
 
856
    @dbus_service_property(_interface, signature=u"s",
 
 
858
    def host_dbus_property(self, value=None):
 
 
859
        if value is None:       # get
 
 
860
            return dbus.String(self.host)
 
 
863
        self.PropertyChanged(dbus.String(u"host"),
 
 
864
                             dbus.String(value, variant_level=1))
 
 
867
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
 
868
    def created_dbus_property(self):
 
 
869
        return dbus.String(self._datetime_to_dbus(self.created))
 
 
871
    # last_enabled - property
 
 
872
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
 
873
    def last_enabled_dbus_property(self):
 
 
874
        if self.last_enabled is None:
 
 
875
            return dbus.String(u"")
 
 
876
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
 
 
879
    @dbus_service_property(_interface, signature=u"b",
 
 
881
    def enabled_dbus_property(self, value=None):
 
 
882
        if value is None:       # get
 
 
883
            return dbus.Boolean(self.enabled)
 
 
889
    # last_checked_ok - property
 
 
890
    @dbus_service_property(_interface, signature=u"s",
 
 
892
    def last_checked_ok_dbus_property(self, value=None):
 
 
893
        if value is not None:
 
 
896
        if self.last_checked_ok is None:
 
 
897
            return dbus.String(u"")
 
 
898
        return dbus.String(self._datetime_to_dbus(self
 
 
902
    @dbus_service_property(_interface, signature=u"t",
 
 
904
    def timeout_dbus_property(self, value=None):
 
 
905
        if value is None:       # get
 
 
906
            return dbus.UInt64(self.timeout_milliseconds())
 
 
907
        self.timeout = datetime.timedelta(0, 0, 0, value)
 
 
909
        self.PropertyChanged(dbus.String(u"timeout"),
 
 
910
                             dbus.UInt64(value, variant_level=1))
 
 
911
        if getattr(self, u"disable_initiator_tag", None) is None:
 
 
914
        gobject.source_remove(self.disable_initiator_tag)
 
 
915
        self.disable_initiator_tag = None
 
 
917
                       _timedelta_to_milliseconds((self
 
 
923
            # The timeout has passed
 
 
926
            self.disable_initiator_tag = (gobject.timeout_add
 
 
927
                                          (time_to_die, self.disable))
 
 
929
    # interval - property
 
 
930
    @dbus_service_property(_interface, signature=u"t",
 
 
932
    def interval_dbus_property(self, value=None):
 
 
933
        if value is None:       # get
 
 
934
            return dbus.UInt64(self.interval_milliseconds())
 
 
935
        self.interval = datetime.timedelta(0, 0, 0, value)
 
 
937
        self.PropertyChanged(dbus.String(u"interval"),
 
 
938
                             dbus.UInt64(value, variant_level=1))
 
 
939
        if getattr(self, u"checker_initiator_tag", None) is None:
 
 
941
        # Reschedule checker run
 
 
942
        gobject.source_remove(self.checker_initiator_tag)
 
 
943
        self.checker_initiator_tag = (gobject.timeout_add
 
 
944
                                      (value, self.start_checker))
 
 
945
        self.start_checker()    # Start one now, too
 
 
948
    @dbus_service_property(_interface, signature=u"s",
 
 
950
    def checker_dbus_property(self, value=None):
 
 
951
        if value is None:       # get
 
 
952
            return dbus.String(self.checker_command)
 
 
953
        self.checker_command = value
 
 
955
        self.PropertyChanged(dbus.String(u"checker"),
 
 
956
                             dbus.String(self.checker_command,
 
 
959
    # checker_running - property
 
 
960
    @dbus_service_property(_interface, signature=u"b",
 
 
962
    def checker_running_dbus_property(self, value=None):
 
 
963
        if value is None:       # get
 
 
964
            return dbus.Boolean(self.checker is not None)
 
 
970
    # object_path - property
 
 
971
    @dbus_service_property(_interface, signature=u"o", access=u"read")
 
 
972
    def object_path_dbus_property(self):
 
 
973
        return self.dbus_object_path # is already a dbus.ObjectPath
 
 
976
    @dbus_service_property(_interface, signature=u"ay",
 
 
977
                           access=u"write", byte_arrays=True)
 
 
978
    def secret_dbus_property(self, value):
 
 
979
        self.secret = str(value)