mirror of
https://github.com/correl/SleekXMPP.git
synced 2024-11-27 19:19:54 +00:00
* added proper message and iq stanzas. presence left to do
This commit is contained in:
parent
6be17ebbe2
commit
007b04dd30
6 changed files with 283 additions and 39 deletions
|
@ -37,7 +37,7 @@ import sys
|
||||||
import random
|
import random
|
||||||
import copy
|
import copy
|
||||||
from . import plugins
|
from . import plugins
|
||||||
from . import stanza
|
#from . import stanza
|
||||||
srvsupport = True
|
srvsupport = True
|
||||||
try:
|
try:
|
||||||
import dns.resolver
|
import dns.resolver
|
||||||
|
@ -87,6 +87,8 @@ class ClientXMPP(basexmpp, XMLStream):
|
||||||
#self.register_plugins()
|
#self.register_plugins()
|
||||||
|
|
||||||
def importStanzas(self):
|
def importStanzas(self):
|
||||||
|
pass
|
||||||
|
return
|
||||||
for modname in stanza.__all__:
|
for modname in stanza.__all__:
|
||||||
__import__("%s.%s" % (globals()['stanza'].__name__, modname))
|
__import__("%s.%s" % (globals()['stanza'].__name__, modname))
|
||||||
for register in getattr(stanza, modname).stanzas:
|
for register in getattr(stanza, modname).stanzas:
|
||||||
|
|
|
@ -26,6 +26,8 @@ from . xmlstream.handler.xmlcallback import XMLCallback
|
||||||
from . xmlstream.handler.xmlwaiter import XMLWaiter
|
from . xmlstream.handler.xmlwaiter import XMLWaiter
|
||||||
from . xmlstream.handler.callback import Callback
|
from . xmlstream.handler.callback import Callback
|
||||||
from . import plugins
|
from . import plugins
|
||||||
|
from . stanza.message import Message
|
||||||
|
from . stanza.iq import Iq
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
@ -95,6 +97,8 @@ class basexmpp(object):
|
||||||
self.registerHandler(Callback('IM', MatchMany((MatchXMLMask("<message xmlns='%s' type='chat'><body /></message>" % self.default_ns),MatchXMLMask("<message xmlns='%s' type='normal'><body /></message>" % self.default_ns),MatchXMLMask("<message xmlns='%s' type='__None__'><body /></message>" % self.default_ns))), self._handleMessage, thread=False))
|
self.registerHandler(Callback('IM', MatchMany((MatchXMLMask("<message xmlns='%s' type='chat'><body /></message>" % self.default_ns),MatchXMLMask("<message xmlns='%s' type='normal'><body /></message>" % self.default_ns),MatchXMLMask("<message xmlns='%s' type='__None__'><body /></message>" % self.default_ns))), self._handleMessage, thread=False))
|
||||||
self.registerHandler(Callback('Presence', MatchMany((MatchXMLMask("<presence xmlns='%s' type='available'/>" % self.default_ns),MatchXMLMask("<presence xmlns='%s' type='__None__'/>" % self.default_ns),MatchXMLMask("<presence xmlns='%s' type='unavailable'/>" % self.default_ns))), self._handlePresence, thread=False))
|
self.registerHandler(Callback('Presence', MatchMany((MatchXMLMask("<presence xmlns='%s' type='available'/>" % self.default_ns),MatchXMLMask("<presence xmlns='%s' type='__None__'/>" % self.default_ns),MatchXMLMask("<presence xmlns='%s' type='unavailable'/>" % self.default_ns))), self._handlePresence, thread=False))
|
||||||
self.registerHandler(Callback('PresenceSubscribe', MatchMany((MatchXMLMask("<presence xmlns='%s' type='subscribe'/>" % self.default_ns),MatchXMLMask("<presence xmlns='%s' type='unsubscribed'/>" % self.default_ns))), self._handlePresenceSubscribe))
|
self.registerHandler(Callback('PresenceSubscribe', MatchMany((MatchXMLMask("<presence xmlns='%s' type='subscribe'/>" % self.default_ns),MatchXMLMask("<presence xmlns='%s' type='unsubscribed'/>" % self.default_ns))), self._handlePresenceSubscribe))
|
||||||
|
self.registerStanza(Message)
|
||||||
|
self.registerStanza(Iq)
|
||||||
|
|
||||||
def set_jid(self, jid):
|
def set_jid(self, jid):
|
||||||
"""Rip a JID apart and claim it as our own."""
|
"""Rip a JID apart and claim it as our own."""
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
from .. xmlstream.stanzabase import StanzaBase
|
||||||
|
from xml.etree import cElementTree as ET
|
||||||
|
|
||||||
|
class Iq(StanzaBase):
|
||||||
|
interfaces = set(('type', 'to', 'from', 'id', 'body', 'subject'))
|
||||||
|
types = set(('get', 'result', 'set', 'error'))
|
||||||
|
name = 'iq'
|
||||||
|
namespace = 'jabber:client'
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
StanzaBase.__init__(self, *args, **kwargs)
|
||||||
|
if self['id'] == '':
|
||||||
|
self['id'] = self.stream.getId()
|
||||||
|
print("________LOADED IQ CLASS")
|
||||||
|
|
||||||
|
def result(self):
|
||||||
|
self['type'] = 'result'
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set(self):
|
||||||
|
self['type'] = 'set'
|
||||||
|
return self
|
||||||
|
|
||||||
|
def error(self):
|
||||||
|
#TODO add error payloads
|
||||||
|
self['type'] = 'error'
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self['type'] = 'get'
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setPayload(self, value):
|
||||||
|
self.clear()
|
||||||
|
StanzaBase.setPayload(self, value)
|
||||||
|
|
||||||
|
def unhandled(self):
|
||||||
|
pass
|
||||||
|
# returned unhandled error
|
|
@ -0,0 +1,39 @@
|
||||||
|
from .. xmlstream.stanzabase import StanzaBase
|
||||||
|
from xml.etree import cElementTree as ET
|
||||||
|
|
||||||
|
class Message(StanzaBase):
|
||||||
|
interfaces = set(('type', 'to', 'from', 'id', 'body', 'subject'))
|
||||||
|
types = set((None, 'normal', 'chat', 'headline', 'error'))
|
||||||
|
sub_interfaces = set(('body', 'subject'))
|
||||||
|
name = 'message'
|
||||||
|
namespace = 'jabber:client'
|
||||||
|
|
||||||
|
def getType(self):
|
||||||
|
return self.xml.attrib.get('type', 'normal')
|
||||||
|
|
||||||
|
def chat(self):
|
||||||
|
self['type'] = 'chat'
|
||||||
|
return self
|
||||||
|
|
||||||
|
def normal(self):
|
||||||
|
self['type'] = 'normal'
|
||||||
|
return self
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
m = Message()
|
||||||
|
m['to'] = 'me'
|
||||||
|
m['from'] = 'you'
|
||||||
|
m['type'] = 'chat'
|
||||||
|
m.reply()
|
||||||
|
m['body'] = 'Hello there!'
|
||||||
|
m['subject'] = 'whatever'
|
||||||
|
m['id'] = 'abc'
|
||||||
|
print(str(m))
|
||||||
|
print(m['body'])
|
||||||
|
print(m['subject'])
|
||||||
|
print(m['id'])
|
||||||
|
m['type'] = None
|
||||||
|
m['body'] = None
|
||||||
|
m['id'] = None
|
||||||
|
print(str(m))
|
||||||
|
print(m['type'])
|
|
@ -1,37 +1,198 @@
|
||||||
from __future__ import absolute_import
|
from xml.etree import cElementTree as ET
|
||||||
from sleekxmpp.xmlstream.matcher.xpath import MatchXPath
|
|
||||||
|
|
||||||
class StanzaBase(object):
|
class StanzaBase(object):
|
||||||
|
name = 'stanza'
|
||||||
|
namespace = 'jabber:client'
|
||||||
|
interfaces = set(('type', 'to', 'from', 'id', 'payload'))
|
||||||
|
types = set(('get', 'set', 'error', None, 'unavailable', 'normal', 'chat'))
|
||||||
|
sub_interfaces = tuple()
|
||||||
|
|
||||||
MATCHER = MatchXPath("")
|
def __init__(self, stream, xml=None, stype=None, sto=None, sfrom=None, sid=None):
|
||||||
|
|
||||||
def __init__(self, stream, xml=None, extensions=[]):
|
|
||||||
self.extensions = extensions
|
|
||||||
self.p = {} #plugins
|
|
||||||
|
|
||||||
self.xml = xml
|
|
||||||
self.stream = stream
|
self.stream = stream
|
||||||
if xml is not None:
|
self.xml = xml
|
||||||
self.fromXML(xml)
|
if xml is None:
|
||||||
|
self.xml = ET.Element("{%(namespace)s}%(name)s" % {'name': self.name, 'namespace': self.namespace})
|
||||||
def fromXML(self, xml):
|
if stype is not None:
|
||||||
"Initialize based on incoming XML"
|
self['type'] = stype
|
||||||
self._processXML(xml)
|
if sto is not None:
|
||||||
for ext in self.extensions:
|
self['to'] = sto
|
||||||
ext.fromXML(self, xml)
|
if sfrom is not None:
|
||||||
|
self['from'] = sfrom
|
||||||
|
self.tag = "{%s}%s" % (self.stream.default_ns, self.name)
|
||||||
def _processXML(self, xml, cur_ns=''):
|
|
||||||
if '}' in xml.tag:
|
|
||||||
ns,tag = xml.tag[1:].split('}')
|
|
||||||
else:
|
|
||||||
tag = xml.tag
|
|
||||||
|
|
||||||
def toXML(self, xml):
|
|
||||||
"Set outgoing XML"
|
|
||||||
|
|
||||||
def extend(self, extension_class, xml=None):
|
|
||||||
"Initialize extension"
|
|
||||||
|
|
||||||
def match(self, xml):
|
def match(self, xml):
|
||||||
return self.MATCHER.match(xml)
|
return xml.tag == self.tag
|
||||||
|
|
||||||
|
def __getitem__(self, attrib):
|
||||||
|
if attrib in self.interfaces:
|
||||||
|
if hasattr(self, "get%s" % attrib.title()):
|
||||||
|
return getattr(self, "get%s" % attrib.title())()
|
||||||
|
else:
|
||||||
|
if attrib in self.sub_interfaces:
|
||||||
|
return self._getSubText(attrib)
|
||||||
|
else:
|
||||||
|
return self._getAttr(attrib)
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def __setitem__(self, attrib, value):
|
||||||
|
if attrib.lower() in self.interfaces:
|
||||||
|
if value is not None:
|
||||||
|
if hasattr(self, "set%s" % attrib.title()):
|
||||||
|
getattr(self, "set%s" % attrib.title())(value,)
|
||||||
|
else:
|
||||||
|
if attrib in self.sub_interfaces:
|
||||||
|
return self._setSubText(attrib, text=value)
|
||||||
|
else:
|
||||||
|
self._setAttr(attrib, value)
|
||||||
|
else:
|
||||||
|
self.__delitem__(attrib)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __delitem__(self, attrib):
|
||||||
|
if attrib.lower() in self.interfaces:
|
||||||
|
if hasattr(self, "del%s" % attrib.title()):
|
||||||
|
getattr(self, "del%s" % attrib.title())()
|
||||||
|
else:
|
||||||
|
if attrib in self.sub_interfaces:
|
||||||
|
return self._delSub(attrib)
|
||||||
|
else:
|
||||||
|
self._delAttr(attrib)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setType(self, value):
|
||||||
|
if value in self.types:
|
||||||
|
if value is None and 'type' in self.xml.attrib:
|
||||||
|
del self.xml.attrib['type']
|
||||||
|
elif value is not None:
|
||||||
|
self.xml.attrib['type'] = value
|
||||||
|
else:
|
||||||
|
raise ValueError
|
||||||
|
return self
|
||||||
|
|
||||||
|
def getPayload(self):
|
||||||
|
return self.xml.getchildren()
|
||||||
|
|
||||||
|
def setPayload(self, value):
|
||||||
|
self.xml.append(value)
|
||||||
|
|
||||||
|
def delPayload(self):
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
for child in self.xml.getchildren():
|
||||||
|
self.xml.remove(child)
|
||||||
|
|
||||||
|
def reply(self):
|
||||||
|
self['from'], self['to'] = self['to'], self['from']
|
||||||
|
return self
|
||||||
|
|
||||||
|
def error(self):
|
||||||
|
self['type'] = 'error'
|
||||||
|
|
||||||
|
def _setAttr(self, name, value):
|
||||||
|
self.xml.attrib[name] = value
|
||||||
|
|
||||||
|
def _delAttr(self, name):
|
||||||
|
if name in self.xml.attrib:
|
||||||
|
del self.xml.attrib[name]
|
||||||
|
|
||||||
|
def _getAttr(self, name):
|
||||||
|
return self.xml.attrib.get(name, '')
|
||||||
|
|
||||||
|
def _getSubText(self, name):
|
||||||
|
stanza = self.xml.find("{%s}%s" % (self.namespace, name))
|
||||||
|
if stanza is None or stanza.text is None:
|
||||||
|
return ''
|
||||||
|
else:
|
||||||
|
return stanza.text
|
||||||
|
|
||||||
|
def _setSubText(self, name, attrib={}, text=None):
|
||||||
|
stanza = self.xml.find("{%s}%s" % (self.namespace, name))
|
||||||
|
if stanza is None:
|
||||||
|
self.xml.append(ET.Element("{%s}%s" % (self.namespace, name), attrib))
|
||||||
|
stanza = self.xml.find("{%s}%s" % (self.namespace, name))
|
||||||
|
if text is not None:
|
||||||
|
stanza.text = text
|
||||||
|
return stanza
|
||||||
|
|
||||||
|
def _delSub(self, name):
|
||||||
|
for child in self.xml.getchildren():
|
||||||
|
if child.tag == "{%s}%s" % (self.namespace, name):
|
||||||
|
self.xml.remove(child)
|
||||||
|
|
||||||
|
def unhandled(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def send(self):
|
||||||
|
self.stream.sendRaw(str(self))
|
||||||
|
|
||||||
|
def __str__(self, xml=None, xmlns='', stringbuffer=''):
|
||||||
|
if xml is None:
|
||||||
|
xml = self.xml
|
||||||
|
newoutput = [stringbuffer]
|
||||||
|
#TODO respect ET mapped namespaces
|
||||||
|
itag = xml.tag.split('}', 1)[-1]
|
||||||
|
if '}' in xml.tag:
|
||||||
|
ixmlns = xml.tag.split('}', 1)[0][1:]
|
||||||
|
else:
|
||||||
|
ixmlns = ''
|
||||||
|
nsbuffer = ''
|
||||||
|
#if xmlns != ixmlns and ixmlns != '':
|
||||||
|
# if ixmlns in self.namespace_map:
|
||||||
|
# if self.namespace_map[ixmlns] != '':
|
||||||
|
# itag = "%s:%s" % (self.namespace_map[ixmlns], itag)
|
||||||
|
# else:
|
||||||
|
# nsbuffer = """ xmlns="%s\"""" % ixmlns
|
||||||
|
if ixmlns not in (xmlns, self.namespace):
|
||||||
|
nsbuffer = """ xmlns="%s\"""" % ixmlns
|
||||||
|
newoutput.append("<%s" % itag)
|
||||||
|
newoutput.append(nsbuffer)
|
||||||
|
for attrib in xml.attrib:
|
||||||
|
newoutput.append(""" %s="%s\"""" % (attrib, self.xmlesc(xml.attrib[attrib])))
|
||||||
|
if len(xml) or xml.text or xml.tail:
|
||||||
|
newoutput.append(">")
|
||||||
|
if xml.text:
|
||||||
|
newoutput.append(self.xmlesc(xml.text))
|
||||||
|
if len(xml):
|
||||||
|
for child in xml.getchildren():
|
||||||
|
newoutput.append(self.__str__(child, ixmlns))
|
||||||
|
newoutput.append("</%s>" % (itag, ))
|
||||||
|
if xml.tail:
|
||||||
|
newoutput.append(self.xmlesc(xml.tail))
|
||||||
|
elif xml.text:
|
||||||
|
newoutput.append(">%s</%s>" % (self.xmlesc(xml.text), itag))
|
||||||
|
else:
|
||||||
|
newoutput.append(" />")
|
||||||
|
return ''.join(newoutput)
|
||||||
|
|
||||||
|
def xmlesc(self, text):
|
||||||
|
text = list(text)
|
||||||
|
cc = 0
|
||||||
|
matches = ('&', '<', '"', '>', "'")
|
||||||
|
for c in text:
|
||||||
|
if c in matches:
|
||||||
|
if c == '&':
|
||||||
|
text[cc] = '&'
|
||||||
|
elif c == '<':
|
||||||
|
text[cc] = '<'
|
||||||
|
elif c == '>':
|
||||||
|
text[cc] = '>'
|
||||||
|
elif c == "'":
|
||||||
|
text[cc] = '''
|
||||||
|
elif self.escape_quotes:
|
||||||
|
text[cc] = '"'
|
||||||
|
cc += 1
|
||||||
|
return ''.join(text)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
x = Stanza()
|
||||||
|
x['from'] = 'you'
|
||||||
|
x['to'] = 'me'
|
||||||
|
print(x['from'], x['to'])
|
||||||
|
x.reply()
|
||||||
|
print(x['from'], x['to'])
|
||||||
|
x['from'] = None
|
||||||
|
print(x['from'], x['to'])
|
||||||
|
print(str(x))
|
||||||
|
|
|
@ -44,7 +44,7 @@ class XMLStream(object):
|
||||||
|
|
||||||
self.__thread = {}
|
self.__thread = {}
|
||||||
|
|
||||||
self.__root_stanza = {}
|
self.__root_stanza = []
|
||||||
self.__stanza = {}
|
self.__stanza = {}
|
||||||
self.__stanza_extension = {}
|
self.__stanza_extension = {}
|
||||||
self.__handlers = []
|
self.__handlers = []
|
||||||
|
@ -251,11 +251,13 @@ class XMLStream(object):
|
||||||
xmlobj = self.incoming_filter(xmlobj)
|
xmlobj = self.incoming_filter(xmlobj)
|
||||||
stanza = None
|
stanza = None
|
||||||
for stanza_class in self.__root_stanza:
|
for stanza_class in self.__root_stanza:
|
||||||
if self.__root_stanza[stanza_class].match(xmlobj):
|
if xmlobj.tag == "{%s}%s" % (self.default_ns, stanza_class.name):
|
||||||
|
#if self.__root_stanza[stanza_class].match(xmlobj):
|
||||||
stanza = stanza_class(self, xmlobj)
|
stanza = stanza_class(self, xmlobj)
|
||||||
break
|
break
|
||||||
if stanza is None:
|
if stanza is None:
|
||||||
stanza = StanzaBase(self, xmlobj)
|
stanza = StanzaBase(self, xmlobj)
|
||||||
|
logging.debug(self.__handlers)
|
||||||
for handler in self.__handlers:
|
for handler in self.__handlers:
|
||||||
if handler.match(xmlobj):
|
if handler.match(xmlobj):
|
||||||
handler.prerun(stanza)
|
handler.prerun(stanza)
|
||||||
|
@ -293,12 +295,9 @@ class XMLStream(object):
|
||||||
return
|
return
|
||||||
idx += 1
|
idx += 1
|
||||||
|
|
||||||
def registerStanza(self, matcher, stanza_class, root=True):
|
def registerStanza(self, stanza_class):
|
||||||
"Adds stanza. If root stanzas build stanzas sent in events while non-root stanzas build substanza objects."
|
"Adds stanza. If root stanzas build stanzas sent in events while non-root stanzas build substanza objects."
|
||||||
if root:
|
self.__root_stanza.append(stanza_class)
|
||||||
self.__root_stanza[stanza_class] = matcher
|
|
||||||
else:
|
|
||||||
self.__stanza[stanza_class] = matcher
|
|
||||||
|
|
||||||
def registerStanzaExtension(self, stanza_class, stanza_extension):
|
def registerStanzaExtension(self, stanza_class, stanza_extension):
|
||||||
if stanza_class not in stanza_extensions:
|
if stanza_class not in stanza_extensions:
|
||||||
|
|
Loading…
Reference in a new issue