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
This commit is contained in:
parent
7b520ddc93
commit
656bea30dd
5 changed files with 175 additions and 109 deletions
|
|
@ -58,11 +58,11 @@ PLUGIN_INFO = {
|
|||
"protocols": ["XEP-0054", "XEP-0153"],
|
||||
"dependencies": [],
|
||||
"main": "XEP_0054",
|
||||
"handler": "yes",
|
||||
"description": """Implementation of vcard-temp"""
|
||||
}
|
||||
|
||||
class XEP_0054(XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
class XEP_0054():
|
||||
|
||||
def __init__(self, host):
|
||||
info("Plugin XEP_0054 initialization")
|
||||
|
|
@ -71,9 +71,12 @@ class XEP_0054(XMPPHandler):
|
|||
self.vcard_cache = host.memory.getPrivate("vcard_cache") or {} #used to store nicknames and avatar, key = jid
|
||||
if not os.path.exists(self.avatar_path):
|
||||
os.makedirs(self.avatar_path)
|
||||
host.bridge.addMethod("getProfile", ".communication", in_sign='s', out_sign='s', method=self.getProfile)
|
||||
host.bridge.addMethod("getCard", ".communication", in_sign='ss', out_sign='s', method=self.getCard)
|
||||
host.bridge.addMethod("getAvatarFile", ".communication", in_sign='s', out_sign='s', method=self.getAvatarFile)
|
||||
host.bridge.addMethod("getProfileCache", ".communication", in_sign='s', out_sign='a{ss}', method=self.getProfileCache)
|
||||
host.bridge.addMethod("getCardCache", ".communication", in_sign='s', out_sign='a{ss}', method=self.getCardCache)
|
||||
|
||||
def getHandler(self):
|
||||
return XEP_0054_handler(self)
|
||||
|
||||
def update_cache(self, jid, name, value):
|
||||
"""update cache value
|
||||
|
|
@ -101,15 +104,6 @@ class XEP_0054(XMPPHandler):
|
|||
return None
|
||||
|
||||
|
||||
def connectionInitialized(self):
|
||||
self.xmlstream.addObserver(VCARD_UPDATE, self.update)
|
||||
|
||||
def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
|
||||
return [disco.DiscoFeature(NS_VCARD)]
|
||||
|
||||
def getDiscoItems(self, requestor, target, nodeIdentifier=''):
|
||||
return []
|
||||
|
||||
def save_photo(self, photo_xml):
|
||||
"""Parse a <PHOTO> elem and save the picture"""
|
||||
for elem in photo_xml.elements():
|
||||
|
|
@ -177,14 +171,18 @@ class XEP_0054(XMPPHandler):
|
|||
error ("Can't find VCard of %s" % failure.value.stanza['from'])
|
||||
self.host.bridge.actionResult("SUPPRESS", failure.value.stanza['id'], {}) #FIXME: maybe an error message would be best
|
||||
|
||||
def getProfile(self, target):
|
||||
def getCard(self, target, profile_key='@DEFAULT@'):
|
||||
"""Ask server for VCard
|
||||
@param target: jid from which we want the VCard
|
||||
@result: id to retrieve the profile"""
|
||||
current_jid, xmlstream = self.host.getJidNStream(profile_key)
|
||||
if not xmlstream:
|
||||
error ('Asking profile for an non-existant or not connected profile')
|
||||
return ""
|
||||
to_jid = jid.JID(target)
|
||||
debug("Asking for %s's VCard" % to_jid.userhost())
|
||||
reg_request=IQ(self.host.xmlstream,'get')
|
||||
reg_request["from"]=self.host.me.full()
|
||||
reg_request=IQ(xmlstream,'get')
|
||||
reg_request["from"]=current_jid.full()
|
||||
reg_request["to"] = to_jid.userhost()
|
||||
query=reg_request.addElement('vCard', NS_VCARD)
|
||||
reg_request.send(to_jid.userhost()).addCallbacks(self.vcard_ok, self.vcard_err)
|
||||
|
|
@ -201,7 +199,7 @@ class XEP_0054(XMPPHandler):
|
|||
return ""
|
||||
return filename
|
||||
|
||||
def getProfileCache(self, target):
|
||||
def getCardCache(self, target):
|
||||
"""Request for cached values of profile
|
||||
return the cached nickname and avatar if exists, else get VCard
|
||||
"""
|
||||
|
|
@ -215,6 +213,24 @@ class XEP_0054(XMPPHandler):
|
|||
result['avatar'] = avatar
|
||||
return result
|
||||
|
||||
|
||||
|
||||
class XEP_0054_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(VCARD_UPDATE, self.update)
|
||||
|
||||
def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
|
||||
return [disco.DiscoFeature(NS_VCARD)]
|
||||
|
||||
def getDiscoItems(self, requestor, target, nodeIdentifier=''):
|
||||
return []
|
||||
|
||||
def update(self, presence):
|
||||
"""Request for VCard's nickname
|
||||
return the cached nickname if exists, else get VCard
|
||||
|
|
@ -224,8 +240,7 @@ class XEP_0054(XMPPHandler):
|
|||
for elem in x_elem.elements():
|
||||
if elem.name == 'photo':
|
||||
hash = str(elem)
|
||||
old_avatar = self.get_cache(to_jid, 'avatar')
|
||||
old_avatar = self.plugin_parent.get_cache(to_jid, 'avatar')
|
||||
if not old_avatar or old_avatar != hash:
|
||||
debug('New avatar found, requesting vcard')
|
||||
self.getProfile(to_jid.userhost())
|
||||
|
||||
self.plugin_parent.getCard(to_jid.userhost(), self.parent.profile)
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ PLUGIN_INFO = {
|
|||
"type": "XEP",
|
||||
"protocols": ["XEP-0065"],
|
||||
"main": "XEP_0065",
|
||||
"handler": "yes",
|
||||
"description": """Implementation of SOCKS5 Bytestreams"""
|
||||
}
|
||||
|
||||
|
|
@ -312,7 +313,7 @@ class SOCKSv5(protocol.Protocol, FileSender):
|
|||
debug("Saving file in %s.", self.data["dest_path"])
|
||||
self.dest_file = open(self.data["dest_path"], 'w')
|
||||
self.state = STATE_TARGET_READY
|
||||
self.activateCB(self.target_jid, self.initiator_jid, self.sid, self.IQ_id)
|
||||
self.activateCB(self.target_jid, self.initiator_jid, self.sid, self.IQ_id, self.xmlstream)
|
||||
|
||||
|
||||
except struct.error, why:
|
||||
|
|
@ -450,8 +451,7 @@ class Socks5ClientFactory(protocol.ClientFactory):
|
|||
debug ("Socks 5 client connection lost (reason: %s)", reason)
|
||||
|
||||
|
||||
class XEP_0065(XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
class XEP_0065():
|
||||
|
||||
params = """
|
||||
<params>
|
||||
|
|
@ -480,20 +480,12 @@ class XEP_0065(XMPPHandler):
|
|||
info("Launching Socks5 Stream server on port %d", port)
|
||||
reactor.listenTCP(port, self.server_factory)
|
||||
|
||||
def getHandler(self):
|
||||
return XEP_0065_handler(self)
|
||||
|
||||
def getExternalIP(self):
|
||||
"""Return IP visible from outside, by asking to a website"""
|
||||
return getPage("http://www.goffi.org/sat_tools/get_ip.php")
|
||||
|
||||
def connectionInitialized(self):
|
||||
self.xmlstream.addObserver(BS_REQUEST, self.getFile)
|
||||
|
||||
|
||||
def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
|
||||
return [disco.DiscoFeature(NS_BS)]
|
||||
|
||||
def getDiscoItems(self, requestor, target, nodeIdentifier=''):
|
||||
return []
|
||||
|
||||
|
||||
def setData(self, data, id):
|
||||
self.data = data
|
||||
|
|
@ -507,8 +499,10 @@ class XEP_0065(XMPPHandler):
|
|||
self.server_factory.protocol.filesize = size
|
||||
self.server_factory.protocol.transfert_id = id
|
||||
|
||||
def getFile(self, iq):
|
||||
def getFile(self, iq, profile_key='@DEFAULT@'):
|
||||
"""Get file using byte stream"""
|
||||
client = self.host.getClient(profile_key)
|
||||
assert(client)
|
||||
iq.handled = True
|
||||
SI_elem = iq.firstChildElement()
|
||||
IQ_id = iq['id']
|
||||
|
|
@ -518,17 +512,18 @@ class XEP_0065(XMPPHandler):
|
|||
factory = self.client_factory
|
||||
self.server_factory.protocol.mode = "target"
|
||||
factory.protocol.host = self.host #needed for progress CB
|
||||
factory.protocol.xmlstream = client.xmlstream
|
||||
factory.protocol.data = self.data
|
||||
factory.protocol.transfert_id = self.transfert_id
|
||||
factory.protocol.filesize = self.data["size"]
|
||||
factory.protocol.sid = SI_elem['sid']
|
||||
factory.protocol.initiator_jid = element['jid']
|
||||
factory.protocol.target_jid = self.host.me.full()
|
||||
factory.protocol.target_jid = client.jid.full()
|
||||
factory.protocol.IQ_id = IQ_id
|
||||
factory.protocol.activateCB = self.activateStream
|
||||
reactor.connectTCP(element['host'], int(element['port']), factory)
|
||||
|
||||
def activateStream(self, from_jid, to_jid, sid, IQ_id):
|
||||
def activateStream(self, from_jid, to_jid, sid, IQ_id, xmlstream):
|
||||
debug("activating stream")
|
||||
result = domish.Element(('', 'iq'))
|
||||
result['type'] = 'result'
|
||||
|
|
@ -539,5 +534,21 @@ class XEP_0065(XMPPHandler):
|
|||
query['sid'] = sid
|
||||
streamhost = query.addElement('streamhost-used')
|
||||
streamhost['jid'] = to_jid #FIXME: use real streamhost
|
||||
self.host.xmlstream.send(result)
|
||||
xmlstream.send(result)
|
||||
|
||||
class XEP_0065_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(BS_REQUEST, self.plugin_parent.getFile)
|
||||
|
||||
|
||||
def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
|
||||
return [disco.DiscoFeature(NS_BS)]
|
||||
|
||||
def getDiscoItems(self, requestor, target, nodeIdentifier=''):
|
||||
return []
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
"""
|
||||
|
||||
from logging import debug, info, error
|
||||
from twisted.words.protocols.jabber import client, jid, xmlstream
|
||||
from twisted.words.protocols.jabber import client, jid
|
||||
from twisted.words.protocols.jabber import error as jab_error
|
||||
from twisted.words.protocols.jabber.xmlstream import IQ
|
||||
from twisted.internet import reactor
|
||||
|
|
@ -47,7 +47,7 @@ class XEP_0077():
|
|||
info("Plugin XEP_0077 initialization")
|
||||
self.host = host
|
||||
self.triggers = {} #used by other protocol (e.g. XEP-0100) to finish registration. key = target_jid
|
||||
host.bridge.addMethod("in_band_register", ".communication", in_sign='s', out_sign='s', method=self.in_band_register)
|
||||
host.bridge.addMethod("in_band_register", ".communication", in_sign='ss', out_sign='s', method=self.in_band_register)
|
||||
host.bridge.addMethod("in_band_submit", ".request", in_sign='sa(ss)', out_sign='s', method=self.in_band_submit)
|
||||
|
||||
def addTrigger(self, target, cb):
|
||||
|
|
@ -127,12 +127,16 @@ class XEP_0077():
|
|||
deferred.addCallbacks(self.registrationAnswer, self.registrationFailure)
|
||||
return id
|
||||
|
||||
def in_band_register(self, target):
|
||||
def in_band_register(self, target, profile_key='@DEFAULT@'):
|
||||
"""register to a target JID"""
|
||||
current_jid, xmlstream = self.host.getJidNStream(profile_key)
|
||||
if not xmlstream:
|
||||
error ('Asking profile for an non-existant or not connected profile')
|
||||
return ""
|
||||
to_jid = jid.JID(target)
|
||||
debug("Asking registration for [%s]" % to_jid.full())
|
||||
reg_request=IQ(self.host.xmlstream,'get')
|
||||
reg_request["from"]=self.host.me.full()
|
||||
reg_request=IQ(xmlstream,'get')
|
||||
reg_request["from"]=current_jid.full()
|
||||
reg_request["to"] = to_jid.full()
|
||||
query=reg_request.addElement('query', NS_REG)
|
||||
reg_request.send(to_jid.full()).addCallbacks(self.reg_ok, self.reg_err)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ 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, xmlstream
|
||||
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 ???
|
||||
|
|
@ -48,29 +48,22 @@ PLUGIN_INFO = {
|
|||
"protocols": ["XEP-0096"],
|
||||
"dependencies": ["XEP_0065"],
|
||||
"main": "XEP_0096",
|
||||
"handler": "yes",
|
||||
"description": """Implementation of SI File Transfert"""
|
||||
}
|
||||
|
||||
class XEP_0096(XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
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='ss', out_sign='s', method=self.sendFile)
|
||||
host.bridge.addMethod("sendFile", ".communication", in_sign='sss', out_sign='s', method=self.sendFile)
|
||||
|
||||
def connectionInitialized(self):
|
||||
self.xmlstream.addObserver(SI_REQUEST, self.xep_96)
|
||||
def getHandler(self):
|
||||
return XEP_0096_handler(self)
|
||||
|
||||
|
||||
def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
|
||||
return [disco.DiscoFeature(NS_SI)]
|
||||
|
||||
def getDiscoItems(self, requestor, target, nodeIdentifier=''):
|
||||
return []
|
||||
|
||||
def xep_96(self, IQ):
|
||||
def xep_96(self, IQ, profile):
|
||||
info ("XEP-0096 management")
|
||||
IQ.handled=True
|
||||
SI_elem = IQ.firstChildElement()
|
||||
|
|
@ -84,7 +77,7 @@ class XEP_0096(XMPPHandler):
|
|||
file_size = element["size"]
|
||||
elif element.name == "feature":
|
||||
from_jid = IQ["from"]
|
||||
self._waiting_for_approval[IQ["id"]] = (element, from_jid, file_size)
|
||||
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)
|
||||
|
||||
|
|
@ -106,13 +99,15 @@ class XEP_0096(XMPPHandler):
|
|||
error ("Approved unknow id !")
|
||||
#TODO: manage this (maybe approved by several frontends)
|
||||
else:
|
||||
element, from_id, size = self._waiting_for_approval[id]
|
||||
element, from_id, size, profile = self._waiting_for_approval[id]
|
||||
del(self._waiting_for_approval[id])
|
||||
self.negociate(element, id, from_id)
|
||||
self.negociate(element, id, from_id, profile)
|
||||
|
||||
def negociate(self, feat_elem, id, to_jid):
|
||||
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()
|
||||
|
|
@ -134,40 +129,41 @@ class XEP_0096(XMPPHandler):
|
|||
field['var'] = 'stream-method'
|
||||
value = field.addElement('value')
|
||||
value.addContent('http://jabber.org/protocol/bytestreams')
|
||||
self.host.xmlstream.send(result)
|
||||
client.xmlstream.send(result)
|
||||
|
||||
def fileCB(self, answer):
|
||||
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(self.host.xmlstream,'set')
|
||||
offer["from"]=self.host.me.full()
|
||||
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']=self.host.me.full()
|
||||
streamhost['jid']=current_jid.full()
|
||||
offer.send()
|
||||
|
||||
|
||||
|
||||
|
||||
def sendFile(self, to, filepath):
|
||||
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 profile 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(self.host.xmlstream,'set')
|
||||
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"]=self.host.me.full()
|
||||
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'
|
||||
|
|
@ -190,7 +186,23 @@ class XEP_0096(XMPPHandler):
|
|||
option = field.addElement('option')
|
||||
value = option.addElement('value', content='http://jabber.org/protocol/bytestreams')
|
||||
|
||||
offer.addCallback(self.fileCB)
|
||||
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 []
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
from logging import debug, info, error
|
||||
from twisted.internet import protocol
|
||||
from twisted.words.protocols.jabber import client, jid, xmlstream
|
||||
from twisted.words.protocols.jabber import client, jid
|
||||
from twisted.words.protocols.jabber import error as jab_error
|
||||
import pdb
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ class XEP_0100():
|
|||
self.__inc_handled_items(request_id)
|
||||
|
||||
|
||||
def discoItems(self, disco, request_id, target):
|
||||
def discoItems(self, disco, request_id, target, client):
|
||||
"""Look for items with disco protocol, and ask infos for each one"""
|
||||
#FIXME: target is used as we can't find the original iq node (parent is None)
|
||||
# an other way would avoid this useless parameter (is there a way with wokkel ?)
|
||||
|
|
@ -88,8 +88,8 @@ class XEP_0100():
|
|||
self.__gateways[request_id] = {'__total_items':len(disco._items), '__handled_items':0, '__private__':{'target':target.full()}}
|
||||
for item in disco._items:
|
||||
debug ("item found: %s", item.name)
|
||||
self.host.disco.requestInfo(item.entity).addCallback(self.discoInfo, entity=item.entity, request_id=request_id)
|
||||
self.host.disco.requestInfo(item.entity).addErrback(self.discoInfoErr, entity=item.entity, request_id=request_id)
|
||||
client.disco.requestInfo(item.entity).addCallback(self.discoInfo, entity=item.entity, request_id=request_id)
|
||||
client.disco.requestInfo(item.entity).addErrback(self.discoInfoErr, entity=item.entity, request_id=request_id)
|
||||
|
||||
def registrationSuccessful(self, target):
|
||||
"""Called when in_band registration is ok, we must now follow the rest of procedure"""
|
||||
|
|
@ -103,12 +103,14 @@ class XEP_0100():
|
|||
self.host.plugins["XEP_0077"].addTrigger(target, self.registrationSuccessful)
|
||||
return self.host.plugins["XEP_0077"].in_band_submit(action, target, fields)
|
||||
|
||||
def findGateways(self, target):
|
||||
def findGateways(self, target, profile_key='@DEFAULT@'):
|
||||
"""Find gateways in the target JID, using discovery protocol
|
||||
Return an id used for retrieving the list of gateways
|
||||
"""
|
||||
client = self.host.getClient(profile_key)
|
||||
assert(client)
|
||||
to_jid = jid.JID(target)
|
||||
debug ("find gateways (target = %s)" % to_jid.full())
|
||||
request_id = self.host.get_next_id()
|
||||
self.host.disco.requestItems(to_jid).addCallback(self.discoItems, request_id=request_id, target = to_jid)
|
||||
client.disco.requestItems(to_jid).addCallback(self.discoItems, request_id=request_id, target = to_jid, client = client)
|
||||
return request_id
|
||||
|
|
|
|||
Loading…
Reference in a new issue