Replaced the ToString class with a tostring function.

The sleekxmpp.xmlstream.tostring and sleekxmpp.xmlstream.tostring26 packages
have been merged to sleekxmpp.xmlstream.tostring. The __init__.py file will
import the appropriate tostring function depending on the Python version.

The setup.py file has been updated with the package changes.

ElementBase is now a direct descendent of object and does not subclass ToString.

Stanza objects now return their XML contents for __repr__.
This commit is contained in:
Lance Stout 2010-08-05 20:26:41 -04:00
parent 58f77d898f
commit e077204a16
8 changed files with 298 additions and 343 deletions

View file

@ -16,13 +16,13 @@ import sys
# min_version = '0.6c6' # min_version = '0.6c6'
# else: # else:
# min_version = '0.6a9' # min_version = '0.6a9'
# #
# try: # try:
# use_setuptools(min_version=min_version) # use_setuptools(min_version=min_version)
# except TypeError: # except TypeError:
# # locally installed ez_setup won't have min_version # # locally installed ez_setup won't have min_version
# use_setuptools() # use_setuptools()
# #
# from setuptools import setup, find_packages, Extension, Feature # from setuptools import setup, find_packages, Extension, Feature
VERSION = '0.2.3.1' VERSION = '0.2.3.1'
@ -37,17 +37,13 @@ CLASSIFIERS = [ 'Intended Audience :: Developers',
'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Libraries :: Python Modules',
] ]
packages = [ 'sleekxmpp', packages = [ 'sleekxmpp',
'sleekxmpp/plugins', 'sleekxmpp/plugins',
'sleekxmpp/stanza', 'sleekxmpp/stanza',
'sleekxmpp/xmlstream', 'sleekxmpp/xmlstream',
'sleekxmpp/xmlstream/matcher', 'sleekxmpp/xmlstream/matcher',
'sleekxmpp/xmlstream/handler' ] 'sleekxmpp/xmlstream/handler',
'sleekxmpp/xmlstream/tostring']
if sys.version_info < (3, 0):
packages.append('sleekxmpp/xmlstream/tostring26')
else:
packages.append('sleekxmpp/xmlstream/tostring')
setup( setup(
name = "sleekxmpp", name = "sleekxmpp",

View file

@ -25,6 +25,7 @@ from . stanza.roster import Roster
from . stanza.nick import Nick from . stanza.nick import Nick
from . stanza.htmlim import HTMLIM from . stanza.htmlim import HTMLIM
from . stanza.error import Error from . stanza.error import Error
from sleekxmpp.xmlstream.tostring import tostring
import logging import logging
import threading import threading
@ -60,7 +61,7 @@ class basexmpp(object):
registerStanzaPlugin(Iq, Roster) registerStanzaPlugin(Iq, Roster)
registerStanzaPlugin(Message, Nick) registerStanzaPlugin(Message, Nick)
registerStanzaPlugin(Message, HTMLIM) registerStanzaPlugin(Message, HTMLIM)
def Message(self, *args, **kwargs): def Message(self, *args, **kwargs):
return Message(self, *args, **kwargs) return Message(self, *args, **kwargs)
@ -69,7 +70,7 @@ class basexmpp(object):
def Presence(self, *args, **kwargs): def Presence(self, *args, **kwargs):
return Presence(self, *args, **kwargs) return Presence(self, *args, **kwargs)
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."""
self.fulljid = jid self.fulljid = jid
@ -77,12 +78,12 @@ class basexmpp(object):
self.jid = self.getjidbare(jid) self.jid = self.getjidbare(jid)
self.username = jid.split('@', 1)[0] self.username = jid.split('@', 1)[0]
self.server = jid.split('@',1)[-1].split('/', 1)[0] self.server = jid.split('@',1)[-1].split('/', 1)[0]
def process(self, *args, **kwargs): def process(self, *args, **kwargs):
for idx in self.plugin: for idx in self.plugin:
if not self.plugin[idx].post_inited: self.plugin[idx].post_init() if not self.plugin[idx].post_inited: self.plugin[idx].post_init()
return super(basexmpp, self).process(*args, **kwargs) return super(basexmpp, self).process(*args, **kwargs)
def registerPlugin(self, plugin, pconfig = {}): def registerPlugin(self, plugin, pconfig = {}):
"""Register a plugin not in plugins.__init__.__all__ but in the plugins """Register a plugin not in plugins.__init__.__all__ but in the plugins
directory.""" directory."""
@ -97,7 +98,7 @@ class basexmpp(object):
if hasattr(self.plugin[plugin], 'xep'): if hasattr(self.plugin[plugin], 'xep'):
xep = "(XEP-%s) " % self.plugin[plugin].xep xep = "(XEP-%s) " % self.plugin[plugin].xep
logging.debug("Loaded Plugin %s%s" % (xep, self.plugin[plugin].description)) logging.debug("Loaded Plugin %s%s" % (xep, self.plugin[plugin].description))
def register_plugins(self): def register_plugins(self):
"""Initiates all plugins in the plugins/__init__.__all__""" """Initiates all plugins in the plugins/__init__.__all__"""
if self.plugin_whitelist: if self.plugin_whitelist:
@ -112,24 +113,24 @@ class basexmpp(object):
# run post_init() for cross-plugin interaction # run post_init() for cross-plugin interaction
for plugin in self.plugin: for plugin in self.plugin:
self.plugin[plugin].post_init() self.plugin[plugin].post_init()
def getNewId(self): def getNewId(self):
with self.id_lock: with self.id_lock:
self.id += 1 self.id += 1
return self.getId() return self.getId()
def add_handler(self, mask, pointer, name=None, disposable=False, threaded=False, filter=False, instream=False): 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 # threaded is no longer needed, but leaving it for backwards compatibility for now
if name is None: if name is None:
name = 'add_handler_%s' % self.getNewId() name = 'add_handler_%s' % self.getNewId()
self.registerHandler(XMLCallback(name, MatchXMLMask(mask), pointer, threaded, disposable, instream)) self.registerHandler(XMLCallback(name, MatchXMLMask(mask), pointer, threaded, disposable, instream))
def getId(self): def getId(self):
return "%x".upper() % self.id return "%x".upper() % self.id
def sendXML(self, data, mask=None, timeout=10): 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): def send(self, data, mask=None, timeout=10):
#logging.warning("Deprecated send used for \"%s\"" % (data,)) #logging.warning("Deprecated send used for \"%s\"" % (data,))
#if not type(data) == type(''): #if not type(data) == type(''):
@ -144,19 +145,19 @@ class basexmpp(object):
self.sendRaw(data) self.sendRaw(data)
if mask is not None: if mask is not None:
return waitfor.wait(timeout) return waitfor.wait(timeout)
def makeIq(self, id=0, ifrom=None): def makeIq(self, id=0, ifrom=None):
return self.Iq().setStanzaValues({'id': str(id), 'from': ifrom}) return self.Iq().setStanzaValues({'id': str(id), 'from': ifrom})
def makeIqGet(self, queryxmlns = None): def makeIqGet(self, queryxmlns = None):
iq = self.Iq().setStanzaValues({'type': 'get'}) iq = self.Iq().setStanzaValues({'type': 'get'})
if queryxmlns: if queryxmlns:
iq.append(ET.Element("{%s}query" % queryxmlns)) iq.append(ET.Element("{%s}query" % queryxmlns))
return iq return iq
def makeIqResult(self, id): def makeIqResult(self, id):
return self.Iq().setStanzaValues({'id': id, 'type': 'result'}) return self.Iq().setStanzaValues({'id': id, 'type': 'result'})
def makeIqSet(self, sub=None): def makeIqSet(self, sub=None):
iq = self.Iq().setStanzaValues({'type': 'set'}) iq = self.Iq().setStanzaValues({'type': 'set'})
if sub != None: if sub != None:
@ -172,13 +173,13 @@ class basexmpp(object):
query = ET.Element("{%s}query" % xmlns) query = ET.Element("{%s}query" % xmlns)
iq.append(query) iq.append(query)
return iq return iq
def makeQueryRoster(self, iq=None): def makeQueryRoster(self, iq=None):
query = ET.Element("{jabber:iq:roster}query") query = ET.Element("{jabber:iq:roster}query")
if iq: if iq:
iq.append(query) iq.append(query)
return query return query
def add_event_handler(self, name, pointer, threaded=False, disposable=False): def add_event_handler(self, name, pointer, threaded=False, disposable=False):
if not name in self.event_handlers: if not name in self.event_handlers:
self.event_handlers[name] = [] self.event_handlers[name] = []
@ -188,13 +189,13 @@ class basexmpp(object):
"""Remove a handler for an event.""" """Remove a handler for an event."""
if not name in self.event_handlers: if not name in self.event_handlers:
return return
# Need to keep handlers that do not use # Need to keep handlers that do not use
# the given function pointer # the given function pointer
def filter_pointers(handler): def filter_pointers(handler):
return handler[0] != pointer return handler[0] != pointer
self.event_handlers[name] = filter(filter_pointers, self.event_handlers[name] = filter(filter_pointers,
self.event_handlers[name]) self.event_handlers[name])
def event(self, name, eventdata = {}): # called on an event def event(self, name, eventdata = {}): # called on an event
@ -209,7 +210,7 @@ class basexmpp(object):
if handler[2]: #disposable if handler[2]: #disposable
with self.lock: with self.lock:
self.event_handlers[name].pop(self.event_handlers[name].index(handler)) 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): 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 = self.Message(sto=mto, stype=mtype, sfrom=mfrom)
message['body'] = mbody message['body'] = mbody
@ -217,7 +218,7 @@ class basexmpp(object):
if mnick is not None: message['nick'] = mnick if mnick is not None: message['nick'] = mnick
if mhtml is not None: message['html']['html'] = mhtml if mhtml is not None: message['html']['html'] = mhtml
return message return message
def makePresence(self, pshow=None, pstatus=None, ppriority=None, pto=None, ptype=None, pfrom=None): def makePresence(self, pshow=None, pstatus=None, ppriority=None, pto=None, ptype=None, pfrom=None):
presence = self.Presence(stype=ptype, sfrom=pfrom, sto=pto) presence = self.Presence(stype=ptype, sfrom=pfrom, sto=pto)
if pshow is not None: presence['type'] = pshow if pshow is not None: presence['type'] = pshow
@ -226,10 +227,10 @@ class basexmpp(object):
presence['priority'] = ppriority presence['priority'] = ppriority
presence['status'] = pstatus presence['status'] = pstatus
return presence return presence
def sendMessage(self, mto, mbody, msubject=None, mtype=None, mhtml=None, mfrom=None, mnick=None): 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)) 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): 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)) self.send(self.makePresence(pshow,pstatus,ppriority,pto, ptype=ptype, pfrom=pfrom))
if not self.sentpresence: if not self.sentpresence:
@ -243,19 +244,19 @@ class basexmpp(object):
nick.text = pnick nick.text = pnick
presence.append(nick) presence.append(nick)
self.send(presence) self.send(presence)
def getjidresource(self, fulljid): def getjidresource(self, fulljid):
if '/' in fulljid: if '/' in fulljid:
return fulljid.split('/', 1)[-1] return fulljid.split('/', 1)[-1]
else: else:
return '' return ''
def getjidbare(self, fulljid): def getjidbare(self, fulljid):
return fulljid.split('/', 1)[0] return fulljid.split('/', 1)[0]
def _handleMessage(self, msg): def _handleMessage(self, msg):
self.event('message', msg) self.event('message', msg)
def _handlePresence(self, presence): def _handlePresence(self, presence):
"""Update roster items based on presence""" """Update roster items based on presence"""
self.event("presence_%s" % presence['type'], presence) self.event("presence_%s" % presence['type'], presence)
@ -296,7 +297,7 @@ class basexmpp(object):
if name: if name:
name = "(%s) " % name name = "(%s) " % name
logging.debug("STATUS: %s%s/%s[%s]: %s" % (name, jid, resource, show,status)) logging.debug("STATUS: %s%s/%s[%s]: %s" % (name, jid, resource, show,status))
def _handlePresenceSubscribe(self, presence): def _handlePresenceSubscribe(self, presence):
"""Handling subscriptions automatically.""" """Handling subscriptions automatically."""
if self.auto_authorize == True: if self.auto_authorize == True:

View file

@ -12,10 +12,7 @@ import weakref
import copy import copy
from . jid import JID from . jid import JID
if sys.version_info < (3,0): from sleekxmpp.xmlstream.tostring import tostring
from . import tostring26 as tostring
else:
from . import tostring
xmltester = type(ET.Element('xml')) xmltester = type(ET.Element('xml'))
@ -29,7 +26,7 @@ def registerStanzaPlugin(stanza, plugin):
stanza.plugin_tag_map[tag] = plugin stanza.plugin_tag_map[tag] = plugin
class ElementBase(tostring.ToString): class ElementBase(object):
name = 'stanza' name = 'stanza'
plugin_attrib = 'plugin' plugin_attrib = 'plugin'
namespace = 'jabber:client' namespace = 'jabber:client'
@ -70,20 +67,20 @@ class ElementBase(tostring.ToString):
def __bool__(self): def __bool__(self):
return True return True
def __next__(self): def __next__(self):
self.idx += 1 self.idx += 1
if self.idx > len(self.iterables): if self.idx > len(self.iterables):
self.idx = 0 self.idx = 0
raise StopIteration raise StopIteration
return self.iterables[self.idx - 1] return self.iterables[self.idx - 1]
def next(self): def next(self):
return self.__next__() return self.__next__()
def __len__(self): def __len__(self):
return len(self.iterables) return len(self.iterables)
def append(self, item): def append(self, item):
if not isinstance(item, ElementBase): if not isinstance(item, ElementBase):
if type(item) == xmltester: if type(item) == xmltester:
@ -93,18 +90,18 @@ class ElementBase(tostring.ToString):
self.xml.append(item.xml) self.xml.append(item.xml)
self.iterables.append(item) self.iterables.append(item)
return self return self
def pop(self, idx=0): def pop(self, idx=0):
aff = self.iterables.pop(idx) aff = self.iterables.pop(idx)
self.xml.remove(aff.xml) self.xml.remove(aff.xml)
return aff return aff
def get(self, key, defaultvalue=None): def get(self, key, defaultvalue=None):
value = self[key] value = self[key]
if value is None or value == '': if value is None or value == '':
return defaultvalue return defaultvalue
return value return value
def keys(self): def keys(self):
out = [] out = []
out += [x for x in self.interfaces] out += [x for x in self.interfaces]
@ -112,7 +109,7 @@ class ElementBase(tostring.ToString):
if self.iterables: if self.iterables:
out.append('substanzas') out.append('substanzas')
return tuple(out) return tuple(out)
def match(self, matchstring): def match(self, matchstring):
if isinstance(matchstring, str): if isinstance(matchstring, str):
nodes = matchstring.split('/') nodes = matchstring.split('/')
@ -136,13 +133,13 @@ class ElementBase(tostring.ToString):
else: else:
return False return False
return True return True
def find(self, xpath): # for backwards compatiblity, expose elementtree interface def find(self, xpath): # for backwards compatiblity, expose elementtree interface
return self.xml.find(xpath) return self.xml.find(xpath)
def findall(self, xpath): def findall(self, xpath):
return self.xml.findall(xpath) return self.xml.findall(xpath)
def setup(self, xml=None): def setup(self, xml=None):
if self.xml is None: if self.xml is None:
self.xml = xml self.xml = xml
@ -162,11 +159,11 @@ class ElementBase(tostring.ToString):
def enable(self, attrib): def enable(self, attrib):
self.initPlugin(attrib) self.initPlugin(attrib)
return self return self
def initPlugin(self, attrib): def initPlugin(self, attrib):
if attrib not in self.plugins: if attrib not in self.plugins:
self.plugins[attrib] = self.plugin_attrib_map[attrib](parent=self) self.plugins[attrib] = self.plugin_attrib_map[attrib](parent=self)
def __getitem__(self, attrib): def __getitem__(self, attrib):
if attrib == 'substanzas': if attrib == 'substanzas':
return self.iterables return self.iterables
@ -183,7 +180,7 @@ class ElementBase(tostring.ToString):
return self.plugins[attrib] return self.plugins[attrib]
else: else:
return '' return ''
def __setitem__(self, attrib, value): def __setitem__(self, attrib, value):
if attrib in self.interfaces: if attrib in self.interfaces:
if value is not None: if value is not None:
@ -201,7 +198,7 @@ class ElementBase(tostring.ToString):
self.initPlugin(attrib) self.initPlugin(attrib)
self.plugins[attrib][attrib] = value self.plugins[attrib][attrib] = value
return self return self
def __delitem__(self, attrib): def __delitem__(self, attrib):
if attrib.lower() in self.interfaces: if attrib.lower() in self.interfaces:
if hasattr(self, "del%s" % attrib.title()): if hasattr(self, "del%s" % attrib.title()):
@ -215,7 +212,7 @@ class ElementBase(tostring.ToString):
if attrib in self.plugins: if attrib in self.plugins:
del self.plugins[attrib] del self.plugins[attrib]
return self return self
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, ElementBase): if not isinstance(other, ElementBase):
return False return False
@ -224,20 +221,20 @@ class ElementBase(tostring.ToString):
if key not in values or values[key] != other[key]: if key not in values or values[key] != other[key]:
return False return False
return True return True
def _setAttr(self, name, value): def _setAttr(self, name, value):
if value is None or value == '': if value is None or value == '':
self.__delitem__(name) self.__delitem__(name)
else: else:
self.xml.attrib[name] = value self.xml.attrib[name] = value
def _delAttr(self, name): def _delAttr(self, name):
if name in self.xml.attrib: if name in self.xml.attrib:
del self.xml.attrib[name] del self.xml.attrib[name]
def _getAttr(self, name, default=''): def _getAttr(self, name, default=''):
return self.xml.attrib.get(name, default) return self.xml.attrib.get(name, default)
def _getSubText(self, name): def _getSubText(self, name):
if '}' not in name: if '}' not in name:
name = "{%s}%s" % (self.namespace, name) name = "{%s}%s" % (self.namespace, name)
@ -246,7 +243,7 @@ class ElementBase(tostring.ToString):
return '' return ''
else: else:
return stanza.text return stanza.text
def _setSubText(self, name, attrib={}, text=None): def _setSubText(self, name, attrib={}, text=None):
if '}' not in name: if '}' not in name:
name = "{%s}%s" % (self.namespace, name) name = "{%s}%s" % (self.namespace, name)
@ -258,14 +255,14 @@ class ElementBase(tostring.ToString):
self.xml.append(stanza) self.xml.append(stanza)
stanza.text = text stanza.text = text
return stanza return stanza
def _delSub(self, name): def _delSub(self, name):
if '}' not in name: if '}' not in name:
name = "{%s}%s" % (self.namespace, name) name = "{%s}%s" % (self.namespace, name)
for child in self.xml.getchildren(): for child in self.xml.getchildren():
if child.tag == name: if child.tag == name:
self.xml.remove(child) self.xml.remove(child)
def getStanzaValues(self): def getStanzaValues(self):
out = {} out = {}
for interface in self.interfaces: for interface in self.interfaces:
@ -279,7 +276,7 @@ class ElementBase(tostring.ToString):
iterables[-1].update({'__childtag__': "{%s}%s" % (stanza.namespace, stanza.name)}) iterables[-1].update({'__childtag__': "{%s}%s" % (stanza.namespace, stanza.name)})
out['substanzas'] = iterables out['substanzas'] = iterables
return out return out
def setStanzaValues(self, attrib): def setStanzaValues(self, attrib):
for interface in attrib: for interface in attrib:
if interface == 'substanzas': if interface == 'substanzas':
@ -298,14 +295,20 @@ class ElementBase(tostring.ToString):
if interface in self.plugins: if interface in self.plugins:
self.plugins[interface].setStanzaValues(attrib[interface]) self.plugins[interface].setStanzaValues(attrib[interface])
return self return self
def appendxml(self, xml): def appendxml(self, xml):
self.xml.append(xml) self.xml.append(xml)
return self return self
def __copy__(self): def __copy__(self):
return self.__class__(xml=copy.deepcopy(self.xml), parent=self.parent) 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 #def __del__(self): #prevents garbage collection of reference cycle
# if self.parent is not None: # if self.parent is not None:
# self.parent.xml.remove(self.xml) # self.parent.xml.remove(self.xml)
@ -329,7 +332,7 @@ class StanzaBase(ElementBase):
if sfrom is not None: if sfrom is not None:
self['from'] = sfrom self['from'] = sfrom
self.tag = "{%s}%s" % (self.namespace, self.name) self.tag = "{%s}%s" % (self.namespace, self.name)
def setType(self, value): def setType(self, value):
if value in self.types: if value in self.types:
self.xml.attrib['type'] = value self.xml.attrib['type'] = value
@ -337,22 +340,22 @@ class StanzaBase(ElementBase):
def getPayload(self): def getPayload(self):
return self.xml.getchildren() return self.xml.getchildren()
def setPayload(self, value): def setPayload(self, value):
self.xml.append(value) self.xml.append(value)
return self return self
def delPayload(self): def delPayload(self):
self.clear() self.clear()
return self return self
def clear(self): def clear(self):
for child in self.xml.getchildren(): for child in self.xml.getchildren():
self.xml.remove(child) self.xml.remove(child)
for plugin in list(self.plugins.keys()): for plugin in list(self.plugins.keys()):
del self.plugins[plugin] del self.plugins[plugin]
return self return self
def reply(self): def reply(self):
# if it's a component, use from # if it's a component, use from
if self.stream and hasattr(self.stream, "is_component") and self.stream.is_component: if self.stream and hasattr(self.stream, "is_component") and self.stream.is_component:
@ -362,32 +365,34 @@ class StanzaBase(ElementBase):
del self['from'] del self['from']
self.clear() self.clear()
return self return self
def error(self): def error(self):
self['type'] = 'error' self['type'] = 'error'
return self return self
def getTo(self): def getTo(self):
return JID(self._getAttr('to')) return JID(self._getAttr('to'))
def setTo(self, value): def setTo(self, value):
return self._setAttr('to', str(value)) return self._setAttr('to', str(value))
def getFrom(self): def getFrom(self):
return JID(self._getAttr('from')) return JID(self._getAttr('from'))
def setFrom(self, value): def setFrom(self, value):
return self._setAttr('from', str(value)) return self._setAttr('from', str(value))
def unhandled(self): def unhandled(self):
pass pass
def exception(self, e): def exception(self, e):
logging.exception('Error handling {%s}%s stanza' % (self.namespace, self.name)) logging.exception('Error handling {%s}%s stanza' % (self.namespace, self.name))
def send(self): def send(self):
self.stream.sendRaw(self.__str__()) self.stream.sendRaw(self.__str__())
def __copy__(self): def __copy__(self):
return self.__class__(xml=copy.deepcopy(self.xml), stream=self.stream) 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)

View file

@ -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 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): if sys.version_info < (3, 0):
from sleekxmpp.xmlstream.tostring.tostring26 import ToString from sleekxmpp.xmlstream.tostring.tostring26 import tostring, xml_escape
else: else:
from sleekxmpp.xmlstream.tostring.tostring import ToString from sleekxmpp.xmlstream.tostring.tostring import tostring, xml_escape
__all__ = ['ToString'] __all__ = ['tostring', 'xml_escape']

View file

@ -1,60 +1,91 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
class ToString(object): See the file LICENSE for copying permission.
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) def tostring(xml=None, xmlns='', stanza_ns='', stream=None, outbuffer=''):
cc = 0 """
matches = ('&', '<', '"', '>', "'") Serialize an XML object to a Unicode string.
for c in text:
if c in matches: Arguments:
if c == '&': xml -- The XML object to serialize. If the value is None,
text[cc] = '&amp;' then the XML object contained in this stanza
elif c == '<': object will be used.
text[cc] = '&lt;' xmlns -- Optional namespace of an element wrapping the XML
elif c == '>': object.
text[cc] = '&gt;' stanza_ns -- The namespace of the stanza object that contains
elif c == "'": the XML object.
text[cc] = '&apos;' stream -- The XML stream that generated the XML object.
else: outbuffer -- Optional buffer for storing serializations during
text[cc] = '&quot;' recursive calls.
cc += 1 """
return ''.join(text) # 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)

View file

@ -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 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): def tostring(xml=None, xmlns='', stanza_ns='', stream=None, outbuffer=''):
if type(text) != types.UnicodeType: """
text = list(unicode(text, 'utf-8', 'ignore')) Serialize an XML object to a Unicode string.
else:
text = list(text)
cc = 0 Arguments:
matches = (u'&', u'<', u'"', u'>', u"'") xml -- The XML object to serialize. If the value is None,
for c in text: then the XML object contained in this stanza
if c in matches: object will be used.
if c == u'&': xmlns -- Optional namespace of an element wrapping the XML
text[cc] = u'&amp;' object.
elif c == u'<': stanza_ns -- The namespace of the stanza object that contains
text[cc] = u'&lt;' the XML object.
elif c == u'>': stream -- The XML stream that generated the XML object.
text[cc] = u'&gt;' outbuffer -- Optional buffer for storing serializations during
elif c == u"'": recursive calls.
text[cc] = u'&apos;' """
else: # Add previous results to the start of the output.
text[cc] = u'&quot;' output = [outbuffer]
cc += 1
return ''.join(text) # 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.
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)

View file

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

View file

@ -23,6 +23,7 @@ import types
import copy import copy
import xml.sax.saxutils import xml.sax.saxutils
from . import scheduler from . import scheduler
from sleekxmpp.xmlstream.tostring import tostring
RESPONSE_TIMEOUT = 10 RESPONSE_TIMEOUT = 10
HANDLER_THREADS = 1 HANDLER_THREADS = 1
@ -37,7 +38,7 @@ if sys.version_info < (3, 0):
#monkey patch broken filesocket object #monkey patch broken filesocket object
from . import filesocket from . import filesocket
#socket._fileobject = filesocket.filesocket #socket._fileobject = filesocket.filesocket
class RestartStream(Exception): class RestartStream(Exception):
pass pass
@ -82,7 +83,7 @@ class XMLStream(object):
self.namespace_map = {} self.namespace_map = {}
self.run = True self.run = True
def setSocket(self, socket): def setSocket(self, socket):
"Set the socket" "Set the socket"
self.socket = 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.filesocket = socket.makefile('rb', 0) # ElementTree.iterparse requires a file. 0 buffer files have to be binary
self.state.set('connected', True) self.state.set('connected', True)
def setFileSocket(self, filesocket): def setFileSocket(self, filesocket):
self.filesocket = filesocket self.filesocket = filesocket
def connect(self, host='', port=0, use_ssl=False, use_tls=True): def connect(self, host='', port=0, use_ssl=False, use_tls=True):
"Link to connectTCP" "Link to connectTCP"
return self.connectTCP(host, port, use_ssl, use_tls) return self.connectTCP(host, port, use_ssl, use_tls)
@ -125,7 +126,7 @@ class XMLStream(object):
except socket.error as serr: except socket.error as serr:
logging.error("Could not connect. Socket Error #%s: %s" % (serr.errno, serr.strerror)) logging.error("Could not connect. Socket Error #%s: %s" % (serr.errno, serr.strerror))
time.sleep(1) time.sleep(1)
def connectUnix(self, filepath): def connectUnix(self, filepath):
"Connect to Unix file and create socket" "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.") logging.warning("Tried to enable TLS, but ssl module not found.")
return False return False
raise RestartStream() raise RestartStream()
def process(self, threaded=True): def process(self, threaded=True):
self.scheduler.process(threaded=True) self.scheduler.process(threaded=True)
for t in range(0, HANDLER_THREADS): for t in range(0, HANDLER_THREADS):
@ -160,10 +161,10 @@ class XMLStream(object):
self.__thread['process'].start() self.__thread['process'].start()
else: else:
self._process() self._process()
def schedule(self, name, seconds, callback, args=None, kwargs=None, repeat=False): def schedule(self, name, seconds, callback, args=None, kwargs=None, repeat=False):
self.scheduler.add(name, seconds, callback, args, kwargs, repeat, qpointer=self.eventqueue) self.scheduler.add(name, seconds, callback, args, kwargs, repeat, qpointer=self.eventqueue)
def _process(self): def _process(self):
"Start processing the socket." "Start processing the socket."
firstrun = True firstrun = True
@ -212,7 +213,7 @@ class XMLStream(object):
#self.__thread['readXML'].start() #self.__thread['readXML'].start()
#self.__thread['spawnEvents'] = threading.Thread(name='spawnEvents', target=self.__spawnEvents) #self.__thread['spawnEvents'] = threading.Thread(name='spawnEvents', target=self.__spawnEvents)
#self.__thread['spawnEvents'].start() #self.__thread['spawnEvents'].start()
def __readXML(self): def __readXML(self):
"Parses the incoming stream, adding to xmlin queue as it goes" "Parses the incoming stream, adding to xmlin queue as it goes"
#build cElementTree object from expat was we go #build cElementTree object from expat was we go
@ -245,7 +246,7 @@ class XMLStream(object):
if event == b'start': if event == b'start':
edepth += 1 edepth += 1
logging.debug("Ending readXML loop") logging.debug("Ending readXML loop")
def _sendThread(self): def _sendThread(self):
while self.run: while self.run:
data = self.sendqueue.get(True) data = self.sendqueue.get(True)
@ -260,11 +261,11 @@ class XMLStream(object):
if self.state.reconnect: if self.state.reconnect:
logging.exception("Disconnected. Socket Error.") logging.exception("Disconnected. Socket Error.")
self.disconnect(reconnect=True) self.disconnect(reconnect=True)
def sendRaw(self, data): def sendRaw(self, data):
self.sendqueue.put(data) self.sendqueue.put(data)
return True return True
def disconnect(self, reconnect=False): def disconnect(self, reconnect=False):
self.state.set('reconnect', reconnect) self.state.set('reconnect', reconnect)
if self.state['disconnecting']: if self.state['disconnecting']:
@ -290,20 +291,20 @@ class XMLStream(object):
if self.state['processing']: if self.state['processing']:
#raise CloseStream #raise CloseStream
pass pass
def reconnect(self): def reconnect(self):
self.state.set('tls',False) self.state.set('tls',False)
self.state.set('ssl',False) self.state.set('ssl',False)
time.sleep(1) time.sleep(1)
self.connect() self.connect()
def incoming_filter(self, xmlobj): def incoming_filter(self, xmlobj):
return xmlobj return xmlobj
def __spawnEvent(self, xmlobj): def __spawnEvent(self, xmlobj):
"watching xmlOut and processes handlers" "watching xmlOut and processes handlers"
#convert XML into Stanza #convert XML into Stanza
logging.debug("RECV: %s" % cElementTree.tostring(xmlobj)) logging.debug("RECV: %s" % tostring(xmlobj))
xmlobj = self.incoming_filter(xmlobj) xmlobj = self.incoming_filter(xmlobj)
stanza_type = StanzaBase stanza_type = StanzaBase
for stanza_class in self.__root_stanza: for stanza_class in self.__root_stanza:
@ -323,7 +324,7 @@ class XMLStream(object):
stanza.unhandled() stanza.unhandled()
#loop through handlers and test match #loop through handlers and test match
#spawn threads as necessary, call handlers, sending Stanza #spawn threads as necessary, call handlers, sending Stanza
def _eventRunner(self): def _eventRunner(self):
logging.debug("Loading event runner") logging.debug("Loading event runner")
while self.run: while self.run:
@ -354,11 +355,11 @@ class XMLStream(object):
elif etype == 'quit': elif etype == 'quit':
logging.debug("Quitting eventRunner thread") logging.debug("Quitting eventRunner thread")
return False return False
def registerHandler(self, handler, before=None, after=None): def registerHandler(self, handler, before=None, after=None):
"Add handler with matcher class and parameters." "Add handler with matcher class and parameters."
self.__handlers.append(handler) self.__handlers.append(handler)
def removeHandler(self, name): def removeHandler(self, name):
"Removes the handler." "Removes the handler."
idx = 0 idx = 0
@ -367,81 +368,27 @@ class XMLStream(object):
self.__handlers.pop(idx) self.__handlers.pop(idx)
return return
idx += 1 idx += 1
def registerStanza(self, stanza_class): 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."
self.__root_stanza.append(stanza_class) self.__root_stanza.append(stanza_class)
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:
stanza_extensions[stanza_class] = [stanza_extension] stanza_extensions[stanza_class] = [stanza_extension]
else: else:
stanza_extensions[stanza_class].append(stanza_extension) stanza_extensions[stanza_class].append(stanza_extension)
def removeStanza(self, stanza_class, root=False): def removeStanza(self, stanza_class, root=False):
"Removes the stanza's registration." "Removes the stanza's registration."
if root: if root:
del self.__root_stanza[stanza_class] del self.__root_stanza[stanza_class]
else: else:
del self.__stanza[stanza_class] del self.__stanza[stanza_class]
def removeStanzaExtension(self, stanza_class, stanza_extension): def removeStanzaExtension(self, stanza_class, stanza_extension):
stanza_extension[stanza_class].pop(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): def start_stream_handler(self, xml):
"""Meant to be overridden""" """Meant to be overridden"""
pass pass