Create separate plugins for connection methods

This commit is contained in:
Dan Williams 2007-02-25 19:24:48 -05:00
parent 1f91f7f7af
commit a72175ff68
6 changed files with 279 additions and 238 deletions

View File

@ -4,7 +4,9 @@ sugar_PYTHON = \
activity.py \ activity.py \
buddy.py \ buddy.py \
buddyiconcache.py \ buddyiconcache.py \
presenceservice.py linklocal_plugin.py \
presenceservice.py \
server_plugin.py
bin_SCRIPTS = sugar-presence-service2 bin_SCRIPTS = sugar-presence-service2

View File

@ -32,7 +32,7 @@ class Buddy(dbus.service.Object):
"""Represents another person on the network and keeps track of the """Represents another person on the network and keeps track of the
activities and resources they make available for sharing.""" activities and resources they make available for sharing."""
def __init__(self, bus_name, object_id, icon_cache): def __init__(self, bus_name, object_id, icon_cache, handle=None):
if not bus_name: if not bus_name:
raise ValueError("DBus bus name must be valid") raise ValueError("DBus bus name must be valid")
if not object_id or not isinstance(object_id, int): if not object_id or not isinstance(object_id, int):
@ -48,6 +48,8 @@ class Buddy(dbus.service.Object):
self._icon_cache = icon_cache self._icon_cache = icon_cache
self._handle = handle
self._nick_name = None self._nick_name = None
self._color = None self._color = None
self._key = None self._key = None

View File

@ -21,11 +21,12 @@ from telepathy.interfaces import (CONN_MGR_INTERFACE, CONN_INTERFACE)
from telepathy.constants import (CONNECTION_STATUS_CONNECTING, CONNECTION_STATUS_CONNECTED, from telepathy.constants import (CONNECTION_STATUS_CONNECTING, CONNECTION_STATUS_CONNECTED,
CONNECTION_STATUS_DISCONNECTED, CONNECTION_HANDLE_TYPE_CONTACT) CONNECTION_STATUS_DISCONNECTED, CONNECTION_HANDLE_TYPE_CONTACT)
import telepathyclient from server_plugin import ServerPlugin
from linklocal_plugin import LinkLocalPlugin
from buddy import Buddy, Owner from buddy import Buddy, Owner
from activity import Activity from activity import Activity
import buddyiconcache import buddyiconcache
from sugar import profile
_PRESENCE_SERVICE = "org.laptop.Sugar.Presence" _PRESENCE_SERVICE = "org.laptop.Sugar.Presence"
@ -60,50 +61,23 @@ class PresenceService(dbus.service.Object):
self._registry = ManagerRegistry() self._registry = ManagerRegistry()
self._registry.LoadManagers() self._registry.LoadManagers()
self._server_client = self._connect_to_server() # Set up the server connection
self._handles[self._server_client] = {} self._server_plugin = ServerPlugin(self._registry)
self._handles[self._server_plugin] = {}
# Telepathy link local connection self._server_plugin.connect('status', self._server_status_cb)
self._ll_client = None self._server_plugin.connect('contact-online', self._contact_online)
self._server_plugin.connect('contact-offline', self._contact_offline)
self._server_plugin.start()
self._server_client.connect('contact-online', self._contact_online) # Set up the link local connection
self._server_client.connect('contact-offline', self._contact_offline) self._ll_plugin = LinkLocalPlugin(self._registry)
self._server_client.run() self._handles[self._ll_plugin] = {}
dbus.service.Object.__init__(self, self._bus_name, _PRESENCE_PATH) dbus.service.Object.__init__(self, self._bus_name, _PRESENCE_PATH)
def _connect_to_server(self): def _server_status_cb(self, plugin, status):
protocol = 'jabber' pass
account = {
'account': 'blah@collabora.co.uk',
'password': 'learn',
'server': 'light.bluelinux.co.uk'
}
mgr = self._registry.GetManager('gabble')
conn = None
# Search existing connections, if any, that we might be able to use
connections = Connection.get_connections()
for item in connections:
if item[CONN_INTERFACE].GetProtocol() != protocol:
continue
if not item.object_path.startswith("/org/freedesktop/Telepathy/Connection/gabble/jabber/"):
continue
if item[CONN_INTERFACE].GetStatus() == CONNECTION_STATUS_CONNECTED:
self_name = account['account']
test_handle = item[CONN_INTERFACE].RequestHandles(CONNECTION_HANDLE_TYPE_CONTACT, [self_name])[0]
if item[CONN_INTERFACE].GetSelfHandle() != test_handle:
continue
conn = item
if not conn:
# Create a new connection
conn_bus_name, conn_object_path = \
mgr[CONN_MGR_INTERFACE].RequestConnection(protocol, account)
conn = Connection(conn_bus_name, conn_object_path)
return telepathyclient.TelepathyClient(conn)
def _contact_online(self, tp, handle, key): def _contact_online(self, tp, handle, key):
buddy = self._buddies.get(key) buddy = self._buddies.get(key)
@ -111,7 +85,7 @@ class PresenceService(dbus.service.Object):
if not buddy: if not buddy:
# we don't know yet this buddy # we don't know yet this buddy
objid = self._get_next_object_id() objid = self._get_next_object_id()
buddy = Buddy(self._bus_name, objid, self._icon_cache) buddy = Buddy(self._bus_name, objid, self._icon_cache, handle=handle)
buddy.set_key(key) buddy.set_key(key)
print "create buddy" print "create buddy"
self._buddies[key] = buddy self._buddies[key] = buddy

