1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 from common import gajim
21 import select
22 import re
23 from common.zeroconf.zeroconf import C_BARE_NAME, C_DOMAIN
24
25 try:
26 import pybonjour
27 except ImportError, e:
28 pass
29
30
31 resolve_timeout = 1
32
34 - def __init__(self, new_serviceCB, remove_serviceCB, name_conflictCB,
35 disconnected_CB, error_CB, name, host, port):
36 self.domain = None
37 self.stype = '_presence._tcp'
38 self.port = port
39 self.username = name
40 self.host = host
41 self.txt = pybonjour.TXTRecord()
42
43
44
45 self.new_serviceCB = new_serviceCB
46 self.remove_serviceCB = remove_serviceCB
47 self.name_conflictCB = name_conflictCB
48 self.disconnected_CB = disconnected_CB
49 self.error_CB = error_CB
50
51 self.contacts = {}
52 self.connected = False
53 self.announced = False
54 self.invalid_self_contact = {}
55 self.resolved = []
56
57
58 - def browse_callback(self, sdRef, flags, interfaceIndex, errorCode, serviceName, regtype, replyDomain):
59 gajim.log.debug('Found service %s in domain %s on %i(type: %s).' % (serviceName, replyDomain, interfaceIndex, regtype))
60 if not self.connected:
61 return
62 if errorCode != pybonjour.kDNSServiceErr_NoError:
63 return
64 if not (flags & pybonjour.kDNSServiceFlagsAdd):
65 self.remove_service_callback(serviceName)
66 return
67
68
69 resolve_sdRef = pybonjour.DNSServiceResolve(0, interfaceIndex, serviceName, regtype, replyDomain, self.service_resolved_callback)
70
71 try:
72 while not self.resolved:
73 ready = select.select([resolve_sdRef], [], [], resolve_timeout)
74 if resolve_sdRef not in ready[0]:
75 gajim.log.debug('Resolve timed out')
76 break
77 pybonjour.DNSServiceProcessResult(resolve_sdRef)
78 else:
79 self.resolved.pop()
80 finally:
81 resolve_sdRef.close()
82
93
94 - def new_domain_callback(self, interface, protocol, domain, flags):
95 if domain != "local":
96 self.browse_domain(interface, protocol, domain)
97
98
100 items = pybonjour.TXTRecord.parse(txt)._items
101 return dict((v[0], v[1]) for v in items.values())
102
105
106
107 escaping= {
108 r'\.': '.',
109 r'\032': ' ',
110 r'\064': '@',
111 }
112
113
114 result = re.split('(?<!\\\\)\.', fullname)
115 name = result[0]
116 protocol, domain = result[2:4]
117
118
119 for src, trg in escaping.items():
120 name = name.replace(src, trg)
121
122 txt = pybonjour.TXTRecord.parse(txtRecord)
123
124 gajim.log.debug('Service data for service %s on %i:' % (fullname, interfaceIndex))
125 gajim.log.debug('Host %s, port %i, TXT data: %s' % (hosttarget, port, txt._items))
126
127 if not self.connected:
128 return
129
130 bare_name = name
131 if '@' not in name:
132 name = name + '@' + name
133
134
135 if name != self.name:
136 self.contacts[name] = (name, domain, interfaceIndex, protocol, hosttarget, hosttarget, port, bare_name, txtRecord)
137
138 self.new_serviceCB(name)
139 else:
140
141
142
143
144 self.invalid_self_contact[name] = (name, domain, interfaceIndex, protocol, hosttarget, hosttarget, port, bare_name, txtRecord)
145
146 self.resolved.append(True)
147
148
150 if not self.connected:
151 return
152
153 escaping= {
154 r'\.': '.',
155 r'\032': ' ',
156 r'\064': '@',
157 }
158
159 name, stype, protocol, domain, dummy = fullname.split('.')
160
161
162 for src, trg in escaping.items():
163 name = name.replace(src, trg)
164
165 bare_name = name
166 if name.find('@') == -1:
167 name = name + '@' + name
168
169
170 if name != self.name:
171 self.contacts[name] = (name, domain, interfaceIndex, protocol, hosttarget, hosttarget, port, bare_name, txtRecord)
172
173
175 if errorCode == pybonjour.kDNSServiceErr_NoError:
176 gajim.log.debug('Service successfully added')
177
179 if err[0][0] == pybonjour.kDNSServiceErr_NameConflict:
180 gajim.log.debug('Error while adding service. %s' % str(err))
181 parts = self.username.split(' ')
182
183
184 try:
185 stripped = str(int(parts[-1]))
186 except Exception:
187 stripped = 1
188 alternative_name = self.username + str(stripped+1)
189 self.name_conflictCB(alternative_name)
190 return
191 self.error_CB(_('Error while adding service. %s') % str(err))
192 self.disconnect()
193
194
196 if show in ['chat', 'online', '']:
197 return 'avail'
198 elif show == 'xa':
199 return 'away'
200 return show
201
203 txt = {}
204
205
206 for key, val in self.txt:
207 if val:
208 txt[key] = val
209
210 txt['port.p2pj'] = self.port
211 txt['version'] = 1
212 txt['txtvers'] = 1
213
214
215 if 'status' in self.txt:
216 txt['status'] = self.replace_show(self.txt['status'])
217 else:
218 txt['status'] = 'avail'
219
220 self.txt = pybonjour.TXTRecord(txt, strict=True)
221
222 try:
223 sdRef = pybonjour.DNSServiceRegister(name = self.name,
224 regtype = self.stype, port = self.port, txtRecord = self.txt,
225 callBack = self.service_added_callback)
226 self.service_sdRef = sdRef
227 except pybonjour.BonjourError, e:
228 self.service_add_fail_callback(e)
229 else:
230 gajim.log.debug('Publishing service %s of type %s' % (self.name, self.stype))
231
232 ready = select.select([sdRef], [], [], resolve_timeout)
233 if sdRef in ready[0]:
234 pybonjour.DNSServiceProcessResult(sdRef)
235
237 if not self.connected:
238 return False
239
240 self.create_service()
241 self.announced = True
242 return True
243
245 if not self.announced:
246 return False
247 try:
248 self.service_sdRef.close()
249 self.announced = False
250 return True
251 except pybonjour.BonjourError, e:
252 gajim.log.debug(e)
253 return False
254
255
257 self.name = self.username + '@' + self.host
258
259 self.connected = True
260
261
262 if self.domain is None:
263
264 self.browse_domain()
265
266
267
268
269 else:
270 self.browse_domain(self.domain)
271
272 return True
273
275 if self.connected:
276 self.connected = False
277 if hasattr(self, 'browse_sdRef'):
278 self.browse_sdRef.close()
279 self.remove_announce()
280
281 - def browse_domain(self, domain=None):
282 gajim.log.debug('starting to browse')
283 try:
284 self.browse_sdRef = pybonjour.DNSServiceBrowse(regtype=self.stype, domain=domain, callBack=self.browse_callback)
285 except pybonjour.BonjourError, e:
286 self.error_CB("Error while browsing: %s" % e)
287
289 ready = select.select([self.browse_sdRef], [], [], 2)
290 if self.browse_sdRef in ready[0]:
291 pybonjour.DNSServiceProcessResult(self.browse_sdRef)
292
293
295 if not self.connected:
296 return
297
298
299 self.browse_loop()
300
301 for val in self.contacts.values():
302 resolve_sdRef = pybonjour.DNSServiceResolve(0,
303 pybonjour.kDNSServiceInterfaceIndexAny, val[C_BARE_NAME],
304 self.stype + '.', val[C_DOMAIN] + '.',
305 self.service_resolved_all_callback)
306
307 try:
308 ready = select.select([resolve_sdRef], [], [], resolve_timeout)
309 if resolve_sdRef not in ready[0]:
310 gajim.log.debug('Resolve timed out (in resolve_all)')
311 break
312 pybonjour.DNSServiceProcessResult(resolve_sdRef)
313 finally:
314 resolve_sdRef.close()
315
318
323
325 if show:
326 self.txt['status'] = self.replace_show(show)
327
328 try:
329 pybonjour.DNSServiceUpdateRecord(self.service_sdRef, None, 0, self.txt)
330 except pybonjour.BonjourError:
331 return False
332 return True
333