Mercurial > libervia-backend
annotate plugins/plugin_xep_0065.py @ 15:218ec9984fa5
wokkel integration part III + memory saved again
- disco handler (plugins handled)
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 31 Oct 2009 00:18:35 +0100 |
parents | 63ab3d8058f4 |
children | f2a745ca0fbc |
rev | line source |
---|---|
0 | 1 #!/usr/bin/python |
2 #-*- coding: utf-8 -*- | |
3 """ | |
4 SAT plugin for managing xep-0065 | |
5 | |
6 Copyright (C) | |
7 2002-2004 Dave Smith (dizzyd@jabber.org) | |
8 2007-2008 Fabio Forno (xmpp:ff@jabber.bluendo.com) | |
9 2009 Jérôme Poisson (goffi@goffi.org) | |
10 | |
11 This program is free software: you can redistribute it and/or modify | |
12 it under the terms of the GNU General Public License as published by | |
13 the Free Software Foundation, either version 3 of the License, or | |
14 (at your option) any later version. | |
15 | |
16 This program is distributed in the hope that it will be useful, | |
17 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 GNU General Public License for more details. | |
20 | |
21 You should have received a copy of the GNU General Public License | |
22 along with this program. If not, see <http://www.gnu.org/licenses/>. | |
23 | |
24 -- | |
25 | |
26 This program is based on proxy65 (http://code.google.com/p/proxy65), | |
27 originaly written by David Smith and modified by Fabio Forno. | |
28 It is sublicensed under GPL v3 (or any later version) as allowed by the original | |
29 license. | |
30 | |
31 -- | |
32 | |
33 Here is a copy of the original license: | |
34 | |
35 Copyright (C) | |
36 2002-2004 Dave Smith (dizzyd@jabber.org) | |
37 2007-2008 Fabio Forno (xmpp:ff@jabber.bluendo.com) | |
38 | |
39 Permission is hereby granted, free of charge, to any person obtaining a copy | |
40 of this software and associated documentation files (the "Software"), to deal | |
41 in the Software without restriction, including without limitation the rights | |
42 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
43 copies of the Software, and to permit persons to whom the Software is | |
44 furnished to do so, subject to the following conditions: | |
45 | |
46 The above copyright notice and this permission notice shall be included in | |
47 all copies or substantial portions of the Software. | |
48 | |
49 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
50 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
51 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
52 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
53 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
54 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
55 THE SOFTWARE. | |
56 """ | |
57 | |
58 from logging import debug, info, error | |
59 from twisted.internet import protocol, reactor | |
60 from twisted.protocols.basic import FileSender | |
61 from twisted.words.xish import domish | |
62 import struct | |
63 from binascii import hexlify | |
64 import hashlib, pdb | |
65 | |
15
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
66 from zope.interface import implements |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
67 |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
68 try: |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
69 from twisted.words.protocols.xmlstream import XMPPHandler |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
70 except ImportError: |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
71 from wokkel.subprotocols import XMPPHandler |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
72 |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
73 from wokkel import disco, iwokkel |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
74 |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
75 IQ_SET = '/iq[@type="set"]' |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
76 NS_BS = 'http://jabber.org/protocol/bytestreams' |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
77 BS_REQUEST = IQ_SET + '/query[@xmlns="' + NS_BS + '"]' |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
78 |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
79 |
0 | 80 |
81 PLUGIN_INFO = { | |
82 "name": "XEP 0065 Plugin", | |
83 "import_name": "XEP_0065", | |
84 "type": "XEP", | |
85 "main": "XEP_0065", | |
9 | 86 "description": """Implementation of SOCKS5 Bytestreams""" |
0 | 87 } |
88 | |
89 STATE_INITIAL = 0 | |
90 STATE_AUTH = 1 | |
91 STATE_REQUEST = 2 | |
92 STATE_READY = 3 | |
93 STATE_AUTH_USERPASS = 4 | |
94 STATE_TARGET_INITIAL = 5 | |
95 STATE_TARGET_AUTH = 6 | |
96 STATE_TARGET_REQUEST = 7 | |
97 STATE_TARGET_READY = 8 | |
98 STATE_LAST = 9 | |
99 | |
100 STATE_CONNECT_PENDING = STATE_LAST + 1 | |
101 | |
102 SOCKS5_VER = 0x05 | |
103 | |
104 ADDR_IPV4 = 0x01 | |
105 ADDR_DOMAINNAME = 0x03 | |
106 ADDR_IPV6 = 0x04 | |
107 | |
108 CMD_CONNECT = 0x01 | |
109 CMD_BIND = 0x02 | |
110 CMD_UDPASSOC = 0x03 | |
111 | |
112 AUTHMECH_ANON = 0x00 | |
113 AUTHMECH_USERPASS = 0x02 | |
114 AUTHMECH_INVALID = 0xFF | |
115 | |
116 REPLY_SUCCESS = 0x00 | |
117 REPLY_GENERAL_FAILUR = 0x01 | |
118 REPLY_CONN_NOT_ALLOWED = 0x02 | |
119 REPLY_NETWORK_UNREACHABLE = 0x03 | |
120 REPLY_HOST_UNREACHABLE = 0x04 | |
121 REPLY_CONN_REFUSED = 0x05 | |
122 REPLY_TTL_EXPIRED = 0x06 | |
123 REPLY_CMD_NOT_SUPPORTED = 0x07 | |
124 REPLY_ADDR_NOT_SUPPORTED = 0x08 | |
125 | |
126 | |
127 | |
128 | |
129 | |
130 class SOCKSv5(protocol.Protocol, FileSender): | |
131 def __init__(self): | |
132 debug("Protocol init") | |
133 self.state = STATE_INITIAL | |
134 self.buf = "" | |
135 self.supportedAuthMechs = [ AUTHMECH_ANON ] | |
136 self.supportedAddrs = [ ADDR_DOMAINNAME ] | |
137 self.enabledCommands = [ CMD_CONNECT ] | |
138 self.peersock = None | |
139 self.addressType = 0 | |
140 self.requestType = 0 | |
141 self.activeConns = {} | |
142 self.pendingConns = {} | |
143 self.transfered = 0 #nb of bytes already copied | |
144 | |
145 def _startNegotiation(self): | |
146 debug("_startNegotiation") | |
147 self.state = STATE_TARGET_AUTH | |
148 self.transport.write(struct.pack('!3B', SOCKS5_VER, 1, AUTHMECH_ANON)) | |
149 | |
150 def _parseNegotiation(self): | |
151 debug("_parseNegotiation") | |
152 try: | |
153 # Parse out data | |
154 ver, nmethod = struct.unpack('!BB', self.buf[:2]) | |
155 methods = struct.unpack('%dB' % nmethod, self.buf[2:nmethod+2]) | |
156 | |
157 # Ensure version is correct | |
158 if ver != 5: | |
159 self.transport.write(struct.pack('!BB', SOCKS5_VER, AUTHMECH_INVALID)) | |
160 self.transport.loseConnection() | |
161 return | |
162 | |
163 # Trim off front of the buffer | |
164 self.buf = self.buf[nmethod+2:] | |
165 | |
166 # Check for supported auth mechs | |
167 for m in self.supportedAuthMechs: | |
168 if m in methods: | |
169 # Update internal state, according to selected method | |
170 if m == AUTHMECH_ANON: | |
171 self.state = STATE_REQUEST | |
172 elif m == AUTHMECH_USERPASS: | |
173 self.state = STATE_AUTH_USERPASS | |
174 # Complete negotiation w/ this method | |
175 self.transport.write(struct.pack('!BB', SOCKS5_VER, m)) | |
176 return | |
177 | |
178 # No supported mechs found, notify client and close the connection | |
179 self.transport.write(struct.pack('!BB', SOCKS5_VER, AUTHMECH_INVALID)) | |
180 self.transport.loseConnection() | |
181 except struct.error: | |
182 pass | |
183 | |
184 def _parseUserPass(self): | |
185 debug("_parseUserPass") | |
186 try: | |
187 # Parse out data | |
188 ver, ulen = struct.unpack('BB', self.buf[:2]) | |
189 uname, = struct.unpack('%ds' % ulen, self.buf[2:ulen + 2]) | |
190 plen, = struct.unpack('B', self.buf[ulen + 2]) | |
191 password, = struct.unpack('%ds' % plen, self.buf[ulen + 3:ulen + 3 + plen]) | |
192 # Trim off fron of the buffer | |
193 self.buf = self.buf[3 + ulen + plen:] | |
194 # Fire event to authenticate user | |
195 if self.authenticateUserPass(uname, password): | |
196 # Signal success | |
197 self.state = STATE_REQUEST | |
198 self.transport.write(struct.pack('!BB', SOCKS5_VER, 0x00)) | |
199 else: | |
200 # Signal failure | |
201 self.transport.write(struct.pack('!BB', SOCKS5_VER, 0x01)) | |
202 self.transport.loseConnection() | |
203 except struct.error: | |
204 pass | |
205 | |
206 def sendErrorReply(self, errorcode): | |
207 debug("sendErrorReply") | |
208 # Any other address types are not supported | |
209 result = struct.pack('!BBBBIH', SOCKS5_VER, errorcode, 0, 1, 0, 0) | |
210 self.transport.write(result) | |
211 self.transport.loseConnection() | |
212 | |
213 def addConnection(self, address, connection): | |
214 info("Adding connection: %s, %s", address, connection) | |
215 olist = self.pendingConns.get(address, []) | |
216 if len(olist) <= 1: | |
217 olist.append(connection) | |
218 self.pendingConns[address] = olist | |
219 return True | |
220 else: | |
221 return False | |
222 | |
223 def removePendingConnection(self, address, connection): | |
224 olist = self.pendingConns[address] | |
225 if len(olist) == 1: | |
226 del self.pendingConns[address] | |
227 else: | |
228 olist.remove(connection) | |
229 self.pendingConns[address] = olist | |
230 | |
231 def removeActiveConnection(self, address): | |
232 del self.activeConns[address] | |
233 | |
234 def _parseRequest(self): | |
235 debug("_parseRequest") | |
236 try: | |
237 # Parse out data and trim buffer accordingly | |
238 ver, cmd, rsvd, self.addressType = struct.unpack('!BBBB', self.buf[:4]) | |
239 | |
240 # Ensure we actually support the requested address type | |
241 if self.addressType not in self.supportedAddrs: | |
242 self.sendErrorReply(REPLY_ADDR_NOT_SUPPORTED) | |
243 return | |
244 | |
245 # Deal with addresses | |
246 if self.addressType == ADDR_IPV4: | |
247 addr, port = struct.unpack('!IH', self.buf[4:10]) | |
248 self.buf = self.buf[10:] | |
249 elif self.addressType == ADDR_DOMAINNAME: | |
250 nlen = ord(self.buf[4]) | |
251 addr, port = struct.unpack('!%dsH' % nlen, self.buf[5:]) | |
252 self.buf = self.buf[7 + len(addr):] | |
253 else: | |
254 # Any other address types are not supported | |
255 self.sendErrorReply(REPLY_ADDR_NOT_SUPPORTED) | |
256 return | |
257 | |
258 # Ensure command is supported | |
259 if cmd not in self.enabledCommands: | |
260 # Send a not supported error | |
261 self.sendErrorReply(REPLY_CMD_NOT_SUPPORTED) | |
262 return | |
263 | |
264 # Process the command | |
265 if cmd == CMD_CONNECT: | |
266 self.connectRequested(addr, port) | |
267 elif cmd == CMD_BIND: | |
268 self.bindRequested(addr, port) | |
269 else: | |
270 # Any other command is not supported | |
271 self.sendErrorReply(REPLY_CMD_NOT_SUPPORTED) | |
272 | |
273 except struct.error, why: | |
274 return None | |
275 | |
276 def _makeRequest(self): | |
277 debug("_makeRequest") | |
278 self.state = STATE_TARGET_REQUEST | |
279 sha1 = hashlib.sha1(self.sid + self.initiator_jid + self.target_jid).hexdigest() | |
280 request = struct.pack('!5B%dsH' % len(sha1), SOCKS5_VER, CMD_CONNECT, 0, ADDR_DOMAINNAME, len(sha1), sha1, 0) | |
281 self.transport.write(request) | |
282 | |
283 def _parseRequestReply(self): | |
284 debug("_parseRequestReply") | |
285 try: | |
286 ver, rep, rsvd, self.addressType = struct.unpack('!BBBB', self.buf[:4]) | |
287 # Ensure we actually support the requested address type | |
288 if self.addressType not in self.supportedAddrs: | |
289 self.sendErrorReply(REPLY_ADDR_NOT_SUPPORTED) | |
290 return | |
291 | |
292 # Deal with addresses | |
293 if self.addressType == ADDR_IPV4: | |
294 addr, port = struct.unpack('!IH', self.buf[4:10]) | |
295 self.buf = self.buf[10:] | |
296 elif self.addressType == ADDR_DOMAINNAME: | |
297 nlen = ord(self.buf[4]) | |
298 addr, port = struct.unpack('!%dsH' % nlen, self.buf[5:]) | |
299 self.buf = self.buf[7 + len(addr):] | |
300 else: | |
301 # Any other address types are not supported | |
302 self.sendErrorReply(REPLY_ADDR_NOT_SUPPORTED) | |
303 return | |
304 | |
305 # Ensure reply is OK | |
306 if rep != REPLY_SUCCESS: | |
307 self.loseConnection() | |
308 return | |
309 | |
310 debug("Saving file in %s.", self.data["dest_path"]) | |
311 self.dest_file = open(self.data["dest_path"], 'w') | |
312 self.state = STATE_TARGET_READY | |
313 self.activateCB(self.target_jid, self.initiator_jid, self.sid, self.IQ_id) | |
314 | |
315 | |
316 except struct.error, why: | |
317 return None | |
318 | |
319 def connectionMade(self): | |
320 debug("connectionMade (mode = %s)" % self.mode) | |
321 self.host.registerProgressCB(self.transfert_id, self.getProgress) | |
322 | |
323 if self.mode == "target": | |
324 self.state = STATE_TARGET_INITIAL | |
325 self._startNegotiation() | |
326 | |
327 def connectRequested(self, addr, port): | |
328 debug(("connectRequested")) | |
329 # Check for special connect to the namespace -- this signifies that the client | |
330 # is just checking to ensure it can connect to the streamhost | |
331 if addr == "http://jabber.org/protocol/bytestreams": | |
332 self.connectCompleted(addr, 0) | |
333 self.transport.loseConnection() | |
334 return | |
335 | |
336 # Save addr, for cleanup | |
337 self.addr = addr | |
338 | |
339 # Check to see if the requested address is already | |
340 # activated -- send an error if so | |
341 if addr in self.activeConns: | |
342 self.sendErrorReply(socks5.REPLY_CONN_NOT_ALLOWED) | |
343 return | |
344 | |
345 # Add this address to the pending connections | |
346 if self.addConnection(addr, self): | |
347 self.connectCompleted(addr, 0) | |
348 self.transport.stopReading() | |
349 else: | |
350 self.sendErrorReply(socks5.REPLY_CONN_REFUSED) | |
351 | |
352 def getProgress(self, data): | |
353 """Fill data with position of current transfert""" | |
354 data["size"] = self.filesize | |
355 try: | |
356 data["position"] = str(self.dest_file.tell()) | |
357 except (ValueError, AttributeError): | |
358 data["position"] = "" | |
359 | |
360 def fileTransfered(self, d): | |
361 info("File transfer completed, closing connection") | |
362 self.transport.loseConnection() | |
363 | |
364 def updateTransfered(self, data): | |
365 self.transfered+=len(data) | |
366 return data | |
367 | |
368 def connectCompleted(self, remotehost, remoteport): | |
369 debug("connectCompleted") | |
370 if self.addressType == ADDR_IPV4: | |
371 result = struct.pack('!BBBBIH', SOCKS5_VER, REPLY_SUCCESS, 0, 1, remotehost, remoteport) | |
372 elif self.addressType == ADDR_DOMAINNAME: | |
373 result = struct.pack('!BBBBB%dsH' % len(remotehost), SOCKS5_VER, REPLY_SUCCESS, 0, | |
374 ADDR_DOMAINNAME, len(remotehost), remotehost, remoteport) | |
375 self.transport.write(result) | |
376 self.state = STATE_READY | |
377 self.dest_file=open(self.filepath) | |
378 d=self.beginFileTransfer(self.dest_file, self.transport, self.updateTransfered) | |
379 d.addCallback(self.fileTransfered) | |
380 | |
381 def bindRequested(self, addr, port): | |
382 pass | |
383 | |
384 def authenticateUserPass(self, user, passwd): | |
385 debug("User/pass: %s/%s", user, passwd) | |
386 return True | |
387 | |
388 def dataReceived(self, buf): | |
389 if self.state == STATE_TARGET_READY: | |
390 self.dest_file.write(buf) | |
391 self.transfered+=len(buf) | |
392 return | |
393 | |
394 self.buf = self.buf + buf | |
395 if self.state == STATE_INITIAL: | |
396 self._parseNegotiation() | |
397 if self.state == STATE_AUTH_USERPASS: | |
398 self._parseUserPass() | |
399 if self.state == STATE_REQUEST: | |
400 self._parseRequest() | |
401 if self.state == STATE_TARGET_AUTH: | |
402 ver, method = struct.unpack('!BB', buf) | |
403 self.buf = self.buf[2:] | |
404 if ver!=SOCKS5_VER or method!=AUTHMECH_ANON: | |
405 self.transport.loseConnection() | |
406 else: | |
407 self._makeRequest() | |
408 if self.state == STATE_TARGET_REQUEST: | |
409 self._parseRequestReply() | |
410 | |
411 | |
412 def clientConnectionLost(self, reason): | |
413 debug("clientConnectionLost") | |
414 self.transport.loseConnection() | |
415 | |
416 def connectionLost(self, reason): | |
417 debug("connectionLost") | |
418 self.host.removeProgressCB(self.transfert_id) | |
419 if self.state == STATE_CONNECT_PENDING: | |
420 self.removePendingConnection(self.addr, self) | |
421 else: | |
422 self.transport.unregisterProducer() | |
423 if self.peersock != None: | |
424 self.peersock.peersock = None | |
425 self.peersock.transport.unregisterProducer() | |
426 self.peersock = None | |
427 self.removeActiveConnection(self.addr) | |
428 | |
429 class Socks5ServerFactory(protocol.ServerFactory): | |
430 protocol = SOCKSv5 | |
431 protocol.mode = "initiator" #FIXME: Q&D way, fix it | |
432 | |
433 | |
434 def startedConnecting(self, connector): | |
435 debug ("Socks 5 server connection started") | |
436 | |
437 def clientConnectionLost(self, connector, reason): | |
438 debug ("Socks 5 server connection lost (reason: %s)", reason) | |
439 | |
440 class Socks5ClientFactory(protocol.ClientFactory): | |
441 protocol = SOCKSv5 | |
442 protocol.mode = "target" #FIXME: Q&D way, fix it | |
443 | |
444 def startedConnecting(self, connector): | |
445 debug ("Socks 5 client connection started") | |
446 | |
447 def clientConnectionLost(self, connector, reason): | |
448 debug ("Socks 5 client connection lost (reason: %s)", reason) | |
449 | |
450 | |
15
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
451 class XEP_0065(XMPPHandler): |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
452 implements(iwokkel.IDisco) |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
453 |
0 | 454 def __init__(self, host): |
455 info("Plugin XEP_0065 initialization") | |
456 self.host = host | |
457 debug("registering") | |
458 self.server_factory = Socks5ServerFactory() | |
459 self.server_factory.protocol.host = self.host #needed for progress CB | |
460 self.client_factory = Socks5ClientFactory() | |
461 port = int(self.host.memory.getParamV("Port", "File Transfert")) | |
462 info("Launching Socks5 Stream server on port %d", port) | |
463 reactor.listenTCP(port, self.server_factory) | |
15
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
464 |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
465 def connectionInitialized(self): |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
466 self.xmlstream.addObserver(BS_REQUEST, self.getFile) |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
467 |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
468 |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
469 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
470 return [disco.DiscoFeature(NS_BS)] |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
471 |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
472 def getDiscoItems(self, requestor, target, nodeIdentifier=''): |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
473 return [] |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
474 |
0 | 475 |
476 def setData(self, data, id): | |
477 self.data = data | |
478 self.transfert_id = id | |
479 | |
480 def sendFile(self, id, filepath, size): | |
481 #lauching socks5 initiator | |
8 | 482 debug("Launching socks5 initiator") |
0 | 483 self.server_factory.protocol.mode = "initiator" |
484 self.server_factory.protocol.filepath = filepath | |
485 self.server_factory.protocol.filesize = size | |
486 self.server_factory.protocol.transfert_id = id | |
487 | |
15
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
488 def getFile(self, iq): |
0 | 489 """Get file using byte stream""" |
15
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
490 iq.handled = True |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
491 SI_elem = iq.firstChildElement() |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
492 IQ_id = iq['id'] |
0 | 493 for element in SI_elem.elements(): |
494 if element.name == "streamhost": | |
495 info ("Stream proposed: host=[%s] port=[%s]", element['host'], element['port']) | |
496 factory = self.client_factory | |
497 self.server_factory.protocol.mode = "target" | |
498 factory.protocol.host = self.host #needed for progress CB | |
499 factory.protocol.data = self.data | |
500 factory.protocol.transfert_id = self.transfert_id | |
501 factory.protocol.filesize = self.data["size"] | |
502 factory.protocol.sid = SI_elem['sid'] | |
503 factory.protocol.initiator_jid = element['jid'] | |
504 factory.protocol.target_jid = self.host.me.full() | |
505 factory.protocol.IQ_id = IQ_id | |
506 factory.protocol.activateCB = self.activateStream | |
507 reactor.connectTCP(element['host'], int(element['port']), factory) | |
508 | |
509 def activateStream(self, from_jid, to_jid, sid, IQ_id): | |
510 debug("activating stream") | |
511 result = domish.Element(('', 'iq')) | |
512 result['type'] = 'result' | |
513 result['id'] = IQ_id | |
514 result['from'] = from_jid | |
515 result['to'] = to_jid | |
516 query = result.addElement('query', 'http://jabber.org/protocol/bytestreams') | |
517 query['sid'] = sid | |
518 streamhost = query.addElement('streamhost-used') | |
519 streamhost['jid'] = to_jid #FIXME: use real streamhost | |
520 self.host.xmlstream.send(result) | |
521 |