summaryrefslogtreecommitdiff
path: root/sleekxmpp
diff options
context:
space:
mode:
Diffstat (limited to 'sleekxmpp')
-rw-r--r--sleekxmpp/__init__.py4
-rw-r--r--sleekxmpp/basexmpp.py4
-rw-r--r--sleekxmpp/stanza/iq.py39
-rw-r--r--sleekxmpp/stanza/message.py39
-rw-r--r--sleekxmpp/xmlstream/stanzabase.py215
-rw-r--r--sleekxmpp/xmlstream/xmlstream.py13
6 files changed, 279 insertions, 35 deletions
diff --git a/sleekxmpp/__init__.py b/sleekxmpp/__init__.py
index f95c6665..49c9a66b 100644
--- a/sleekxmpp/__init__.py
+++ b/sleekxmpp/__init__.py
@@ -37,7 +37,7 @@ import sys
import random
import copy
from . import plugins
-from . import stanza
+#from . import stanza
srvsupport = True
try:
import dns.resolver
@@ -87,6 +87,8 @@ class ClientXMPP(basexmpp, XMLStream):
#self.register_plugins()
def importStanzas(self):
+ pass
+ return
for modname in stanza.__all__:
__import__("%s.%s" % (globals()['stanza'].__name__, modname))
for register in getattr(stanza, modname).stanzas:
diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py
index 07715e0c..0f4f799c 100644
--- a/sleekxmpp/basexmpp.py
+++ b/sleekxmpp/basexmpp.py
@@ -26,6 +26,8 @@ from . xmlstream.handler.xmlcallback import XMLCallback
from . xmlstream.handler.xmlwaiter import XMLWaiter
from . xmlstream.handler.callback import Callback
from . import plugins
+from . stanza.message import Message
+from . stanza.iq import Iq
import logging
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('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.registerStanza(Message)
+ self.registerStanza(Iq)
def set_jid(self, jid):
"""Rip a JID apart and claim it as our own."""
diff --git a/sleekxmpp/stanza/iq.py b/sleekxmpp/stanza/iq.py
index e69de29b..eece37bd 100644
--- a/sleekxmpp/stanza/iq.py
+++ b/sleekxmpp/stanza/iq.py
@@ -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
diff --git a/sleekxmpp/stanza/message.py b/sleekxmpp/stanza/message.py
index e69de29b..37e2e5f3 100644
--- a/sleekxmpp/stanza/message.py
+++ b/sleekxmpp/stanza/message.py
@@ -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'])
diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py
index 5232ff5e..787ae5e3 100644
--- a/sleekxmpp/xmlstream/stanzabase.py
+++ b/sleekxmpp/xmlstream/stanzabase.py
@@ -1,37 +1,198 @@
-from __future__ import absolute_import
-from sleekxmpp.xmlstream.matcher.xpath import MatchXPath
+from xml.etree import cElementTree as ET
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, extensions=[]):
- self.extensions = extensions
- self.p = {} #plugins
-
- self.xml = xml
+ def __init__(self, stream, xml=None, stype=None, sto=None, sfrom=None, sid=None):
self.stream = stream
- if xml is not None:
- self.fromXML(xml)
-
- def fromXML(self, xml):
- "Initialize based on incoming XML"
- self._processXML(xml)
- for ext in self.extensions:
- ext.fromXML(self, xml)
+ self.xml = xml
+ if xml is None:
+ self.xml = ET.Element("{%(namespace)s}%(name)s" % {'name': self.name, 'namespace': self.namespace})
+ if stype is not None:
+ self['type'] = stype
+ if sto is not None:
+ self['to'] = sto
+ if sfrom is not None:
+ self['from'] = sfrom
+ self.tag = "{%s}%s" % (self.stream.default_ns, self.name)
+
+ def match(self, 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 _processXML(self, xml, cur_ns=''):
- if '}' in xml.tag:
- ns,tag = xml.tag[1:].split('}')
+ 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:
- tag = xml.tag
+ raise ValueError
+ return self
+
+ def getPayload(self):
+ return self.xml.getchildren()
- def toXML(self, xml):
- "Set outgoing XML"
+ def setPayload(self, value):
+ self.xml.append(value)
- def extend(self, extension_class, xml=None):
- "Initialize extension"
+ def delPayload(self):
+ self.clear()
- def match(self, xml):
- return self.MATCHER.match(xml)
+ 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))
diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py
index 8ffb7841..8971a02c 100644
--- a/sleekxmpp/xmlstream/xmlstream.py
+++ b/sleekxmpp/xmlstream/xmlstream.py
@@ -44,7 +44,7 @@ class XMLStream(object):
self.__thread = {}
- self.__root_stanza = {}
+ self.__root_stanza = []
self.__stanza = {}
self.__stanza_extension = {}
self.__handlers = []
@@ -251,11 +251,13 @@ class XMLStream(object):
xmlobj = self.incoming_filter(xmlobj)
stanza = None
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)
break
if stanza is None:
stanza = StanzaBase(self, xmlobj)
+ logging.debug(self.__handlers)
for handler in self.__handlers:
if handler.match(xmlobj):
handler.prerun(stanza)
@@ -293,12 +295,9 @@ class XMLStream(object):
return
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."
- if root:
- self.__root_stanza[stanza_class] = matcher
- else:
- self.__stanza[stanza_class] = matcher
+ self.__root_stanza.append(stanza_class)
def registerStanzaExtension(self, stanza_class, stanza_extension):
if stanza_class not in stanza_extensions: