Mercurial > libervia-backend
annotate src/plugins/plugin_xep_0065.py @ 341:9eebdc655b8b
code: added asyncConnect
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 26 May 2011 16:49:47 +0200 |
parents | 7c79d4a8c9e6 |
children | 8f3551ceee17 |
rev | line source |
---|---|
0 | 1 #!/usr/bin/python |
2 #-*- coding: utf-8 -*- | |
3 """ | |
4 SAT plugin for managing xep-0065 | |
5 | |
6 Copyright (C) | |
251
dbe1803eca7c
minor copyright update, date format harmonisation
Goffi <goffi@goffi.org>
parents:
228
diff
changeset
|
7 2002, 2003, 2004 Dave Smith (dizzyd@jabber.org) |
dbe1803eca7c
minor copyright update, date format harmonisation
Goffi <goffi@goffi.org>
parents:
228
diff
changeset
|
8 2007, 2008 Fabio Forno (xmpp:ff@jabber.bluendo.com) |
dbe1803eca7c
minor copyright update, date format harmonisation
Goffi <goffi@goffi.org>
parents:
228
diff
changeset
|
9 2009, 2010, 2011 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", | |
291 | 84 "import_name": "XEP-0065", |
0 | 85 "type": "XEP", |
48 | 86 "protocols": ["XEP-0065"], |
0 | 87 "main": "XEP_0065", |
64 | 88 "handler": "yes", |
69 | 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): | |
69 | 135 debug(_("Protocol init")) |
0 | 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): | |
69 | 217 info(_("Adding connection: %(address)s, %(connection)s") % {'address':address, 'connection':connection}) |
0 | 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 | |
69 | 313 debug(_("Saving file in %s."), self.data["dest_path"]) |
0 | 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): | |
69 | 331 debug("connectRequested") |
0 | 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 try: | |
358 data["position"] = str(self.dest_file.tell()) | |
178
bd24f2aed80c
Plugin XEP-0065: dest_file is now propertly closed after copy, and data dict in getProgress is not filled in case of file access error (file closed)
Goffi <goffi@goffi.org>
parents:
127
diff
changeset
|
359 data["size"] = self.filesize |
0 | 360 except (ValueError, AttributeError): |
178
bd24f2aed80c
Plugin XEP-0065: dest_file is now propertly closed after copy, and data dict in getProgress is not filled in case of file access error (file closed)
Goffi <goffi@goffi.org>
parents:
127
diff
changeset
|
361 pass |
0 | 362 |
363 def fileTransfered(self, d): | |
69 | 364 info(_("File transfer completed, closing connection")) |
0 | 365 self.transport.loseConnection() |
178
bd24f2aed80c
Plugin XEP-0065: dest_file is now propertly closed after copy, and data dict in getProgress is not filled in case of file access error (file closed)
Goffi <goffi@goffi.org>
parents:
127
diff
changeset
|
366 try: |
bd24f2aed80c
Plugin XEP-0065: dest_file is now propertly closed after copy, and data dict in getProgress is not filled in case of file access error (file closed)
Goffi <goffi@goffi.org>
parents:
127
diff
changeset
|
367 self.dest_file.close() |
bd24f2aed80c
Plugin XEP-0065: dest_file is now propertly closed after copy, and data dict in getProgress is not filled in case of file access error (file closed)
Goffi <goffi@goffi.org>
parents:
127
diff
changeset
|
368 except: |
bd24f2aed80c
Plugin XEP-0065: dest_file is now propertly closed after copy, and data dict in getProgress is not filled in case of file access error (file closed)
Goffi <goffi@goffi.org>
parents:
127
diff
changeset
|
369 pass |
0 | 370 |
371 def updateTransfered(self, data): | |
372 self.transfered+=len(data) | |
373 return data | |
374 | |
375 def connectCompleted(self, remotehost, remoteport): | |
376 debug("connectCompleted") | |
377 if self.addressType == ADDR_IPV4: | |
378 result = struct.pack('!BBBBIH', SOCKS5_VER, REPLY_SUCCESS, 0, 1, remotehost, remoteport) | |
379 elif self.addressType == ADDR_DOMAINNAME: | |
380 result = struct.pack('!BBBBB%dsH' % len(remotehost), SOCKS5_VER, REPLY_SUCCESS, 0, | |
381 ADDR_DOMAINNAME, len(remotehost), remotehost, remoteport) | |
382 self.transport.write(result) | |
383 self.state = STATE_READY | |
384 self.dest_file=open(self.filepath) | |
385 d=self.beginFileTransfer(self.dest_file, self.transport, self.updateTransfered) | |
386 d.addCallback(self.fileTransfered) | |
387 | |
388 def bindRequested(self, addr, port): | |
389 pass | |
390 | |
391 def authenticateUserPass(self, user, passwd): | |
392 debug("User/pass: %s/%s", user, passwd) | |
393 return True | |
394 | |
395 def dataReceived(self, buf): | |
396 if self.state == STATE_TARGET_READY: | |
397 self.dest_file.write(buf) | |
398 self.transfered+=len(buf) | |
399 return | |
400 | |
401 self.buf = self.buf + buf | |
402 if self.state == STATE_INITIAL: | |
403 self._parseNegotiation() | |
404 if self.state == STATE_AUTH_USERPASS: | |
405 self._parseUserPass() | |
406 if self.state == STATE_REQUEST: | |
407 self._parseRequest() | |
408 if self.state == STATE_TARGET_AUTH: | |
409 ver, method = struct.unpack('!BB', buf) | |
410 self.buf = self.buf[2:] | |
411 if ver!=SOCKS5_VER or method!=AUTHMECH_ANON: | |
412 self.transport.loseConnection() | |
413 else: | |
414 self._makeRequest() | |
415 if self.state == STATE_TARGET_REQUEST: | |
416 self._parseRequestReply() | |
417 | |
418 | |
419 def clientConnectionLost(self, reason): | |
420 debug("clientConnectionLost") | |
421 self.transport.loseConnection() | |
422 | |
423 def connectionLost(self, reason): | |
424 debug("connectionLost") | |
425 self.host.removeProgressCB(self.transfert_id) | |
426 if self.state == STATE_CONNECT_PENDING: | |
427 self.removePendingConnection(self.addr, self) | |
428 else: | |
429 self.transport.unregisterProducer() | |
430 if self.peersock != None: | |
431 self.peersock.peersock = None | |
432 self.peersock.transport.unregisterProducer() | |
433 self.peersock = None | |
434 self.removeActiveConnection(self.addr) | |
435 | |
436 class Socks5ServerFactory(protocol.ServerFactory): | |
437 protocol = SOCKSv5 | |
438 protocol.mode = "initiator" #FIXME: Q&D way, fix it | |
439 | |
440 | |
441 def startedConnecting(self, connector): | |
69 | 442 debug (_("Socks 5 server connection started")) |
0 | 443 |
444 def clientConnectionLost(self, connector, reason): | |
69 | 445 debug (_("Socks 5 server connection lost (reason: %s)"), reason) |
0 | 446 |
447 class Socks5ClientFactory(protocol.ClientFactory): | |
448 protocol = SOCKSv5 | |
449 protocol.mode = "target" #FIXME: Q&D way, fix it | |
450 | |
451 def startedConnecting(self, connector): | |
69 | 452 debug (_("Socks 5 client connection started")) |
0 | 453 |
454 def clientConnectionLost(self, connector, reason): | |
69 | 455 debug (_("Socks 5 client connection lost (reason: %s)"), reason) |
0 | 456 |
457 | |
64 | 458 class XEP_0065(): |
15
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
459 |
19
f2a745ca0fbc
refactoring: using xml params part III (parameters import)
Goffi <goffi@goffi.org>
parents:
15
diff
changeset
|
460 params = """ |
f2a745ca0fbc
refactoring: using xml params part III (parameters import)
Goffi <goffi@goffi.org>
parents:
15
diff
changeset
|
461 <params> |
60
9764e027ecc0
SàT: multi-profile parameters, first draft
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
462 <general> |
19
f2a745ca0fbc
refactoring: using xml params part III (parameters import)
Goffi <goffi@goffi.org>
parents:
15
diff
changeset
|
463 <category name="File Transfert"> |
20
fc8c202cda87
refactoring: using xml params part IV (default values)
Goffi <goffi@goffi.org>
parents:
19
diff
changeset
|
464 <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
|
465 <param name="Port" value="28915" type="string" /> |
f2a745ca0fbc
refactoring: using xml params part III (parameters import)
Goffi <goffi@goffi.org>
parents:
15
diff
changeset
|
466 </category> |
60
9764e027ecc0
SàT: multi-profile parameters, first draft
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
467 </general> |
19
f2a745ca0fbc
refactoring: using xml params part III (parameters import)
Goffi <goffi@goffi.org>
parents:
15
diff
changeset
|
468 </params> |
f2a745ca0fbc
refactoring: using xml params part III (parameters import)
Goffi <goffi@goffi.org>
parents:
15
diff
changeset
|
469 """ |
20
fc8c202cda87
refactoring: using xml params part IV (default values)
Goffi <goffi@goffi.org>
parents:
19
diff
changeset
|
470 |
0 | 471 def __init__(self, host): |
69 | 472 info(_("Plugin XEP_0065 initialization")) |
0 | 473 self.host = host |
69 | 474 debug(_("registering")) |
0 | 475 self.server_factory = Socks5ServerFactory() |
476 self.server_factory.protocol.host = self.host #needed for progress CB | |
477 self.client_factory = Socks5ClientFactory() | |
21
633c5ed65701
parameters: new button type (not finished)
Goffi <goffi@goffi.org>
parents:
20
diff
changeset
|
478 |
633c5ed65701
parameters: new button type (not finished)
Goffi <goffi@goffi.org>
parents:
20
diff
changeset
|
479 #parameters |
38 | 480 host.memory.importParams(XEP_0065.params) |
20
fc8c202cda87
refactoring: using xml params part IV (default values)
Goffi <goffi@goffi.org>
parents:
19
diff
changeset
|
481 host.memory.setDefault("IP", "File Transfert", self.getExternalIP) |
21
633c5ed65701
parameters: new button type (not finished)
Goffi <goffi@goffi.org>
parents:
20
diff
changeset
|
482 |
22
bb72c29f3432
added action cb mechanism for buttons. Tested with a temporary new user registration button.
Goffi <goffi@goffi.org>
parents:
21
diff
changeset
|
483 port = int(self.host.memory.getParamA("Port", "File Transfert")) |
69 | 484 info(_("Launching Socks5 Stream server on port %d"), port) |
0 | 485 reactor.listenTCP(port, self.server_factory) |
15
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
486 |
72 | 487 def getHandler(self, profile): |
64 | 488 return XEP_0065_handler(self) |
489 | |
20
fc8c202cda87
refactoring: using xml params part IV (default values)
Goffi <goffi@goffi.org>
parents:
19
diff
changeset
|
490 def getExternalIP(self): |
fc8c202cda87
refactoring: using xml params part IV (default values)
Goffi <goffi@goffi.org>
parents:
19
diff
changeset
|
491 """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
|
492 return getPage("http://www.goffi.org/sat_tools/get_ip.php") |
0 | 493 |
494 def setData(self, data, id): | |
495 self.data = data | |
496 self.transfert_id = id | |
497 | |
498 def sendFile(self, id, filepath, size): | |
499 #lauching socks5 initiator | |
69 | 500 debug(_("Launching socks5 initiator")) |
0 | 501 self.server_factory.protocol.mode = "initiator" |
502 self.server_factory.protocol.filepath = filepath | |
503 self.server_factory.protocol.filesize = size | |
504 self.server_factory.protocol.transfert_id = id | |
505 | |
64 | 506 def getFile(self, iq, profile_key='@DEFAULT@'): |
0 | 507 """Get file using byte stream""" |
64 | 508 client = self.host.getClient(profile_key) |
509 assert(client) | |
15
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
510 iq.handled = True |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
511 SI_elem = iq.firstChildElement() |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
9
diff
changeset
|
512 IQ_id = iq['id'] |
0 | 513 for element in SI_elem.elements(): |
514 if element.name == "streamhost": | |
127
55d3ef84f01f
Primitivus: mouse wheel changed to 'up' and 'down' key press
Goffi <goffi@goffi.org>
parents:
72
diff
changeset
|
515 info (_("Stream proposed: host=[%(host)s] port=[%(port)s]") % {'host':element['host'], 'port':element['port']}) |
0 | 516 factory = self.client_factory |
517 self.server_factory.protocol.mode = "target" | |
518 factory.protocol.host = self.host #needed for progress CB | |
64 | 519 factory.protocol.xmlstream = client.xmlstream |
0 | 520 factory.protocol.data = self.data |
521 factory.protocol.transfert_id = self.transfert_id | |
522 factory.protocol.filesize = self.data["size"] | |
523 factory.protocol.sid = SI_elem['sid'] | |
524 factory.protocol.initiator_jid = element['jid'] | |
64 | 525 factory.protocol.target_jid = client.jid.full() |
0 | 526 factory.protocol.IQ_id = IQ_id |
527 factory.protocol.activateCB = self.activateStream | |
528 reactor.connectTCP(element['host'], int(element['port']), factory) | |
529 | |
64 | 530 def activateStream(self, from_jid, to_jid, sid, IQ_id, xmlstream): |
69 | 531 debug(_("activating stream")) |
0 | 532 result = domish.Element(('', 'iq')) |
533 result['type'] = 'result' | |
534 result['id'] = IQ_id | |
535 result['from'] = from_jid | |
536 result['to'] = to_jid | |
537 query = result.addElement('query', 'http://jabber.org/protocol/bytestreams') | |
538 query['sid'] = sid | |
539 streamhost = query.addElement('streamhost-used') | |
540 streamhost['jid'] = to_jid #FIXME: use real streamhost | |
64 | 541 xmlstream.send(result) |
0 | 542 |
64 | 543 class XEP_0065_handler(XMPPHandler): |
544 implements(iwokkel.IDisco) | |
545 | |
546 def __init__(self, plugin_parent): | |
547 self.plugin_parent = plugin_parent | |
548 self.host = plugin_parent.host | |
549 | |
550 def connectionInitialized(self): | |
551 self.xmlstream.addObserver(BS_REQUEST, self.plugin_parent.getFile) | |
552 | |
553 | |
554 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | |
555 return [disco.DiscoFeature(NS_BS)] | |
556 | |
557 def getDiscoItems(self, requestor, target, nodeIdentifier=''): | |
558 return [] |