Mercurial > libervia-backend
view src/plugins/plugin_xep_0096.py @ 283:68cd30d982a5
core: added plugins for PubSub et PEP (first drafts)
- plugin for XEP 0060, 0163 and 0107, based on Wokkel's pubsub
- XEP 0115 plugin has been fixed, so generateHash can be called when features have changed (can be usefull to filter PEP)
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 03 Feb 2011 18:06:25 +0100 |
parents | b1794cbb88e5 |
children | 7c79d4a8c9e6 |
line wrap: on
line source
#!/usr/bin/python # -*- coding: utf-8 -*- """ SAT plugin for managing xep-0096 Copyright (C) 2009, 2010, 2011 Jérôme Poisson (goffi@goffi.org) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. """ from logging import debug, info, error from twisted.words.xish import domish from twisted.internet import protocol from twisted.words.protocols.jabber import client, jid from twisted.words.protocols.jabber import error as jab_error import os.path from twisted.internet import reactor #FIXME best way ??? import pdb from zope.interface import implements try: from twisted.words.protocols.xmlstream import XMPPHandler except ImportError: from wokkel.subprotocols import XMPPHandler from wokkel import disco, iwokkel IQ_SET = '/iq[@type="set"]' NS_SI = 'http://jabber.org/protocol/si' SI_REQUEST = IQ_SET + '/si[@xmlns="' + NS_SI + '"]' PLUGIN_INFO = { "name": "XEP 0096 Plugin", "import_name": "XEP_0096", "type": "XEP", "protocols": ["XEP-0096"], "dependencies": ["XEP_0065"], "main": "XEP_0096", "handler": "yes", "description": _("""Implementation of SI File Transfert""") } class XEP_0096(): def __init__(self, host): info(_("Plugin XEP_0096 initialization")) self.host = host self._waiting_for_approval = {} host.bridge.addMethod("sendFile", ".communication", in_sign='sss', out_sign='s', method=self.sendFile) def getHandler(self, profile): return XEP_0096_handler(self) def xep_96(self, IQ, profile): info (_("XEP-0096 management")) IQ.handled=True SI_elem = IQ.firstChildElement() debug(SI_elem.toXml()) filename = "" file_size = "" for element in SI_elem.elements(): if element.name == "file": info (_("File proposed: name=[%(name)s] size=%(size)s") % {'name':element['name'], 'size':element['size']}) filename = element["name"] file_size = element["size"] elif element.name == "feature": from_jid = IQ["from"] self._waiting_for_approval[IQ["id"]] = (element, from_jid, file_size, profile) data={ "filename":filename, "from":from_jid, "size":file_size } self.host.askConfirmation(IQ["id"], "FILE_TRANSFERT", data, self.confirmationCB) def confirmationCB(self, id, accepted, data): """Called on confirmation answer""" if accepted: data['size'] = self._waiting_for_approval[id][2] self.host.plugins["XEP_0065"].setData(data, id) self.approved(id) else: debug (_("Transfert [%s] refused"), id) del(self._waiting_for_approval[id]) def approved(self, id): """must be called when a file transfert has be accepted by client""" debug (_("Transfert [%s] accepted"), id) if ( not self._waiting_for_approval.has_key(id) ): error (_("Approved unknow id !")) #TODO: manage this (maybe approved by several frontends) else: element, from_id, size, profile = self._waiting_for_approval[id] del(self._waiting_for_approval[id]) self.negociate(element, id, from_id, profile) def negociate(self, feat_elem, id, to_jid, profile): #TODO: put this in a plugin #FIXME: over ultra mega ugly, need to be generic client = self.host.getClient(profile) assert(client) info (_("Feature negociation")) data = feat_elem.firstChildElement() field = data.firstChildElement() #FIXME: several options ! Q&D code for test only option = field.firstChildElement() value = option.firstChildElement() if unicode(value) == "http://jabber.org/protocol/bytestreams": #ugly, as usual, need to be entirely rewritten (just for test !) result = domish.Element(('', 'iq')) result['type'] = 'result' result['id'] = id result['to'] = to_jid si = result.addElement('si', 'http://jabber.org/protocol/si') file = si.addElement('file', 'http://jabber.org/protocol/si/profile/file-transfer') feature = si.addElement('feature', 'http://jabber.org/protocol/feature-neg') x = feature.addElement('x', 'jabber:x:data') x['type'] = 'submit' field = x.addElement('field') field['var'] = 'stream-method' value = field.addElement('value') value.addContent('http://jabber.org/protocol/bytestreams') client.xmlstream.send(result) def fileCB(self, answer, xmlstream, current_jid): if answer['type']=="result": #FIXME FIXME FIXME ugly ugly ugly ! and temp FIXME FIXME FIXME info("SENDING UGLY ANSWER") offer=client.IQ(xmlstream,'set') offer["from"]=current_jid.full() offer["to"]=answer['from'] query=offer.addElement('query', 'http://jabber.org/protocol/bytestreams') query['mode']='tcp' streamhost=query.addElement('streamhost') streamhost['host']=self.host.memory.getParamA("IP", "File Transfert") streamhost['port']=self.host.memory.getParamA("Port", "File Transfert") streamhost['jid']=current_jid.full() offer.send() def sendFile(self, to, filepath, profile_key='@DEFAULT@'): """send a file using XEP-0096 Return an unique id to identify the transfert """ current_jid, xmlstream = self.host.getJidNStream(profile_key) if not xmlstream: error (_('Asking for an non-existant or not connected profile')) return "" debug ("sendfile (%s) to %s", filepath, to ) print type(filepath), type(to) statinfo = os.stat(filepath) offer=client.IQ(xmlstream,'set') debug ("Transfert ID: %s", offer["id"]) self.host.plugins["XEP_0065"].sendFile(offer["id"], filepath, str(statinfo.st_size)) offer["from"]=current_jid.full() offer["to"]=jid.JID(to).full() si=offer.addElement('si','http://jabber.org/protocol/si') si["mime-type"]='text/plain' si["profile"]='http://jabber.org/protocol/si/profile/file-transfer' file = si.addElement('file', 'http://jabber.org/protocol/si/profile/file-transfer') file['name']=os.path.basename(filepath) file['size']=str(statinfo.st_size) ### # FIXME: Ugly temporary hard coded implementation of XEP-0020 & XEP-0004, # Need to be recoded elsewhere in a more generic way ### feature=si.addElement('feature', "http://jabber.org/protocol/feature-neg") x=feature.addElement('x', "jabber:x:data") x['type']='form' field=x.addElement('field') field['type']='list-single' field['var']='stream-method' option = field.addElement('option') value = option.addElement('value', content='http://jabber.org/protocol/bytestreams') offer.addCallback(self.fileCB, current_jid = current_jid, xmlstream = xmlstream) offer.send() return offer["id"] #XXX: using IQ id as file transfert id seems OK as IQ id are required class XEP_0096_handler(XMPPHandler): implements(iwokkel.IDisco) def __init__(self, plugin_parent): self.plugin_parent = plugin_parent self.host = plugin_parent.host def connectionInitialized(self): self.xmlstream.addObserver(SI_REQUEST, self.plugin_parent.xep_96, profile = self.parent.profile) def getDiscoInfo(self, requestor, target, nodeIdentifier=''): return [disco.DiscoFeature(NS_SI)] def getDiscoItems(self, requestor, target, nodeIdentifier=''): return []