comparison plugins/plugin_xep_0096.py @ 64:d46f849664aa

SàT: multi-profile, plugins updated - core: 2 new convenient methods: getJidNStream and getClient - new param in plugin info: "handler" to know if there is a handler to plug on profiles clients - plugins with handler now use an other class which is returned to profile client with the new method "getHandler" and pluged when connecting
author Goffi <goffi@goffi.org>
date Sat, 30 Jan 2010 16:17:33 +1100
parents a5b5fb5fc9fd
children 86f1f7f6d332
comparison
equal deleted inserted replaced
63:0db25931b60d 64:d46f849664aa
20 """ 20 """
21 21
22 from logging import debug, info, error 22 from logging import debug, info, error
23 from twisted.words.xish import domish 23 from twisted.words.xish import domish
24 from twisted.internet import protocol 24 from twisted.internet import protocol
25 from twisted.words.protocols.jabber import client, jid, xmlstream 25 from twisted.words.protocols.jabber import client, jid
26 from twisted.words.protocols.jabber import error as jab_error 26 from twisted.words.protocols.jabber import error as jab_error
27 import os.path 27 import os.path
28 from twisted.internet import reactor #FIXME best way ??? 28 from twisted.internet import reactor #FIXME best way ???
29 import pdb 29 import pdb
30 30
46 "import_name": "XEP_0096", 46 "import_name": "XEP_0096",
47 "type": "XEP", 47 "type": "XEP",
48 "protocols": ["XEP-0096"], 48 "protocols": ["XEP-0096"],
49 "dependencies": ["XEP_0065"], 49 "dependencies": ["XEP_0065"],
50 "main": "XEP_0096", 50 "main": "XEP_0096",
51 "handler": "yes",
51 "description": """Implementation of SI File Transfert""" 52 "description": """Implementation of SI File Transfert"""
52 } 53 }
53 54
54 class XEP_0096(XMPPHandler): 55 class XEP_0096():
55 implements(iwokkel.IDisco)
56 56
57 def __init__(self, host): 57 def __init__(self, host):
58 info("Plugin XEP_0096 initialization") 58 info("Plugin XEP_0096 initialization")
59 self.host = host 59 self.host = host
60 self._waiting_for_approval = {} 60 self._waiting_for_approval = {}
61 host.bridge.addMethod("sendFile", ".communication", in_sign='ss', out_sign='s', method=self.sendFile) 61 host.bridge.addMethod("sendFile", ".communication", in_sign='sss', out_sign='s', method=self.sendFile)
62 62
63 def connectionInitialized(self): 63 def getHandler(self):
64 self.xmlstream.addObserver(SI_REQUEST, self.xep_96) 64 return XEP_0096_handler(self)
65 65
66 66 def xep_96(self, IQ, profile):
67 def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
68 return [disco.DiscoFeature(NS_SI)]
69
70 def getDiscoItems(self, requestor, target, nodeIdentifier=''):
71 return []
72
73 def xep_96(self, IQ):
74 info ("XEP-0096 management") 67 info ("XEP-0096 management")
75 IQ.handled=True 68 IQ.handled=True
76 SI_elem = IQ.firstChildElement() 69 SI_elem = IQ.firstChildElement()
77 debug(SI_elem.toXml()) 70 debug(SI_elem.toXml())
78 filename = "" 71 filename = ""
82 info ("File proposed: name=[%s] size=%s", element['name'], element['size']) 75 info ("File proposed: name=[%s] size=%s", element['name'], element['size'])
83 filename = element["name"] 76 filename = element["name"]
84 file_size = element["size"] 77 file_size = element["size"]
85 elif element.name == "feature": 78 elif element.name == "feature":
86 from_jid = IQ["from"] 79 from_jid = IQ["from"]
87 self._waiting_for_approval[IQ["id"]] = (element, from_jid, file_size) 80 self._waiting_for_approval[IQ["id"]] = (element, from_jid, file_size, profile)
88 data={ "filename":filename, "from":from_jid, "size":file_size } 81 data={ "filename":filename, "from":from_jid, "size":file_size }
89 self.host.askConfirmation(IQ["id"], "FILE_TRANSFERT", data, self.confirmationCB) 82 self.host.askConfirmation(IQ["id"], "FILE_TRANSFERT", data, self.confirmationCB)
90 83
91 def confirmationCB(self, id, accepted, data): 84 def confirmationCB(self, id, accepted, data):
92 """Called on confirmation answer""" 85 """Called on confirmation answer"""
104 97
105 if ( not self._waiting_for_approval.has_key(id) ): 98 if ( not self._waiting_for_approval.has_key(id) ):
106 error ("Approved unknow id !") 99 error ("Approved unknow id !")
107 #TODO: manage this (maybe approved by several frontends) 100 #TODO: manage this (maybe approved by several frontends)
108 else: 101 else:
109 element, from_id, size = self._waiting_for_approval[id] 102 element, from_id, size, profile = self._waiting_for_approval[id]
110 del(self._waiting_for_approval[id]) 103 del(self._waiting_for_approval[id])
111 self.negociate(element, id, from_id) 104 self.negociate(element, id, from_id, profile)
112 105
113 def negociate(self, feat_elem, id, to_jid): 106 def negociate(self, feat_elem, id, to_jid, profile):
114 #TODO: put this in a plugin 107 #TODO: put this in a plugin
115 #FIXME: over ultra mega ugly, need to be generic 108 #FIXME: over ultra mega ugly, need to be generic
109 client = self.host.getClient(profile)
110 assert(client)
116 info ("Feature negociation") 111 info ("Feature negociation")
117 data = feat_elem.firstChildElement() 112 data = feat_elem.firstChildElement()
118 field = data.firstChildElement() 113 field = data.firstChildElement()
119 #FIXME: several options ! Q&D code for test only 114 #FIXME: several options ! Q&D code for test only
120 option = field.firstChildElement() 115 option = field.firstChildElement()
132 x['type'] = 'submit' 127 x['type'] = 'submit'
133 field = x.addElement('field') 128 field = x.addElement('field')
134 field['var'] = 'stream-method' 129 field['var'] = 'stream-method'
135 value = field.addElement('value') 130 value = field.addElement('value')
136 value.addContent('http://jabber.org/protocol/bytestreams') 131 value.addContent('http://jabber.org/protocol/bytestreams')
137 self.host.xmlstream.send(result) 132 client.xmlstream.send(result)
138 133
139 def fileCB(self, answer): 134 def fileCB(self, answer, xmlstream, current_jid):
140 if answer['type']=="result": #FIXME FIXME FIXME ugly ugly ugly ! and temp FIXME FIXME FIXME 135 if answer['type']=="result": #FIXME FIXME FIXME ugly ugly ugly ! and temp FIXME FIXME FIXME
141 info("SENDING UGLY ANSWER") 136 info("SENDING UGLY ANSWER")
142 offer=client.IQ(self.host.xmlstream,'set') 137 offer=client.IQ(xmlstream,'set')
143 offer["from"]=self.host.me.full() 138 offer["from"]=current_jid.full()
144 offer["to"]=answer['from'] 139 offer["to"]=answer['from']
145 query=offer.addElement('query', 'http://jabber.org/protocol/bytestreams') 140 query=offer.addElement('query', 'http://jabber.org/protocol/bytestreams')
146 query['mode']='tcp' 141 query['mode']='tcp'
147 streamhost=query.addElement('streamhost') 142 streamhost=query.addElement('streamhost')
148 streamhost['host']=self.host.memory.getParamA("IP", "File Transfert") 143 streamhost['host']=self.host.memory.getParamA("IP", "File Transfert")
149 streamhost['port']=self.host.memory.getParamA("Port", "File Transfert") 144 streamhost['port']=self.host.memory.getParamA("Port", "File Transfert")
150 streamhost['jid']=self.host.me.full() 145 streamhost['jid']=current_jid.full()
151 offer.send() 146 offer.send()
152 147
153 148 def sendFile(self, to, filepath, profile_key='@DEFAULT@'):
154
155
156 def sendFile(self, to, filepath):
157 """send a file using XEP-0096 149 """send a file using XEP-0096
158 Return an unique id to identify the transfert 150 Return an unique id to identify the transfert
159 """ 151 """
152 current_jid, xmlstream = self.host.getJidNStream(profile_key)
153 if not xmlstream:
154 error ('Asking profile for an non-existant or not connected profile')
155 return ""
160 debug ("sendfile (%s) to %s", filepath, to ) 156 debug ("sendfile (%s) to %s", filepath, to )
161 print type(filepath), type(to) 157 print type(filepath), type(to)
162 158
163 statinfo = os.stat(filepath) 159 statinfo = os.stat(filepath)
164 160
165 offer=client.IQ(self.host.xmlstream,'set') 161 offer=client.IQ(xmlstream,'set')
166 debug ("Transfert ID: %s", offer["id"]) 162 debug ("Transfert ID: %s", offer["id"])
167 163
168 self.host.plugins["XEP_0065"].sendFile(offer["id"], filepath, str(statinfo.st_size)) 164 self.host.plugins["XEP_0065"].sendFile(offer["id"], filepath, str(statinfo.st_size))
169 165
170 offer["from"]=self.host.me.full() 166 offer["from"]=current_jid.full()
171 offer["to"]=jid.JID(to).full() 167 offer["to"]=jid.JID(to).full()
172 si=offer.addElement('si','http://jabber.org/protocol/si') 168 si=offer.addElement('si','http://jabber.org/protocol/si')
173 si["mime-type"]='text/plain' 169 si["mime-type"]='text/plain'
174 si["profile"]='http://jabber.org/protocol/si/profile/file-transfer' 170 si["profile"]='http://jabber.org/protocol/si/profile/file-transfer'
175 file = si.addElement('file', 'http://jabber.org/protocol/si/profile/file-transfer') 171 file = si.addElement('file', 'http://jabber.org/protocol/si/profile/file-transfer')
188 field['type']='list-single' 184 field['type']='list-single'
189 field['var']='stream-method' 185 field['var']='stream-method'
190 option = field.addElement('option') 186 option = field.addElement('option')
191 value = option.addElement('value', content='http://jabber.org/protocol/bytestreams') 187 value = option.addElement('value', content='http://jabber.org/protocol/bytestreams')
192 188
193 offer.addCallback(self.fileCB) 189 offer.addCallback(self.fileCB, current_jid = current_jid, xmlstream = xmlstream)
194 offer.send() 190 offer.send()
195 return offer["id"] #XXX: using IQ id as file transfert id seems OK as IQ id are required 191 return offer["id"] #XXX: using IQ id as file transfert id seems OK as IQ id are required
196 192
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