Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/shell/PresenceService/Buddy.py
blob: c3d17e12c061de1ec5d7079d4cf777f8d9e2e3f9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
import base64
import logging

import gtk
import gobject

from sugar.p2p import Stream
from sugar.p2p import network
from sugar.presence import Service

PRESENCE_SERVICE_TYPE = "_presence_olpc._tcp"
BUDDY_DBUS_INTERFACE = "org.laptop.Presence.Buddy"

class NotFoundError(Exception):
	pass

class BuddyDBusHelper(dbus.service.Object):
	def __init__(self, parent, bus_name, object_path):
		self._parent = parent
		self._bus_name = bus_name
		self._object_path = object_path
		dbus.service.Object.__init__(self, bus_name, self._object_path)

	@dbus.service.signal(BUDDY_DBUS_INTERFACE)
	def ServiceAppeared(self, object_path):
		pass

	@dbus.service.signal(BUDDY_DBUS_INTERFACE)
	def ServiceDisappeared(self, object_path):
		pass

	@dbus.service.signal(BUDDY_DBUS_INTERFACE)
	def IconChanged(self):
		pass

	@dbus.service.signal(BUDDY_DBUS_INTERFACE)
	def JoinedActivity(self, object_path):
		pass

	@dbus.service.signal(BUDDY_DBUS_INTERFACE)
	def LeftActivity(self, object_path):
		pass

	@dbus.service.method(BUDDY_DBUS_INTERFACE,
						in_signature="", out_signature="ay")
	def getIcon(self):
		icon = self._parent.get_icon()
		if not icon:
			return ""
		return icon

	@dbus.service.method(BUDDY_DBUS_INTERFACE,
						in_signature="", out_signature="o")
	def getServiceOfType(self, stype):
		service = self._parent.get_service_of_type(stype)
		if not service:
			raise NotFoundError("Not found")
		return service

	@dbus.service.method(BUDDY_DBUS_INTERFACE,
						in_signature="", out_signature="ao")
	def getJoinedActivities(self):
		acts = []
		return acts

	@dbus.service.method(BUDDY_DBUS_INTERFACE,
						in_signature="", out_signature="a{sv}")
	def getProperties(self):
		props = {}
		props['name'] = self._parent.get_nick_name()
		props['ip4_address'] = self._parent.get_address()
		props['owner'] = self._parent.is_owner()
		return props


