class XMPPDispatcher(PlugIn):
    """
    Handles XMPP stream and is the first who takes control over a fresh stanza

    Is plugged into NonBlockingClient but can be replugged to restart handled
    stream headers (used by SASL f.e.).
    """

    def __init__(self):
        PlugIn.__init__(self)
        self.handlers = {}
        self._expected = {}
        self._defaultHandler = None
        self._pendingExceptions = []
        self._eventHandler = None
        self._cycleHandlers = []
        self._exported_methods=[self.RegisterHandler, self.RegisterDefaultHandler,
                self.RegisterEventHandler, self.UnregisterCycleHandler,
                self.RegisterCycleHandler, self.RegisterHandlerOnce,
                self.UnregisterHandler, self.RegisterProtocol,
                self.SendAndWaitForResponse, self.SendAndCallForResponse,
                self.getAnID, self.Event, self.send]

        # Let the dispatcher know if there is support for stream management
        self.sm = None

        # \ufddo -> \ufdef range
        c = u'\ufdd0'
        r = c.encode('utf8')
        while (c < u'\ufdef'):
            c = unichr(ord(c) + 1)
            r += '|' + c.encode('utf8')

        # \ufffe-\uffff, \u1fffe-\u1ffff, ..., \u10fffe-\u10ffff
        c = u'\ufffe'
        r += '|' + c.encode('utf8')
        r += '|' + unichr(ord(c) + 1).encode('utf8')
        while (c < u'\U0010fffe'):
            c = unichr(ord(c) + 0x10000)
            r += '|' + c.encode('utf8')
            r += '|' + unichr(ord(c) + 1).encode('utf8')

        self.invalid_chars_re = re.compile(r)

    def getAnID(self):
        global outgoingID
        outgoingID += 1
        return repr(outgoingID)

    def dumpHandlers(self):
        """
        Return set of user-registered callbacks in it's internal format. Used
        within the library to carry user handlers set over Dispatcher replugins
        """
        return self.handlers

    def restoreHandlers(self, handlers):
        """
        Restore user-registered callbacks structure from dump previously obtained
        via dumpHandlers. Used within the library to carry user handlers set over
        Dispatcher replugins.
        """
        self.handlers = handlers

    def _init(self):
        """
        Register default namespaces/protocols/handlers. Used internally
        """
        # FIXME: inject dependencies, do not rely that they are defined by our
        # owner
        self.RegisterNamespace('unknown')
        self.RegisterNamespace(NS_STREAMS)
        self.RegisterNamespace(self._owner.defaultNamespace)
        self.RegisterProtocol('iq', Iq)
        self.RegisterProtocol('presence', Presence)
        self.RegisterProtocol('message', Message)
        self.RegisterDefaultHandler(self.returnStanzaHandler)
        self.RegisterEventHandler(self._owner._caller._event_dispatcher)
        self.on_responses = {}

    def plugin(self, owner):
        """
        Plug the Dispatcher instance into Client class instance and send initial
        stream header. Used internally
        """
        self._init()
        self._owner.lastErrNode = None
        self._owner.lastErr = None
        self._owner.lastErrCode = None
        if hasattr(self._owner, 'StreamInit'):
            self._owner.StreamInit()
        else:
            self.StreamInit()

    def plugout(self):
        """
        Prepare instance to be destructed
        """
        self.Stream.dispatch = None
        self.Stream.features = None
        self.Stream.destroy()
        self._owner = None
        self.Stream = None

    def StreamInit(self):
        """
        Send an initial stream header
        """
        self._owner.Connection.sendqueue = []
        self.Stream = simplexml.NodeBuilder()
        self.Stream.dispatch = self.dispatch
        self.Stream._dispatch_depth = 2
        self.Stream.stream_header_received = self._check_stream_start
        self.Stream.features = None
        self._metastream = Node('stream:stream')
        self._metastream.setNamespace(self._owner.Namespace)
        self._metastream.setAttr('version', '1.0')
        self._metastream.setAttr('xmlns:stream', NS_STREAMS)
        self._metastream.setAttr('to', self._owner.Server)
        if locale.getdefaultlocale()[0]:
            self._metastream.setAttr('xml:lang',
                    locale.getdefaultlocale()[0].split('_')[0])
        self._owner.send("%s%s>" % (XML_DECLARATION, str(self._metastream)[:-2]))

    def _check_stream_start(self, ns, tag, attrs):
        if ns != NS_STREAMS or tag!='stream':
            raise ValueError('Incorrect stream start: (%s,%s). Terminating.'
                    % (tag, ns))

    def replace_non_character(self, data):
        return re.sub(self.invalid_chars_re, u'\ufffd'.encode('utf-8'), data)

    def ProcessNonBlocking(self, data):
        """
        Check incoming stream for data waiting

        :param data: data received from transports/IO sockets
        :return:
                1) length of processed data if some data were processed;
                2) '0' string if no data were processed but link is alive;
                3) 0 (zero) if underlying connection is closed.
        """
        # FIXME:
        # When an error occurs we disconnect the transport directly. Client's
        # disconnect method will never be called.
        # Is this intended?
        # also look at transports start_disconnect()
        data = self.replace_non_character(data)
        for handler in self._cycleHandlers:
            handler(self)
        if len(self._pendingExceptions) > 0:
            _pendingException = self._pendingExceptions.pop()
            raise _pendingException[0], _pendingException[1], _pendingException[2]
        try:
            self.Stream.Parse(data)
            # end stream:stream tag received
            if self.Stream and self.Stream.has_received_endtag():
                self._owner.disconnect(self.Stream.streamError)
                return 0
        except ExpatError:
            log.error('Invalid XML received from server. Forcing disconnect.')
            self._owner.Connection.disconnect()
            return 0
        except ValueError, e:
            log.debug('ValueError: %s' % str(e))
            self._owner.Connection.pollend()
            return 0
        if len(self._pendingExceptions) > 0:
            _pendingException = self._pendingExceptions.pop()
            raise _pendingException[0], _pendingException[1], _pendingException[2]
        if len(data) == 0:
            return '0'
        return len(data)
