Mercurial > libervia-backend
annotate plugins/plugin_xep_0065.py @ 65:d35c5edab53f
SàT: multi-profile: memory & dbus bridge's methods profile management
/!\ profiles not managed yet for dbus signals
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 31 Jan 2010 15:57:03 +1100 |
parents | d46f849664aa |
children | 86f1f7f6d332 |
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) | |
57 | 9 2009, 2010 Jérôme Poisson (goffi@goffi.org) |
0 | 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 | |
20
fc8c202cda87
refactoring: using xml params part IV (default values)
Goffi <goffi@goffi.org>
parents:
19
diff
changeset
|
62 from twisted.web.client import getPage |
0 | 63 import struct |
20
fc8c202cda87
refactoring: using xml params part IV (default values)
Goffi <goffi@goffi.org>
parents:
19
diff
changeset
|
64 import urllib |
0 | 65 import hashlib, pdb |
66 | |
15
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
67 from zope.interface import implements |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
68 |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
69 try: |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
70 from twisted.words.protocols.xmlstream import XMPPHandler |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
71 except ImportError: |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
72 from wokkel.subprotocols import XMPPHandler |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
73 |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
74 from wokkel import disco, iwokkel |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
75 |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
76 IQ_SET = '/iq[@type="set"]' |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
77 NS_BS = 'http://jabber.org/protocol/bytestreams' |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
78 BS_REQUEST = IQ_SET + '/query[@xmlns="' + NS_BS + '"]' |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
79 |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
80 |
0 | 81 |
82 PLUGIN_INFO = { | |
83 "name": "XEP 0065 Plugin", | |
84 "import_name": "XEP_0065", | |
85 "type": "XEP", | |
48 | 86 "protocols": ["XEP-0065"], |
0 | 87 "main": "XEP_0065", |
64 | 88 "handler": "yes", |
9 | 89 "description": """Implementation of SOCKS5 Bytestreams""" |
0 | 90 } |
91 | |
92 STATE_INITIAL = 0 | |
93 STATE_AUTH = 1 | |
94 STATE_REQUEST = 2 | |
95 STATE_READY = 3 | |
96 STATE_AUTH_USERPASS = 4 | |
97 STATE_TARGET_INITIAL = 5 | |
98 STATE_TARGET_AUTH = 6 | |
99 STATE_TARGET_REQUEST = 7 | |
100 STATE_TARGET_READY = 8 | |
101 STATE_LAST = 9 | |
102 | |
103 STATE_CONNECT_PENDING = STATE_LAST + 1 | |
104 | |
105 SOCKS5_VER = 0x05 | |
106 | |
107 ADDR_IPV4 = 0x01 | |
108 ADDR_DOMAINNAME = 0x03 | |
109 ADDR_IPV6 = 0x04 | |
110 | |
111 CMD_CONNECT = 0x01 | |
112 CMD_BIND = 0x02 | |
113 CMD_UDPASSOC = 0x03 | |
114 | |
115 AUTHMECH_ANON = 0x00 | |
116 AUTHMECH_USERPASS = 0x02 | |
117 AUTHMECH_INVALID = 0xFF | |
118 | |
119 REPLY_SUCCESS = 0x00 | |
120 REPLY_GENERAL_FAILUR = 0x01 | |
121 REPLY_CONN_NOT_ALLOWED = 0x02 | |
122 REPLY_NETWORK_UNREACHABLE = 0x03 | |
123 REPLY_HOST_UNREACHABLE = 0x04 | |
124 REPLY_CONN_REFUSED = 0x05 | |
125 REPLY_TTL_EXPIRED = 0x06 | |
126 REPLY_CMD_NOT_SUPPORTED = 0x07 | |
127 REPLY_ADDR_NOT_SUPPORTED = 0x08 | |
128 | |
129 | |
130 | |
131 | |
132 | |
133 class SOCKSv5(protocol.Protocol, FileSender): | |
134 def __init__(self): | |
135 debug("Protocol init") | |
136 self.state = STATE_INITIAL | |
137 self.buf = "" | |
138 self.supportedAuthMechs = [ AUTHMECH_ANON ] | |
139 self.supportedAddrs = [ ADDR_DOMAINNAME ] | |
140 self.enabledCommands = [ CMD_CONNECT ] | |
141 self.peersock = None | |
142 self.addressType = 0 | |
143 self.requestType = 0 | |
144 self.activeConns = {} | |
145 self.pendingConns = {} | |
146 self.transfered = 0 #nb of bytes already copied | |
147 | |
148 def _startNegotiation(self): | |
149 debug("_startNegotiation") | |
150 self.state = STATE_TARGET_AUTH | |
151 self.transport.write(struct.pack('!3B', SOCKS5_VER, 1, AUTHMECH_ANON)) | |
152 | |
153 def _parseNegotiation(self): | |
154 debug("_parseNegotiation") | |
155 try: | |
156 # Parse out data | |
157 ver, nmethod = struct.unpack('!BB', self.buf[:2]) | |
158 methods = struct.unpack('%dB' % nmethod, self.buf[2:nmethod+2]) | |
159 | |
160 # Ensure version is correct | |
161 if ver != 5: | |
162 self.transport.write(struct.pack('!BB', SOCKS5_VER, AUTHMECH_INVALID)) | |
163 self.transport.loseConnection() | |
164 return | |
165 | |
166 # Trim off front of the buffer | |
167 self.buf = self.buf[nmethod+2:] | |
168 | |
169 # Check for supported auth mechs | |
170 for m in self.supportedAuthMechs: | |
171 if m in methods: | |
172 # Update internal state, according to selected method | |
173 if m == AUTHMECH_ANON: | |
174 self.state = STATE_REQUEST | |
175 elif m == AUTHMECH_USERPASS: | |
176 self.state = STATE_AUTH_USERPASS | |
177 # Complete negotiation w/ this method | |
178 self.transport.write(struct.pack('!BB', SOCKS5_VER, m)) | |
179 return | |
180 | |
181 # No supported mechs found, notify client and close the connection | |
182 self.transport.write(struct.pack('!BB', SOCKS5_VER, AUTHMECH_INVALID)) | |
183 self.transport.loseConnection() | |
184 except struct.error: | |
185 pass | |
186 | |
187 def _parseUserPass(self): | |
188 debug("_parseUserPass") | |
189 try: | |
190 # Parse out data | |
191 ver, ulen = struct.unpack('BB', self.buf[:2]) | |
192 uname, = struct.unpack('%ds' % ulen, self.buf[2:ulen + 2]) | |
193 plen, = struct.unpack('B', self.buf[ulen + 2]) | |
194 password, = struct.unpack('%ds' % plen, self.buf[ulen + 3:ulen + 3 + plen]) | |
195 # Trim off fron of the buffer | |
196 self.buf = self.buf[3 + ulen + plen:] | |
197 # Fire event to authenticate user | |
198 if self.authenticateUserPass(uname, password): | |
199 # Signal success | |
200 self.state = STATE_REQUEST | |
201 self.transport.write(struct.pack('!BB', SOCKS5_VER, 0x00)) | |
202 else: | |
203 # Signal failure | |
204 self.transport.write(struct.pack('!BB', SOCKS5_VER, 0x01)) | |
205 self.transport.loseConnection() | |
206 except struct.error: | |
207 pass | |
208 | |
209 def sendErrorReply(self, errorcode): | |
210 debug("sendErrorReply") | |
211 # Any other address types are not supported | |
212 result = struct.pack('!BBBBIH', SOCKS5_VER, errorcode, 0, 1, 0, 0) | |
213 self.transport.write(result) | |
214 self.transport.loseConnection() | |
215 | |
216 def addConnection(self, address, connection): | |
217 info("Adding connection: %s, %s", address, connection) | |
218 olist = self.pendingConns.get(address, []) | |
219 if len(olist) <= 1: | |
220 olist.append(connection) | |
221 self.pendingConns[address] = olist | |
222 return True | |
223 else: | |
224 return False | |
225 | |
226 def removePendingConnection(self, address, connection): | |
227 olist = self.pendingConns[address] | |
228 if len(olist) == 1: | |
229 del self.pendingConns[address] | |
230 else: | |
231 olist.remove(connection) | |
232 self.pendingConns[address] = olist | |
233 | |
234 def removeActiveConnection(self, address): | |
235 del self.activeConns[address] | |
236 | |
237 def _parseRequest(self): | |
238 debug("_parseRequest") | |
239 try: | |
240 # Parse out data and trim buffer accordingly | |
241 ver, cmd, rsvd, self.addressType = struct.unpack('!BBBB', self.buf[:4]) | |
242 | |
243 # Ensure we actually support the requested address type | |
244 if self.addressType not in self.supportedAddrs: | |
245 self.sendErrorReply(REPLY_ADDR_NOT_SUPPORTED) | |
246 return | |
247 | |
248 # Deal with addresses | |
249 if self.addressType == ADDR_IPV4: | |
250 addr, port = struct.unpack('!IH', self.buf[4:10]) | |
251 self.buf = self.buf[10:] | |
252 elif self.addressType == ADDR_DOMAINNAME: | |
253 nlen = ord(self.buf[4]) | |
254 addr, port = struct.unpack('!%dsH' % nlen, self.buf[5:]) | |
255 self.buf = self.buf[7 + len(addr):] | |
256 else: | |
257 # Any other address types are not supported | |
258 self.sendErrorReply(REPLY_ADDR_NOT_SUPPORTED) | |
259 return | |
260 | |
261 # Ensure command is supported | |
262 if cmd not in self.enabledCommands: | |
263 # Send a not supported error | |
264 self.sendErrorReply(REPLY_CMD_NOT_SUPPORTED) | |
265 return | |
266 | |
267 # Process the command | |
268 if cmd == CMD_CONNECT: | |
269 self.connectRequested(addr, port) | |
270 elif cmd == CMD_BIND: | |
271 self.bindRequested(addr, port) | |
272 else: | |
273 # Any other command is not supported | |
274 self.sendErrorReply(REPLY_CMD_NOT_SUPPORTED) | |
275 | |
276 except struct.error, why: | |
277 return None | |
278 | |
279 def _makeRequest(self): | |
280 debug("_makeRequest") | |
281 self.state = STATE_TARGET_REQUEST | |
282 sha1 = hashlib.sha1(self.sid + self.initiator_jid + self.target_jid).hexdigest() | |
283 request = struct.pack('!5B%dsH' % len(sha1), SOCKS5_VER, CMD_CONNECT, 0, ADDR_DOMAINNAME, len(sha1), sha1, 0) | |
284 self.transport.write(request) | |
285 | |
286 def _parseRequestReply(self): | |
287 debug("_parseRequestReply") | |
288 try: | |
289 ver, rep, rsvd, self.addressType = struct.unpack('!BBBB', self.buf[:4]) | |
290 # Ensure we actually support the requested address type | |
291 if self.addressType not in self.supportedAddrs: | |
292 self.sendErrorReply(REPLY_ADDR_NOT_SUPPORTED) | |
293 return | |
294 | |
295 # Deal with addresses | |
296 if self.addressType == ADDR_IPV4: | |
297 addr, port = struct.unpack('!IH', self.buf[4:10]) | |
298 self.buf = self.buf[10:] | |
299 elif self.addressType == ADDR_DOMAINNAME: | |
300 nlen = ord(self.buf[4]) | |
301 addr, port = struct.unpack('!%dsH' % nlen, self.buf[5:]) | |
302 self.buf = self.buf[7 + len(addr):] | |
303 else: | |
304 # Any other address types are not supported | |
305 self.sendErrorReply(REPLY_ADDR_NOT_SUPPORTED) | |
306 return | |
307 | |
308 # Ensure reply is OK | |
309 if rep != REPLY_SUCCESS: | |
310 self.loseConnection() | |
311 return | |
312 | |
313 debug("Saving file in %s.", self.data["dest_path"]) | |
314 self.dest_file = open(self.data["dest_path"], 'w') | |
315 self.state = STATE_TARGET_READY | |
64 | 316 self.activateCB(self.target_jid, self.initiator_jid, self.sid, self.IQ_id, self.xmlstream) |
0 | 317 |
318 | |
319 except struct.error, why: | |
320 return None | |
321 | |
322 def connectionMade(self): | |
323 debug("connectionMade (mode = %s)" % self.mode) | |
324 self.host.registerProgressCB(self.transfert_id, self.getProgress) | |
325 | |
326 if self.mode == "target": | |
327 self.state = STATE_TARGET_INITIAL | |
328 self._startNegotiation() | |
329 | |
330 def connectRequested(self, addr, port): | |
331 debug(("connectRequested")) | |
332 # Check for special connect to the namespace -- this signifies that the client | |
333 # is just checking to ensure it can connect to the streamhost | |
334 if addr == "http://jabber.org/protocol/bytestreams": | |
335 self.connectCompleted(addr, 0) | |
336 self.transport.loseConnection() | |
337 return | |
338 | |
339 # Save addr, for cleanup | |
340 self.addr = addr | |
341 | |
342 # Check to see if the requested address is already | |
343 # activated -- send an error if so | |
344 if addr in self.activeConns: | |
345 self.sendErrorReply(socks5.REPLY_CONN_NOT_ALLOWED) | |
346 return | |
347 | |
348 # Add this address to the pending connections | |
349 if self.addConnection(addr, self): | |
350 self.connectCompleted(addr, 0) | |
351 self.transport.stopReading() | |
352 else: | |
353 self.sendErrorReply(socks5.REPLY_CONN_REFUSED) | |
354 | |
355 def getProgress(self, data): | |
356 """Fill data with position of current transfert""" | |
357 data["size"] = self.filesize | |
358 try: | |
359 data["position"] = str(self.dest_file.tell()) | |
360 except (ValueError, AttributeError): | |
361 data["position"] = "" | |
362 | |
363 def fileTransfered(self, d): | |
364 info("File transfer completed, closing connection") | |
365 self.transport.loseConnection() | |
366 | |
367 def updateTransfered(self, data): | |
368 self.transfered+=len(data) | |
369 return data | |
370 | |
371 def connectCompleted(self, remotehost, remoteport): | |
372 debug("connectCompleted") | |
373 if self.addressType == ADDR_IPV4: | |
374 result = struct.pack('!BBBBIH', SOCKS5_VER, REPLY_SUCCESS, 0, 1, remotehost, remoteport) | |
375 elif self.addressType == ADDR_DOMAINNAME: | |
376 result = struct.pack('!BBBBB%dsH' % len(remotehost), SOCKS5_VER, REPLY_SUCCESS, 0, | |
377 ADDR_DOMAINNAME, len(remotehost), remotehost, remoteport) | |
378 self.transport.write(result) | |
379 self.state = STATE_READY | |
380 self.dest_file=open(self.filepath) | |
381 d=self.beginFileTransfer(self.dest_file, self.transport, self.updateTransfered) | |
382 d.addCallback(self.fileTransfered) | |
383 | |
384 def bindRequested(self, addr, port): | |
385 pass | |
386 | |
387 def authenticateUserPass(self, user, passwd): | |
388 debug("User/pass: %s/%s", user, passwd) | |
389 return True | |
390 | |
391 def dataReceived(self, buf): | |
392 if self.state == STATE_TARGET_READY: | |
393 self.dest_file.write(buf) | |
394 self.transfered+=len(buf) | |
395 return | |
396 | |
397 self.buf = self.buf + buf | |
398 if self.state == STATE_INITIAL: | |
399 self._parseNegotiation() | |
400 if self.state == STATE_AUTH_USERPASS: | |
401 self._parseUserPass() | |
402 if self.state == STATE_REQUEST: | |
403 self._parseRequest() | |
404 if self.state == STATE_TARGET_AUTH: | |
405 ver, method = struct.unpack('!BB', buf) | |
406 self.buf = self.buf[2:] | |
407 if ver!=SOCKS5_VER or method!=AUTHMECH_ANON: | |
408 self.transport.loseConnection() | |
409 else: | |
410 self._makeRequest() | |
411 if self.state == STATE_TARGET_REQUEST: | |
412 self._parseRequestReply() | |
413 | |
414 | |
415 def clientConnectionLost(self, reason): | |
416 debug("clientConnectionLost") | |
417 self.transport.loseConnection() | |
418 | |
419 def connectionLost(self, reason): | |
420 debug("connectionLost") | |
421 self.host.removeProgressCB(self.transfert_id) | |
422 if self.state == STATE_CONNECT_PENDING: | |
423 self.removePendingConnection(self.addr, self) | |
424 else: | |
425 self.transport.unregisterProducer() | |
426 if self.peersock != None: | |
427 self.peersock.peersock = None | |
428 self.peersock.transport.unregisterProducer() | |
429 self.peersock = None | |
430 self.removeActiveConnection(self.addr) | |
431 | |
432 class Socks5ServerFactory(protocol.ServerFactory): | |
433 protocol = SOCKSv5 | |
434 protocol.mode = "initiator" #FIXME: Q&D way, fix it | |
435 | |
436 | |
437 def startedConnecting(self, connector): | |
438 debug ("Socks 5 server connection started") | |
439 | |
440 def clientConnectionLost(self, connector, reason): | |
441 debug ("Socks 5 server connection lost (reason: %s)", reason) | |
442 | |
443 class Socks5ClientFactory(protocol.ClientFactory): | |
444 protocol = SOCKSv5 | |
445 protocol.mode = "target" #FIXME: Q&D way, fix it | |
446 | |
447 def startedConnecting(self, connector): | |
448 debug ("Socks 5 client connection started") | |
449 | |
450 def clientConnectionLost(self, connector, reason): | |
451 debug ("Socks 5 client connection lost (reason: %s)", reason) | |
452 | |
453 | |
64 | 454 class XEP_0065(): |
15
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
455 |
19
f2a745ca0fbc
refactoring: using xml params part III (parameters import)
Goffi <goffi@goffi.org>
parents:
15
diff
changeset
|
456 params = """ |
f2a745ca0fbc
refactoring: using xml params part III (parameters import)
Goffi <goffi@goffi.org>
parents:
15
diff
changeset
|
457 <params> |
60
9764e027ecc0
SàT: multi-profile parameters, first draft
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
458 <general> |
19
f2a745ca0fbc
refactoring: using xml params part III (parameters import)
Goffi <goffi@goffi.org>
parents:
15
diff
changeset
|
459 <category name="File Transfert"> |
20
fc8c202cda87
refactoring: using xml params part IV (default values)
Goffi <goffi@goffi.org>
parents:
19
diff
changeset
|
460 <param name="IP" value='0.0.0.0' default_cb='yes' type="string" /> |
19
f2a745ca0fbc
refactoring: using xml params part III (parameters import)
Goffi <goffi@goffi.org>
parents:
15
diff
changeset
|
461 <param name="Port" value="28915" type="string" /> |
f2a745ca0fbc
refactoring: using xml params part III (parameters import)
Goffi <goffi@goffi.org>
parents:
15
diff
changeset
|
462 </category> |
60
9764e027ecc0
SàT: multi-profile parameters, first draft
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
463 </general> |
19
f2a745ca0fbc
refactoring: using xml params part III (parameters import)
Goffi <goffi@goffi.org>
parents:
15
diff
changeset
|
464 </params> |
f2a745ca0fbc
refactoring: using xml params part III (parameters import)
Goffi <goffi@goffi.org>
parents:
15
diff
changeset
|
465 """ |
20
fc8c202cda87
refactoring: using xml params part IV (default values)
Goffi <goffi@goffi.org>
parents:
19
diff
changeset
|
466 |
0 | 467 def __init__(self, host): |
468 info("Plugin XEP_0065 initialization") | |
469 self.host = host | |
470 debug("registering") | |
471 self.server_factory = Socks5ServerFactory() | |
472 self.server_factory.protocol.host = self.host #needed for progress CB | |
473 self.client_factory = Socks5ClientFactory() | |
21
633c5ed65701
parameters: new button type (not finished)
Goffi <goffi@goffi.org>
parents:
20
diff
changeset
|
474 |
633c5ed65701
parameters: new button type (not finished)
Goffi <goffi@goffi.org>
parents:
20
diff
changeset
|
475 #parameters |
38 | 476 host.memory.importParams(XEP_0065.params) |
20
fc8c202cda87
refactoring: using xml params part IV (default values)
Goffi <goffi@goffi.org>
parents:
19
diff
changeset
|
477 host.memory.setDefault("IP", "File Transfert", self.getExternalIP) |
21
633c5ed65701
parameters: new button type (not finished)
Goffi <goffi@goffi.org>
parents:
20
diff
changeset
|
478 |
22
bb72c29f3432
added action cb mechanism for buttons. Tested with a temporary new user registration button.
Goffi <goffi@goffi.org>
parents:
21
diff
changeset
|
479 port = int(self.host.memory.getParamA("Port", "File Transfert")) |
0 | 480 info("Launching Socks5 Stream server on port %d", port) |
481 reactor.listenTCP(port, self.server_factory) | |
15
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
482 |
64 | 483 def getHandler(self): |
484 return XEP_0065_handler(self) | |
485 | |
20
fc8c202cda87
refactoring: using xml params part IV (default values)
Goffi <goffi@goffi.org>
parents:
19
diff
changeset
|
486 def getExternalIP(self): |
fc8c202cda87
refactoring: using xml params part IV (default values)
Goffi <goffi@goffi.org>
parents:
19
diff
changeset
|
487 """Return IP visible from outside, by asking to a website""" |
fc8c202cda87
refactoring: using xml params part IV (default values)
Goffi <goffi@goffi.org>
parents:
19
diff
changeset
|
488 return getPage("http://www.goffi.org/sat_tools/get_ip.php") |
0 | 489 |
490 def setData(self, data, id): | |
491 self.data = data | |
492 self.transfert_id = id | |
493 | |
494 def sendFile(self, id, filepath, size): | |
495 #lauching socks5 initiator | |
8 | 496 debug("Launching socks5 initiator") |
0 | 497 self.server_factory.protocol.mode = "initiator" |
498 self.server_factory.protocol.filepath = filepath | |
499 self.server_factory.protocol.filesize = size | |
500 self.server_factory.protocol.transfert_id = id | |
501 | |
64 | 502 def getFile(self, iq, profile_key='@DEFAULT@'): |
0 | 503 """Get file using byte stream""" |
64 | 504 client = self.host.getClient(profile_key) |
505 assert(client) | |
15
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
506 iq.handled = True |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
507 SI_elem = iq.firstChildElement() |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
508 IQ_id = iq['id'] |
0 | 509 for element in SI_elem.elements(): |
510 if element.name == "streamhost": | |
511 info ("Stream proposed: host=[%s] port=[%s]", element['host'], element['port']) | |
512 factory = self.client_factory | |
513 self.server_factory.protocol.mode = "target" | |
514 factory.protocol.host = self.host #needed for progress CB | |
64 | 515 factory.protocol.xmlstream = client.xmlstream |
0 | 516 factory.protocol.data = self.data |
517 factory.protocol.transfert_id = self.transfert_id | |
518 factory.protocol.filesize = self.data["size"] | |
519 factory.protocol.sid = SI_elem['sid'] | |
520 factory.protocol.initiator_jid = element['jid'] | |
64 | 521 factory.protocol.target_jid = client.jid.full() |
0 | 522 factory.protocol.IQ_id = IQ_id |
523 factory.protocol.activateCB = self.activateStream | |
524 reactor.connectTCP(element['host'], int(element['port']), factory) | |
525 | |
64 | 526 def activateStream(self, from_jid, to_jid, sid, IQ_id, xmlstream): |
0 | 527 debug("activating stream") |
528 result = domish.Element(('', 'iq')) | |
529 result['type'] = 'result' | |
530 result['id'] = IQ_id | |
531 result['from'] = from_jid | |
532 result['to'] = to_jid | |
533 query = result.addElement('query', 'http://jabber.org/protocol/bytestreams') | |
534 query['sid'] = sid | |
535 streamhost = query.addElement('streamhost-used') | |
536 streamhost['jid'] = to_jid #FIXME: use real streamhost | |
64 | 537 xmlstream.send(result) |
0 | 538 |
64 | 539 class XEP_0065_handler(XMPPHandler): |
540 implements(iwokkel.IDisco) | |
541 | |
542 def __init__(self, plugin_parent): | |
543 self.plugin_parent = plugin_parent | |
544 self.host = plugin_parent.host | |
545 | |
546 def connectionInitialized(self): | |
547 self.xmlstream.addObserver(BS_REQUEST, self.plugin_parent.getFile) | |
548 | |
549 | |
550 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | |
551 return [disco.DiscoFeature(NS_BS)] | |
552 | |
553 def getDiscoItems(self, requestor, target, nodeIdentifier=''): | |
554 return [] |