summaryrefslogtreecommitdiff
path: root/sleekxmpp/xmlstream/xmlstream.py
diff options
context:
space:
mode:
Diffstat (limited to 'sleekxmpp/xmlstream/xmlstream.py')
-rw-r--r--sleekxmpp/xmlstream/xmlstream.py131
1 files changed, 39 insertions, 92 deletions
diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py
index 6b92abca..bf39bb33 100644
--- a/sleekxmpp/xmlstream/xmlstream.py
+++ b/sleekxmpp/xmlstream/xmlstream.py
@@ -3,7 +3,7 @@
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
- See the file license.txt for copying permission.
+ See the file LICENSE for copying permission.
"""
from __future__ import with_statement, unicode_literals
@@ -19,11 +19,13 @@ import logging
import socket
import threading
import time
-import traceback
import types
+import copy
import xml.sax.saxutils
from . import scheduler
+from sleekxmpp.xmlstream.tostring import tostring
+RESPONSE_TIMEOUT = 10
HANDLER_THREADS = 1
ssl_support = True
@@ -36,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
@@ -71,6 +73,7 @@ class XMLStream(object):
self.use_ssl = False
self.use_tls = False
+ self.default_ns = ''
self.stream_header = "<stream>"
self.stream_footer = "</stream>"
@@ -81,7 +84,7 @@ class XMLStream(object):
self.namespace_map = {}
self.run = True
-
+
def setSocket(self, socket):
"Set the socket"
self.socket = socket
@@ -89,10 +92,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)
@@ -124,7 +127,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"
@@ -145,7 +148,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):
@@ -159,10 +162,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
@@ -194,14 +197,14 @@ class XMLStream(object):
return
else:
self.state.set('processing', False)
- traceback.print_exc()
+ logging.exception('Socket Error')
self.disconnect(reconnect=True)
except:
if not self.state.reconnect:
return
else:
self.state.set('processing', False)
- traceback.print_exc()
+ logging.exception('Connection error. Reconnecting.')
self.disconnect(reconnect=True)
if self.state['reconnect']:
self.reconnect()
@@ -211,7 +214,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
@@ -244,7 +247,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)
@@ -257,14 +260,13 @@ class XMLStream(object):
logging.warning("Failed to send %s" % data)
self.state.set('connected', False)
if self.state.reconnect:
- logging.error("Disconnected. Socket Error.")
- traceback.print_exc()
+ 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,41 +292,40 @@ 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, xmlns=self.default_ns, stream=self))
xmlobj = self.incoming_filter(xmlobj)
- stanza = None
+ stanza_type = StanzaBase
for stanza_class in self.__root_stanza:
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_type = stanza_class
break
- if stanza is None:
- stanza = StanzaBase(self, xmlobj)
unhandled = True
+ stanza = stanza_type(self, xmlobj)
for handler in self.__handlers:
if handler.match(stanza):
- handler.prerun(stanza)
- self.eventqueue.put(('stanza', handler, stanza))
+ stanza_copy = stanza_type(self, copy.deepcopy(xmlobj))
+ handler.prerun(stanza_copy)
+ self.eventqueue.put(('stanza', handler, stanza_copy))
if handler.checkDelete(): self.__handlers.pop(self.__handlers.index(handler))
unhandled = False
if unhandled:
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:
@@ -344,22 +345,22 @@ class XMLStream(object):
try:
handler.run(args[0])
except Exception as e:
- traceback.print_exc()
+ logging.exception('Error processing event handler: %s' % handler.name)
args[0].exception(e)
elif etype == 'schedule':
try:
logging.debug(args)
handler(*args[0])
except:
- logging.error(traceback.format_exc())
+ logging.exception('Error processing scheduled task')
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
@@ -368,81 +369,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