comparison plugins/plugin_xep_0096.py @ 0:c4bc297b82f0

sat: - first public release, initial commit
author goffi@necton2
date Sat, 29 Aug 2009 13:34:59 +0200
parents
children a06a151fc31f
comparison
equal deleted inserted replaced
-1:000000000000 0:c4bc297b82f0
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 """
5 SAT plugin for managing xep-0096
6 Copyright (C) 2009 Jérôme Poisson (goffi@goffi.org)
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
25 from twisted.words.protocols.jabber import client, jid, xmlstream, error
26 import os.path
27 from twisted.internet import reactor #FIXME best way ???
28
29
30 PLUGIN_INFO = {
31 "name": "XEP 0096 Plugin",
32 "import_name": "XEP_0096",
33 "type": "XEP",
34 "dependencies": ["XEP_0065"],
35 "main": "XEP_0096",
36 "description": """Implementation of SI File Transfert"""
37 }
38
39 class XEP_0096:
40 def __init__(self, host):
41 info("Plugin XEP_0096 initialization")
42 self.host = host
43 self._waiting_for_approval = {}
44 host.add_IQ_cb("http://jabber.org/protocol/si", self.xep_96)
45 host.bridge.register("sendFile", self.sendFile)
46
47
48 def xep_96(self, IQ):
49 info ("XEP-0096 management")
50 SI_elem = IQ.firstChildElement()
51 debug(SI_elem.toXml())
52 filename = ""
53 file_size = ""
54 for element in SI_elem.elements():
55 if element.name == "file":
56 info ("File proposed: name=[%s] size=%s", element['name'], element['size'])
57 filename = element["name"]
58 file_size = element["size"]
59 elif element.name == "feature":
60 from_jid = IQ["from"]
61 self._waiting_for_approval[IQ["id"]] = (element, from_jid, file_size)
62 data={ "filename":filename, "from":from_jid, "size":file_size }
63 self.host.askConfirmation(IQ["id"], "FILE_TRANSFERT", data, self.confirmationCB)
64
65 def confirmationCB(self, id, accepted, data):
66 """Called on confirmation answer"""
67 if accepted:
68 data['size'] = self._waiting_for_approval[id][2]
69 self.host.plugins["XEP_0065"].setData(data, id)
70 self.approved(id)
71 else:
72 debug ("Transfert [%s] refused", id)
73 del(self._waiting_for_approval[id])
74
75 def approved(self, id):
76 """must be called when a file transfert has be accepted by client"""
77 debug ("Transfert [%s] accepted", id)
78
79 if ( not self._waiting_for_approval.has_key(id) ):
80 error ("Approved unknow id !")
81 #TODO: manage this (maybe approved by several frontends)
82 else:
83 element, from_id, size = self._waiting_for_approval[id]
84 del(self._waiting_for_approval[id])
85 self.negociate(element, id, from_id)
86
87 def negociate(self, feat_elem, id, to_jid):
88 #TODO: put this in a plugin
89 #FIXME: over ultra mega ugly, need to be generic
90 info ("Feature negociation")
91 data = feat_elem.firstChildElement()
92 field = data.firstChildElement()
93 #FIXME: several options ! Q&D code for test only
94 option = field.firstChildElement()
95 value = option.firstChildElement()
96 if unicode(value) == "http://jabber.org/protocol/bytestreams":
97 #ugly, as usual, need to be entirely rewritten (just for test !)
98 result = domish.Element(('', 'iq'))
99 result['type'] = 'result'
100 result['id'] = id
101 result['to'] = to_jid
102 si = result.addElement('si', 'http://jabber.org/protocol/si')
103 file = si.addElement('file', 'http://jabber.org/protocol/si/profile/file-transfer')
104 feature = si.addElement('feature', 'http://jabber.org/protocol/feature-neg')
105 x = feature.addElement('x', 'jabber:x:data')
106 x['type'] = 'submit'
107 field = x.addElement('field')
108 field['var'] = 'stream-method'
109 value = field.addElement('value')
110 value.addContent('http://jabber.org/protocol/bytestreams')
111 self.host.xmlstream.send(result)
112
113 def fileCB(self, answer):
114 if answer['type']=="result": #FIXME FIXME FIXME ugly ugly ugly ! and temp FIXME FIXME FIXME
115 info("SENDING UGLY ANSWER")
116 offer=client.IQ(self.host.xmlstream,'set')
117 offer["from"]=self.host.me.full()
118 offer["to"]=answer['from']
119 query=offer.addElement('query', 'http://jabber.org/protocol/bytestreams')
120 query['mode']='tcp'
121 streamhost=query.addElement('streamhost')
122 streamhost['host']=self.host.memory.getParamV("IP", "File Transfert")
123 streamhost['port']=self.host.memory.getParamV("Port", "File Transfert")
124 streamhost['jid']=self.host.me.full()
125 offer.send()
126
127
128
129
130 def sendFile(self, to, filepath):
131 """send a file using XEP-0096
132 Return an unique id to identify the transfert
133 """
134 debug ("sendfile (%s) to %s", filepath, to )
135 print type(filepath), type(to)
136
137 statinfo = os.stat(filepath)
138
139 offer=client.IQ(self.host.xmlstream,'set')
140 debug ("Transfert ID: %s", offer["id"])
141
142 self.host.plugins["XEP_0065"].sendFile(offer["id"], filepath, str(statinfo.st_size))
143
144 offer["from"]=self.host.me.full()
145 offer["to"]=jid.JID(to).full()
146 si=offer.addElement('si','http://jabber.org/protocol/si')
147 si["mime-type"]='text/plain'
148 si["profile"]='http://jabber.org/protocol/si/profile/file-transfer'
149 file = si.addElement('file', 'http://jabber.org/protocol/si')
150 file['name']=os.path.basename(filepath)
151 file['size']=str(statinfo.st_size)
152
153 ###
154 # FIXME: Ugly temporary hard coded implementation of XEP-0020 & XEP-0004,
155 # Need to be recoded elsewhere in a more generic way
156 ###
157
158 feature=si.addElement('feature', "http://jabber.org/protocol/feature-neg")
159 x=feature.addElement('x', "jabber:x:data")
160 x['type']='form'
161 field=x.addElement('field')
162 field['type']='list-single'
163 field['var']='stream-method'
164 option = field.addElement('option')
165 value = option.addElement('value', content='http://jabber.org/protocol/bytestreams')
166
167 offer.addCallback(self.fileCB)
168 offer.send()
169 return offer["id"] #XXX: using IQ id as file transfert id seems OK as IQ id are required
170