| 1 | ## |
|---|
| 2 | ## Copyright (C) 2006 Gajim Team |
|---|
| 3 | ## |
|---|
| 4 | ## Contributors for this file: |
|---|
| 5 | ## - Yann Le Boulanger <asterix@lagaule.org> |
|---|
| 6 | ## - Nikos Kouremenos <kourem@gmail.com> |
|---|
| 7 | ## - Dimitur Kirov <dkirov@gmail.com> |
|---|
| 8 | ## - Travis Shirk <travis@pobox.com> |
|---|
| 9 | ## |
|---|
| 10 | ## This program is free software; you can redistribute it and/or modify |
|---|
| 11 | ## it under the terms of the GNU General Public License as published |
|---|
| 12 | ## by the Free Software Foundation; version 2 only. |
|---|
| 13 | ## |
|---|
| 14 | ## This program is distributed in the hope that it will be useful, |
|---|
| 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 17 | ## GNU General Public License for more details. |
|---|
| 18 | ## |
|---|
| 19 | |
|---|
| 20 | import os |
|---|
| 21 | import base64 |
|---|
| 22 | import sha |
|---|
| 23 | import socket |
|---|
| 24 | import sys |
|---|
| 25 | |
|---|
| 26 | from time import (altzone, daylight, gmtime, localtime, mktime, strftime, |
|---|
| 27 | time as time_time, timezone, tzname) |
|---|
| 28 | from calendar import timegm |
|---|
| 29 | |
|---|
| 30 | import socks5 |
|---|
| 31 | import common.xmpp |
|---|
| 32 | |
|---|
| 33 | from common import GnuPG |
|---|
| 34 | from common import helpers |
|---|
| 35 | from common import gajim |
|---|
| 36 | from common import atom |
|---|
| 37 | from common import exceptions |
|---|
| 38 | from common.commands import ConnectionCommands |
|---|
| 39 | from common.pubsub import ConnectionPubSub |
|---|
| 40 | |
|---|
| 41 | STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd', |
|---|
| 42 | 'invisible'] |
|---|
| 43 | # kind of events we can wait for an answer |
|---|
| 44 | VCARD_PUBLISHED = 'vcard_published' |
|---|
| 45 | VCARD_ARRIVED = 'vcard_arrived' |
|---|
| 46 | AGENT_REMOVED = 'agent_removed' |
|---|
| 47 | METACONTACTS_ARRIVED = 'metacontacts_arrived' |
|---|
| 48 | PRIVACY_ARRIVED = 'privacy_arrived' |
|---|
| 49 | HAS_IDLE = True |
|---|
| 50 | try: |
|---|
| 51 | import idle |
|---|
| 52 | except: |
|---|
| 53 | gajim.log.debug(_('Unable to load idle module')) |
|---|
| 54 | HAS_IDLE = False |
|---|
| 55 | |
|---|
| 56 | class ConnectionBytestream: |
|---|
| 57 | def __init__(self): |
|---|
| 58 | self.files_props = {} |
|---|
| 59 | |
|---|
| 60 | def is_transfer_stoped(self, file_props): |
|---|
| 61 | if file_props.has_key('error') and file_props['error'] != 0: |
|---|
| 62 | return True |
|---|
| 63 | if file_props.has_key('completed') and file_props['completed']: |
|---|
| 64 | return True |
|---|
| 65 | if file_props.has_key('connected') and file_props['connected'] == False: |
|---|
| 66 | return True |
|---|
| 67 | if not file_props.has_key('stopped') or not file_props['stopped']: |
|---|
| 68 | return False |
|---|
| 69 | return True |
|---|
| 70 | |
|---|
| 71 | def send_success_connect_reply(self, streamhost): |
|---|
| 72 | ''' send reply to the initiator of FT that we |
|---|
| 73 | made a connection |
|---|
| 74 | ''' |
|---|
| 75 | if streamhost is None: |
|---|
| 76 | return None |
|---|
| 77 | iq = common.xmpp.Iq(to = streamhost['initiator'], typ = 'result', |
|---|
| 78 | frm = streamhost['target']) |
|---|
| 79 | iq.setAttr('id', streamhost['id']) |
|---|
| 80 | query = iq.setTag('query') |
|---|
| 81 | query.setNamespace(common.xmpp.NS_BYTESTREAM) |
|---|
| 82 | stream_tag = query.setTag('streamhost-used') |
|---|
| 83 | stream_tag.setAttr('jid', streamhost['jid']) |
|---|
| 84 | self.connection.send(iq) |
|---|
| 85 | |
|---|
| 86 | def remove_transfers_for_contact(self, contact): |
|---|
| 87 | ''' stop all active transfer for contact ''' |
|---|
| 88 | for file_props in self.files_props.values(): |
|---|
| 89 | if self.is_transfer_stoped(file_props): |
|---|
| 90 | continue |
|---|
| 91 | receiver_jid = unicode(file_props['receiver']).split('/')[0] |
|---|
| 92 | if contact.jid == receiver_jid: |
|---|
| 93 | file_props['error'] = -5 |
|---|
| 94 | self.remove_transfer(file_props) |
|---|
| 95 | self.dispatch('FILE_REQUEST_ERROR', (contact.jid, file_props, '')) |
|---|
| 96 | sender_jid = unicode(file_props['sender']).split('/')[0] |
|---|
| 97 | if contact.jid == sender_jid: |
|---|
| 98 | file_props['error'] = -3 |
|---|
| 99 | self.remove_transfer(file_props) |
|---|
| 100 | |
|---|
| 101 | def remove_all_transfers(self): |
|---|
| 102 | ''' stops and removes all active connections from the socks5 pool ''' |
|---|
| 103 | for file_props in self.files_props.values(): |
|---|
| 104 | self.remove_transfer(file_props, remove_from_list = False) |
|---|
| 105 | del(self.files_props) |
|---|
| 106 | self.files_props = {} |
|---|
| 107 | |
|---|
| 108 | def remove_transfer(self, file_props, remove_from_list = True): |
|---|
| 109 | if file_props is None: |
|---|
| 110 | return |
|---|
| 111 | self.disconnect_transfer(file_props) |
|---|
| 112 | sid = file_props['sid'] |
|---|
| 113 | gajim.socks5queue.remove_file_props(self.name, sid) |
|---|
| 114 | |
|---|
| 115 | if remove_from_list: |
|---|
| 116 | if self.files_props.has_key('sid'): |
|---|
| 117 | del(self.files_props['sid']) |
|---|
| 118 | |
|---|
| 119 | def disconnect_transfer(self, file_props): |
|---|
| 120 | if file_props is None: |
|---|
| 121 | return |
|---|
| 122 | if file_props.has_key('hash'): |
|---|
| 123 | gajim.socks5queue.remove_sender(file_props['hash']) |
|---|
| 124 | |
|---|
| 125 | if file_props.has_key('streamhosts'): |
|---|
| 126 | for host in file_props['streamhosts']: |
|---|
| 127 | if host.has_key('idx') and host['idx'] > 0: |
|---|
| 128 | gajim.socks5queue.remove_receiver(host['idx']) |
|---|
| 129 | gajim.socks5queue.remove_sender(host['idx']) |
|---|
| 130 | |
|---|
| 131 | def send_socks5_info(self, file_props, fast = True, receiver = None, |
|---|
| 132 | sender = None): |
|---|
| 133 | ''' send iq for the present streamhosts and proxies ''' |
|---|
| 134 | if type(self.peerhost) != tuple: |
|---|
| 135 | return |
|---|
| 136 | port = gajim.config.get('file_transfers_port') |
|---|
| 137 | ft_override_host_to_send = gajim.config.get('ft_override_host_to_send') |
|---|
| 138 | cfg_proxies = gajim.config.get_per('accounts', self.name, |
|---|
| 139 | 'file_transfer_proxies') |
|---|
| 140 | if receiver is None: |
|---|
| 141 | receiver = file_props['receiver'] |
|---|
| 142 | if sender is None: |
|---|
| 143 | sender = file_props['sender'] |
|---|
| 144 | proxyhosts = [] |
|---|
| 145 | if fast and cfg_proxies: |
|---|
| 146 | proxies = map(lambda e:e.strip(), cfg_proxies.split(',')) |
|---|
| 147 | default = gajim.proxy65_manager.get_default_for_name(self.name) |
|---|
| 148 | if default: |
|---|
| 149 | # add/move default proxy at top of the others |
|---|
| 150 | if proxies.__contains__(default): |
|---|
| 151 | proxies.remove(default) |
|---|
| 152 | proxies.insert(0, default) |
|---|
| 153 | |
|---|
| 154 | for proxy in proxies: |
|---|
| 155 | (host, _port, jid) = gajim.proxy65_manager.get_proxy(proxy, self.name) |
|---|
| 156 | if host is None: |
|---|
| 157 | continue |
|---|
| 158 | host_dict={ |
|---|
| 159 | 'state': 0, |
|---|
| 160 | 'target': unicode(receiver), |
|---|
| 161 | 'id': file_props['sid'], |
|---|
| 162 | 'sid': file_props['sid'], |
|---|
| 163 | 'initiator': proxy, |
|---|
| 164 | 'host': host, |
|---|
| 165 | 'port': unicode(_port), |
|---|
| 166 | 'jid': jid |
|---|
| 167 | } |
|---|
| 168 | proxyhosts.append(host_dict) |
|---|
| 169 | sha_str = helpers.get_auth_sha(file_props['sid'], sender, |
|---|
| 170 | receiver) |
|---|
| 171 | file_props['sha_str'] = sha_str |
|---|
| 172 | if not ft_override_host_to_send: |
|---|
| 173 | ft_override_host_to_send = self.peerhost[0] |
|---|
| 174 | try: |
|---|
| 175 | ft_override_host_to_send = socket.gethostbyname( |
|---|
| 176 | ft_override_host_to_send) |
|---|
| 177 | except socket.gaierror: |
|---|
| 178 | self.dispatch('ERROR', (_('Wrong host'), _('The host you configured as the ft_override_host_to_send advanced option is not valid, so ignored.'))) |
|---|
| 179 | ft_override_host_to_send = self.peerhost[0] |
|---|
| 180 | listener = gajim.socks5queue.start_listener(port, |
|---|
| 181 | sha_str, self._result_socks5_sid, file_props['sid']) |
|---|
| 182 | if listener == None: |
|---|
| 183 | file_props['error'] = -5 |
|---|
| 184 | self.dispatch('FILE_REQUEST_ERROR', (unicode(receiver), file_props, |
|---|
| 185 | '')) |
|---|
| 186 | self._connect_error(unicode(receiver), file_props['sid'], |
|---|
| 187 | file_props['sid'], code = 406) |
|---|
| 188 | return |
|---|
| 189 | |
|---|
| 190 | iq = common.xmpp.Protocol(name = 'iq', to = unicode(receiver), |
|---|
| 191 | typ = 'set') |
|---|
| 192 | file_props['request-id'] = 'id_' + file_props['sid'] |
|---|
| 193 | iq.setID(file_props['request-id']) |
|---|
| 194 | query = iq.setTag('query') |
|---|
| 195 | query.setNamespace(common.xmpp.NS_BYTESTREAM) |
|---|
| 196 | query.setAttr('mode', 'tcp') |
|---|
| 197 | query.setAttr('sid', file_props['sid']) |
|---|
| 198 | streamhost = query.setTag('streamhost') |
|---|
| 199 | streamhost.setAttr('port', unicode(port)) |
|---|
| 200 | streamhost.setAttr('host', ft_override_host_to_send) |
|---|
| 201 | streamhost.setAttr('jid', sender) |
|---|
| 202 | if fast and proxyhosts != [] and gajim.config.get_per('accounts', |
|---|
| 203 | self.name, 'use_ft_proxies'): |
|---|
| 204 | file_props['proxy_receiver'] = unicode(receiver) |
|---|
| 205 | file_props['proxy_sender'] = unicode(sender) |
|---|
| 206 | file_props['proxyhosts'] = proxyhosts |
|---|
| 207 | for proxyhost in proxyhosts: |
|---|
| 208 | streamhost = common.xmpp.Node(tag = 'streamhost') |
|---|
| 209 | query.addChild(node=streamhost) |
|---|
| 210 | streamhost.setAttr('port', proxyhost['port']) |
|---|
| 211 | streamhost.setAttr('host', proxyhost['host']) |
|---|
| 212 | streamhost.setAttr('jid', proxyhost['jid']) |
|---|
| 213 | |
|---|
| 214 | # don't add the proxy child tag for streamhosts, which are proxies |
|---|
| 215 | # proxy = streamhost.setTag('proxy') |
|---|
| 216 | # proxy.setNamespace(common.xmpp.NS_STREAM) |
|---|
| 217 | self.connection.send(iq) |
|---|
| 218 | |
|---|
| 219 | def send_file_rejection(self, file_props): |
|---|
| 220 | ''' informs sender that we refuse to download the file ''' |
|---|
| 221 | # user response to ConfirmationDialog may come after we've disconneted |
|---|
| 222 | if not self.connection or self.connected < 2: |
|---|
| 223 | return |
|---|
| 224 | iq = common.xmpp.Protocol(name = 'iq', |
|---|
| 225 | to = unicode(file_props['sender']), typ = 'error') |
|---|
| 226 | iq.setAttr('id', file_props['request-id']) |
|---|
| 227 | err = common.xmpp.ErrorNode(code = '403', typ = 'cancel', name = |
|---|
| 228 | 'forbidden', text = 'Offer Declined') |
|---|
| 229 | iq.addChild(node=err) |
|---|
| 230 | self.connection.send(iq) |
|---|
| 231 | |
|---|
| 232 | def send_file_approval(self, file_props): |
|---|
| 233 | ''' send iq, confirming that we want to download the file ''' |
|---|
| 234 | # user response to ConfirmationDialog may come after we've disconneted |
|---|
| 235 | if not self.connection or self.connected < 2: |
|---|
| 236 | return |
|---|
| 237 | iq = common.xmpp.Protocol(name = 'iq', |
|---|
| 238 | to = unicode(file_props['sender']), typ = 'result') |
|---|
| 239 | iq.setAttr('id', file_props['request-id']) |
|---|
| 240 | si = iq.setTag('si') |
|---|
| 241 | si.setNamespace(common.xmpp.NS_SI) |
|---|
| 242 | if file_props.has_key('offset') and file_props['offset']: |
|---|
| 243 | file_tag = si.setTag('file') |
|---|
| 244 | file_tag.setNamespace(common.xmpp.NS_FILE) |
|---|
| 245 | range_tag = file_tag.setTag('range') |
|---|
| 246 | range_tag.setAttr('offset', file_props['offset']) |
|---|
| 247 | feature = si.setTag('feature') |
|---|
| 248 | feature.setNamespace(common.xmpp.NS_FEATURE) |
|---|
| 249 | _feature = common.xmpp.DataForm(typ='submit') |
|---|
| 250 | feature.addChild(node=_feature) |
|---|
| 251 | field = _feature.setField('stream-method') |
|---|
| 252 | field.delAttr('type') |
|---|
| 253 | field.setValue(common.xmpp.NS_BYTESTREAM) |
|---|
| 254 | self.connection.send(iq) |
|---|
| 255 | |
|---|
| 256 | def send_file_request(self, file_props): |
|---|
| 257 | ''' send iq for new FT request ''' |
|---|
| 258 | if not self.connection or self.connected < 2: |
|---|
| 259 | return |
|---|
| 260 | our_jid = gajim.get_jid_from_account(self.name) |
|---|
| 261 | resource = self.server_resource |
|---|
| 262 | frm = our_jid + '/' + resource |
|---|
| 263 | file_props['sender'] = frm |
|---|
| 264 | fjid = file_props['receiver'].jid + '/' + file_props['receiver'].resource |
|---|
| 265 | iq = common.xmpp.Protocol(name = 'iq', to = fjid, |
|---|
| 266 | typ = 'set') |
|---|
| 267 | iq.setID(file_props['sid']) |
|---|
| 268 | self.files_props[file_props['sid']] = file_props |
|---|
| 269 | si = iq.setTag('si') |
|---|
| 270 | si.setNamespace(common.xmpp.NS_SI) |
|---|
| 271 | si.setAttr('profile', common.xmpp.NS_FILE) |
|---|
| 272 | si.setAttr('id', file_props['sid']) |
|---|
| 273 | file_tag = si.setTag('file') |
|---|
| 274 | file_tag.setNamespace(common.xmpp.NS_FILE) |
|---|
| 275 | file_tag.setAttr('name', file_props['name']) |
|---|
| 276 | file_tag.setAttr('size', file_props['size']) |
|---|
| 277 | desc = file_tag.setTag('desc') |
|---|
| 278 | if file_props.has_key('desc'): |
|---|
| 279 | desc.setData(file_props['desc']) |
|---|
| 280 | file_tag.setTag('range') |
|---|
| 281 | feature = si.setTag('feature') |
|---|
| 282 | feature.setNamespace(common.xmpp.NS_FEATURE) |
|---|
| 283 | _feature = common.xmpp.DataForm(typ='form') |
|---|
| 284 | feature.addChild(node=_feature) |
|---|
| 285 | field = _feature.setField('stream-method') |
|---|
| 286 | field.setAttr('type', 'list-single') |
|---|
| 287 | field.addOption(common.xmpp.NS_BYTESTREAM) |
|---|
| 288 | self.connection.send(iq) |
|---|
| 289 | |
|---|
| 290 | def _result_socks5_sid(self, sid, hash_id): |
|---|
| 291 | ''' store the result of sha message from auth. ''' |
|---|
| 292 | if not self.files_props.has_key(sid): |
|---|
| 293 | return |
|---|
| 294 | file_props = self.files_props[sid] |
|---|
| 295 | file_props['hash'] = hash_id |
|---|
| 296 | return |
|---|
| 297 | |
|---|
| 298 | def _connect_error(self, to, _id, sid, code = 404): |
|---|
| 299 | ''' cb, when there is an error establishing BS connection, or |
|---|
| 300 | when connection is rejected''' |
|---|
| 301 | msg_dict = { |
|---|
| 302 | 404: 'Could not connect to given hosts', |
|---|
| 303 | 405: 'Cancel', |
|---|
| 304 | 406: 'Not acceptable', |
|---|
| 305 | } |
|---|
| 306 | msg = msg_dict[code] |
|---|
| 307 | iq = None |
|---|
| 308 | iq = common.xmpp.Protocol(name = 'iq', to = to, |
|---|
| 309 | typ = 'error') |
|---|
| 310 | iq.setAttr('id', _id) |
|---|
| 311 | err = iq.setTag('error') |
|---|
| 312 | err.setAttr('code', unicode(code)) |
|---|
| 313 | err.setData(msg) |
|---|
| 314 | self.connection.send(iq) |
|---|
| 315 | if code == 404: |
|---|
| 316 | file_props = gajim.socks5queue.get_file_props(self.name, sid) |
|---|
| 317 | if file_props is not None: |
|---|
| 318 | self.disconnect_transfer(file_props) |
|---|
| 319 | file_props['error'] = -3 |
|---|
| 320 | self.dispatch('FILE_REQUEST_ERROR', (to, file_props, msg)) |
|---|
| 321 | |
|---|
| 322 | def _proxy_auth_ok(self, proxy): |
|---|
| 323 | '''cb, called after authentication to proxy server ''' |
|---|
| 324 | file_props = self.files_props[proxy['sid']] |
|---|
| 325 | iq = common.xmpp.Protocol(name = 'iq', to = proxy['initiator'], |
|---|
| 326 | typ = 'set') |
|---|
| 327 | auth_id = "au_" + proxy['sid'] |
|---|
| 328 | iq.setID(auth_id) |
|---|
| 329 | query = iq.setTag('query') |
|---|
| 330 | query.setNamespace(common.xmpp.NS_BYTESTREAM) |
|---|
| 331 | query.setAttr('sid', proxy['sid']) |
|---|
| 332 | activate = query.setTag('activate') |
|---|
| 333 | activate.setData(file_props['proxy_receiver']) |
|---|
| 334 | iq.setID(auth_id) |
|---|
| 335 | self.connection.send(iq) |
|---|
| 336 | |
|---|
| 337 | # register xmpppy handlers for bytestream and FT stanzas |
|---|
| 338 | def _bytestreamErrorCB(self, con, iq_obj): |
|---|
| 339 | gajim.log.debug('_bytestreamErrorCB') |
|---|
| 340 | id = unicode(iq_obj.getAttr('id')) |
|---|
| 341 | frm = helpers.get_full_jid_from_iq(iq_obj) |
|---|
| 342 | query = iq_obj.getTag('query') |
|---|
| 343 | gajim.proxy65_manager.error_cb(frm, query) |
|---|
| 344 | jid = helpers.get_jid_from_iq(iq_obj) |
|---|
| 345 | id = id[3:] |
|---|
| 346 | if not self.files_props.has_key(id): |
|---|
| 347 | return |
|---|
| 348 | file_props = self.files_props[id] |
|---|
| 349 | file_props['error'] = -4 |
|---|
| 350 | self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, '')) |
|---|
| 351 | raise common.xmpp.NodeProcessed |
|---|
| 352 | |
|---|
| 353 | def _bytestreamSetCB(self, con, iq_obj): |
|---|
| 354 | gajim.log.debug('_bytestreamSetCB') |
|---|
| 355 | target = unicode(iq_obj.getAttr('to')) |
|---|
| 356 | id = unicode(iq_obj.getAttr('id')) |
|---|
| 357 | query = iq_obj.getTag('query') |
|---|
| 358 | sid = unicode(query.getAttr('sid')) |
|---|
| 359 | file_props = gajim.socks5queue.get_file_props( |
|---|
| 360 | self.name, sid) |
|---|
| 361 | streamhosts=[] |
|---|
| 362 | for item in query.getChildren(): |
|---|
| 363 | if item.getName() == 'streamhost': |
|---|
| 364 | host_dict={ |
|---|
| 365 | 'state': 0, |
|---|
| 366 | 'target': target, |
|---|
| 367 | 'id': id, |
|---|
| 368 | 'sid': sid, |
|---|
| 369 | 'initiator': helpers.get_full_jid_from_iq(iq_obj) |
|---|
| 370 | } |
|---|
| 371 | for attr in item.getAttrs(): |
|---|
| 372 | host_dict[attr] = item.getAttr(attr) |
|---|
| 373 | streamhosts.append(host_dict) |
|---|
| 374 | if file_props is None: |
|---|
| 375 | if self.files_props.has_key(sid): |
|---|
| 376 | file_props = self.files_props[sid] |
|---|
| 377 | file_props['fast'] = streamhosts |
|---|
| 378 | if file_props['type'] == 's': # FIXME: remove fast xmlns |
|---|
| 379 | # only psi do this |
|---|
| 380 | |
|---|
| 381 | if file_props.has_key('streamhosts'): |
|---|
| 382 | file_props['streamhosts'].extend(streamhosts) |
|---|
| 383 | else: |
|---|
| 384 | file_props['streamhosts'] = streamhosts |
|---|
| 385 | if not gajim.socks5queue.get_file_props(self.name, sid): |
|---|
| 386 | gajim.socks5queue.add_file_props(self.name, file_props) |
|---|
| 387 | gajim.socks5queue.connect_to_hosts(self.name, sid, |
|---|
| 388 | self.send_success_connect_reply, None) |
|---|
| 389 | raise common.xmpp.NodeProcessed |
|---|
| 390 | |
|---|
| 391 | file_props['streamhosts'] = streamhosts |
|---|
| 392 | if file_props['type'] == 'r': |
|---|
| 393 | gajim.socks5queue.connect_to_hosts(self.name, sid, |
|---|
| 394 | self.send_success_connect_reply, self._connect_error) |
|---|
| 395 | raise common.xmpp.NodeProcessed |
|---|
| 396 | |
|---|
| 397 | def _ResultCB(self, con, iq_obj): |
|---|
| 398 | gajim.log.debug('_ResultCB') |
|---|
| 399 | # if we want to respect xep-0065 we have to check for proxy |
|---|
| 400 | # activation result in any result iq |
|---|
| 401 | real_id = unicode(iq_obj.getAttr('id')) |
|---|
| 402 | if real_id[:3] != 'au_': |
|---|
| 403 | return |
|---|
| 404 | frm = helpers.get_full_jid_from_iq(iq_obj) |
|---|
| 405 | id = real_id[3:] |
|---|
| 406 | if self.files_props.has_key(id): |
|---|
| 407 | file_props = self.files_props[id] |
|---|
| 408 | if file_props['streamhost-used']: |
|---|
| 409 | for host in file_props['proxyhosts']: |
|---|
| 410 | if host['initiator'] == frm and host.has_key('idx'): |
|---|
| 411 | gajim.socks5queue.activate_proxy(host['idx']) |
|---|
| 412 | raise common.xmpp.NodeProcessed |
|---|
| 413 | |
|---|
| 414 | def _bytestreamResultCB(self, con, iq_obj): |
|---|
| 415 | gajim.log.debug('_bytestreamResultCB') |
|---|
| 416 | frm = helpers.get_full_jid_from_iq(iq_obj) |
|---|
| 417 | real_id = unicode(iq_obj.getAttr('id')) |
|---|
| 418 | query = iq_obj.getTag('query') |
|---|
| 419 | gajim.proxy65_manager.resolve_result(frm, query) |
|---|
| 420 | |
|---|
| 421 | try: |
|---|
| 422 | streamhost = query.getTag('streamhost-used') |
|---|
| 423 | except: # this bytestream result is not what we need |
|---|
| 424 | pass |
|---|
| 425 | id = real_id[3:] |
|---|
| 426 | if self.files_props.has_key(id): |
|---|
| 427 | file_props = self.files_props[id] |
|---|
| 428 | else: |
|---|
| 429 | raise common.xmpp.NodeProcessed |
|---|
| 430 | if streamhost is None: |
|---|
| 431 | # proxy approves the activate query |
|---|
| 432 | if real_id[:3] == 'au_': |
|---|
| 433 | id = real_id[3:] |
|---|
| 434 | if not file_props.has_key('streamhost-used') or \ |
|---|
| 435 | file_props['streamhost-used'] is False: |
|---|
| 436 | raise common.xmpp.NodeProcessed |
|---|
| 437 | if not file_props.has_key('proxyhosts'): |
|---|
| 438 | raise common.xmpp.NodeProcessed |
|---|
| 439 | for host in file_props['proxyhosts']: |
|---|
| 440 | if host['initiator'] == frm and \ |
|---|
| 441 | unicode(query.getAttr('sid')) == file_props['sid']: |
|---|
| 442 | gajim.socks5queue.activate_proxy(host['idx']) |
|---|
| 443 | break |
|---|
| 444 | raise common.xmpp.NodeProcessed |
|---|
| 445 | jid = streamhost.getAttr('jid') |
|---|
| 446 | if file_props.has_key('streamhost-used') and \ |
|---|
| 447 | file_props['streamhost-used'] is True: |
|---|
| 448 | raise common.xmpp.NodeProcessed |
|---|
| 449 | |
|---|
| 450 | if real_id[:3] == 'au_': |
|---|
| 451 | gajim.socks5queue.send_file(file_props, self.name) |
|---|
| 452 | raise common.xmpp.NodeProcessed |
|---|
| 453 | |
|---|
| 454 | proxy = None |
|---|
| 455 | if file_props.has_key('proxyhosts'): |
|---|
| 456 | for proxyhost in file_props['proxyhosts']: |
|---|
| 457 | if proxyhost['jid'] == jid: |
|---|
| 458 | proxy = proxyhost |
|---|
| 459 | |
|---|
| 460 | if proxy != None: |
|---|
| 461 | file_props['streamhost-used'] = True |
|---|
| 462 | if not file_props.has_key('streamhosts'): |
|---|
| 463 | file_props['streamhosts'] = [] |
|---|
| 464 | file_props['streamhosts'].append(proxy) |
|---|
| 465 | file_props['is_a_proxy'] = True |
|---|
| 466 | receiver = socks5.Socks5Receiver(gajim.idlequeue, proxy, file_props['sid'], file_props) |
|---|
| 467 | gajim.socks5queue.add_receiver(self.name, receiver) |
|---|
| 468 | proxy['idx'] = receiver.queue_idx |
|---|
| 469 | gajim.socks5queue.on_success = self._proxy_auth_ok |
|---|
| 470 | raise common.xmpp.NodeProcessed |
|---|
| 471 | |
|---|
| 472 | else: |
|---|
| 473 | gajim.socks5queue.send_file(file_props, self.name) |
|---|
| 474 | if file_props.has_key('fast'): |
|---|
| 475 | fasts = file_props['fast'] |
|---|
| 476 | if len(fasts) > 0: |
|---|
| 477 | self._connect_error(frm, fasts[0]['id'], file_props['sid'], |
|---|
| 478 | code = 406) |
|---|
| 479 | |
|---|
| 480 | raise common.xmpp.NodeProcessed |
|---|
| 481 | |
|---|
| 482 | def _siResultCB(self, con, iq_obj): |
|---|
| 483 | gajim.log.debug('_siResultCB') |
|---|
| 484 | id = iq_obj.getAttr('id') |
|---|
| 485 | if not self.files_props.has_key(id): |
|---|
| 486 | # no such jid |
|---|
| 487 | return |
|---|
| 488 | file_props = self.files_props[id] |
|---|
| 489 | if file_props is None: |
|---|
| 490 | # file properties for jid is none |
|---|
| 491 | return |
|---|
| 492 | if file_props.has_key('request-id'): |
|---|
| 493 | # we have already sent streamhosts info |
|---|
| 494 | return |
|---|
| 495 | file_props['receiver'] = helpers.get_full_jid_from_iq(iq_obj) |
|---|
| 496 | si = iq_obj.getTag('si') |
|---|
| 497 | file_tag = si.getTag('file') |
|---|
| 498 | range_tag = None |
|---|
| 499 | if file_tag: |
|---|
| 500 | range_tag = file_tag.getTag('range') |
|---|
| 501 | if range_tag: |
|---|
| 502 | offset = range_tag.getAttr('offset') |
|---|
| 503 | if offset: |
|---|
| 504 | file_props['offset'] = int(offset) |
|---|
| 505 | length = range_tag.getAttr('length') |
|---|
| 506 | if length: |
|---|
| 507 | file_props['length'] = int(length) |
|---|
| 508 | feature = si.setTag('feature') |
|---|
| 509 | if feature.getNamespace() != common.xmpp.NS_FEATURE: |
|---|
| 510 | return |
|---|
| 511 | form_tag = feature.getTag('x') |
|---|
| 512 | form = common.xmpp.DataForm(node=form_tag) |
|---|
| 513 | field = form.getField('stream-method') |
|---|
| 514 | if field.getValue() != common.xmpp.NS_BYTESTREAM: |
|---|
| 515 | return |
|---|
| 516 | self.send_socks5_info(file_props, fast = True) |
|---|
| 517 | raise common.xmpp.NodeProcessed |
|---|
| 518 | |
|---|
| 519 | def _siSetCB(self, con, iq_obj): |
|---|
| 520 | gajim.log.debug('_siSetCB') |
|---|
| 521 | jid = helpers.get_jid_from_iq(iq_obj) |
|---|
| 522 | si = iq_obj.getTag('si') |
|---|
| 523 | profile = si.getAttr('profile') |
|---|
| 524 | mime_type = si.getAttr('mime-type') |
|---|
| 525 | if profile != common.xmpp.NS_FILE: |
|---|
| 526 | return |
|---|
| 527 | file_tag = si.getTag('file') |
|---|
| 528 | file_props = {'type': 'r'} |
|---|
| 529 | for attribute in file_tag.getAttrs(): |
|---|
| 530 | if attribute in ('name', 'size', 'hash', 'date'): |
|---|
| 531 | val = file_tag.getAttr(attribute) |
|---|
| 532 | if val is None: |
|---|
| 533 | continue |
|---|
| 534 | file_props[attribute] = val |
|---|
| 535 | file_desc_tag = file_tag.getTag('desc') |
|---|
| 536 | if file_desc_tag is not None: |
|---|
| 537 | file_props['desc'] = file_desc_tag.getData() |
|---|
| 538 | |
|---|
| 539 | if mime_type is not None: |
|---|
| 540 | file_props['mime-type'] = mime_type |
|---|
| 541 | our_jid = gajim.get_jid_from_account(self.name) |
|---|
| 542 | resource = self.server_resource |
|---|
| 543 | file_props['receiver'] = our_jid + '/' + resource |
|---|
| 544 | file_props['sender'] = helpers.get_full_jid_from_iq(iq_obj) |
|---|
| 545 | file_props['request-id'] = unicode(iq_obj.getAttr('id')) |
|---|
| 546 | file_props['sid'] = unicode(si.getAttr('id')) |
|---|
| 547 | gajim.socks5queue.add_file_props(self.name, file_props) |
|---|
| 548 | self.dispatch('FILE_REQUEST', (jid, file_props)) |
|---|
| 549 | raise common.xmpp.NodeProcessed |
|---|
| 550 | |
|---|
| 551 | def _siErrorCB(self, con, iq_obj): |
|---|
| 552 | gajim.log.debug('_siErrorCB') |
|---|
| 553 | si = iq_obj.getTag('si') |
|---|
| 554 | profile = si |
|---|