class BOSHDispatcher(XMPPDispatcher):

    def PlugIn(self, owner, after_SASL=False, old_features=None):
        self.old_features = old_features
        self.after_SASL = after_SASL
        XMPPDispatcher.PlugIn(self, owner)

    def StreamInit(self):
        """
        Send an initial stream header
        """
        self.Stream = simplexml.NodeBuilder()
        self.Stream.dispatch = self.dispatch
        self.Stream._dispatch_depth = 2
        self.Stream.stream_header_received = self._check_stream_start
        self.Stream.features = self.old_features

        self._metastream = Node('stream:stream')
        self._metastream.setNamespace(self._owner.Namespace)
        self._metastream.setAttr('version', '1.0')
        self._metastream.setAttr('xmlns:stream', NS_STREAMS)
        self._metastream.setAttr('to', self._owner.Server)
        if locale.getdefaultlocale()[0]:
            self._metastream.setAttr('xml:lang',
                    locale.getdefaultlocale()[0].split('_')[0])

        self.restart = True
        self._owner.Connection.send_init(after_SASL=self.after_SASL)

    def StreamTerminate(self):
        """
        Send a stream terminator
        """
        self._owner.Connection.send_terminator()

    def ProcessNonBlocking(self, data=None):
        if self.restart:
            fromstream = self._metastream
            fromstream.setAttr('from', fromstream.getAttr('to'))
            fromstream.delAttr('to')
            data = '%s%s>%s' % (XML_DECLARATION, str(fromstream)[:-2], data)
            self.restart = False
        return XMPPDispatcher.ProcessNonBlocking(self, data)

    def dispatch(self, stanza, session=None, direct=0):
        if stanza.getName() == 'body' and stanza.getNamespace() == NS_HTTP_BIND:

            stanza_attrs = stanza.getAttrs()
            if 'authid' in stanza_attrs:
                # should be only in init response
                # auth module expects id of stream in document attributes
                self.Stream._document_attrs['id'] = stanza_attrs['authid']
            self._owner.Connection.handle_body_attrs(stanza_attrs)

            children = stanza.getChildren()
            if children:
                for child in children:
                    # if child doesn't have any ns specified, simplexml (or expat)
                    # thinks it's of parent's (BOSH body) namespace, so we have to
                    # rewrite it to jabber:client
                    if child.getNamespace() == NS_HTTP_BIND:
                        child.setNamespace(self._owner.defaultNamespace)
                    XMPPDispatcher.dispatch(self, child, session, direct)
        else:
            XMPPDispatcher.dispatch(self, stanza, session, direct)