View File

@ -0,0 +1,225 @@
# Copyright (C) 2007, Red Hat, Inc.
# Copyright (C) 2007, Collabora Ltd.
#
# 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 gobject
from sugar import profile
from sugar import util
import logging
from telepathy.client import ConnectionManager, ManagerRegistry, Connection, Channel
from telepathy.interfaces import (
CONN_MGR_INTERFACE, CONN_INTERFACE, CHANNEL_TYPE_CONTACT_LIST, CHANNEL_INTERFACE_GROUP, CONN_INTERFACE_ALIASING,
CONN_INTERFACE_AVATARS, CONN_INTERFACE_PRESENCE)
from telepathy.constants import (
CONNECTION_HANDLE_TYPE_NONE, CONNECTION_HANDLE_TYPE_CONTACT,
CONNECTION_STATUS_CONNECTED, CONNECTION_STATUS_DISCONNECTED, CONNECTION_STATUS_CONNECTING,
CONNECTION_HANDLE_TYPE_LIST, CONNECTION_HANDLE_TYPE_CONTACT)
class ServerPlugin(gobject.GObject):
__gsignals__ = {
'contact-online': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])),
'contact-offline': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'status': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_INT]))
}
def __init__(self, registry):
gobject.GObject.__init__(self)
self._registry = registry
self._online_contacts = set()
self._account = self._get_account_info()
self._conn = self._init_connection()
def _get_account_info(self):
account_info = {'server': 'olpc.collabora.co.uk'}
pubkey = profile.get_pubkey()
khash = util.printable_hash(util._sha_data(pubkey))
account_info['account'] = "%s@%s" % (khash, account_info['server'])
account_info['password'] = profile.get_private_key_hash()
return account_info
def _get_connection(self):
protocol = 'jabber'
mgr = self._registry.GetManager('gabble')
# Search existing connections, if any, that we might be able to use
connections = Connection.get_connections()
conn = None
for item in connections:
if not item.object_path.startswith("/org/freedesktop/Telepathy/Connection/gabble/jabber/"):
continue
if item[CONN_INTERFACE].GetStatus() == CONNECTION_STATUS_DISCONNECTED:
item[CONN_INTERFACE].Disconnect()
continue
if item[CONN_INTERFACE].GetProtocol() != protocol:
continue
if item[CONN_INTERFACE].GetStatus() == CONNECTION_STATUS_CONNECTED:
self_name = self._account['account']
test_handle = item[CONN_INTERFACE].RequestHandles(CONNECTION_HANDLE_TYPE_CONTACT, [self_name])[0]
if item[CONN_INTERFACE].GetSelfHandle() != test_handle:
continue
conn = item
break
if not conn:
# Create a new connection
conn_bus_name, conn_object_path = \
mgr[CONN_MGR_INTERFACE].RequestConnection(protocol,
self._account)
conn = Connection(conn_bus_name, conn_object_path)
conn[CONN_INTERFACE].connect_to_signal('StatusChanged', self._status_changed_cb)
# hack
conn._valid_interfaces.add(CONN_INTERFACE_PRESENCE)
conn[CONN_INTERFACE_PRESENCE].connect_to_signal('PresenceUpdate',
self._presence_update_cb)
return conn
def _request_list_channel(self, name):
handle = self._conn[CONN_INTERFACE].RequestHandles(
CONNECTION_HANDLE_TYPE_LIST, [name])[0]
chan_path = self._conn[CONN_INTERFACE].RequestChannel(
CHANNEL_TYPE_CONTACT_LIST, CONNECTION_HANDLE_TYPE_LIST,
handle, True)
channel = Channel(self._conn._dbus_object._named_service, chan_path)
# hack
channel._valid_interfaces.add(CHANNEL_INTERFACE_GROUP)
return channel
def _connected_cb(self):
# the group of contacts who may receive your presence
publish = self._request_list_channel('publish')
publish_handles, local_pending, remote_pending = publish[CHANNEL_INTERFACE_GROUP].GetAllMembers()
# the group of contacts for whom you wish to receive presence
subscribe = self._request_list_channel('subscribe')
subscribe_handles = subscribe[CHANNEL_INTERFACE_GROUP].GetMembers()
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.add(self_handle)
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], '')
# hack
self._conn._valid_interfaces.add(CONN_INTERFACE_ALIASING)
if CONN_INTERFACE_ALIASING in self._conn:
aliases = self._conn[CONN_INTERFACE_ALIASING].RequestAliases(subscribe_handles)
else:
aliases = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, subscribe_handles)
#for handle, alias in zip(subscribe_handles, aliases):
# print alias
# self.buddies[handle].alias = alias
# hack
self._conn._valid_interfaces.add(CONN_INTERFACE_AVATARS)
#if CONN_INTERFACE_AVATARS in self._conn:
# #tokens = self._conn[CONN_INTERFACE_AVATARS].RequestAvatarTokens(subscribe_handles)
# #for handle, token in zip(subscribe_handles, tokens):
# for handle in subscribe_handles:
# avatar, mime_type = self._conn[CONN_INTERFACE_AVATARS].RequestAvatar(handle)
# self.buddies[handle].avatar = ''.join(map(chr, avatar))
# import gtk
# window = gtk.Window()
# window.set_title(self.buddies[handle].alias)
# loader = gtk.gdk.PixbufLoader()
# loader.write(self.buddies[handle].avatar)
# loader.close()
# image = gtk.Image()
# image.set_from_pixbuf(loader.get_pixbuf())
# window.add(image)
# window.show_all()
def _status_changed_cb(self, state, reason):
if state == CONNECTION_STATUS_CONNECTING:
print 'connecting: %r' % reason
elif state == CONNECTION_STATUS_CONNECTED:
print 'connected: %r' % reason
self.emit('status', state)
self._connected_cb()
elif state == CONNECTION_STATUS_DISCONNECTED:
print 'disconnected: %r' % reason
self.emit('status', state, int(reason))
def start(self):
# 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()
def disconnect(self):
self._conn[CONN_INTERFACE].Disconnect()
def _contact_go_offline(self, handle):
name = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
print name, "offline"
self._online_contacts.remove(handle)
self.emit("contact-offline", handle)
def _contact_go_online(self, handle):
name = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
print name, "online"
# TODO: use the OLPC interface to get the key
key = handle
self._online_contacts.add(handle)
self.emit("contact-online", handle, key)
def _presence_update_cb(self, presence):
for handle in presence:
timestamp, statuses = presence[handle]
name = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
online = handle in self._online_contacts
for status, params in statuses.items():
if not online and status in ["available", "away", "brb", "busy", "dnd", "xa"]:
self._contact_go_online(handle)
elif online and status in ["offline", "invisible"]:
self._contact_go_offline(handle)

