1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 import logging
21 log = logging.getLogger('gajim.c.z.zeroconf_avahi')
22
23 try:
24 import dbus.glib
25 except ImportError, e:
26 pass
27
28 from common.zeroconf.zeroconf import C_BARE_NAME, C_INTERFACE, C_PROTOCOL, C_DOMAIN
29
31 - def __init__(self, new_serviceCB, remove_serviceCB, name_conflictCB,
32 disconnected_CB, error_CB, name, host, port):
33 self.avahi = None
34 self.domain = None
35 self.stype = '_presence._tcp'
36 self.port = port
37 self.username = name
38 self.host = host
39 self.txt = {}
40
41
42
43 self.new_serviceCB = new_serviceCB
44 self.remove_serviceCB = remove_serviceCB
45 self.name_conflictCB = name_conflictCB
46 self.disconnected_CB = disconnected_CB
47 self.error_CB = error_CB
48
49 self.service_browser = None
50 self.domain_browser = None
51 self.bus = None
52 self.server = None
53 self.contacts = {}
54 self.entrygroup = None
55 self.connected = False
56 self.announced = False
57 self.invalid_self_contact = {}
58
59
60
64
66 log.debug('Error while resolving: ' + str(err))
67
69 log.debug(str(err))
70
71 if str(err) != 'Timeout reached':
72 self.disconnect()
73 self.disconnected_CB()
74
77 log.debug('Found service %s in domain %s on %i.%i.' % (name, domain,
78 interface, protocol))
79 if not self.connected:
80 return
81
82
83 self.server.ResolveService( int(interface), int(protocol), name, stype,
84 domain, self.avahi.PROTO_UNSPEC, dbus.UInt32(0),
85 reply_handler=self.service_resolved_callback,
86 error_handler=self.error_callback1)
87
100
102
103 if self.service_browser:
104 return
105
106 object_path = self.server.ServiceBrowserNew(interface, protocol, \
107 stype, domain, dbus.UInt32(0))
108
109 self.service_browser = dbus.Interface(self.bus.get_object(
110 self.avahi.DBUS_NAME, object_path),
111 self.avahi.DBUS_INTERFACE_SERVICE_BROWSER)
112 self.service_browser.connect_to_signal('ItemNew',
113 self.new_service_callback)
114 self.service_browser.connect_to_signal('ItemRemove',
115 self.remove_service_callback)
116 self.service_browser.connect_to_signal('Failure', self.error_callback)
117
118 - def new_domain_callback(self, interface, protocol, domain, flags):
119 if domain != 'local':
120 self.browse_domain(interface, protocol, domain)
121
123 txt_dict = {}
124 for els in txt_array:
125 key, val = '', None
126 for c in els:
127 c = chr(c)
128 if val is None:
129 if c == '=':
130 val = ''
131 else:
132 key += c
133 else:
134 val += c
135 if val is None:
136 val = ''
137 txt_dict[key] = val.decode('utf-8', 'ignore')
138 return txt_dict
139
140 - def service_resolved_callback(self, interface, protocol, name, stype, domain,
141 host, aprotocol, address, port, txt, flags):
142 log.debug('Service data for service %s in domain %s on %i.%i:'
143 % (name, domain, interface, protocol))
144 log.debug('Host %s (%s), port %i, TXT data: %s' % (host, address,
145 port, self.txt_array_to_dict(txt)))
146 if not self.connected:
147 return
148 bare_name = name
149 if name.find('@') == -1:
150 name = name + '@' + name
151
152
153 if name != self.name:
154 self.contacts[name] = (name, domain, interface, protocol, host,
155 address, port, bare_name, txt)
156 self.new_serviceCB(name)
157 else:
158
159
160
161
162 self.invalid_self_contact[name] = (name, domain, interface, protocol,
163 host, address, port, bare_name, txt)
164
165
166
167 - def service_resolved_all_callback(self, interface, protocol, name, stype,
168 domain, host, aprotocol, address, port, txt, flags):
176
178 log.debug('Service successfully added')
179
181 log.debug('Service successfully committed')
182
184 log.debug('Service successfully updated')
185
187 log.debug('Error while adding service. %s' % str(err))
188 if 'Local name collision' in str(err):
189 alternative_name = self.server.GetAlternativeServiceName(self.username)
190 self.name_conflictCB(alternative_name)
191 return
192 self.error_CB(_('Error while adding service. %s') % str(err))
193 self.disconnect()
194
196 log.debug('server state changed to %s' % state)
197 if state == self.avahi.SERVER_RUNNING:
198 self.create_service()
199 elif state in (self.avahi.SERVER_COLLISION,
200 self.avahi.SERVER_REGISTERING):
201 self.disconnect()
202 self.entrygroup.Reset()
203 elif state == self.avahi.CLIENT_FAILURE:
204
205 log.debug('CLIENT FAILURE')
206
208
209 if state == self.avahi.ENTRY_GROUP_COLLISION:
210 log.debug('zeroconf.py: local name collision')
211 self.service_add_fail_callback('Local name collision')
212 elif state == self.avahi.ENTRY_GROUP_FAILURE:
213 self.disconnect()
214 self.entrygroup.Reset()
215 log.debug('zeroconf.py: ENTRY_GROUP_FAILURE reached(that'
216 ' should not happen)')
217
218
220 if show in ['chat', 'online', '']:
221 return 'avail'
222 elif show == 'xa':
223 return 'away'
224 return show
225
227 utf8_dict = {}
228 for key in self.txt:
229 val = self.txt[key]
230 if isinstance(val, unicode):
231 utf8_dict[key] = val.encode('utf-8')
232 else:
233 utf8_dict[key] = val
234 return self.avahi.dict_to_txt_array(utf8_dict)
235
237 try:
238 if not self.entrygroup:
239
240 self.entrygroup = dbus.Interface(self.bus.get_object(
241 self.avahi.DBUS_NAME, self.server.EntryGroupNew()),
242 self.avahi.DBUS_INTERFACE_ENTRY_GROUP)
243 self.entrygroup.connect_to_signal('StateChanged',
244 self.entrygroup_state_changed_callback)
245
246 txt = {}
247
248
249 for key, val in self.txt.iteritems():
250 if val:
251 txt[key] = val
252
253 txt['port.p2pj'] = self.port
254 txt['version'] = 1
255 txt['txtvers'] = 1
256
257
258 if 'status' in self.txt:
259 txt['status'] = self.replace_show(self.txt['status'])
260 else:
261 txt['status'] = 'avail'
262
263 self.txt = txt
264 log.debug('Publishing service %s of type %s' % (self.name,
265 self.stype))
266 self.entrygroup.AddService(self.avahi.IF_UNSPEC,
267 self.avahi.PROTO_UNSPEC, dbus.UInt32(0), self.name, self.stype, '',
268 '', dbus.UInt16(self.port), self.avahi_txt(),
269 reply_handler=self.service_added_callback,
270 error_handler=self.service_add_fail_callback)
271
272 self.entrygroup.Commit(reply_handler=self.service_committed_callback,
273 error_handler=self.entrygroup_commit_error_CB)
274
275 return True
276
277 except dbus.DBusException, e:
278 log.debug(str(e))
279 return False
280
282 if not self.connected:
283 return False
284
285 state = self.server.GetState()
286 if state == self.avahi.SERVER_RUNNING:
287 self.create_service()
288 self.announced = True
289 return True
290
292 if self.announced == False:
293 return False
294 try:
295 if self.entrygroup.GetState() != self.avahi.ENTRY_GROUP_FAILURE:
296 self.entrygroup.Reset()
297 self.entrygroup.Free()
298
299 self.entrygroup._obj._bus = None
300 self.entrygroup._obj = None
301 self.entrygroup = None
302 self.announced = False
303
304 return True
305 else:
306 return False
307 except dbus.DBusException:
308 log.debug("Can't remove service. That should not happen")
309
310 - def browse_domain(self, interface, protocol, domain):
311 self.new_service_type(interface, protocol, self.stype, domain, '')
312
314 if connect != "":
315 log.debug('Lost connection to avahi-daemon')
316 self.disconnect()
317 if self.disconnected_CB:
318 self.disconnected_CB()
319 else:
320 log.debug('We are connected to avahi-daemon')
321
322
324 try:
325 import dbus
326 except ImportError:
327 log.debug('Error: python-dbus needs to be installed. No '
328 'zeroconf support.')
329 return False
330 if self.bus:
331 return True
332 try:
333 self.bus = dbus.SystemBus()
334 self.bus.add_signal_receiver(self.avahi_dbus_connect_cb,
335 'NameOwnerChanged', 'org.freedesktop.DBus',
336 arg0='org.freedesktop.Avahi')
337 except Exception, e:
338
339 self.bus = None
340 log.debug(str(e))
341 return False
342 else:
343 return True
344
345
347 if not self.connect_dbus():
348 return False
349 try:
350 import avahi
351 self.avahi = avahi
352 except ImportError:
353 log.debug('Error: python-avahi needs to be installed. No '
354 'zeroconf support.')
355 return False
356
357 if self.server:
358 return True
359 try:
360 self.server = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME,
361 self.avahi.DBUS_PATH_SERVER), self.avahi.DBUS_INTERFACE_SERVER)
362 self.server.connect_to_signal('StateChanged',
363 self.server_state_changed_callback)
364 except Exception, e:
365
366 self.server = None
367 log.debug(str(e))
368 return False
369 else:
370 return True
371
373 self.name = self.username + '@' + self.host
374 if not self.connect_avahi():
375 return False
376
377 self.connected = True
378
379 if self.domain is None:
380
381 self.browse_domain(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC,
382 'local')
383
384
385 self.domain_browser = dbus.Interface(self.bus.get_object(
386 self.avahi.DBUS_NAME, self.server.DomainBrowserNew(
387 self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, '',
388 self.avahi.DOMAIN_BROWSER_BROWSE, dbus.UInt32(0))),
389 self.avahi.DBUS_INTERFACE_DOMAIN_BROWSER)
390 self.domain_browser.connect_to_signal('ItemNew',
391 self.new_domain_callback)
392 self.domain_browser.connect_to_signal('Failure', self.error_callback)
393 else:
394 self.browse_domain(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC,
395 self.domain)
396
397 return True
398
400 if self.connected:
401 self.connected = False
402 if self.service_browser:
403 self.service_browser.Free()
404 self.service_browser._obj._bus = None
405 self.service_browser._obj = None
406 if self.domain_browser:
407 self.domain_browser.Free()
408 self.domain_browser._obj._bus = None
409 self.domain_browser._obj = None
410 self.remove_announce()
411 self.server._obj._bus = None
412 self.server._obj = None
413 self.server = None
414 self.service_browser = None
415 self.domain_browser = None
416
417
427
430
435
437 if show:
438 self.txt['status'] = self.replace_show(show)
439
440 txt = self.avahi_txt()
441 if self.connected and self.entrygroup:
442 self.entrygroup.UpdateServiceTxt(self.avahi.IF_UNSPEC,
443 self.avahi.PROTO_UNSPEC, dbus.UInt32(0), self.name, self.stype, '',
444 txt, reply_handler=self.service_updated_callback,
445 error_handler=self.error_callback)
446 return True
447 else:
448 return False
449
450
451
452