Mercurial > libervia-backend
view 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 |
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 []