| Trees | Indices | Help |
|
|---|
|
|
1 # -*- coding:utf-8 -*-
2 ## src/common/proxy65_manager.py
3 ##
4 ## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
5 ## Jean-Marie Traissard <jim AT lapin.org>
6 ## Copyright (C) 2007-2010 Yann Leboulanger <asterix AT lagaule.org>
7 ##
8 ## This file is part of Gajim.
9 ##
10 ## Gajim 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 3 only.
13 ##
14 ## Gajim 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 ## You should have received a copy of the GNU General Public License
20 ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
21 ##
22
23 import socket
24 import struct
25 import errno
26 import logging
27 log = logging.getLogger('gajim.c.proxy65_manager')
28
29 import common.xmpp
30 from common import gajim
31 from common import helpers
32 from socks5 import Socks5
33 from common.xmpp.idlequeue import IdleObject
34
35 S_INITIAL = 0
36 S_STARTED = 1
37 S_RESOLVED = 2
38 S_ACTIVATED = 3
39 S_FINISHED = 4
40
41 CONNECT_TIMEOUT = 20
42
44 """
45 Keep records for file transfer proxies. Each time account establishes a
46 connection to its server call proxy65manger.resolve(proxy) for every proxy
47 that is convigured within the account. The class takes care to resolve and
48 test each proxy only once
49 """
50
52 # dict {proxy: proxy properties}
53 self.idlequeue = idlequeue
54 self.proxies = {}
55 # dict {account: proxy} default proxy for account
56 self.default_proxies = {}
57
59 """
60 Start
61 """
62 if proxy in self.proxies:
63 resolver = self.proxies[proxy]
64 else:
65 # proxy is being ressolved for the first time
66 resolver = ProxyResolver(proxy, sender_jid)
67 self.proxies[proxy] = resolver
68 resolver.add_connection(connection)
69 if default:
70 # add this proxy as default for account
71 self.default_proxies[default] = proxy
72
76
78 if proxy not in self.proxies:
79 return
80 jid = None
81 for item in query.getChildren():
82 if item.getName() == 'streamhost':
83 host = item.getAttr('host')
84 port = item.getAttr('port')
85 jid = item.getAttr('jid')
86 if not host or not port or not jid:
87 self.proxies[proxy]._on_connect_failure()
88 self.proxies[proxy].resolve_result(host, port, jid)
89 # we can have only one streamhost
90 raise common.xmpp.NodeProcessed
91
93 sid = query.getAttr('sid')
94 for resolver in self.proxies.values():
95 if resolver.sid == sid:
96 resolver.keep_conf()
97 break
98
102
109
112 """
113 Test if host has a real proxy65 listening on port
114 """
115 self.host = str(host)
116 self.port = int(port)
117 self.jid = unicode(jid)
118 self.state = S_INITIAL
119 log.info('start resolving %s:%s' % (self.host, self.port))
120 self.receiver_tester = ReceiverTester(self.host, self.port, self.jid,
121 self.sid, self.sender_jid, self._on_receiver_success,
122 self._on_connect_failure)
123 self.receiver_tester.connect()
124
126 log.debug('Receiver successfully connected %s:%s' % (self.host,
127 self.port))
128 self.host_tester = HostTester(self.host, self.port, self.jid,
129 self.sid, self.sender_jid, self._on_connect_success,
130 self._on_connect_failure)
131 self.host_tester.connect()
132
134 log.debug('Host successfully connected %s:%s' % (self.host, self.port))
135 iq = common.xmpp.Protocol(name='iq', to=self.jid, typ='set')
136 query = iq.setTag('query')
137 query.setNamespace(common.xmpp.NS_BYTESTREAM)
138 query.setAttr('sid', self.sid)
139
140 activate = query.setTag('activate')
141 activate.setData('test@gajim.org/test2')
142
143 if self.active_connection:
144 log.debug('Activating bytestream on %s:%s' % (self.host, self.port))
145 self.active_connection.SendAndCallForResponse(iq,
146 self._result_received)
147 self.state = S_ACTIVATED
148 else:
149 self.state = S_INITIAL
150
152 self.disconnect(self.active_connection)
153 if data.getType() == 'result':
154 self.keep_conf()
155 else:
156 self._on_connect_failure()
157
161
167
169 if self.host_tester:
170 self.host_tester.disconnect()
171 self.host_tester = None
172 if self.receiver_tester:
173 self.receiver_tester.disconnect()
174 self.receiver_tester = None
175 try:
176 self.connections.remove(connection)
177 except ValueError:
178 pass
179 if connection == self.active_connection:
180 self.active_connection = None
181 if self.state != S_FINISHED:
182 self.state = S_INITIAL
183 self.try_next_connection()
184
186 """
187 Try to resolve proxy with the next possible connection
188 """
189 if self.connections:
190 connection = self.connections.pop(0)
191 self.start_resolve(connection)
192
194 """
195 Add a new connection in case the first fails
196 """
197 self.connections.append(connection)
198 if self.state == S_INITIAL:
199 self.start_resolve(connection)
200
202 """
203 Request network address from proxy
204 """
205 self.state = S_STARTED
206 self.active_connection = connection
207 iq = common.xmpp.Protocol(name='iq', to=self.proxy, typ='get')
208 query = iq.setTag('query')
209 query.setNamespace(common.xmpp.NS_BYTESTREAM)
210 connection.send(iq)
211
213 self.proxy = proxy
214 self.state = S_INITIAL
215 self.active_connection = None
216 self.connections = []
217 self.host_tester = None
218 self.receiver_tester = None
219 self.jid = None
220 self.host = None
221 self.port = None
222 self.sid = helpers.get_random_string_16()
223 self.sender_jid = sender_jid
224
226 """
227 Fake proxy tester
228 """
229
231 """
232 Try to establish and auth to proxy at (host, port)
233
234 Calls on_success, or on_failure according to the result.
235 """
236 self.host = host
237 self.port = port
238 self.jid = jid
239 self.on_success = on_success
240 self.on_failure = on_failure
241 self._sock = None
242 self.file_props = {'is_a_proxy': True,
243 'proxy_sender': sender_jid,
244 'proxy_receiver': 'test@gajim.org/test2'}
245 Socks5.__init__(self, gajim.idlequeue, host, port, None, None, None)
246 self.sid = sid
247
249 """
250 Create the socket and plug it to the idlequeue
251 """
252 if self.host is None:
253 self.on_failure()
254 return None
255 self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
256 self._sock.setblocking(False)
257 self.fd = self._sock.fileno()
258 self.state = 0 # about to be connected
259 gajim.idlequeue.plug_idle(self, True, False)
260 self.do_connect()
261 self.idlequeue.set_read_timeout(self.fd, CONNECT_TIMEOUT)
262 return None
263
267
271
273 self.idlequeue.remove_timeout(self.fd)
274 if self.state == 0:
275 self.do_connect()
276 return
277 elif self.state == 1: # send initially: version and auth types
278 data = self._get_auth_buff()
279 self.send_raw(data)
280 else:
281 return
282 self.state += 1
283 # unplug and plug for reading
284 gajim.idlequeue.plug_idle(self, False, True)
285 gajim.idlequeue.set_read_timeout(self.fd, CONNECT_TIMEOUT)
286
288 self.idlequeue.remove_timeout(self.fd)
289 if self.state == 2:
290 self.idlequeue.set_read_timeout(self.fd, CONNECT_TIMEOUT)
291 # begin negotiation. on success 'address' != 0
292 buff = self.receive()
293 if buff == '':
294 # end connection
295 self.pollend()
296 return
297 # read auth response
298 if buff is None or len(buff) != 2:
299 return None
300 version, method = struct.unpack('!BB', buff[:2])
301 if version != 0x05 or method == 0xff:
302 self.pollend()
303 return
304 data = self._get_request_buff(self._get_sha1_auth())
305 self.send_raw(data)
306 self.state += 1
307 log.debug('Host authenticating to %s:%s' % (self.host, self.port))
308 elif self.state == 3:
309 log.debug('Host authenticated to %s:%s' % (self.host, self.port))
310 self.on_success()
311 self.disconnect()
312 self.state += 1
313 else:
314 assert False, 'unexpected state: %d' % self.state
315
317 try:
318 self._sock.connect((self.host, self.port))
319 self._sock.setblocking(False)
320 log.debug('Host Connecting to %s:%s' % (self.host, self.port))
321 self._send = self._sock.send
322 self._recv = self._sock.recv
323 except Exception, ee:
324 errnum = ee[0]
325 # 56 is for freebsd
326 if errnum in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK):
327 # still trying to connect
328 return
329 # win32 needs this
330 if errnum not in (0, 10056, errno.EISCONN):
331 # connection failed
332 self.on_failure()
333 return
334 # socket is already connected
335 self._sock.setblocking(False)
336 self._send = self._sock.send
337 self._recv = self._sock.recv
338 self.buff = ''
339 self.state = 1 # connected
340 log.debug('Host connected to %s:%s' % (self.host, self.port))
341 self.idlequeue.plug_idle(self, True, False)
342 return
343
345 """
346 Fake proxy tester
347 """
348
350 """
351 Try to establish and auth to proxy at (host, port)
352
353 Call on_success, or on_failure according to the result.
354 """
355 self.host = host
356 self.port = port
357 self.jid = jid
358 self.on_success = on_success
359 self.on_failure = on_failure
360 self._sock = None
361 self.file_props = {'is_a_proxy': True,
362 'proxy_sender': sender_jid,
363 'proxy_receiver': 'test@gajim.org/test2'}
364 Socks5.__init__(self, gajim.idlequeue, host, port, None, None, None)
365 self.sid = sid
366
368 """
369 Create the socket and plug it to the idlequeue
370 """
371 if self.host is None:
372 self.on_failure()
373 return None
374 self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
375 self._sock.setblocking(False)
376 self.fd = self._sock.fileno()
377 self.state = 0 # about to be connected
378 gajim.idlequeue.plug_idle(self, True, False)
379 self.do_connect()
380 self.idlequeue.set_read_timeout(self.fd, CONNECT_TIMEOUT)
381 return None
382
386
390
392 self.idlequeue.remove_timeout(self.fd)
393 if self.state == 0:
394 self.do_connect()
395 return
396 elif self.state == 1: # send initially: version and auth types
397 data = self._get_auth_buff()
398 self.send_raw(data)
399 else:
400 return
401 self.state += 1
402 # unplug and plug for reading
403 gajim.idlequeue.plug_idle(self, False, True)
404 gajim.idlequeue.set_read_timeout(self.fd, CONNECT_TIMEOUT)
405
407 self.idlequeue.remove_timeout(self.fd)
408 if self.state in (2, 3):
409 self.idlequeue.set_read_timeout(self.fd, CONNECT_TIMEOUT)
410 # begin negotiation. on success 'address' != 0
411 buff = self.receive()
412 if buff == '':
413 # end connection
414 self.pollend()
415 return
416 if self.state == 2:
417 # read auth response
418 if buff is None or len(buff) != 2:
419 return None
420 version, method = struct.unpack('!BB', buff[:2])
421 if version != 0x05 or method == 0xff:
422 self.pollend()
423 return
424 log.debug('Receiver authenticating to %s:%s' % (self.host, self.port))
425 data = self._get_request_buff(self._get_sha1_auth())
426 self.send_raw(data)
427 self.state += 1
428 elif self.state == 3:
429 # read connect response
430 if buff is None or len(buff) < 2:
431 return None
432 version, reply = struct.unpack('!BB', buff[:2])
433 if version != 0x05 or reply != 0x00:
434 self.pollend()
435 return
436 log.debug('Receiver authenticated to %s:%s' % (self.host, self.port))
437 self.on_success()
438 self.disconnect()
439 self.state += 1
440 else:
441 assert False, 'unexpected state: %d' % self.state
442
444 try:
445 self._sock.setblocking(False)
446 self._sock.connect((self.host, self.port))
447 log.debug('Receiver Connecting to %s:%s' % (self.host, self.port))
448 self._send = self._sock.send
449 self._recv = self._sock.recv
450 except Exception, ee:
451 errnum = ee[0]
452 # 56 is for freebsd
453 if errnum in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK):
454 # still trying to connect
455 return
456 # win32 needs this
457 if errnum not in (0, 10056, errno.EISCONN):
458 # connection failed
459 self.on_failure()
460 return
461 # socket is already connected
462 self._sock.setblocking(False)
463 self._send = self._sock.send
464 self._recv = self._sock.recv
465 self.buff = ''
466 self.state = 1 # connected
467 log.debug('Receiver connected to %s:%s' % (self.host, self.port))
468 self.idlequeue.plug_idle(self, True, False)
469
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Thu Aug 12 02:08:13 2010 | http://epydoc.sourceforge.net |