Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/psutils.py
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2007-06-06 15:20:00 (GMT)
committer Simon McVittie <simon.mcvittie@collabora.co.uk>2007-06-06 15:20:00 (GMT)
commit72ffc806b07d6bd0d3ba42d6dc2f55b798fc93af (patch)
tree80f348b2f8682116d958c38019edb70cbe6de5fb /src/psutils.py
parent947cd5b251f9f67fc6c48614a3be871a1514a7fb (diff)
Copy sugar services/presence/ to presence-service src/
Diffstat (limited to 'src/psutils.py')
-rw-r--r--src/psutils.py259
1 files changed, 259 insertions, 0 deletions
diff --git a/src/psutils.py b/src/psutils.py
new file mode 100644
index 0000000..25b24b9
--- /dev/null
+++ b/src/psutils.py
@@ -0,0 +1,259 @@
+# Copyright (C) 2007, Red Hat, Inc.
+# Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import logging
+from string import ascii_letters, digits
+try:
+ from hashlib import sha1
+except ImportError:
+ # Python < 2.5
+ from sha import new as sha1
+
+import dbus
+import gobject
+
+
+_logger = logging.getLogger('s-p-s.psutils')
+
+_ASCII_ALNUM = ascii_letters + digits
+
+
+def pubkey_to_keyid(key):
+ """Return the key ID for the given public key. This is currently its SHA-1
+ in hex.
+
+ :Parameters:
+ `key` : str
+ The public key as a Base64 string
+ :Returns:
+ The key ID as a string of hex digits
+ """
+ return sha1(key).hexdigest()
+
+
+def escape_identifier(identifier):
+ """Escape the given string to be a valid D-Bus object path or service
+ name component, using a reversible encoding to ensure uniqueness.
+
+ The reversible encoding is as follows:
+
+ * The empty string becomes '_'
+ * Otherwise, each non-alphanumeric character is replaced by '_' plus
+ two lower-case hex digits; the same replacement is carried out on
+ the first character, if it's a digit
+ """
+ # '' -> '_'
+ if not identifier:
+ return '_'
+
+ # A bit of a fast path for strings which are already OK.
+ # We deliberately omit '_' because, for reversibility, that must also
+ # be escaped.
+ if (identifier.strip(_ASCII_ALNUM) == '' and
+ identifier[0] in ascii_letters):
+ return identifier
+
+ # The first character may not be a digit
+ if identifier[0] not in ascii_letters:
+ ret = ['_%02x' % ord(identifier[0])]
+ else:
+ ret = [identifier[0]]
+
+ # Subsequent characters may be digits or ASCII letters
+ for c in identifier[1:]:
+ if c in _ASCII_ALNUM:
+ ret.append(c)
+ else:
+ ret.append('_%02x' % ord(c))
+
+ return ''.join(ret)
+
+
+NM_SERVICE = 'org.freedesktop.NetworkManager'
+NM_IFACE = 'org.freedesktop.NetworkManager'
+NM_IFACE_DEVICES = 'org.freedesktop.NetworkManager.Devices'
+NM_PATH = '/org/freedesktop/NetworkManager'
+
+_ip4am = None
+
+class IP4AddressMonitor(gobject.GObject):
+ """This class, and direct buddy IPv4 address access, will go away quite soon"""
+
+ __gsignals__ = {
+ 'address-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT]))
+ }
+
+ __gproperties__ = {
+ 'address' : (str, None, None, None, gobject.PARAM_READABLE)
+ }
+
+ def get_instance():
+ """Retrieve (or create) the IP4Address monitor singleton instance"""
+ global _ip4am
+ if not _ip4am:
+ _ip4am = IP4AddressMonitor()
+ return _ip4am
+ get_instance = staticmethod(get_instance)
+
+ def __init__(self):
+ gobject.GObject.__init__(self)
+ self._nm_present = False
+ self._nm_has_been_present = False
+ self._matches = []
+ self._addr = None
+ self._nm_obj = None
+
+ sys_bus = dbus.SystemBus()
+ self._watch = sys_bus.watch_name_owner(NM_SERVICE, self._nm_owner_cb)
+ if not sys_bus.name_has_owner(NM_SERVICE):
+ addr = self._get_address_fallback()
+ self._update_address(addr)
+
+ def do_get_property(self, pspec):
+ if pspec.name == "address":
+ return self._addr
+
+ def _update_address(self, new_addr):
+ if new_addr == "0.0.0.0":
+ new_addr = None
+ if new_addr == self._addr:
+ return
+
+ self._addr = new_addr
+ _logger.debug("IP4 address now '%s'" % new_addr)
+ self.emit('address-changed', new_addr)
+
+ def _connect_to_nm(self):
+ """Connect to NM device state signals to tell when the IPv4 address changes"""
+ try:
+ sys_bus = dbus.SystemBus()
+ proxy = sys_bus.get_object(NM_SERVICE, NM_PATH)
+ self._nm_obj = dbus.Interface(proxy, NM_IFACE)
+ except dbus.DBusException, err:
+ _logger.debug("Error finding NetworkManager: %s" % err)
+ self._nm_present = False
+ return
+
+ sys_bus = dbus.SystemBus()
+ match = sys_bus.add_signal_receiver(self._nm_device_active_cb,
+ signal_name="DeviceNowActive",
+ dbus_interface=NM_IFACE)
+ self._matches.append(match)
+
+ match = sys_bus.add_signal_receiver(self._nm_device_no_longer_active_cb,
+ signal_name="DeviceNoLongerActive",
+ dbus_interface=NM_IFACE,
+ bus_name=NM_SERVICE)
+ self._matches.append(match)
+
+ match = sys_bus.add_signal_receiver(self._nm_state_change_cb,
+ signal_name="StateChange",
+ dbus_interface=NM_IFACE,
+ bus_name=NM_SERVICE)
+ self._matches.append(match)
+
+ state = self._nm_obj.state()
+ if state == 3: # NM_STATE_CONNECTED
+ self._query_devices()
+
+ def _device_properties_cb(self, *props):
+ active = props[4]
+ if not active:
+ return
+ act_stage = props[5]
+ # HACK: OLPC NM has an extra stage, so activated == 8 on OLPC
+ # but 7 everywhere else
+ if act_stage != 8 and act_stage != 7:
+ # not activated
+ return
+ self._update_address(props[6])
+
+ def _device_properties_error_cb(self, err):
+ _logger.debug("Error querying device properties: %s" % err)
+
+ def _query_device_properties(self, device):
+ sys_bus = dbus.SystemBus()
+ proxy = sys_bus.get_object(NM_SERVICE, device)
+ dev = dbus.Interface(proxy, NM_IFACE_DEVICES)
+ dev.getProperties(reply_handler=self._device_properties_cb,
+ error_handler=self._device_properties_error_cb)
+
+ def _get_devices_cb(self, ops):
+ """Query each device's properties"""
+ for op in ops:
+ self._query_device_properties(op)
+
+ def _get_devices_error_cb(self, err):
+ _logger.debug("Error getting NetworkManager devices: %s" % err)
+
+ def _query_devices(self):
+ """Query NM for a list of network devices"""
+ self._nm_obj.getDevices(reply_handler=self._get_devices_cb,
+ error_handler=self._get_devices_error_cb)
+
+ def _nm_device_active_cb(self, device, ssid=None):
+ self._query_device_properties(device)
+
+ def _nm_device_no_longer_active_cb(self, device):
+ self._update_address(None)
+
+ def _nm_state_change_cb(self, new_state):
+ if new_state == 4: # NM_STATE_DISCONNECTED
+ self._update_address(None)
+
+ def _nm_owner_cb(self, unique_name):
+ """Clear state when NM goes away"""
+ if unique_name == '':
+ # NM went away, or isn't there at all
+ self._nm_present = False
+ for match in self._matches:
+ match.remove()
+ self._matches = []
+ if self._nm_has_been_present:
+ self._update_address(None)
+ else:
+ addr = self._get_address_fallback()
+ self._update_address(addr)
+ elif not self._nm_present:
+ # NM started up
+ self._nm_present = True
+ self._nm_has_been_present = True
+ self._connect_to_nm()
+
+ def _get_iface_address(self, iface):
+ import socket
+ import fcntl
+ import struct
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ fd = s.fileno()
+ SIOCGIFADDR = 0x8915
+ addr = fcntl.ioctl(fd, SIOCGIFADDR, struct.pack('256s', iface[:15]))[20:24]
+ s.close()
+ return socket.inet_ntoa(addr)
+
+ def _get_address_fallback(self):
+ import commands
+ (s, o) = commands.getstatusoutput("/sbin/route -n")
+ if s != 0:
+ return
+ for line in o.split('\n'):
+ fields = line.split(" ")
+ if fields[0] == "0.0.0.0":
+ iface = fields[len(fields) - 1]
+ return self._get_iface_address(iface)
+ return None