View File

@ -1,194 +0,0 @@
import dbus.glib
import gobject
from telepathy.client import ConnectionManager, ManagerRegistry, Connection, Channel
from telepathy.interfaces import (
CONN_MGR_INTERFACE, CONN_INTERFACE, CHANNEL_TYPE_CONTACT_LIST, CHANNEL_INTERFACE_GROUP, CONN_INTERFACE_ALIASING,
CONN_INTERFACE_AVATARS, CONN_INTERFACE_PRESENCE)
from telepathy.constants import (
CONNECTION_HANDLE_TYPE_NONE, CONNECTION_HANDLE_TYPE_CONTACT,
CONNECTION_STATUS_CONNECTED, CONNECTION_STATUS_DISCONNECTED, CONNECTION_STATUS_CONNECTING,
CONNECTION_HANDLE_TYPE_LIST, CONNECTION_HANDLE_TYPE_CONTACT)
loop = None
import buddy
class TelepathyClient(gobject.GObject):
__gsignals__ = {
'contact-online':(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])),
'contact-offline':(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
}
def __init__(self, conn):
gobject.GObject.__init__(self)
self._online_contacts = set()
conn[CONN_INTERFACE].connect_to_signal('StatusChanged',
self._status_changed_cb)
# hack
conn._valid_interfaces.add(CONN_INTERFACE_PRESENCE)
conn[CONN_INTERFACE_PRESENCE].connect_to_signal('PresenceUpdate',
self._presence_update_cb)
self.conn = conn
def _request_list_channel(self, name):
handle = self.conn[CONN_INTERFACE].RequestHandles(
CONNECTION_HANDLE_TYPE_LIST, [name])[0]
chan_path = self.conn[CONN_INTERFACE].RequestChannel(
CHANNEL_TYPE_CONTACT_LIST, CONNECTION_HANDLE_TYPE_LIST,
handle, True)
channel = Channel(self.conn._dbus_object._named_service, chan_path)
# hack
channel._valid_interfaces.add(CHANNEL_INTERFACE_GROUP)
return channel
def _connected_cb(self):
# the group of contacts who may receive your presence
publish = self._request_list_channel('publish')
publish_handles, local_pending, remote_pending = publish[CHANNEL_INTERFACE_GROUP].GetAllMembers()
# the group of contacts for whom you wish to receive presence
subscribe = self._request_list_channel('subscribe')
subscribe_handles = subscribe[CHANNEL_INTERFACE_GROUP].GetMembers()
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.add(self_handle)
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], '')
# hack
self.conn._valid_interfaces.add(CONN_INTERFACE_ALIASING)
if CONN_INTERFACE_ALIASING in self.conn:
aliases = self.conn[CONN_INTERFACE_ALIASING].RequestAliases(subscribe_handles)
else:
aliases = self.conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, subscribe_handles)
#for handle, alias in zip(subscribe_handles, aliases):
# print alias
# self.buddies[handle].alias = alias
# hack
self.conn._valid_interfaces.add(CONN_INTERFACE_AVATARS)
#if CONN_INTERFACE_AVATARS in self.conn:
# #tokens = self.conn[CONN_INTERFACE_AVATARS].RequestAvatarTokens(subscribe_handles)
# #for handle, token in zip(subscribe_handles, tokens):
# for handle in subscribe_handles:
# avatar, mime_type = self.conn[CONN_INTERFACE_AVATARS].RequestAvatar(handle)
# self.buddies[handle].avatar = ''.join(map(chr, avatar))
# import gtk
# window = gtk.Window()
# window.set_title(self.buddies[handle].alias)
# loader = gtk.gdk.PixbufLoader()
# loader.write(self.buddies[handle].avatar)
# loader.close()
# image = gtk.Image()
# image.set_from_pixbuf(loader.get_pixbuf())
# window.add(image)
# window.show_all()
def _status_changed_cb(self, state, reason):
if state == CONNECTION_STATUS_CONNECTING:
print 'connecting'
elif state == CONNECTION_STATUS_CONNECTED:
print 'connected'
self._connected_cb()
elif state == CONNECTION_STATUS_DISCONNECTED:
print 'disconnected'
loop.quit()
def run(self):
# 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()
def disconnect(self):
self.conn[CONN_INTERFACE].Disconnect()
def _contact_go_offline(self, handle):
name = self.conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
print name, "offline"
self._online_contacts.remove(handle)
self.emit("contact-offline", handle)
def _contact_go_online(self, handle):
name = self.conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
print name, "online"
# TODO: use the OLPC interface to get the key
key = handle
self._online_contacts.add(handle)
self.emit("contact-online", handle, key)
def _presence_update_cb(self, presence):
for handle in presence:
timestamp, statuses = presence[handle]
name = self.conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
online = handle in self._online_contacts
for status, params in statuses.items():
if not online and status in ["available", "away", "brb", "busy", "dnd", "xa"]:
self._contact_go_online(handle)
elif online and status in ["offline", "invisible"]:
self._contact_go_offline(handle)
if __name__ == '__main__':
import logging
logging.basicConfig()
registry = ManagerRegistry()
registry.LoadManagers()
mgr = registry.GetManager('gabble')
protocol = 'jabber'
account = {
'account': 'olpc@collabora.co.uk',
'password': 'learn',
'server': 'light.bluelinux.co.uk'
}
loop = gobject.MainLoop()
conn_bus_name, conn_object_path = \
mgr[CONN_MGR_INTERFACE].RequestConnection(protocol, account)
print conn_bus_name
print conn_object_path
conn = Connection(conn_bus_name, conn_object_path)
client = TelepathyClient(conn)
try:
loop.run()
finally:
try:
#conn[CONN_INTERFACE].Disconnect()
client.disconnect()
except:
pass