class Buddy(object):
	"""Represents another person on the network and keeps track of the
	activities and resources they make available for sharing."""

	def __init__(self, bus_name, object_id, service):
		if not bus_name:
			raise ValueError("DBus bus name must be valid")
		if not object_id or type(object_id) != type(1):
			raise ValueError("object id must be a valid number")

		self._services = {}
		self._activities = {}

		self._nick_name = service.get_name()
		self._address = service.get_publisher_address()
		self._valid = False
		self._icon = None
		self._icon_tries = 0
		self._owner = False
		self.add_service(service)

		self._object_id = object_id
		self._object_path = "/org/laptop/Presence/Buddies/%d" % self._object_id
		self._dbus_helper = BuddyDBusHelper(self, bus_name, self._object_path)

	def object_path(self):
		return self._object_path

	def _request_buddy_icon_cb(self, result_status, response, user_data):
		"""Callback when icon request has completed."""
		icon = response
		service = user_data
		if result_status == network.RESULT_SUCCESS:
			if icon and len(icon):
				icon = base64.b64decode(icon)
				print "Buddy icon for '%s' is size %d" % (self._nick_name, len(icon))
				self._set_icon(icon)

		if (result_status == network.RESULT_FAILED or not icon) and self._icon_tries < 3:
			self._icon_tries = self._icon_tries + 1
			print "Failed to retrieve buddy icon for '%s' on try %d of %d" % (self._nick_name, \
					self._icon_tries, 3)
			gobject.timeout_add(1000, self._request_buddy_icon, service)
		return False

	def _request_buddy_icon(self, service):
		"""Contact the buddy to retrieve the buddy icon."""
		buddy_stream = Stream.Stream.new_from_service(service, start_reader=False)
		writer = buddy_stream.new_writer(service)
		success = writer.custom_request("get_buddy_icon", self._request_buddy_icon_cb, service)
		if not success:
			del writer, buddy_stream
			gobject.timeout_add(1000, self._request_buddy_icon, service)
		return False

	def add_service(self, service):
		"""Adds a new service to this buddy's service list, returning
		True if the service was successfully added, and False if it was not."""
		if service.get_name() != self._nick_name:
			return False
		publisher_addr = service.get_publisher_address()
		if publisher_addr != self._address:
			logging.error('Service publisher and buddy address doesnt match: %s %s' % (publisher_addr, self._address))
			return False
		stype = service.get_type()
		if stype in self._services.keys():
			return False
		self._services[stype] = service
		if self._valid:
			self._dbus_helper.ServiceAppeared(dbus.ObjectPath(service.object_path()))

		# If this is the first service we've seen that's owned  by
		# a particular activity, send out the 'joined-activity' signal
		actid = service.get_activity_id()
		if actid is not None:
			found = False
			for serv in self._services.values():
				if serv.get_activity_id() == actid and serv.get_type() != stype:
					found = True
					break
			if not found:
				print "Buddy (%s) joined activity %s." % (self._nick_name, actid)
				self._dbus_helper.JoinedActivity(dbus.ObjectPath(activity.object_path()))

		if stype == PRESENCE_SERVICE_TYPE:
			# A buddy isn't valid until its official presence
			# service has been found and resolved
			self._valid = True
			print 'Requesting buddy icon %s' % self._nick_name
			self._request_buddy_icon(service)
		return True

	def remove_service(self, service):
		"""Remove a service from a buddy; ie, the activity was closed
		or the buddy went away."""
		if service.get_publisher_address() != self._address:
			return
		if service.get_name() != self._nick_name:
			return
		stype = service.get_type()
		if self._services.has_key(stype):
			if self._valid:
				self._dbus_helper.ServiceDisappeared(dbus.ObjectPath(service.object_path()))
			del self._services[stype]

		# If this is the lase service owned  by a particular activity,
		# and it's just been removed, send out the 'left-actvity' signal
		actid = service.get_activity_id()
		if actid is not None:
			found = False
			for serv in self._services.values():
				if serv.get_activity_id() == actid:
					found = True
					break
			if not found:
				print "Buddy (%s) left activity %s." % (self._nick_name, actid)
				self._dbus_helper.LeftActivity(dbus.ObjectPath(activity.object_path()))

		if stype == PRESENCE_SERVICE_TYPE:
			self._valid = False

	def get_service_of_type(self, stype=None, activity=None):
		"""Return a service of a certain type, or None if the buddy
		doesn't provide that service."""
		if not stype:
			raise RuntimeError("Need to specify a service type.")

		if activity:
			actid = activity.get_id()
			for service in self._services.values():
				if service.get_type() == stype and service.get_activity_id() == actid:
					return service
		if self._services.has_key(stype):
			return self._services[stype]
		return None

	def is_valid(self):
		"""Return whether the buddy is valid or not.  A buddy is
		not valid until its official presence service has been found
		and successfully resolved."""
		return self._valid

	def get_icon_pixbuf(self):
		if self._icon:
			pbl = gtk.gdk.PixbufLoader()
			pbl.write(self._icon)
			pbl.close()
			return pbl.get_pixbuf()
		else:
			return None

	def get_icon(self):
		"""Return the buddies icon, if any."""
		return self._icon
		
	def get_address(self):
		return self._address

	def get_nick_name(self):
		return self._nick_name

	def _set_icon(self, icon):
		"""Can only set icon for other buddies.  The Owner
		takes care of setting it's own icon."""
		if icon != self._icon:
			self._icon = icon
			self._dbus_helper.IconChanged()

	def is_owner(self):
		return self._owner


class Owner(Buddy):
	"""Class representing the owner of the machine.  This is the client
	portion of the Owner, paired with the server portion in Owner.py."""
	def __init__(self, service):
		Buddy.__init__(self, service)
		self._owner = True