Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/sugar/presence/Buddy.py
blob: 65405e884708ebef6b5da16b6a09dd1946480932 (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
import base64
import logging

import pygtk
pygtk.require('2.0')
import gtk, gobject

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

PRESENCE_SERVICE_TYPE = "_presence_olpc._tcp"

class Buddy(gobject.GObject):
	"""Represents another person on the network and keeps track of the
	activities and resources they make available for sharing."""
	__gsignals__ = {
		'icon-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
						 ([])),
		'service-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
						 ([gobject.TYPE_PYOBJECT])),
		'service-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
						 ([gobject.TYPE_PYOBJECT])),
		'joined-activity': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
						 ([gobject.TYPE_STRING])),
		'left-activity': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
						 ([gobject.TYPE_STRING]))
	}

	def __init__(self, service):
		gobject.GObject.__init__(self)
		self._services = {}
		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)

	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
		full_type = service.get_full_type()
		if full_type in self._services.keys():
			return False
		self._services[full_type] = service
		if self._valid:
			self.emit("service-added", service)

		# If this is the first service we've seen that's owned  by
		# a particular activity, send out the 'joined-activity' signal
		(uid, short_stype) = Service._decompose_service_type(full_type)
		if uid is not None:
			found = False
			for serv in self._services.values():
				if serv.get_activity_uid() == uid and serv.get_full_type() != full_type:
					found = True
					break
			if not found:
				print "Buddy (%s) joined activity %s." % (self._nick_name, service.get_activity_uid())
				self.emit("joined-activity", service)

		if full_type == 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
		full_type = service.get_full_type()
		if self._services.has_key(full_type):
			if self._valid:
				self.emit("service-removed", service)
			del self._services[full_type]

		# If this is the lase service owned  by a particular activity,
		# and it's just been removed, send out the 'left-actvity' signal
		(uid, short_stype) = Service._decompose_service_type(full_type)
		if uid is not None:
			found = False
			for serv in self._services.values():
				if serv.get_activity_uid() == uid:
					found = True
					break
			if not found:
				print "Buddy (%s) left activity %s." % (self._nick_name, service.get_activity_uid())
				self.emit("left-activity", service)

		if full_type == 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."""
		short_stype = stype
		if not short_stype:
			raise RuntimeError("Need to specify a service type.")
		# Ensure we're only passed short service types
		(dec_uid, dec_stype) = Service._decompose_service_type(short_stype)
		if dec_uid:
			raise RuntimeError("Use plain service types please!")

		uid = None
		if activity:
			uid = activity.get_id()
		if uid is not None:
			for service in self._services.values():
				if service.get_type() == short_stype and service.get_activity_uid() == uid:
					return service
		if self._services.has_key(short_stype):
			return self._services[short_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.emit("icon-changed")

	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