View File

@ -19,6 +19,7 @@ import logging
from ConfigParser import ConfigParser from ConfigParser import ConfigParser
from sugar import env from sugar import env
from sugar import util
from sugar.graphics.xocolor import XoColor from sugar.graphics.xocolor import XoColor
class _Profile(object): class _Profile(object):
@ -26,6 +27,7 @@ class _Profile(object):
self.name = None self.name = None
self.color = None self.color = None
self.pubkey = None self.pubkey = None
self.privkey_hash = None
self._load() self._load()
def update(self): def update(self):
@ -45,6 +47,7 @@ class _Profile(object):
del cp del cp
self._load_pubkey() self._load_pubkey()
self._hash_private_key()
def _load_pubkey(self): def _load_pubkey(self):
self.pubkey = None self.pubkey = None
@ -68,6 +71,32 @@ class _Profile(object):
if not self.pubkey: if not self.pubkey:
logging.error("Error parsing public key.") logging.error("Error parsing public key.")
def _hash_private_key(self):
self.privkey_hash = None
key_path = os.path.join(env.get_profile_path(), 'owner.key')
try:
f = open(key_path, "r")
lines = f.readlines()
f.close()
except IOError, e:
logging.error("Error reading private key: %s" % e)
return
key = ""
for l in lines:
l = l.strip()
if l.startswith("-----BEGIN DSA PRIVATE KEY-----"):
continue
if l.startswith("-----END DSA PRIVATE KEY-----"):
continue
key += l
if not len(key):
logging.error("Error parsing public key.")
# hash it
key_hash = util._sha_data(key)
self.privkey_hash = util.printable_hash(key_hash)
def get_nick_name(): def get_nick_name():
return _profile.name return _profile.name
@ -78,6 +107,9 @@ def get_color():
def get_pubkey(): def get_pubkey():
return _profile.pubkey return _profile.pubkey
def get_private_key_hash():
return _profile.privkey_hash
def update(): def update():
_profile.update() _profile.update()