* added proper message and iq stanzas. presence left to do

This commit is contained in:
Nathan Fritz 2009-12-10 01:23:03 +00:00
parent 6be17ebbe2
commit 007b04dd30
6 changed files with 283 additions and 39 deletions

View file

@ -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:

View file

@ -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."""

View file

@ -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

View file

@ -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'])

View file

@ -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] = '&amp;'
elif c == '<':
text[cc] = '&lt;'
elif c == '>':
text[cc] = '&gt;'
elif c == "'":
text[cc] = '&apos;'
elif self.escape_quotes:
text[cc] = '&quot;'
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))

View file

@ -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: