Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2007-05-14 01:34:15 (GMT)
committer Dan Williams <dcbw@redhat.com>2007-05-14 01:34:15 (GMT)
commit5e2ea16e3aaf3db3629164de786ad5083f600600 (patch)
tree2df4bff5ffae78d16707b203843e542128021e02 /services
parent66dfd363ba7cc169ffaeafffe6c6b302c4779eeb (diff)
Redo PS connection handling; ensure valid network connection before talking to server
Diffstat (limited to 'services')
-rw-r--r--services/presence/buddy.py183
-rw-r--r--services/presence/psutils.py183
-rw-r--r--services/presence/server_plugin.py124
3 files changed, 265 insertions, 225 deletions
diff --git a/services/presence/buddy.py b/services/presence/buddy.py
index f302b8c..200401b 100644
--- a/services/presence/buddy.py
+++ b/services/presence/buddy.py
@@ -22,6 +22,7 @@ import dbus
import dbus.service
from dbus.gobject_service import ExportedGObject
from ConfigParser import ConfigParser, NoOptionError
+import psutils
from sugar import env, profile, util
import logging
@@ -132,9 +133,19 @@ class Buddy(ExportedGObject):
logging.debug("Invalid init property '%s'; ignoring..." % key)
del kwargs[key]
+ # Set icon after superclass init, because it sends DBus and GObject
+ # signals when set
+ icon_data = None
+ if kwargs.has_key(_PROP_ICON):
+ icon_data = kwargs[_PROP_ICON]
+ del kwargs[_PROP_ICON]
+
ExportedGObject.__init__(self, bus_name, self._object_path,
gobject_properties=kwargs)
+ if icon_data:
+ self.props.icon = icon_data
+
def do_get_property(self, pspec):
"""Retrieve current value for the given property specifier
@@ -399,176 +410,6 @@ class Buddy(ExportedGObject):
except AttributeError:
self._valid = False
-
-NM_SERVICE = 'org.freedesktop.NetworkManager'
-NM_IFACE = 'org.freedesktop.NetworkManager'
-NM_IFACE_DEVICES = 'org.freedesktop.NetworkManager.Devices'
-NM_PATH = '/org/freedesktop/NetworkManager'
-
-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 __init__(self):
- gobject.GObject.__init__(self)
- self._nm_present = False
- self._matches = []
- self._addr = None
- self._nm_obj = None
-
- sys_bus = dbus.SystemBus()
- bus_object = sys_bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
- try:
- if bus_object.GetNameOwner(NM_SERVICE, dbus_interface='org.freedesktop.DBus'):
- self._nm_present = True
- except dbus.DBusException:
- pass
-
- if self._nm_present:
- self._connect_to_nm()
- else:
- 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
- logging.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:
- logging.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,
- named_service=NM_SERVICE)
- self._matches.append(match)
-
- match = sys_bus.add_signal_receiver(self._nm_state_change_cb,
- signal_name="StateChange",
- dbus_interface=NM_IFACE,
- named_service=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):
- logging.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):
- logging.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 handle_name_owner_changed(self, name, old, new):
- """Clear state when NM goes away"""
- if name != NM_SERVICE:
- return
- if (old and len(old)) and (not new and not len(new)):
- # NM went away
- self._nm_present = False
- for match in self._matches:
- match.remove()
- self._matches = []
- self._update_address(None)
- elif (not old and not len(old)) and (new and len(new)):
- # NM started up
- self._nm_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
-
class GenericOwner(Buddy):
"""Common functionality for Local User-like objects
@@ -617,7 +458,7 @@ class GenericOwner(Buddy):
signal_name="NameOwnerChanged",
dbus_interface="org.freedesktop.DBus")
- self._ip4_addr_monitor = IP4AddressMonitor()
+ self._ip4_addr_monitor = psutils.IP4AddressMonitor.get_instance()
self._ip4_addr_monitor.connect("address-changed", self._ip4_address_changed_cb)
def _ip4_address_changed_cb(self, monitor, address):
diff --git a/services/presence/psutils.py b/services/presence/psutils.py
index 76583d6..b24b1df 100644
--- a/services/presence/psutils.py
+++ b/services/presence/psutils.py
@@ -14,6 +14,9 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+import dbus, dbus.glib, gobject
+import logging
+
def bytes_to_string(bytes):
"""The function converts a D-BUS byte array provided by dbus to string format.
@@ -28,3 +31,183 @@ def bytes_to_string(bytes):
# Python string
ret = ''.join([str(item) for item in bytes])
return 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._matches = []
+ self._addr = None
+ self._nm_obj = None
+
+ sys_bus = dbus.SystemBus()
+ bus_object = sys_bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
+ try:
+ if bus_object.GetNameOwner(NM_SERVICE, dbus_interface='org.freedesktop.DBus'):
+ self._nm_present = True
+ except dbus.DBusException:
+ pass
+
+ if self._nm_present:
+ self._connect_to_nm()
+ else:
+ 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
+ logging.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:
+ logging.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,
+ named_service=NM_SERVICE)
+ self._matches.append(match)
+
+ match = sys_bus.add_signal_receiver(self._nm_state_change_cb,
+ signal_name="StateChange",
+ dbus_interface=NM_IFACE,
+ named_service=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):
+ logging.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):
+ logging.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 handle_name_owner_changed(self, name, old, new):
+ """Clear state when NM goes away"""
+ if name != NM_SERVICE:
+ return
+ if (old and len(old)) and (not new and not len(new)):
+ # NM went away
+ self._nm_present = False
+ for match in self._matches:
+ match.remove()
+ self._matches = []
+ self._update_address(None)
+ elif (not old and not len(old)) and (new and len(new)):
+ # NM started up
+ self._nm_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
diff --git a/services/presence/server_plugin.py b/services/presence/server_plugin.py
index efb97fc..63de279 100644
--- a/services/presence/server_plugin.py
+++ b/services/presence/server_plugin.py
@@ -136,11 +136,22 @@ class ServerPlugin(gobject.GObject):
self._owner.connect("icon-changed", self._owner_icon_changed_cb)
self._account = self._get_account_info()
-
- self._conn = self._init_connection()
self._conn_status = CONNECTION_STATUS_DISCONNECTED
- self._reconnect_id = 0
+ # Monitor IPv4 address as an indicator of the network connection
+ self._ip4am = psutils.IP4AddressMonitor.get_instance()
+ self._ip4am.connect('address-changed', self._ip4_address_changed_cb)
+
+ def _ip4_address_changed_cb(self, ip4am, address):
+ logging.debug("::: IP4 address now %s" % address)
+ if address:
+ logging.debug("::: valid IP4 address, conn_status %s" % self._conn_status)
+ if self._conn_status == CONNECTION_STATUS_DISCONNECTED:
+ logging.debug("::: will connect")
+ self.start()
+ else:
+ logging.debug("::: invalid IP4 address, will disconnect")
+ self.cleanup()
def _owner_property_changed_cb(self, owner, properties):
"""Local user's configuration properties have changed
@@ -229,6 +240,14 @@ class ServerPlugin(gobject.GObject):
"""Retrieve our telepathy.client.Connection object"""
return self._conn
+ def _connect_reply_cb(self):
+ """Handle connection success"""
+ pass
+
+ def _connect_error_cb(self, exception):
+ """Handle connection failure"""
+ logging.debug("Connect error: %s" % exception)
+
def _init_connection(self):
"""Set up our connection
@@ -262,7 +281,12 @@ class ServerPlugin(gobject.GObject):
conn[CONN_INTERFACE_PRESENCE].connect_to_signal('PresenceUpdate',
self._presence_update_cb)
- return conn
+ self._conn = conn
+ status = self._conn[CONN_INTERFACE].GetStatus()
+ if status == CONNECTION_STATUS_DISCONNECTED:
+ self._conn[CONN_INTERFACE].Connect(reply_handler=self._connect_reply_cb,
+ error_handler=self._connect_error_cb)
+ self._handle_connection_status_change(self._conn, status)
def _request_list_channel(self, name):
"""Request a contact-list channel from Telepathy
@@ -297,16 +321,14 @@ class ServerPlugin(gobject.GObject):
if local_pending:
# accept pending subscriptions
- #print 'pending: %r' % local_pending
publish[CHANNEL_INTERFACE_GROUP].AddMembers(local_pending, '')
- not_subscribed = list(set(publish_handles) - set(subscribe_handles))
self_handle = self._conn[CONN_INTERFACE].GetSelfHandle()
self._online_contacts[self_handle] = self._account['account']
- for handle in not_subscribed:
- # request subscriptions from people subscribed to us if we're not subscribed to them
- subscribe[CHANNEL_INTERFACE_GROUP].AddMembers([self_handle], '')
+ # request subscriptions from people subscribed to us if we're not subscribed to them
+ not_subscribed = list(set(publish_handles) - set(subscribe_handles))
+ subscribe[CHANNEL_INTERFACE_GROUP].AddMembers(not_subscribed, '')
if CONN_INTERFACE_BUDDY_INFO not in self._conn.get_valid_interfaces():
logging.debug('OLPC information not available')
@@ -334,8 +356,8 @@ class ServerPlugin(gobject.GObject):
self._set_self_avatar()
# Request presence for everyone on the channel
- self._conn[CONN_INTERFACE_PRESENCE].GetPresence(subscribe_handles,
- ignore_reply=True)
+ subscribe_handles = subscribe[CHANNEL_INTERFACE_GROUP].GetMembers()
+ self._conn[CONN_INTERFACE_PRESENCE].RequestPresence(subscribe_handles)
return True
def _set_self_avatar_cb(self, token):
@@ -504,31 +526,38 @@ class ServerPlugin(gobject.GObject):
return handle
return None
- def _status_changed_cb(self, state, reason):
- """Handle notification of connection-status change
-
- state -- CONNECTION_STATUS_*
- reason -- integer code describing the reason...
- """
- if state == CONNECTION_STATUS_CONNECTING:
- self._conn_status = state
- logging.debug("State: connecting...")
- elif state == CONNECTION_STATUS_CONNECTED:
+ def _handle_connection_status_change(self, status, reason):
+ if status == self._conn_status:
+ return
+
+ if status == CONNECTION_STATUS_CONNECTING:
+ self._conn_status = status
+ logging.debug("status: connecting...")
+ elif status == CONNECTION_STATUS_CONNECTED:
if self._connected_cb():
- logging.debug("State: connected")
- self._conn_status = state
+ logging.debug("status: connected")
+ self._conn_status = status
else:
self.cleanup()
- logging.debug("State: was connected, but an error occurred")
- elif state == CONNECTION_STATUS_DISCONNECTED:
+ logging.debug("status: was connected, but an error occurred")
+ elif status == CONNECTION_STATUS_DISCONNECTED:
self.cleanup()
- logging.debug("State: disconnected (reason %r)" % reason)
+ logging.debug("status: disconnected (reason %r)" % reason)
if reason == CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED:
# FIXME: handle connection failure; retry later?
pass
self.emit('status', self._conn_status, int(reason))
+ def _status_changed_cb(self, status, reason):
+ """Handle notification of connection-status change
+
+ status -- CONNECTION_STATUS_*
+ reason -- integer code describing the reason...
+ """
+ logging.debug("::: connection status changed to %s" % status)
+ self._handle_connection_status_change(status, reason)
+
def start(self):
"""Start up the Telepathy networking connections
@@ -541,35 +570,13 @@ class ServerPlugin(gobject.GObject):
_connect_reply_cb or _connect_error_cb
"""
logging.debug("Starting up...")
- # If the connection is already connected query initial contacts
- conn_status = self._conn[CONN_INTERFACE].GetStatus()
- if conn_status == CONNECTION_STATUS_CONNECTED:
- self._connected_cb()
- subscribe = self._request_list_channel('subscribe')
- subscribe_handles = subscribe[CHANNEL_INTERFACE_GROUP].GetMembers()
- self._conn[CONN_INTERFACE_PRESENCE].RequestPresence(subscribe_handles)
- elif conn_status == CONNECTION_STATUS_CONNECTING:
- pass
- else:
- self._conn[CONN_INTERFACE].Connect(reply_handler=self._connect_reply_cb,
- error_handler=self._connect_error_cb)
-
- def _connect_reply_cb(self):
- """Handle connection success"""
- if self._reconnect_id > 0:
- gobject.source_remove(self._reconnect_id)
-
- def _reconnect(self):
- """Reset number-of-attempted connections and re-attempt"""
- self._reconnect_id = 0
- self.start()
- return False
- def _connect_error_cb(self, exception):
- """Handle connection failure"""
- logging.debug("Connect error: %s" % exception)
- if not self._reconnect_id:
- self._reconnect_id = gobject.timeout_add(10000, self._reconnect)
+ # Only init connection if we have a valid IP address
+ if self._ip4am.props.address:
+ logging.debug("::: Have IP4 address %s, will connect" % self._ip4am.props.address)
+ self._init_connection()
+ else:
+ logging.debug("::: No IP4 address, postponing connection")
def cleanup(self):
"""If we still have a connection, disconnect it"""
@@ -578,6 +585,12 @@ class ServerPlugin(gobject.GObject):
self._conn = None
self._conn_status = CONNECTION_STATUS_DISCONNECTED
+ for handle in self._online_contacts.keys():
+ self._contact_offline(handle)
+ self._online_contacts = {}
+ self._joined_activites = []
+ self._activites = {}
+
def _contact_offline(self, handle):
"""Handle contact going offline (send message, update set)"""
if not self._online_contacts.has_key(handle):
@@ -659,6 +672,9 @@ class ServerPlugin(gobject.GObject):
timestamp, statuses = presence[handle]
online = handle in self._online_contacts
for status, params in statuses.items():
+ if not online and status == "offline":
+ # weren't online in the first place...
+ continue
jid = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
olstr = "ONLINE"
if not online: olstr = "OFFLINE"