Mercurial > libervia-backend
annotate src/plugins/plugin_xep_0065.py @ 297:c5554e2939dd
plugin XEP 0277: author for in request + author, updated management for out request
- a workaround is now used to parse "nick" tag (Jappix behaviour)
- author and updated can now be used in data when sendind microblog. Is no author is given, user jid is used, if no updated is given, current timestamp is used
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 18 Feb 2011 22:32:02 +0100 |
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 [] |