Mercurial > libervia-backend
annotate src/plugins/plugin_xep_0096.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 | f964dcec1611 |
rev | line source |
---|---|
0 | 1 #!/usr/bin/python |
2 # -*- coding: utf-8 -*- | |
3 | |
4 """ | |
5 SAT plugin for managing xep-0096 | |
228 | 6 Copyright (C) 2009, 2010, 2011 Jérôme Poisson (goffi@goffi.org) |
0 | 7 |
8 This program is free software: you can redistribute it and/or modify | |
9 it under the terms of the GNU General Public License as published by | |
10 the Free Software Foundation, either version 3 of the License, or | |
11 (at your option) any later version. | |
12 | |
13 This program is distributed in the hope that it will be useful, | |
14 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 GNU General Public License for more details. | |
17 | |
18 You should have received a copy of the GNU General Public License | |
19 along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 """ | |
21 | |
22 from logging import debug, info, error | |
23 from twisted.words.xish import domish | |
24 from twisted.internet import protocol | |
64 | 25 from twisted.words.protocols.jabber import client, jid |
39
2e3411a6baad
Wix: external server management in gateways manager, SàT: bug fixes in gateway management
Goffi <goffi@goffi.org>
parents:
22
diff
changeset
|
26 from twisted.words.protocols.jabber import error as jab_error |
0 | 27 import os.path |
28 from twisted.internet import reactor #FIXME best way ??? | |
1 | 29 import pdb |
0 | 30 |
15
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
8
diff
changeset
|
31 from zope.interface import implements |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
8
diff
changeset
|
32 |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
8
diff
changeset
|
33 try: |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
8
diff
changeset
|
34 from twisted.words.protocols.xmlstream import XMPPHandler |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
8
diff
changeset
|
35 except ImportError: |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
8
diff
changeset
|
36 from wokkel.subprotocols import XMPPHandler |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
8
diff
changeset
|
37 |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
8
diff
changeset
|
38 from wokkel import disco, iwokkel |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
8
diff
changeset
|
39 |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
8
diff
changeset
|
40 IQ_SET = '/iq[@type="set"]' |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
8
diff
changeset
|
41 NS_SI = 'http://jabber.org/protocol/si' |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
8
diff
changeset
|
42 SI_REQUEST = IQ_SET + '/si[@xmlns="' + NS_SI + '"]' |
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
8
diff
changeset
|
43 |
0 | 44 PLUGIN_INFO = { |
45 "name": "XEP 0096 Plugin", | |
291 | 46 "import_name": "XEP-0096", |
0 | 47 "type": "XEP", |
48 | 48 "protocols": ["XEP-0096"], |
291 | 49 "dependencies": ["XEP-0065"], |
0 | 50 "main": "XEP_0096", |
64 | 51 "handler": "yes", |
69 | 52 "description": _("""Implementation of SI File Transfert""") |
0 | 53 } |
54 | |
64 | 55 class XEP_0096(): |
19
f2a745ca0fbc
refactoring: using xml params part III (parameters import)
Goffi <goffi@goffi.org>
parents:
15
diff
changeset
|
56 |
0 | 57 def __init__(self, host): |
69 | 58 info(_("Plugin XEP_0096 initialization")) |
0 | 59 self.host = host |
60 self._waiting_for_approval = {} | |
64 | 61 host.bridge.addMethod("sendFile", ".communication", in_sign='sss', out_sign='s', method=self.sendFile) |
0 | 62 |
72 | 63 def getHandler(self, profile): |
64 | 64 return XEP_0096_handler(self) |
15
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
8
diff
changeset
|
65 |
64 | 66 def xep_96(self, IQ, profile): |
69 | 67 info (_("XEP-0096 management")) |
15
218ec9984fa5
wokkel integration part III + memory saved again
Goffi <goffi@goffi.org>
parents:
8
diff
changeset
|
68 IQ.handled=True |
0 | 69 SI_elem = IQ.firstChildElement() |
70 debug(SI_elem.toXml()) | |
71 filename = "" | |
72 file_size = "" | |
73 for element in SI_elem.elements(): | |
74 if element.name == "file": | |
69 | 75 info (_("File proposed: name=[%(name)s] size=%(size)s") % {'name':element['name'], 'size':element['size']}) |
0 | 76 filename = element["name"] |
77 file_size = element["size"] | |
78 elif element.name == "feature": | |
79 from_jid = IQ["from"] | |
64 | 80 self._waiting_for_approval[IQ["id"]] = (element, from_jid, file_size, profile) |
0 | 81 data={ "filename":filename, "from":from_jid, "size":file_size } |
82 self.host.askConfirmation(IQ["id"], "FILE_TRANSFERT", data, self.confirmationCB) | |
83 | |
84 def confirmationCB(self, id, accepted, data): | |
85 """Called on confirmation answer""" | |
86 if accepted: | |
87 data['size'] = self._waiting_for_approval[id][2] | |
291 | 88 self.host.plugins["XEP-0065"].setData(data, id) |
0 | 89 self.approved(id) |
90 else: | |
69 | 91 debug (_("Transfert [%s] refused"), id) |
0 | 92 del(self._waiting_for_approval[id]) |
93 | |
94 def approved(self, id): | |
95 """must be called when a file transfert has be accepted by client""" | |
69 | 96 debug (_("Transfert [%s] accepted"), id) |
0 | 97 |
98 if ( not self._waiting_for_approval.has_key(id) ): | |
69 | 99 error (_("Approved unknow id !")) |
0 | 100 #TODO: manage this (maybe approved by several frontends) |
101 else: | |
64 | 102 element, from_id, size, profile = self._waiting_for_approval[id] |
0 | 103 del(self._waiting_for_approval[id]) |
64 | 104 self.negociate(element, id, from_id, profile) |
0 | 105 |
64 | 106 def negociate(self, feat_elem, id, to_jid, profile): |
0 | 107 #TODO: put this in a plugin |
108 #FIXME: over ultra mega ugly, need to be generic | |
64 | 109 client = self.host.getClient(profile) |
110 assert(client) | |
69 | 111 info (_("Feature negociation")) |
0 | 112 data = feat_elem.firstChildElement() |
113 field = data.firstChildElement() | |
114 #FIXME: several options ! Q&D code for test only | |
115 option = field.firstChildElement() | |
116 value = option.firstChildElement() | |
117 if unicode(value) == "http://jabber.org/protocol/bytestreams": | |
118 #ugly, as usual, need to be entirely rewritten (just for test !) | |
119 result = domish.Element(('', 'iq')) | |
120 result['type'] = 'result' | |
121 result['id'] = id | |
122 result['to'] = to_jid | |
123 si = result.addElement('si', 'http://jabber.org/protocol/si') | |
124 file = si.addElement('file', 'http://jabber.org/protocol/si/profile/file-transfer') | |
125 feature = si.addElement('feature', 'http://jabber.org/protocol/feature-neg') | |
126 x = feature.addElement('x', 'jabber:x:data') | |
127 x['type'] = 'submit' | |
128 field = x.addElement('field') | |
129 field['var'] = 'stream-method' | |
130 value = field.addElement('value') | |
131 value.addContent('http://jabber.org/protocol/bytestreams') | |
64 | 132 client.xmlstream.send(result) |
0 | 133 |
64 | 134 def fileCB(self, answer, xmlstream, current_jid): |
0 | 135 if answer['type']=="result": #FIXME FIXME FIXME ugly ugly ugly ! and temp FIXME FIXME FIXME |
136 info("SENDING UGLY ANSWER") | |
64 | 137 offer=client.IQ(xmlstream,'set') |
138 offer["from"]=current_jid.full() | |
0 | 139 offer["to"]=answer['from'] |
140 query=offer.addElement('query', 'http://jabber.org/protocol/bytestreams') | |
141 query['mode']='tcp' | |
142 streamhost=query.addElement('streamhost') | |
22
bb72c29f3432
added action cb mechanism for buttons. Tested with a temporary new user registration button.
Goffi <goffi@goffi.org>
parents:
19
diff
changeset
|
143 streamhost['host']=self.host.memory.getParamA("IP", "File Transfert") |
bb72c29f3432
added action cb mechanism for buttons. Tested with a temporary new user registration button.
Goffi <goffi@goffi.org>
parents:
19
diff
changeset
|
144 streamhost['port']=self.host.memory.getParamA("Port", "File Transfert") |
64 | 145 streamhost['jid']=current_jid.full() |
0 | 146 offer.send() |
147 | |
64 | 148 def sendFile(self, to, filepath, profile_key='@DEFAULT@'): |
0 | 149 """send a file using XEP-0096 |
150 Return an unique id to identify the transfert | |
151 """ | |
64 | 152 current_jid, xmlstream = self.host.getJidNStream(profile_key) |
153 if not xmlstream: | |
69 | 154 error (_('Asking for an non-existant or not connected profile')) |
64 | 155 return "" |
0 | 156 debug ("sendfile (%s) to %s", filepath, to ) |
157 print type(filepath), type(to) | |
158 | |
159 statinfo = os.stat(filepath) | |
160 | |
64 | 161 offer=client.IQ(xmlstream,'set') |
0 | 162 debug ("Transfert ID: %s", offer["id"]) |
163 | |
291 | 164 self.host.plugins["XEP-0065"].sendFile(offer["id"], filepath, str(statinfo.st_size)) |
0 | 165 |
64 | 166 offer["from"]=current_jid.full() |
0 | 167 offer["to"]=jid.JID(to).full() |
168 si=offer.addElement('si','http://jabber.org/protocol/si') | |
169 si["mime-type"]='text/plain' | |
170 si["profile"]='http://jabber.org/protocol/si/profile/file-transfer' | |
8 | 171 file = si.addElement('file', 'http://jabber.org/protocol/si/profile/file-transfer') |
0 | 172 file['name']=os.path.basename(filepath) |
173 file['size']=str(statinfo.st_size) | |
174 | |
175 ### | |
176 # FIXME: Ugly temporary hard coded implementation of XEP-0020 & XEP-0004, | |
177 # Need to be recoded elsewhere in a more generic way | |
178 ### | |
179 | |
180 feature=si.addElement('feature', "http://jabber.org/protocol/feature-neg") | |
181 x=feature.addElement('x', "jabber:x:data") | |
182 x['type']='form' | |
183 field=x.addElement('field') | |
184 field['type']='list-single' | |
185 field['var']='stream-method' | |
186 option = field.addElement('option') | |
187 value = option.addElement('value', content='http://jabber.org/protocol/bytestreams') | |
188 | |
64 | 189 offer.addCallback(self.fileCB, current_jid = current_jid, xmlstream = xmlstream) |
0 | 190 offer.send() |
191 return offer["id"] #XXX: using IQ id as file transfert id seems OK as IQ id are required | |
192 | |
64 | 193 class XEP_0096_handler(XMPPHandler): |
194 implements(iwokkel.IDisco) | |
195 | |
196 def __init__(self, plugin_parent): | |
197 self.plugin_parent = plugin_parent | |
198 self.host = plugin_parent.host | |
199 | |
200 def connectionInitialized(self): | |
201 self.xmlstream.addObserver(SI_REQUEST, self.plugin_parent.xep_96, profile = self.parent.profile) | |
202 | |
203 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | |
204 return [disco.DiscoFeature(NS_SI)] | |
205 | |
206 def getDiscoItems(self, requestor, target, nodeIdentifier=''): | |
207 return [] | |
208 |