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 import socket
32
33 from common import xmpp
34 from common import gajim
35 from common import helpers
36 from common import dataforms
37
38 from common.socks5 import Socks5Receiver
39
40
42 if 'stopped' in file_props and file_props['stopped']:
43 return False
44 if 'completed' in file_props and file_props['completed']:
45 return False
46 if 'disconnect_cb' not in file_props:
47 return False
48 return file_props['paused']
49
51 if 'stopped' in file_props and file_props['stopped']:
52 return False
53 if 'completed' in file_props and file_props['completed']:
54 return False
55 if 'started' not in file_props or not file_props['started']:
56 return False
57 if 'paused' not in file_props:
58 return True
59 return not file_props['paused']
60
62 if 'error' in file_props and file_props['error'] != 0:
63 return True
64 if 'completed' in file_props and file_props['completed']:
65 return True
66 if 'connected' in file_props and file_props['connected'] == False:
67 return True
68 if 'stopped' not in file_props or not file_props['stopped']:
69 return False
70 return True
71
72
74
77
82
84 return file_props['receiver'].jid + '/' + file_props['receiver'].resource
85
88
91
120
142
144 """
145 Inform sender that we refuse to download the file
146
147 typ is used when code = '400', in this case typ can be 'strean' for
148 invalid stream or 'profile' for invalid profile
149 """
150
151 if not self.connection or self.connected < 2:
152 return
153 iq = xmpp.Iq(to=unicode(file_props['sender']), typ='error')
154 iq.setAttr('id', file_props['request-id'])
155 if code == '400' and typ in ('stream', 'profile'):
156 name = 'bad-request'
157 text = ''
158 else:
159 name = 'forbidden'
160 text = 'Offer Declined'
161 err = xmpp.ErrorNode(code=code, typ='cancel', name=name, text=text)
162 if code == '400' and typ in ('stream', 'profile'):
163 if typ == 'stream':
164 err.setTag('no-valid-streams', namespace=xmpp.NS_SI)
165 else:
166 err.setTag('bad-profile', namespace=xmpp.NS_SI)
167 iq.addChild(node=err)
168 self.connection.send(iq)
169
171 file_props = self.files_props.get(iq_obj.getAttr('id'))
172 if not file_props:
173 return
174 if 'request-id' in file_props:
175
176 return
177 file_props['receiver'] = self._ft_get_from(iq_obj)
178 si = iq_obj.getTag('si')
179 file_tag = si.getTag('file')
180 range_tag = None
181 if file_tag:
182 range_tag = file_tag.getTag('range')
183 if range_tag:
184 offset = range_tag.getAttr('offset')
185 if offset:
186 file_props['offset'] = int(offset)
187 length = range_tag.getAttr('length')
188 if length:
189 file_props['length'] = int(length)
190 feature = si.setTag('feature')
191 if feature.getNamespace() != xmpp.NS_FEATURE:
192 return
193 form_tag = feature.getTag('x')
194 form = xmpp.DataForm(node=form_tag)
195 field = form.getField('stream-method')
196 if field.getValue() != xmpp.NS_BYTESTREAM:
197 return
198 self._send_socks5_info(file_props)
199 raise xmpp.NodeProcessed
200
202 jid = self._ft_get_from(iq_obj)
203 file_props = {'type': 'r'}
204 file_props['sender'] = jid
205 file_props['request-id'] = unicode(iq_obj.getAttr('id'))
206 si = iq_obj.getTag('si')
207 profile = si.getAttr('profile')
208 mime_type = si.getAttr('mime-type')
209 if profile != xmpp.NS_FILE:
210 self.send_file_rejection(file_props, code='400', typ='profile')
211 raise xmpp.NodeProcessed
212 feature_tag = si.getTag('feature', namespace=xmpp.NS_FEATURE)
213 if not feature_tag:
214 return
215 form_tag = feature_tag.getTag('x', namespace=xmpp.NS_DATA)
216 if not form_tag:
217 return
218 form = dataforms.ExtendForm(node=form_tag)
219 for f in form.iter_fields():
220 if f.var == 'stream-method' and f.type == 'list-single':
221 values = [o[1] for o in f.options]
222 if xmpp.NS_BYTESTREAM in values:
223 break
224 else:
225 self.send_file_rejection(file_props, code='400', typ='stream')
226 raise xmpp.NodeProcessed
227 file_tag = si.getTag('file')
228 for attribute in file_tag.getAttrs():
229 if attribute in ('name', 'size', 'hash', 'date'):
230 val = file_tag.getAttr(attribute)
231 if val is None:
232 continue
233 file_props[attribute] = val
234 file_desc_tag = file_tag.getTag('desc')
235 if file_desc_tag is not None:
236 file_props['desc'] = file_desc_tag.getData()
237
238 if mime_type is not None:
239 file_props['mime-type'] = mime_type
240 file_props['receiver'] = self._ft_get_our_jid()
241 file_props['sid'] = unicode(si.getAttr('id'))
242 file_props['transfered_size'] = []
243 gajim.socks5queue.add_file_props(self.name, file_props)
244 self.dispatch('FILE_REQUEST', (jid, file_props))
245 raise xmpp.NodeProcessed
246
248 si = iq_obj.getTag('si')
249 profile = si.getAttr('profile')
250 if profile != xmpp.NS_FILE:
251 return
252 file_props = self.files_props.get(iq_obj.getAttr('id'))
253 if not file_props:
254 return
255 jid = self._ft_get_from(iq_obj)
256 file_props['error'] = -3
257 self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, ''))
258 raise xmpp.NodeProcessed
259
261
263 """
264 Send reply to the initiator of FT that we made a connection
265 """
266 if not self.connection or self.connected < 2:
267 return
268 if streamhost is None:
269 return None
270 iq = xmpp.Iq(to=streamhost['initiator'], typ='result',
271 frm=streamhost['target'])
272 iq.setAttr('id', streamhost['id'])
273 query = iq.setTag('query', namespace=xmpp.NS_BYTESTREAM)
274 stream_tag = query.setTag('streamhost-used')
275 stream_tag.setAttr('jid', streamhost['jid'])
276 self.connection.send(iq)
277
279 """
280 Stop all active transfer to or from the given contact
281 """
282 for file_props in self.files_props.values():
283 if is_transfer_stopped(file_props):
284 continue
285 receiver_jid = unicode(file_props['receiver'])
286 if contact.get_full_jid() == receiver_jid:
287 file_props['error'] = -5
288 self.remove_transfer(file_props)
289 self.dispatch('FILE_REQUEST_ERROR', (contact.jid, file_props, ''))
290 sender_jid = unicode(file_props['sender'])
291 if contact.get_full_jid() == sender_jid:
292 file_props['error'] = -3
293 self.remove_transfer(file_props)
294
296 """
297 Stop and remove all active connections from the socks5 pool
298 """
299 for file_props in self.files_props.values():
300 self.remove_transfer(file_props, remove_from_list=False)
301 self.files_props = {}
302
304 if file_props is None:
305 return
306 self.disconnect_transfer(file_props)
307 sid = file_props['sid']
308 gajim.socks5queue.remove_file_props(self.name, sid)
309
310 if remove_from_list:
311 if 'sid' in self.files_props:
312 del(self.files_props['sid'])
313
315 if file_props is None:
316 return
317 if 'hash' in file_props:
318 gajim.socks5queue.remove_sender(file_props['hash'])
319
320 if 'streamhosts' in file_props:
321 for host in file_props['streamhosts']:
322 if 'idx' in host and host['idx'] > 0:
323 gajim.socks5queue.remove_receiver(host['idx'])
324 gajim.socks5queue.remove_sender(host['idx'])
325
327 """
328 Send iq for the present streamhosts and proxies
329 """
330 if not self.connection or self.connected < 2:
331 return
332 receiver = file_props['receiver']
333 sender = file_props['sender']
334
335 sha_str = helpers.get_auth_sha(file_props['sid'], sender, receiver)
336 file_props['sha_str'] = sha_str
337
338 port = gajim.config.get('file_transfers_port')
339 listener = gajim.socks5queue.start_listener(port, sha_str,
340 self._result_socks5_sid, file_props['sid'])
341 if not listener:
342 file_props['error'] = -5
343 self.dispatch('FILE_REQUEST_ERROR', (unicode(receiver), file_props, ''))
344 self._connect_error(unicode(receiver), file_props['sid'],
345 file_props['sid'], code=406)
346 else:
347 iq = xmpp.Iq(to=unicode(receiver), typ='set')
348 file_props['request-id'] = 'id_' + file_props['sid']
349 iq.setID(file_props['request-id'])
350 query = iq.setTag('query', namespace=xmpp.NS_BYTESTREAM)
351 query.setAttr('mode', 'plain')
352 query.setAttr('sid', file_props['sid'])
353
354 self._add_addiditional_streamhosts_to_query(query, file_props)
355 self._add_local_ips_as_streamhosts_to_query(query, file_props)
356 self._add_proxy_streamhosts_to_query(query, file_props)
357
358 self.connection.send(iq)
359
361 for host in hosts:
362 streamhost = xmpp.Node(tag='streamhost')
363 query.addChild(node=streamhost)
364 streamhost.setAttr('port', unicode(port))
365 streamhost.setAttr('host', host)
366 streamhost.setAttr('jid', sender)
367
369 try:
370 my_ips = [self.peerhost[0]]
371
372 for addr in socket.getaddrinfo(socket.gethostname(), None):
373 if not addr[4][0] in my_ips and not addr[4][0].startswith('127'):
374 my_ips.append(addr[4][0])
375
376 sender = file_props['sender']
377 port = gajim.config.get('file_transfers_port')
378 self._add_streamhosts_to_query(query, sender, port, my_ips)
379 except socket.gaierror:
380 self.dispatch('ERROR', (_('Wrong host'),
381 _('Invalid local address? :-O')))
382
384 sender = file_props['sender']
385 port = gajim.config.get('file_transfers_port')
386 ft_add_hosts_to_send = gajim.config.get('ft_add_hosts_to_send')
387 additional_hosts = []
388 if ft_add_hosts_to_send:
389 additional_hosts = [e.strip() for e in ft_add_hosts_to_send.split(',')]
390 else:
391 additional_hosts = []
392 self._add_streamhosts_to_query(query, sender, port, additional_hosts)
393
395 proxyhosts = self._get_file_transfer_proxies_from_config(file_props)
396 if proxyhosts:
397 file_props['proxy_receiver'] = unicode(file_props['receiver'])
398 file_props['proxy_sender'] = unicode(file_props['sender'])
399 file_props['proxyhosts'] = proxyhosts
400
401 for proxyhost in proxyhosts:
402 self._add_streamhosts_to_query(query, proxyhost['jid'],
403 proxyhost['port'], [proxyhost['host']])
404
406 configured_proxies = gajim.config.get_per('accounts', self.name,
407 'file_transfer_proxies')
408 shall_use_proxies = gajim.config.get_per('accounts', self.name,
409 'use_ft_proxies')
410 if shall_use_proxies and configured_proxies:
411 proxyhost_dicts = []
412 proxies = [item.strip() for item in configured_proxies.split(',')]
413 default_proxy = gajim.proxy65_manager.get_default_for_name(self.name)
414 if default_proxy:
415
416 if default_proxy in proxies:
417 proxies.remove(default_proxy)
418 proxies.insert(0, default_proxy)
419
420 for proxy in proxies:
421 (host, _port, jid) = gajim.proxy65_manager.get_proxy(proxy, self.name)
422 if not host:
423 continue
424 host_dict = {
425 'state': 0,
426 'target': unicode(file_props['receiver']),
427 'id': file_props['sid'],
428 'sid': file_props['sid'],
429 'initiator': proxy,
430 'host': host,
431 'port': unicode(_port),
432 'jid': jid
433 }
434 proxyhost_dicts.append(host_dict)
435 return proxyhost_dicts
436 else:
437 return []
438
440 """
441 Store the result of SHA message from auth
442 """
443 if sid not in self.files_props:
444 return
445 file_props = self.files_props[sid]
446 file_props['hash'] = hash_id
447 return
448
450 """
451 Called when there is an error establishing BS connection, or when
452 connection is rejected
453 """
454 if not self.connection or self.connected < 2:
455 return
456 msg_dict = {
457 404: 'Could not connect to given hosts',
458 405: 'Cancel',
459 406: 'Not acceptable',
460 }
461 msg = msg_dict[code]
462 iq = xmpp.Iq(to=to, typ='error')
463 iq.setAttr('id', _id)
464 err = iq.setTag('error')
465 err.setAttr('code', unicode(code))
466 err.setData(msg)
467 self.connection.send(iq)
468 if code == 404:
469 file_props = gajim.socks5queue.get_file_props(self.name, sid)
470 if file_props is not None:
471 self.disconnect_transfer(file_props)
472 file_props['error'] = -3
473 self.dispatch('FILE_REQUEST_ERROR', (to, file_props, msg))
474
491
492
506
508 target = unicode(iq_obj.getAttr('to'))
509 id_ = unicode(iq_obj.getAttr('id'))
510 query = iq_obj.getTag('query')
511 sid = unicode(query.getAttr('sid'))
512 file_props = gajim.socks5queue.get_file_props(self.name, sid)
513 streamhosts = []
514 for item in query.getChildren():
515 if item.getName() == 'streamhost':
516 host_dict = {
517 'state': 0,
518 'target': target,
519 'id': id_,
520 'sid': sid,
521 'initiator': self._ft_get_from(iq_obj)
522 }
523 for attr in item.getAttrs():
524 host_dict[attr] = item.getAttr(attr)
525 streamhosts.append(host_dict)
526 if file_props is None:
527 if sid in self.files_props:
528 file_props = self.files_props[sid]
529 file_props['fast'] = streamhosts
530 if file_props['type'] == 's':
531
532 if 'streamhosts' in file_props:
533 file_props['streamhosts'].extend(streamhosts)
534 else:
535 file_props['streamhosts'] = streamhosts
536 if not gajim.socks5queue.get_file_props(self.name, sid):
537 gajim.socks5queue.add_file_props(self.name, file_props)
538 gajim.socks5queue.connect_to_hosts(self.name, sid,
539 self.send_success_connect_reply, None)
540 raise xmpp.NodeProcessed
541
542 file_props['streamhosts'] = streamhosts
543 if file_props['type'] == 'r':
544 gajim.socks5queue.connect_to_hosts(self.name, sid,
545 self.send_success_connect_reply, self._connect_error)
546 raise xmpp.NodeProcessed
547
549
550
551 real_id = unicode(iq_obj.getAttr('id'))
552 if not real_id.startswith('au_'):
553 return
554 frm = self._ft_get_from(iq_obj)
555 id_ = real_id[3:]
556 if id_ in self.files_props:
557 file_props = self.files_props[id_]
558 if file_props['streamhost-used']:
559 for host in file_props['proxyhosts']:
560 if host['initiator'] == frm and 'idx' in host:
561 gajim.socks5queue.activate_proxy(host['idx'])
562 raise xmpp.NodeProcessed
563
565 frm = self._ft_get_from(iq_obj)
566 real_id = unicode(iq_obj.getAttr('id'))
567 query = iq_obj.getTag('query')
568 gajim.proxy65_manager.resolve_result(frm, query)
569
570 try:
571 streamhost = query.getTag('streamhost-used')
572 except Exception:
573 pass
574 id_ = real_id[3:]
575 if id_ in self.files_props:
576 file_props = self.files_props[id_]
577 else:
578 raise xmpp.NodeProcessed
579 if streamhost is None:
580
581 if real_id.startswith('au_'):
582 if 'streamhost-used' not in file_props or \
583 file_props['streamhost-used'] is False:
584 raise xmpp.NodeProcessed
585 if 'proxyhosts' not in file_props:
586 raise xmpp.NodeProcessed
587 for host in file_props['proxyhosts']:
588 if host['initiator'] == frm and \
589 unicode(query.getAttr('sid')) == file_props['sid']:
590 gajim.socks5queue.activate_proxy(host['idx'])
591 break
592 raise xmpp.NodeProcessed
593 jid = self._ft_get_streamhost_jid_attr(streamhost)
594 if 'streamhost-used' in file_props and \
595 file_props['streamhost-used'] is True:
596 raise xmpp.NodeProcessed
597
598 if real_id.startswith('au_'):
599 if 'stopped' in file_props and file_props['stopped']:
600 self.remove_transfer(file_props)
601 else:
602 gajim.socks5queue.send_file(file_props, self.name)
603 raise xmpp.NodeProcessed
604
605 proxy = None
606 if 'proxyhosts' in file_props:
607 for proxyhost in file_props['proxyhosts']:
608 if proxyhost['jid'] == jid:
609 proxy = proxyhost
610
611 if 'stopped' in file_props and file_props['stopped']:
612 self.remove_transfer(file_props)
613 raise xmpp.NodeProcessed
614 if proxy is not None:
615 file_props['streamhost-used'] = True
616 if 'streamhosts' not in file_props:
617 file_props['streamhosts'] = []
618 file_props['streamhosts'].append(proxy)
619 file_props['is_a_proxy'] = True
620 receiver = Socks5Receiver(gajim.idlequeue, proxy,
621 file_props['sid'], file_props)
622 gajim.socks5queue.add_receiver(self.name, receiver)
623 proxy['idx'] = receiver.queue_idx
624 gajim.socks5queue.on_success = self._proxy_auth_ok
625 raise xmpp.NodeProcessed
626
627 else:
628 gajim.socks5queue.send_file(file_props, self.name)
629 if 'fast' in file_props:
630 fasts = file_props['fast']
631 if len(fasts) > 0:
632 self._connect_error(frm, fasts[0]['id'], file_props['sid'],
633 code=406)
634
635 raise xmpp.NodeProcessed
636
638
640 return unicode(iq_obj.getFrom())
641
644
646 return file_props['receiver'].jid
647
649 return streamhost.getAttr('jid')
650