summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--setup.py14
-rw-r--r--sleekxmpp/basexmpp.py53
-rw-r--r--sleekxmpp/xmlstream/stanzabase.py91
-rw-r--r--sleekxmpp/xmlstream/tostring/__init__.py13
-rw-r--r--sleekxmpp/xmlstream/tostring/tostring.py149
-rw-r--r--sleekxmpp/xmlstream/tostring/tostring26.py157
-rw-r--r--sleekxmpp/xmlstream/tostring26/__init__.py65
-rw-r--r--sleekxmpp/xmlstream/xmlstream.py101
8 files changed, 299 insertions, 344 deletions
diff --git a/setup.py b/setup.py
index 280ec3c0..e3acf18a 100644
--- a/setup.py
+++ b/setup.py
@@ -16,13 +16,13 @@ import sys
# min_version = '0.6c6'
# else:
# min_version = '0.6a9'
-#
+#
# try:
# use_setuptools(min_version=min_version)
# except TypeError:
# # locally installed ez_setup won't have min_version
# use_setuptools()
-#
+#
# from setuptools import setup, find_packages, Extension, Feature
VERSION = '0.2.3.1'
@@ -37,17 +37,13 @@ CLASSIFIERS = [ 'Intended Audience :: Developers',
'Topic :: Software Development :: Libraries :: Python Modules',
]
-packages = [ 'sleekxmpp',
+packages = [ 'sleekxmpp',
'sleekxmpp/plugins',
'sleekxmpp/stanza',
'sleekxmpp/xmlstream',
'sleekxmpp/xmlstream/matcher',
- 'sleekxmpp/xmlstream/handler' ]
-
-if sys.version_info < (3, 0):
- packages.append('sleekxmpp/xmlstream/tostring26')
-else:
- packages.append('sleekxmpp/xmlstream/tostring')
+ 'sleekxmpp/xmlstream/handler',
+ 'sleekxmpp/xmlstream/tostring']
setup(
name = "sleekxmpp",
diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py
index 2c2bb91e..b7b605b0 100644
--- a/sleekxmpp/basexmpp.py
+++ b/sleekxmpp/basexmpp.py
@@ -25,6 +25,7 @@ from . stanza.roster import Roster
from . stanza.nick import Nick
from . stanza.htmlim import HTMLIM
from . stanza.error import Error
+from sleekxmpp.xmlstream.tostring import tostring
import logging
import threading
@@ -60,7 +61,7 @@ class basexmpp(object):
registerStanzaPlugin(Iq, Roster)
registerStanzaPlugin(Message, Nick)
registerStanzaPlugin(Message, HTMLIM)
-
+
def Message(self, *args, **kwargs):
return Message(self, *args, **kwargs)
@@ -69,7 +70,7 @@ class basexmpp(object):
def Presence(self, *args, **kwargs):
return Presence(self, *args, **kwargs)
-
+
def set_jid(self, jid):
"""Rip a JID apart and claim it as our own."""
self.fulljid = jid
@@ -77,12 +78,12 @@ class basexmpp(object):
self.jid = self.getjidbare(jid)
self.username = jid.split('@', 1)[0]
self.server = jid.split('@',1)[-1].split('/', 1)[0]
-
+
def process(self, *args, **kwargs):
for idx in self.plugin:
if not self.plugin[idx].post_inited: self.plugin[idx].post_init()
return super(basexmpp, self).process(*args, **kwargs)
-
+
def registerPlugin(self, plugin, pconfig = {}):
"""Register a plugin not in plugins.__init__.__all__ but in the plugins
directory."""
@@ -97,7 +98,7 @@ class basexmpp(object):
if hasattr(self.plugin[plugin], 'xep'):
xep = "(XEP-%s) " % self.plugin[plugin].xep
logging.debug("Loaded Plugin %s%s" % (xep, self.plugin[plugin].description))
-
+
def register_plugins(self):
"""Initiates all plugins in the plugins/__init__.__all__"""
if self.plugin_whitelist:
@@ -112,24 +113,24 @@ class basexmpp(object):
# run post_init() for cross-plugin interaction
for plugin in self.plugin:
self.plugin[plugin].post_init()
-
+
def getNewId(self):
with self.id_lock:
self.id += 1
return self.getId()
-
+
def add_handler(self, mask, pointer, name=None, disposable=False, threaded=False, filter=False, instream=False):
# threaded is no longer needed, but leaving it for backwards compatibility for now
if name is None:
name = 'add_handler_%s' % self.getNewId()
self.registerHandler(XMLCallback(name, MatchXMLMask(mask), pointer, threaded, disposable, instream))
-
+
def getId(self):
return "%x".upper() % self.id
def sendXML(self, data, mask=None, timeout=10):
- return self.send(self.tostring(data), mask, timeout)
-
+ return self.send(tostring(data), mask, timeout)
+
def send(self, data, mask=None, timeout=10):
#logging.warning("Deprecated send used for \"%s\"" % (data,))
#if not type(data) == type(''):
@@ -144,19 +145,19 @@ class basexmpp(object):
self.sendRaw(data)
if mask is not None:
return waitfor.wait(timeout)
-
+
def makeIq(self, id=0, ifrom=None):
return self.Iq().setStanzaValues({'id': str(id), 'from': ifrom})
-
+
def makeIqGet(self, queryxmlns = None):
iq = self.Iq().setStanzaValues({'type': 'get'})
if queryxmlns:
iq.append(ET.Element("{%s}query" % queryxmlns))
return iq
-
+
def makeIqResult(self, id):
return self.Iq().setStanzaValues({'id': id, 'type': 'result'})
-
+
def makeIqSet(self, sub=None):
iq = self.Iq().setStanzaValues({'type': 'set'})
if sub != None:
@@ -172,13 +173,13 @@ class basexmpp(object):
query = ET.Element("{%s}query" % xmlns)
iq.append(query)
return iq
-
+
def makeQueryRoster(self, iq=None):
query = ET.Element("{jabber:iq:roster}query")
if iq:
iq.append(query)
return query
-
+
def add_event_handler(self, name, pointer, threaded=False, disposable=False):
if not name in self.event_handlers:
self.event_handlers[name] = []
@@ -188,13 +189,13 @@ class basexmpp(object):
"""Remove a handler for an event."""
if not name in self.event_handlers:
return
-
+
# Need to keep handlers that do not use
# the given function pointer
def filter_pointers(handler):
return handler[0] != pointer
- self.event_handlers[name] = filter(filter_pointers,
+ self.event_handlers[name] = filter(filter_pointers,
self.event_handlers[name])
def event(self, name, eventdata = {}): # called on an event
@@ -209,7 +210,7 @@ class basexmpp(object):
if handler[2]: #disposable
with self.lock:
self.event_handlers[name].pop(self.event_handlers[name].index(handler))
-
+
def makeMessage(self, mto, mbody=None, msubject=None, mtype=None, mhtml=None, mfrom=None, mnick=None):
message = self.Message(sto=mto, stype=mtype, sfrom=mfrom)
message['body'] = mbody
@@ -217,7 +218,7 @@ class basexmpp(object):
if mnick is not None: message['nick'] = mnick
if mhtml is not None: message['html']['html'] = mhtml
return message
-
+
def makePresence(self, pshow=None, pstatus=None, ppriority=None, pto=None, ptype=None, pfrom=None):
presence = self.Presence(stype=ptype, sfrom=pfrom, sto=pto)
if pshow is not None: presence['type'] = pshow
@@ -226,10 +227,10 @@ class basexmpp(object):
presence['priority'] = ppriority
presence['status'] = pstatus
return presence
-
+
def sendMessage(self, mto, mbody, msubject=None, mtype=None, mhtml=None, mfrom=None, mnick=None):
self.send(self.makeMessage(mto,mbody,msubject,mtype,mhtml,mfrom,mnick))
-
+
def sendPresence(self, pshow=None, pstatus=None, ppriority=None, pto=None, pfrom=None, ptype=None):
self.send(self.makePresence(pshow,pstatus,ppriority,pto, ptype=ptype, pfrom=pfrom))
if not self.sentpresence:
@@ -243,19 +244,19 @@ class basexmpp(object):
nick.text = pnick
presence.append(nick)
self.send(presence)
-
+
def getjidresource(self, fulljid):
if '/' in fulljid:
return fulljid.split('/', 1)[-1]
else:
return ''
-
+
def getjidbare(self, fulljid):
return fulljid.split('/', 1)[0]
def _handleMessage(self, msg):
self.event('message', msg)
-
+
def _handlePresence(self, presence):
"""Update roster items based on presence"""
self.event("presence_%s" % presence['type'], presence)
@@ -296,7 +297,7 @@ class basexmpp(object):
if name:
name = "(%s) " % name
logging.debug("STATUS: %s%s/%s[%s]: %s" % (name, jid, resource, show,status))
-
+
def _handlePresenceSubscribe(self, presence):
"""Handling subscriptions automatically."""
if self.auto_authorize == True:
diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py
index 94ff958c..3b5f0bf4 100644
--- a/sleekxmpp/xmlstream/stanzabase.py
+++ b/sleekxmpp/xmlstream/stanzabase.py
@@ -12,10 +12,7 @@ import weakref
import copy
from . jid import JID
-if sys.version_info < (3,0):
- from . import tostring26 as tostring
-else:
- from . import tostring
+from sleekxmpp.xmlstream.tostring import tostring
xmltester = type(ET.Element('xml'))
@@ -29,7 +26,7 @@ def registerStanzaPlugin(stanza, plugin):
stanza.plugin_tag_map[tag] = plugin
-class ElementBase(tostring.ToString):
+class ElementBase(object):
name = 'stanza'
plugin_attrib = 'plugin'
namespace = 'jabber:client'
@@ -70,20 +67,20 @@ class ElementBase(tostring.ToString):
def __bool__(self):
return True
-
+
def __next__(self):
self.idx += 1
if self.idx > len(self.iterables):
self.idx = 0
raise StopIteration
return self.iterables[self.idx - 1]
-
+
def next(self):
return self.__next__()
def __len__(self):
return len(self.iterables)
-
+
def append(self, item):
if not isinstance(item, ElementBase):
if type(item) == xmltester:
@@ -93,18 +90,18 @@ class ElementBase(tostring.ToString):
self.xml.append(item.xml)
self.iterables.append(item)
return self
-
+
def pop(self, idx=0):
aff = self.iterables.pop(idx)
self.xml.remove(aff.xml)
return aff
-
+
def get(self, key, defaultvalue=None):
value = self[key]
if value is None or value == '':
return defaultvalue
return value
-
+
def keys(self):
out = []
out += [x for x in self.interfaces]
@@ -112,7 +109,7 @@ class ElementBase(tostring.ToString):
if self.iterables:
out.append('substanzas')
return tuple(out)
-
+
def match(self, matchstring):
if isinstance(matchstring, str):
nodes = matchstring.split('/')
@@ -136,13 +133,13 @@ class ElementBase(tostring.ToString):
else:
return False
return True
-
+
def find(self, xpath): # for backwards compatiblity, expose elementtree interface
return self.xml.find(xpath)
def findall(self, xpath):
return self.xml.findall(xpath)
-
+
def setup(self, xml=None):
if self.xml is None:
self.xml = xml
@@ -162,11 +159,11 @@ class ElementBase(tostring.ToString):
def enable(self, attrib):
self.initPlugin(attrib)
return self
-
+
def initPlugin(self, attrib):
if attrib not in self.plugins:
self.plugins[attrib] = self.plugin_attrib_map[attrib](parent=self)
-
+
def __getitem__(self, attrib):
if attrib == 'substanzas':
return self.iterables
@@ -183,7 +180,7 @@ class ElementBase(tostring.ToString):
return self.plugins[attrib]
else:
return ''
-
+
def __setitem__(self, attrib, value):
if attrib in self.interfaces:
if value is not None:
@@ -201,7 +198,7 @@ class ElementBase(tostring.ToString):
self.initPlugin(attrib)
self.plugins[attrib][attrib] = value
return self
-
+
def __delitem__(self, attrib):
if attrib.lower() in self.interfaces:
if hasattr(self, "del%s" % attrib.title()):
@@ -215,7 +212,7 @@ class ElementBase(tostring.ToString):
if attrib in self.plugins:
del self.plugins[attrib]
return self
-
+
def __eq__(self, other):
if not isinstance(other, ElementBase):
return False
@@ -224,20 +221,20 @@ class ElementBase(tostring.ToString):
if key not in values or values[key] != other[key]:
return False
return True
-
+
def _setAttr(self, name, value):
if value is None or value == '':
self.__delitem__(name)
else:
self.xml.attrib[name] = value
-
+
def _delAttr(self, name):
if name in self.xml.attrib:
del self.xml.attrib[name]
-
+
def _getAttr(self, name, default=''):
return self.xml.attrib.get(name, default)
-
+
def _getSubText(self, name):
if '}' not in name:
name = "{%s}%s" % (self.namespace, name)
@@ -246,7 +243,7 @@ class ElementBase(tostring.ToString):
return ''
else:
return stanza.text
-
+
def _setSubText(self, name, attrib={}, text=None):
if '}' not in name:
name = "{%s}%s" % (self.namespace, name)
@@ -258,14 +255,14 @@ class ElementBase(tostring.ToString):
self.xml.append(stanza)
stanza.text = text
return stanza
-
+
def _delSub(self, name):
if '}' not in name:
name = "{%s}%s" % (self.namespace, name)
for child in self.xml.getchildren():
if child.tag == name:
self.xml.remove(child)
-
+
def getStanzaValues(self):
out = {}
for interface in self.interfaces:
@@ -279,7 +276,7 @@ class ElementBase(tostring.ToString):
iterables[-1].update({'__childtag__': "{%s}%s" % (stanza.namespace, stanza.name)})
out['substanzas'] = iterables
return out
-
+
def setStanzaValues(self, attrib):
for interface in attrib:
if interface == 'substanzas':
@@ -298,14 +295,20 @@ class ElementBase(tostring.ToString):
if interface in self.plugins:
self.plugins[interface].setStanzaValues(attrib[interface])
return self
-
+
def appendxml(self, xml):
self.xml.append(xml)
return self
def __copy__(self):
return self.__class__(xml=copy.deepcopy(self.xml), parent=self.parent)
-
+
+ def __str__(self):
+ return tostring(self.xml, xmlns='', stanza_ns=self.namespace)
+
+ def __repr__(self):
+ return self.__str__()
+
#def __del__(self): #prevents garbage collection of reference cycle
# if self.parent is not None:
# self.parent.xml.remove(self.xml)
@@ -329,7 +332,7 @@ class StanzaBase(ElementBase):
if sfrom is not None:
self['from'] = sfrom
self.tag = "{%s}%s" % (self.namespace, self.name)
-
+
def setType(self, value):
if value in self.types:
self.xml.attrib['type'] = value
@@ -337,22 +340,22 @@ class StanzaBase(ElementBase):
def getPayload(self):
return self.xml.getchildren()
-
+
def setPayload(self, value):
self.xml.append(value)
return self
-
+
def delPayload(self):
self.clear()
return self
-
+
def clear(self):
for child in self.xml.getchildren():
self.xml.remove(child)
for plugin in list(self.plugins.keys()):
del self.plugins[plugin]
return self
-
+
def reply(self):
# if it's a component, use from
if self.stream and hasattr(self.stream, "is_component") and self.stream.is_component:
@@ -362,32 +365,34 @@ class StanzaBase(ElementBase):
del self['from']
self.clear()
return self
-
+
def error(self):
self['type'] = 'error'
return self
-
+
def getTo(self):
return JID(self._getAttr('to'))
-
+
def setTo(self, value):
return self._setAttr('to', str(value))
-
+
def getFrom(self):
return JID(self._getAttr('from'))
-
+
def setFrom(self, value):
return self._setAttr('from', str(value))
-
+
def unhandled(self):
pass
-
+
def exception(self, e):
logging.exception('Error handling {%s}%s stanza' % (self.namespace, self.name))
-
+
def send(self):
self.stream.sendRaw(self.__str__())
def __copy__(self):
return self.__class__(xml=copy.deepcopy(self.xml), stream=self.stream)
-
+
+ def __str__(self):
+ return tostring(self.xml, xmlns='', stanza_ns=self.namespace, stream=self.stream)
diff --git a/sleekxmpp/xmlstream/tostring/__init__.py b/sleekxmpp/xmlstream/tostring/__init__.py
index d93fe4ea..5852cba2 100644
--- a/sleekxmpp/xmlstream/tostring/__init__.py
+++ b/sleekxmpp/xmlstream/tostring/__init__.py
@@ -1,14 +1,19 @@
"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2010 Nathanael C. Fritz
+ This file is part of SleekXMPP.
+ See the file LICENSE for copying permission.
"""
import sys
+# Import the correct tostring and xml_escape functions based on the Python
+# version in order to properly handle Unicode.
-# Import the correct ToString class based on the Python version.
if sys.version_info < (3, 0):
- from sleekxmpp.xmlstream.tostring.tostring26 import ToString
+ from sleekxmpp.xmlstream.tostring.tostring26 import tostring, xml_escape
else:
- from sleekxmpp.xmlstream.tostring.tostring import ToString
+ from sleekxmpp.xmlstream.tostring.tostring import tostring, xml_escape
-__all__ = ['ToString']
+__all__ = ['tostring', 'xml_escape']
diff --git a/sleekxmpp/xmlstream/tostring/tostring.py b/sleekxmpp/xmlstream/tostring/tostring.py
index 6603cbb8..62ff1181 100644
--- a/sleekxmpp/xmlstream/tostring/tostring.py
+++ b/sleekxmpp/xmlstream/tostring/tostring.py
@@ -1,60 +1,91 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2010 Nathanael C. Fritz
+ This file is part of SleekXMPP.
-class ToString(object):
- 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 != '' and ixmlns != self.namespace:
- if self.stream is not None and ixmlns in self.stream.namespace_map:
- if self.stream.namespace_map[ixmlns] != '':
- itag = "%s:%s" % (self.stream.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:
- if '{' not in 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;'
- else:
- text[cc] = '&quot;'
- cc += 1
- return ''.join(text)
+ See the file LICENSE for copying permission.
+"""
+
+
+def tostring(xml=None, xmlns='', stanza_ns='', stream=None, outbuffer=''):
+ """
+ Serialize an XML object to a Unicode string.
+
+ Arguments:
+ xml -- The XML object to serialize. If the value is None,
+ then the XML object contained in this stanza
+ object will be used.
+ xmlns -- Optional namespace of an element wrapping the XML
+ object.
+ stanza_ns -- The namespace of the stanza object that contains
+ the XML object.
+ stream -- The XML stream that generated the XML object.
+ outbuffer -- Optional buffer for storing serializations during
+ recursive calls.
+ """
+ # Add previous results to the start of the output.
+ output = [outbuffer]
+
+ # Extract the element's tag name.
+ tag_name = xml.tag.split('}', 1)[-1]
+
+ # Extract the element's namespace if it is defined.
+ if '}' in xml.tag:
+ tag_xmlns = xml.tag.split('}', 1)[0][1:]
+ else:
+ tag_xmlns = ''
+
+ # Output the tag name and derived namespace of the element.
+ namespace = ''
+ if tag_xmlns not in ['', xmlns, stanza_ns]:
+ namespace = ' xmlns="%s"' % tag_xmlns
+ if stream and tag_xmlns in stream.namespace_map:
+ mapped_namespace = stream.namespace_map[tag_xmlns]
+ if mapped_namespace:
+ tag = "%s:%s" % (mapped_namespace, tag_name)
+ output.append("<%s" % tag_name)
+ output.append(namespace)
+
+ # Output escaped attribute values.
+ for attrib, value in xml.attrib.items():
+ if '{' not in attrib:
+ value = xml_escape(value)
+ output.append(' %s="%s"' % (attrib, value))
+
+ if len(xml) or xml.text:
+ # If there are additional child elements to serialize.
+ output.append(">")
+ if xml.text:
+ output.append(xml_escape(xml.text))
+ if len(xml):
+ for child in xml.getchildren():
+ output.append(tostring(child, tag_xmlns, stanza_ns, stream))
+ output.append("</%s>" % tag_name)
+ elif xml.text:
+ # If we only have text content.
+ output.append(">%s</%s>" % (xml_escape(xml.text), tag_name))
+ else:
+ # Empty element.
+ output.append(" />")
+ if xml.tail:
+ # If there is additional text after the element.
+ output.append(xml_escape(xml.tail))
+ return ''.join(output)
+
+
+def xml_escape(text):
+ """
+ Convert special characters in XML to escape sequences.
+
+ Arguments:
+ text -- The XML text to convert.
+ """
+ text = list(text)
+ escapes = {'&': '&amp;',
+ '<': '&lt;',
+ '>': '&gt;',
+ "'": '&apos;',
+ '"': '&quot;'}
+ for i, c in enumerate(text):
+ text[i] = escapes.get(c, c)
+ return ''.join(text)
diff --git a/sleekxmpp/xmlstream/tostring/tostring26.py b/sleekxmpp/xmlstream/tostring/tostring26.py
index 9711c300..9dba2717 100644
--- a/sleekxmpp/xmlstream/tostring/tostring26.py
+++ b/sleekxmpp/xmlstream/tostring/tostring26.py
@@ -1,65 +1,100 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2010 Nathanael C. Fritz
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from __future__ import unicode_literals
import types
-class ToString(object):
- 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 != u'' and ixmlns != self.namespace:
- if self.stream is not None and ixmlns in self.stream.namespace_map:
- if self.stream.namespace_map[ixmlns] != u'':
- itag = "%s:%s" % (self.stream.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:
- if '{' not in attrib:
- newoutput.append(""" %s="%s\"""" % (attrib, self.xmlesc(xml.attrib[attrib])))
- if len(xml) or xml.text or xml.tail:
- newoutput.append(u">")
- 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(u"</%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 u''.join(newoutput)
- def xmlesc(self, text):
- if type(text) != types.UnicodeType:
- text = list(unicode(text, 'utf-8', 'ignore'))
- else:
- text = list(text)
+def tostring(xml=None, xmlns='', stanza_ns='', stream=None, outbuffer=''):
+ """
+ Serialize an XML object to a Unicode string.
+
+ Arguments:
+ xml -- The XML object to serialize. If the value is None,
+ then the XML object contained in this stanza
+ object will be used.
+ xmlns -- Optional namespace of an element wrapping the XML
+ object.
+ stanza_ns -- The namespace of the stanza object that contains
+ the XML object.
+ stream -- The XML stream that generated the XML object.
+ outbuffer -- Optional buffer for storing serializations during
+ recursive calls.
+ """
+ # Add previous results to the start of the output.
+ output = [outbuffer]
+
+ # Extract the element's tag name.
+ tag_name = xml.tag.split('}', 1)[-1]
+
+ # Extract the element's namespace if it is defined.
+ if '}' in xml.tag:
+ tag_xmlns = xml.tag.split('}', 1)[0][1:]
+ else:
+ tag_xmlns = u''
+
+ # Output the tag name and derived namespace of the element.
+ namespace = u''
+ if tag_xmlns not in ['', xmlns, stanza_ns]:
+ namespace = u' xmlns="%s"' % tag_xmlns
+ if stream and tag_xmlns in stream.namespace_map:
+ mapped_namespace = stream.namespace_map[tag_xmlns]
+ if mapped_namespace:
+ tag = u"%s:%s" % (mapped_namespace, tag_name)
+ output.append(u"<%s" % tag_name)
+ output.append(namespace)
+
+ # Output escaped attribute values.
+ for attrib, value in xml.attrib.items():
+ if '{' not in attrib:
+ value = xml_escape(value)
+ output.append(u' %s="%s"' % (attrib, value))
+
+ if len(xml) or xml.text:
+ # If there are additional child elements to serialize.
+ output.append(u">")
+ if xml.text:
+ output.append(xml_escape(xml.text))
+ if len(xml):
+ for child in xml.getchildren():
+ output.append(tostring(child, tag_xmlns, stanza_ns, stream))
+ output.append(u"</%s>" % tag_name)
+ if xml.tail:
+ # If there is additional text after the element.
+ output.append(xml_escape(xml.tail))
+ elif xml.text:
+ # If we only have text content.
+ output.append(u">%s</%s>" % (xml_escape(xml.text), tag_name))
+ else:
+ # Empty element.
+ output.append(u" />")
+ if xml.tail:
+ # If there is additional text after the element.
+ output.append(xml_escape(xml.tail))
+ return u''.join(output)
+
+
+def xml_escape(text):
+ """
+ Convert special characters in XML to escape sequences.
- cc = 0
- matches = (u'&', u'<', u'"', u'>', u"'")
- for c in text:
- if c in matches:
- if c == u'&':
- text[cc] = u'&amp;'
- elif c == u'<':
- text[cc] = u'&lt;'
- elif c == u'>':
- text[cc] = u'&gt;'
- elif c == u"'":
- text[cc] = u'&apos;'
- else:
- text[cc] = u'&quot;'
- cc += 1
- return ''.join(text)
+ Arguments:
+ text -- The XML text to convert.
+ """
+ if type(text) != types.UnicodeType:
+ text = list(unicode(text, 'utf-8', 'ignore'))
+ else:
+ text = list(text)
+ escapes = {u'&': u'&amp;',
+ u'<': u'&lt;',
+ u'>': u'&gt;',
+ u"'": u'&apos;',
+ u'"': u'&quot;'}
+ for i, c in enumerate(text):
+ text[i] = escapes.get(c, c)
+ return u''.join(text)
diff --git a/sleekxmpp/xmlstream/tostring26/__init__.py b/sleekxmpp/xmlstream/tostring26/__init__.py
deleted file mode 100644
index 9711c300..00000000
--- a/sleekxmpp/xmlstream/tostring26/__init__.py
+++ /dev/null
@@ -1,65 +0,0 @@
-import types
-
-class ToString(object):
- 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 != u'' and ixmlns != self.namespace:
- if self.stream is not None and ixmlns in self.stream.namespace_map:
- if self.stream.namespace_map[ixmlns] != u'':
- itag = "%s:%s" % (self.stream.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:
- if '{' not in attrib:
- newoutput.append(""" %s="%s\"""" % (attrib, self.xmlesc(xml.attrib[attrib])))
- if len(xml) or xml.text or xml.tail:
- newoutput.append(u">")
- 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(u"</%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 u''.join(newoutput)
-
- def xmlesc(self, text):
- if type(text) != types.UnicodeType:
- text = list(unicode(text, 'utf-8', 'ignore'))
- else:
- text = list(text)
-
- cc = 0
- matches = (u'&', u'<', u'"', u'>', u"'")
- for c in text:
- if c in matches:
- if c == u'&':
- text[cc] = u'&amp;'
- elif c == u'<':
- text[cc] = u'&lt;'
- elif c == u'>':
- text[cc] = u'&gt;'
- elif c == u"'":
- text[cc] = u'&apos;'
- else:
- text[cc] = u'&quot;'
- cc += 1
- return ''.join(text)
diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py
index 94fed64a..ffaa6514 100644
--- a/sleekxmpp/xmlstream/xmlstream.py
+++ b/sleekxmpp/xmlstream/xmlstream.py
@@ -23,6 +23,7 @@ import types
import copy
import xml.sax.saxutils
from . import scheduler
+from sleekxmpp.xmlstream.tostring import tostring
RESPONSE_TIMEOUT = 10
HANDLER_THREADS = 1
@@ -37,7 +38,7 @@ if sys.version_info < (3, 0):
#monkey patch broken filesocket object
from . import filesocket
#socket._fileobject = filesocket.filesocket
-
+
class RestartStream(Exception):
pass
@@ -82,7 +83,7 @@ class XMLStream(object):
self.namespace_map = {}
self.run = True
-
+
def setSocket(self, socket):
"Set the socket"
self.socket = socket
@@ -90,10 +91,10 @@ class XMLStream(object):
self.filesocket = socket.makefile('rb', 0) # ElementTree.iterparse requires a file. 0 buffer files have to be binary
self.state.set('connected', True)
-
+
def setFileSocket(self, filesocket):
self.filesocket = filesocket
-
+
def connect(self, host='', port=0, use_ssl=False, use_tls=True):
"Link to connectTCP"
return self.connectTCP(host, port, use_ssl, use_tls)
@@ -125,7 +126,7 @@ class XMLStream(object):
except socket.error as serr:
logging.error("Could not connect. Socket Error #%s: %s" % (serr.errno, serr.strerror))
time.sleep(1)
-
+
def connectUnix(self, filepath):
"Connect to Unix file and create socket"
@@ -146,7 +147,7 @@ class XMLStream(object):
logging.warning("Tried to enable TLS, but ssl module not found.")
return False
raise RestartStream()
-
+
def process(self, threaded=True):
self.scheduler.process(threaded=True)
for t in range(0, HANDLER_THREADS):
@@ -160,10 +161,10 @@ class XMLStream(object):
self.__thread['process'].start()
else:
self._process()
-
+
def schedule(self, name, seconds, callback, args=None, kwargs=None, repeat=False):
self.scheduler.add(name, seconds, callback, args, kwargs, repeat, qpointer=self.eventqueue)
-
+
def _process(self):
"Start processing the socket."
firstrun = True
@@ -212,7 +213,7 @@ class XMLStream(object):
#self.__thread['readXML'].start()
#self.__thread['spawnEvents'] = threading.Thread(name='spawnEvents', target=self.__spawnEvents)
#self.__thread['spawnEvents'].start()
-
+
def __readXML(self):
"Parses the incoming stream, adding to xmlin queue as it goes"
#build cElementTree object from expat was we go
@@ -245,7 +246,7 @@ class XMLStream(object):
if event == b'start':
edepth += 1
logging.debug("Ending readXML loop")
-
+
def _sendThread(self):
while self.run:
data = self.sendqueue.get(True)
@@ -260,11 +261,11 @@ class XMLStream(object):
if self.state.reconnect:
logging.exception("Disconnected. Socket Error.")
self.disconnect(reconnect=True)
-
+
def sendRaw(self, data):
self.sendqueue.put(data)
return True
-
+
def disconnect(self, reconnect=False):
self.state.set('reconnect', reconnect)
if self.state['disconnecting']:
@@ -290,20 +291,20 @@ class XMLStream(object):
if self.state['processing']:
#raise CloseStream
pass
-
+
def reconnect(self):
self.state.set('tls',False)
self.state.set('ssl',False)
time.sleep(1)
self.connect()
-
+
def incoming_filter(self, xmlobj):
return xmlobj
-
+
def __spawnEvent(self, xmlobj):
"watching xmlOut and processes handlers"
#convert XML into Stanza
- logging.debug("RECV: %s" % cElementTree.tostring(xmlobj))
+ logging.debug("RECV: %s" % tostring(xmlobj))
xmlobj = self.incoming_filter(xmlobj)
stanza_type = StanzaBase
for stanza_class in self.__root_stanza:
@@ -323,7 +324,7 @@ class XMLStream(object):
stanza.unhandled()
#loop through handlers and test match
#spawn threads as necessary, call handlers, sending Stanza
-
+
def _eventRunner(self):
logging.debug("Loading event runner")
while self.run:
@@ -354,11 +355,11 @@ class XMLStream(object):
elif etype == 'quit':
logging.debug("Quitting eventRunner thread")
return False
-
+
def registerHandler(self, handler, before=None, after=None):
"Add handler with matcher class and parameters."
self.__handlers.append(handler)
-
+
def removeHandler(self, name):
"Removes the handler."
idx = 0
@@ -367,81 +368,27 @@ class XMLStream(object):
self.__handlers.pop(idx)
return
idx += 1
-
+
def registerStanza(self, stanza_class):
"Adds stanza. If root stanzas build stanzas sent in events while non-root stanzas build substanza objects."
self.__root_stanza.append(stanza_class)
-
+
def registerStanzaExtension(self, stanza_class, stanza_extension):
if stanza_class not in stanza_extensions:
stanza_extensions[stanza_class] = [stanza_extension]
else:
stanza_extensions[stanza_class].append(stanza_extension)
-
+
def removeStanza(self, stanza_class, root=False):
"Removes the stanza's registration."
if root:
del self.__root_stanza[stanza_class]
else:
del self.__stanza[stanza_class]
-
+
def removeStanzaExtension(self, stanza_class, stanza_extension):
stanza_extension[stanza_class].pop(stanza_extension)
- def tostring(self, xml, xmlns='', stringbuffer=''):
- 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
- 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.tostring(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)
-
def start_stream_handler(self, xml):
"""Meant to be overridden"""
pass