class ElementClass:
    def __subclasshook__(cls, other):
def element_to_unicode(element): class Iq(Stanza):
    """<message /> stanza class."""
    # pylint: disable-msg=R0902
    element_name = "message"
    def __init__(self, element = None, from_jid = None, to_jid = None,
                            stanza_type = None, stanza_id = None,
                            error = None, error_cond=None, return_path = None,
                            language = None):
        """Initialize an `Iq` object.

        :Parameters:
            - `element`: XML element of this stanza.
            - `from_jid`: sender JID.
            - `to_jid`: recipient JID.
            - `stanza_type`: staza type: one of: "get", "set", "response"
              or "error".
            - `stanza_id`: stanza id -- value of stanza's "id" attribute. If
              not given, then unique for the session value is generated.
            - `error_cond`: error condition name. Ignored if `stanza_type`
              is not "error".
            - `language`: default language for the stanza content
        :Types:
            - `element`: :etree:`ElementTree.Element`
            - `from_jid`: `JID`
            - `to_jid`: `JID`
            - `stanza_type`: `unicode`
            - `stanza_id`: `unicode`
            - `error`: `pyxmpp.error.StanzaErrorElement`
            - `error_cond`: `unicode`
            - `language`: `unicode`
        """
        # pylint: disable-msg=R0913

        if stanza_type is None:
            if element is None:
                raise ValueError("Missing iq type")
        elif stanza_type not in IQ_TYPES:
            raise ValueError("Bad iq type")

        if element is None and stanza_id is None and stanza_type in (
                                                                "get", "set"):
            stanza_id = self.gen_id()

        if element is None:
            element = "iq"
        elif not isinstance(element, ElementClass):
            raise TypeError("Couldn't make Iq from " + repr(element))

        Stanza.__init__(self, element, from_jid = from_jid, to_jid = to_jid,
                        stanza_type = stanza_type, stanza_id = stanza_id,
                        error = error, error_cond = error_cond,
                        return_path = return_path, language = language)


        if self.element_name != "iq":
            raise ValueError("The element is not <iq/>")

    def copy(self):
        """Create a deep copy of the stanza.

        :returntype: `Iq`"""
        result = Iq(None, self.from_jid, self.to_jid,
                        self.stanza_type, self.stanza_id, self.error,
                        self._return_path())
        if self._payload is None:
            self.decode_payload()
        for payload in self._payload:
            # use Stanza.add_payload to skip the payload length check
            Stanza.add_payload(result, payload)
        return result

    def make_error_response(self, cond):
        """Create error response for the a "get" or "set" iq stanza.

        :Parameters:
            - `cond`: error condition name, as defined in XMPP specification.

        :return: new `Iq` object with the same "id" as self, "from" and "to"
            attributes swapped, type="error" and containing <error /> element
            plus payload of `self`.
        :returntype: `Iq`"""

        if self.stanza_type in ("result", "error"):
            raise ValueError("Errors may not be generated for"
                                                " 'result' and 'error' iq")

        stanza = Iq(stanza_type="error", from_jid = self.to_jid,
                        to_jid = self.from_jid, stanza_id = self.stanza_id,
                        error_cond = cond)
        if self._payload is None:
            self.decode_payload()
        for payload in self._payload:
            # use Stanza.add_payload to skip the payload length check
            Stanza.add_payload(stanza, payload)
        return stanza

    def make_result_response(self):
        """Create result response for the a "get" or "set" iq stanza.

        :return: new `Iq` object with the same "id" as self, "from" and "to"
            attributes replaced and type="result".
        :returntype: `Iq`"""

        if self.stanza_type not in ("set", "get"):
            raise ValueError("Results may only be generated for"
                                                        " 'set' or 'get' iq")
        stanza = Iq(stanza_type = "result", from_jid = self.to_jid,
                        to_jid = self.from_jid, stanza_id = self.stanza_id)
        return stanza

    def add_payload(self, payload):
        """Add new the stanza payload. Fails if there is already some
        payload element attached (<iq/> stanza can contain only one payload
        element)

        Marks the stanza dirty.

        :Parameters:
            - `payload`: XML element or stanza payload object to add
        :Types:
            - `payload`: :etree:`ElementTree.Element` or
              `interfaces.StanzaPayload`
        """
        if self._payload is None:
            self.decode_payload()
        if len(self._payload) >= 1:
            raise ValueError("Cannot add more payload to Iq stanza")
        return Stanza.add_payload(self, payload) class JID(object):
    """JID.

    :Ivariables:
        - `local`: localpart of the JID
        - `domain`: domainpart of the JID
        - `resource`: resourcepart of the JID

    JID objects are immutable. They are also cached for better performance.
    """
    cache = weakref.WeakValueDictionary()
    __slots__ = ("local", "domain", "resource", "__weakref__",)
    def __new__(cls, local_or_jid = None, domain = None, resource = None,
                                                                check = True):
        """Create a new JID object or take one from the cache.

        :Parameters:
            - `local_or_jid`: localpart of the JID, JID object to copy, or
              Unicode representation of the JID.
            - `domain`: domain part of the JID
            - `resource`: resource part of the JID
            - `check`: if `False` then JID is not checked for specification
              compliance.
        """

        if isinstance(local_or_jid, JID):
            return local_or_jid

        if domain is None and resource is None:
            obj = cls.cache.get(unicode(local_or_jid))
            if obj:
                return obj

        obj = object.__new__(cls)

        if local_or_jid:
            local_or_jid = unicode(local_or_jid)
        if (local_or_jid and not domain and not resource):
            local, domain, resource = cls.__from_unicode(local_or_jid)
            cls.cache[local_or_jid] = obj
        else:
            if domain is None and resource is None:
                raise JIDError("At least domain must be given")
            if check:
                local = cls.__prepare_local(local_or_jid)
                domain = cls.__prepare_domain(domain)
                resource = cls.__prepare_resource(resource)
            else:
                local = local_or_jid
        object.__setattr__(obj, "local", local)
        object.__setattr__(obj, "domain", domain)
        object.__setattr__(obj, "resource", resource)
        return obj

    def __setattr__(self, name, value):
        raise RuntimeError("JID objects are immutable!")

    def __attribute_declarations__(self):
        # to make pylint happy
        self.local = u""
        self.domain = u""
        self.resource = u""

    @classmethod
    def __from_unicode(cls, data, check = True):
        """Return jid tuple from an Unicode string.

        :Parameters:
            - `data`: the JID string
            - `check`: when `False` then the JID is not checked for
              specification compliance.

        :Return: (localpart, domainpart, resourcepart) tuple"""
        parts1 = data.split(u"/", 1)
        parts2 = parts1[0].split(u"@", 1)
        if len(parts2) == 2:
            local = parts2[0]
            domain = parts2[1]
            if check:
                local = cls.__prepare_local(local)
                domain = cls.__prepare_domain(domain)
        else:
            local = None
            domain = parts2[0]
            if check:
                domain = cls.__prepare_domain(domain)
        if len(parts1) == 2:
            resource = parts1[1]
            if check:
                resource = cls.__prepare_resource(parts1[1])
        else:
            resource = None
        if not domain:
            raise JIDError("Domain is required in JID.")
        return (local, domain, resource)

    @staticmethod
    def __prepare_local(data):
        """Prepare localpart of the JID

        :Parameters:
            - `data`: localpart of the JID
        :Types:
            - `data`: `unicode`

        :raise JIDError: if the local name is too long.
        :raise pyxmpp.xmppstringprep.StringprepError: if the
            local name fails Nodeprep preparation."""
        if not data:
            return None
        data = unicode(data)
        try:
            local = NODEPREP.prepare(data)
        except StringprepError, err:
            raise JIDError(u"Local part invalid: {0}".format(err))
        if len(local.encode("utf-8")) > 1023:
            raise JIDError(u"Local part too long")
        return local

    @staticmethod
    def __prepare_domain(data):
        """Prepare domainpart of the JID.

        :Parameters:
            - `data`: Domain part of the JID
        :Types:
            - `data`: `unicode`

        :raise JIDError: if the domain name is too long.
        """
        # pylint: disable=R0912
        if not data:
            raise JIDError("Domain must be given")
        data = unicode(data)
        if not data:
            raise JIDError("Domain must be given")
        if u'[' in data:
            if data[0] == u'[' and data[-1] == u']':
                try:
                    addr = _validate_ip_address(socket.AF_INET6, data[1:-1])
                    return "[{0}]".format(addr)
                except ValueError, err:
                    logger.debug("ValueError: {0}".format(err))
                    raise JIDError(u"Invalid IPv6 literal in JID domainpart")
            else:
                raise JIDError(u"Invalid use of '[' or ']' in JID domainpart")
        elif data[0].isdigit() and data[-1].isdigit():
            try:
                addr = _validate_ip_address(socket.AF_INET, data)
            except ValueError, err:
                logger.debug("ValueError: {0}".format(err))
        data = UNICODE_DOT_RE.sub(u".", data)
        data = data.rstrip(u".")
        labels = data.split(u".")
        try:
            labels = [idna.nameprep(label) for label in labels]
        except UnicodeError:
            raise JIDError(u"Domain name invalid")
        for label in labels:
            if not STD3_LABEL_RE.match(label):
                raise JIDError(u"Domain name invalid")
            try:
                idna.ToASCII(label)
            except UnicodeError:
                raise JIDError(u"Domain name invalid")
        domain = u".".join(labels)
        if len(domain.encode("utf-8")) > 1023:
            raise JIDError(u"Domain name too long")
        return domain

    @staticmethod
    def __prepare_resource(data):
        """Prepare the resourcepart of the JID.

        :Parameters:
            - `data`: Resourcepart of the JID

        :raise JIDError: if the resource name is too long.
        :raise pyxmpp.xmppstringprep.StringprepError: if the
            resourcepart fails Resourceprep preparation."""
        if not data:
            return None
        data = unicode(data)
        try:
            resource = RESOURCEPREP.prepare(data)
        except StringprepError, err:
            raise JIDError(u"Local part invalid: {0}".format(err))
        if len(resource.encode("utf-8")) > 1023:
            raise JIDError("Resource name too long")
        return resource

    def __unicode__(self):
        return self.as_unicode()

    def __repr__(self):
        return "JID(%r)" % (self.as_unicode())

    def as_utf8(self):
        """UTF-8 encoded JID representation.

        :return: UTF-8 encoded JID."""
        return self.as_unicode().encode("utf-8")

    def as_string(self):
        """UTF-8 encoded JID representation.

        *Deprecated* Always use Unicode objects, or `as_utf8` if you really want.

        :return: UTF-8 encoded JID."""
        warnings.warn("JID.as_string() is deprecated. Use unicode()"
                " or `as_utf8` instead.", DeprecationWarning, stacklevel=1)
        return self.as_utf8()

    def as_unicode(self):
        """Unicode string JID representation.

        :return: JID as Unicode string."""
        result = self.domain
        if self.local:
            result = self.local + u'@' + result
        if self.resource:
            result = result + u'/' + self.resource
        if not JID.cache.has_key(result):
            JID.cache[result] = self
        return result

    def bare(self):
        """Make bare JID made by removing resource from current `self`.

        :return: new JID object without resource part."""
        return JID(self.local, self.domain, check = False)

    def __eq__(self, other):
        if other is None:
            return False
        elif type(other) in (str, unicode):
            try:
                other = JID(other)
            except StandardError:
                return False
        elif not isinstance(other, JID):
            return False

        return (self.local == other.local
            and are_domains_equal(self.domain, other.domain)
            and self.resource == other.resource)

    def __ne__(self, other):
        return not self == other

    def __lt__(self, other):
        if other is None:
            return False
        return unicode(self) < unicode(other)

    def __gt__(self, other):
        if other is None:
            return True
        return unicode(self) > unicode(other)

    def __le__(self, other):
        if other is None:
            return False
        return unicode(self) <= unicode(other)

    def __ge__(self, other):
        if other is None:
            return True
        return unicode(self) >= unicode(other)

    def __hash__(self):
        return hash(self.local) ^ hash(self.domain) ^ hash(self.resource) class StanzaProcessor(StanzaRoute):
    """Universal stanza handler/router class.

    Provides facilities to set up custom handlers for various types of stanzas.

    :Ivariables:
        - `lock`: lock object used to synchronize access to the
          `StanzaProcessor` object.
        - `me`: local JID.
        - `peer`: remote endpoint JID.
        - `process_all_stanzas`: when `True` then all stanzas received (and
          not only those addressed to `me`) are considered local.
        - `uplink`: object to route outgoing stanzas through
    :Types:
        - `lock`: :std:`threading.RLock`
        - `me`: `JID`
        - `peer`: `JID`
        - `process_all_stanzas`: `bool`
        - `uplink`: `StanzaRoute`
    """
    # pylint: disable-msg=R0902
    def __init__(self, default_timeout = 300):
        """Initialize a `StanzaProcessor` object.

        :Parameters:
            - `default_timeout`: default timeout for IQ response handlers
        """
        self.me = None
        self.peer = None
        self.uplink = None
        self.process_all_stanzas = True
        self._iq_response_handlers = ExpiringDictionary(default_timeout)
        self._iq_handlers = defaultdict(dict)
        self._message_handlers = []
        self._presence_handlers = []
        self.lock = threading.RLock()

    def _process_handler_result(self, response):
        """Examines out the response returned by a stanza handler and sends all
        stanzas provided.

        :Parameters:
            - `response`: the response to process. `None` or `False` means 'not
              handled'. `True` means 'handled'. Stanza or stanza list means
              handled with the stanzas to send back
        :Types:
            - `response`: `bool` or `Stanza` or iterable of `Stanza`

        :Returns:
            - `True`: if `response` is `Stanza`, iterable or `True` (meaning
              the stanza was processed).
            - `False`: when `response` is `False` or `None`

        :returntype: `bool`
        """

        if response is None or response is False:
            return False

        if isinstance(response, Stanza):
            self.send(response)
            return True

        try:
            response = iter(response)
        except TypeError:
            return bool(response)

        for stanza in response:
            if isinstance(stanza, Stanza):
                self.send(stanza)
            else:
                logger.warning(u"Unexpected object in stanza handler result:"
                                                    u" {0!r}".format(stanza))
        return True

    def _process_iq_response(self, stanza):
        """Process IQ stanza of type 'response' or 'error'.

        :Parameters:
            - `stanza`: the stanza received
        :Types:
            - `stanza`: `Iq`

        If a matching handler is available pass the stanza to it.  Otherwise
        ignore it if it is "error" or "result" stanza or return
        "feature-not-implemented" error if it is "get" or "set".
        """
        stanza_id = stanza.stanza_id
        from_jid = stanza.from_jid
        if from_jid:
            ufrom = from_jid.as_unicode()
        else:
            ufrom = None
        res_handler = err_handler = None
        try:
            res_handler, err_handler = self._iq_response_handlers.pop(
                                                    (stanza_id, ufrom))
        except KeyError:
            logger.debug("No response handler for id={0!r} from={1!r}"
                                                .format(stanza_id, ufrom))
            logger.debug(" from_jid: {0!r} peer: {1!r}  me: {2!r}"
                                        .format(from_jid, self.peer, self.me))
            if ( (from_jid == self.peer or from_jid == self.me
                            or self.me and from_jid == self.me.bare()) ):
                try:
                    logger.debug("  trying id={0!r} from=None"
                                                        .format(stanza_id))
                    res_handler, err_handler = \
                            self._iq_response_handlers.pop(
                                                    (stanza_id, None))
                except KeyError:
                    pass
        if stanza.stanza_type == "result":
            if res_handler:
                response = res_handler(stanza)
            else:
                return False
        else:
            if err_handler:
                response = err_handler(stanza)
            else:
                return False
        self._process_handler_result(response)
        return True

    def process_iq(self, stanza):
        """Process IQ stanza received.

        :Parameters:
            - `stanza`: the stanza received
        :Types:
            - `stanza`: `Iq`

        If a matching handler is available pass the stanza to it.  Otherwise
        ignore it if it is "error" or "result" stanza or return
        "feature-not-implemented" error if it is "get" or "set"."""

        typ = stanza.stanza_type
        if typ in ("result", "error"):
            return self._process_iq_response(stanza)
        if typ not in ("get", "set"):
            raise BadRequestProtocolError("Bad <iq/> type")
        logger.debug("Handling <iq type='{0}'> stanza: {1!r}".format(
                                                            stanza, typ))
        payload = stanza.get_payload(None)
        logger.debug("  payload: {0!r}".format(payload))
        if not payload:
            raise BadRequestProtocolError("<iq/> stanza with no child element")
        handler = self._get_iq_handler(typ, payload)
        if not handler:
            payload = stanza.get_payload(None, specialize = True)
            logger.debug("  specialized payload: {0!r}".format(payload))
            if not isinstance(payload, XMLPayload):
                handler = self._get_iq_handler(typ, payload)
        if handler:
            response = handler(stanza)
            self._process_handler_result(response)
            return True
        else:
            raise ServiceUnavailableProtocolError("Not implemented")

    def _get_iq_handler(self, iq_type, payload):
        """Get an <iq/> handler for given iq  type and payload."""
        key = (payload.__class__, payload.handler_key)
        logger.debug("looking up iq {0} handler for {1!r}, key: {2!r}"
                            .format(iq_type, payload, key))
        logger.debug("handlers: {0!r}".format(self._iq_handlers))
        handler = self._iq_handlers[iq_type].get(key)
        return handler

    def __try_handlers(self, handler_list, stanza, stanza_type = None):
        """ Search the handler list for handlers matching
        given stanza type and payload namespace. Run the
        handlers found ordering them by priority until
        the first one which returns `True`.

        :Parameters:
            - `handler_list`: list of available handlers
            - `stanza`: the stanza to handle
            - `stanza_type`: stanza type override (value of its "type"
              attribute)

        :return: result of the last handler or `False` if no
            handler was found.
        """
        # pylint: disable=W0212
        if stanza_type is None:
            stanza_type = stanza.stanza_type
        payload = stanza.get_all_payload()
        classes = [p.__class__ for p in payload]
        keys = [(p.__class__, p.handler_key) for p in payload]
        for handler in handler_list:
            type_filter = handler._pyxmpp_stanza_handled[1]
            class_filter = handler._pyxmpp_payload_class_handled
            extra_filter = handler._pyxmpp_payload_key
            if type_filter != stanza_type:
                continue
            if class_filter:
                if extra_filter is None and class_filter not in classes:
                    continue
                if extra_filter and (class_filter, extra_filter) not in keys:
                    continue
            response = handler(stanza)
            if self._process_handler_result(response):
                return True
        return False

    def process_message(self, stanza):
        """Process message stanza.

        Pass it to a handler of the stanza's type and payload namespace.
        If no handler for the actual stanza type succeeds then hadlers
        for type "normal" are used.

        :Parameters:
            - `stanza`: message stanza to be handled
        """

        stanza_type = stanza.stanza_type
        if stanza_type is None:
            stanza_type = "normal"

        if self.__try_handlers(self._message_handlers, stanza,
                                                stanza_type = stanza_type):
            return True

        if stanza_type not in ("error", "normal"):
            # try 'normal' handler additionaly to the regular handler
            return self.__try_handlers(self._message_handlers, stanza,
                                                    stanza_type = "normal")
        return False

    def process_presence(self, stanza):
        """Process presence stanza.

        Pass it to a handler of the stanza's type and payload namespace.

        :Parameters:
            - `stanza`: presence stanza to be handled
        """

        stanza_type = stanza.stanza_type
        return self.__try_handlers(self._presence_handlers, stanza, stanza_type)

    def route_stanza(self, stanza):
        """Process stanza not addressed to us.

        Return "recipient-unavailable" return if it is not
        "error" nor "result" stanza.

        This method should be overriden in derived classes if they
        are supposed to handle stanzas not addressed directly to local
        stream endpoint.

        :Parameters:
            - `stanza`: presence stanza to be processed
        """
        if stanza.stanza_type not in ("error", "result"):
            response = stanza.make_error_response(u"recipient-unavailable")
            self.send(response)
        return True

    def process_stanza(self, stanza):
        """Process stanza received from the stream.

        First "fix" the stanza with `self.fix_in_stanza()`,
        then pass it to `self.route_stanza()` if it is not directed
        to `self.me` and `self.process_all_stanzas` is not True. Otherwise
        stanza is passwd to `self.process_iq()`, `self.process_message()`
        or `self.process_presence()` appropriately.

        :Parameters:
            - `stanza`: the stanza received.

        :returns: `True` when stanza was handled
        """

        self.fix_in_stanza(stanza)
        to_jid = stanza.to_jid

        if not self.process_all_stanzas and to_jid and (
                to_jid != self.me and to_jid.bare() != self.me.bare()):
            return self.route_stanza(stanza)

        try:
            if isinstance(stanza, Iq):
                if self.process_iq(stanza):
                    return True
            elif isinstance(stanza, Message):
                if self.process_message(stanza):
                    return True
            elif isinstance(stanza, Presence):
                if self.process_presence(stanza):
                    return True
        except ProtocolError, err:
            typ = stanza.stanza_type
            if typ != 'error' and (typ != 'result'
                                                or stanza.stanza_type != 'iq'):
                response = stanza.make_error_response(err.xmpp_name)
                self.send(response)
                err.log_reported()
            else:
                err.log_ignored()
            return
        logger.debug("Unhandled %r stanza: %r" % (stanza.stanza_type,
                                                        stanza.serialize()))
        return False

    def check_to(self, to_jid):
        """Check "to" attribute of received stream header.

        :return: `to_jid` if it is equal to `self.me`, None otherwise.

        Should be overriden in derived classes which require other logic
        for handling that attribute."""
        if to_jid != self.me:
            return None
        return to_jid

    def set_response_handlers(self, stanza, res_handler, err_handler,
                                    timeout_handler = None, timeout = None):
        """Set response handler for an IQ "get" or "set" stanza.

        This should be called before the stanza is sent.

        :Parameters:
            - `stanza`: an IQ stanza
            - `res_handler`: result handler for the stanza. Will be called
              when matching <iq type="result"/> is received. Its only
              argument will be the stanza received. The handler may return
              a stanza or list of stanzas which should be sent in response.
            - `err_handler`: error handler for the stanza. Will be called
              when matching <iq type="error"/> is received. Its only
              argument will be the stanza received. The handler may return
              a stanza or list of stanzas which should be sent in response
              but this feature should rather not be used (it is better not to
              respond to 'error' stanzas).
            - `timeout_handler`: timeout handler for the stanza. Will be called
              (with no arguments) when no matching <iq type="result"/> or <iq
              type="error"/> is received in next `timeout` seconds.
            - `timeout`: timeout value for the stanza. After that time if no
              matching <iq type="result"/> nor <iq type="error"/> stanza is
              received, then timeout_handler (if given) will be called.
        """
        # pylint: disable-msg=R0913
        self.lock.acquire()
        try:
            self._set_response_handlers(stanza, res_handler, err_handler,
                                                    timeout_handler, timeout)
        finally:
            self.lock.release()

    def _set_response_handlers(self, stanza, res_handler, err_handler,
                                timeout_handler = None, timeout = None):
        """Same as `set_response_handlers` but assume `self.lock` is
        acquired."""
        # pylint: disable-msg=R0913
        self.fix_out_stanza(stanza)
        to_jid = stanza.to_jid
        if to_jid:
            to_jid = unicode(to_jid)
        if timeout_handler:
            def callback(dummy1, dummy2):
                """Wrapper for the timeout handler to make it compatible
                with the `ExpiringDictionary` """
                timeout_handler()
            self._iq_response_handlers.set_item(
                                    (stanza.stanza_id, to_jid),
                                    (res_handler,err_handler),
                                    timeout, callback)
        else:
            self._iq_response_handlers.set_item(
                                    (stanza.stanza_id, to_jid),
                                    (res_handler, err_handler),
                                    timeout)

    def clear_response_handlers(self):
        """Remove all registered response handlers."""
        self._iq_response_handlers.clear()

    def setup_stanza_handlers(self, handler_objects, usage_restriction):
        """Install stanza handlers provided by `handler_objects`"""
        # pylint: disable=W0212
        iq_handlers = {"get": {}, "set": {}}
        message_handlers = []
        presence_handlers = []
        for obj in handler_objects:
            if not isinstance(obj, XMPPFeatureHandler):
                continue
            obj.stanza_processor = self
            for dummy, handler in inspect.getmembers(obj, callable):
                if not hasattr(handler, "_pyxmpp_stanza_handled"):
                    continue
                element_name, stanza_type = handler._pyxmpp_stanza_handled
                restr = handler._pyxmpp_usage_restriction
                if restr and restr != usage_restriction:
                    continue
                if element_name == "iq":
                    payload_class = handler._pyxmpp_payload_class_handled
                    payload_key = handler._pyxmpp_payload_key
                    if (payload_class, payload_key) in iq_handlers[stanza_type]:
                        continue
                    iq_handlers[stanza_type][(payload_class, payload_key)] = \
                            handler
                    continue
                elif element_name == "message":
                    handler_list = message_handlers
                elif element_name == "presence":
                    handler_list = presence_handlers
                else:
                    raise ValueError, "Bad handler decoration"
                handler_list.append(handler)
        with self.lock:
            self._iq_handlers = iq_handlers
            self._presence_handlers = presence_handlers
            self._message_handlers = message_handlers

    def fix_in_stanza(self, stanza):
        """Modify incoming stanza before processing it.

        This implementation does nothig. It should be overriden in derived
        classes if needed."""
        pass

    def fix_out_stanza(self, stanza):
        """Modify outgoing stanza before sending into the stream.

        This implementation does nothig. It should be overriden in derived
        classes if needed."""
        pass

    def uplink_receive(self, stanza):
        self.process_stanza(stanza)

    def send(self, stanza):
        """Send a stanza somwhere.

        The default implementation sends it via the `uplink` if it is defined
        or raises the `NoRouteError`.

        :Parameters:
            - `stanza`: the stanza to send.
        :Types:
            - `stanza`: `pyxmpp.stanza.Stanza`"""
        if self.uplink:
            self.uplink.send(stanza)
        else:
            raise NoRouteError("No route for stanza") class XMPPSettings(MutableMapping):
    """Container for various parameters used all over PyXMPP.

    It can be used like a regular dictionary, but will provide reasonable
    defaults for PyXMPP for parameters which are not explicitly set.

    All known PyXMPP settings are included in the :r:`settings list`.

    :CVariables:
        - `_defaults`: defaults for registered parameters.
        - `_defaults_factories`: factory functions providing default values
          which cannot be hard-coded.
    :Ivariables:
        - `_settings`: current values of the parameters explicitly set.
    """
    _defs = {}
    def __init__(self, data = None):
        """Create settings, optionally initialized with `data`.

        :Parameters:
            - `data`: initial data
        :Types:
            - `data`: any mapping, including `XMPPSettings`
        """
        if data is None:
            self._settings = {}
        else:
            self._settings = dict(data)
    def __len__(self):
        """Number of parameters set."""
        return len(self._settings)
    def __iter__(self):
        """Iterate over the parameter names."""
        for key in self._settings.iterkeys():
            return self[key]
    def __contains__(self, key):
        """Check if a parameter is set.

        :Parameters:
            - `key`: the parameter name
        :Types:
            - `key`: `unicode`
        """
        return key in self._settings
    def __getitem__(self, key):
        """Get a parameter value. Return the default if no value is set
        and the default is provided by PyXMPP.

        :Parameters:
            - `key`: the parameter name
        :Types:
            - `key`: `unicode`
        """
        return self.get(key, required = True)
    def __setitem__(self, key, value):
        """Set a parameter value.

        :Parameters:
            - `key`: the parameter name
            - `value`: the new value
        :Types:
            - `key`: `unicode`
        """
        self._settings[unicode(key)] = value
    def __delitem__(self, key):
        """Unset a parameter value.

        :Parameters:
            - `key`: the parameter name
        :Types:
            - `key`: `unicode`
        """
        del self._settings[key]
    def get(self, key, local_default = None, required = False):
        """Get a parameter value.

        If parameter is not set, return `local_default` if it is not `None`
        or the PyXMPP global default otherwise.

        :Raise `KeyError`: if parameter has no value and no global default

        :Return: parameter value
        """
        # pylint: disable-msg=W0221
        if key in self._settings:
            return self._settings[key]
        if local_default is not None:
            return local_default
        if key in self._defs:
            setting_def = self._defs[key]
            if setting_def.default is not None:
                return setting_def.default
            factory = setting_def.factory
            if factory is None:
                return None
            value = factory(self)
            if setting_def.cache is True:
                setting_def.default = value
            return value
        if required:
            raise KeyError(key)
        return local_default
    def keys(self):
        """Return names of parameters set.

        :Returntype: - `list` of `unicode`
        """
        return self._settings.keys()
    def items(self):
        """Return names and values of parameters set.

        :Returntype: - `list` of tuples
        """
        return self._settings.items()

    def load_arguments(self, args):
        """Load settings from :std:`ArgumentParser` output.

        :Parameters:
            - `args`: output of argument parsed based on the one
              returned by `get_arg_parser()`
        """
        for name, setting in self._defs.items():
            if sys.version_info.major < 3:
                # pylint: disable-msg=W0404
                from locale import getpreferredencoding
                encoding = getpreferredencoding()
                name = name.encode(encoding, "replace")
            attr = "pyxmpp2_" + name
            try:
                self[setting.name] = getattr(args, attr)
            except AttributeError:
                pass

    @classmethod
    def add_setting(cls, name, type = unicode, default = None, factory = None,
                        cache = False, default_d = None, doc = None,
                        cmdline_help = None, validator = None, basic = False):
        """Add a new setting definition.

        :Parameters:
            - `name`: setting name
            - `type`: setting type object or type description
            - `default`: default value
            - `factory`: default value factory
            - `cache`: if `True` the `factory` will be called only once
              and its value stored as a constant default.
            - `default_d`: description of the default value
            - `doc`: setting documentation
            - `cmdline_help`: command line argument description. When not
              provided then the setting won't be available as a command-line
              option
            - `basic`: when `True` the option is considered a basic option -
              one of those which should usually stay configurable in
              an application.
            - `validator`: function validating command-line option value string
              and returning proper value for the settings objects. Defaults
              to `type`.
        :Types:
            - `name`: `unicode`
            - `type`: type or `unicode`
            - `factory`: a callable
            - `cache`: `bool`
            - `default_d`: `unicode`
            - `doc`: `unicode`
            - `cmdline_help`: `unicode`
            - `basic`: `bool`
            - `validator`: a callable
        """
        # pylint: disable-msg=W0622,R0913
        setting_def = _SettingDefinition(name, type, default, factory,
                                            cache, default_d, doc,
                                            cmdline_help, validator, basic)
        if name not in cls._defs:
            cls._defs[name] = setting_def
            return
        duplicate = cls._defs[name]
        if duplicate.type != setting_def.type:
            raise ValueError("Setting duplicate, with a different type")
        if duplicate.default != setting_def.default:
            raise ValueError("Setting duplicate, with a different default")
        if duplicate.factory != setting_def.factory:
            raise ValueError("Setting duplicate, with a different factory")

    @staticmethod
    def validate_string_list(value):
        """Validator for string lists to be used with `add_setting`."""
        try:
            if sys.version_info.major < 3:
                # pylint: disable-msg=W0404
                from locale import getpreferredencoding
                encoding = getpreferredencoding()
                value = value.decode(encoding)
            return [x.strip() for x in value.split(u",")]
        except (AttributeError, TypeError, UnicodeError):
            raise ValueError("Bad string list")

    @staticmethod
    def validate_positive_int(value):
        """Positive integer validator to be used with `add_setting`."""
        value = int(value)
        if value <= 0:
            raise ValueError("Positive number required")
        return value

    @staticmethod
    def validate_positive_float(value):
        """Positive float validator to be used with `add_setting`."""
        value = float(value)
        if value <= 0:
            raise ValueError("Positive number required")
        return value

    @staticmethod
    def get_int_range_validator(start, stop):
        """Return an integer range validator to be used with `add_setting`.

        :Parameters:
            - `start`: minimum value for the integer
            - `stop`: the upper bound (maximum value + 1)
        :Types:
            - `start`: `int`
            - `stop`: `int`

        :return: a validator function
        """
        def validate_int_range(value):
            """Integer range validator."""
            value = int(value)
            if value >= start and value < stop:
                return value
            raise ValueError("Not in <{0},{1}) range".format(start, stop))
        return validate_int_range

    @classmethod
    def list_all(cls, basic = None):
        """List known settings.

        :Parameters:
            - `basic`: When `True` then limit output to the basic settings,
              when `False` list only the extra settings.
        """
        if basic is None:
            return [s for s in cls._defs]
        else:
            return [s.name for s in cls._defs.values() if s.basic == basic]

    @classmethod
    def get_arg_parser(cls, settings = None, option_prefix = u'--',
                                                            add_help = False):
        """Make a command-line option parser.

        The returned parser may be used as a parent parser for application
        argument parser.

        :Parameters:
            - `settings`: list of PyXMPP2 settings to consider. By default
              all 'basic' settings are provided.
            - `option_prefix`: custom prefix for PyXMPP2 options. E.g.
              ``'--xmpp'`` to differentiate them from not xmpp-related
              application options.
            - `add_help`: when `True` a '--help' option will be included
              (probably already added in the application parser object)
        :Types:
            - `settings`: list of `unicode`
            - `option_prefix`: `str`
            - `add_help`:

        :return: an argument parser object.
        :returntype: :std:`argparse.ArgumentParser`
        """
        # pylint: disable-msg=R0914,R0912
        parser = argparse.ArgumentParser(add_help = add_help,
                                            prefix_chars = option_prefix[0])
        if settings is None:
            settings = cls.list_all(basic = True)

        if sys.version_info.major < 3:
            # pylint: disable-msg=W0404
            from locale import getpreferredencoding
            encoding = getpreferredencoding()
            def decode_string_option(value):
                """Decode a string option."""
                return value.decode(encoding)

        for name in settings:
            if name not in cls._defs:
                logger.debug("get_arg_parser: ignoring unknown option {0}"
                                                                .format(name))
                return
            setting = cls._defs[name]
            if not setting.cmdline_help:
                logger.debug("get_arg_parser: option {0} has no cmdline"
                                                                .format(name))
                return
            if sys.version_info.major < 3:
                name = name.encode(encoding, "replace")
            option = option_prefix + name.replace("_", "-")
            dest = "pyxmpp2_" + name
            if setting.validator:
                opt_type = setting.validator
            elif setting.type is unicode and sys.version_info.major < 3:
                opt_type = decode_string_option
            else:
                opt_type = setting.type
            if setting.default_d:
                default_s = setting.default_d
                if sys.version_info.major < 3:
                    default_s = default_s.encode(encoding, "replace")
            elif setting.default is not None:
                default_s = repr(setting.default)
            else:
                default_s = None
            opt_help = setting.cmdline_help
            if sys.version_info.major < 3:
                opt_help = opt_help.encode(encoding, "replace")
            if default_s:
                opt_help += " (Default: {0})".format(default_s)
            if opt_type is bool:
                opt_action = _YesNoAction
            else:
                opt_action = "store"
            parser.add_argument(option,
                                action = opt_action,
                                default = setting.default,
                                type = opt_type,
                                help = opt_help,
                                metavar = name.upper(),
                                dest = dest)
        return parser class XMLPayload(StanzaPayload):
    """Transparent XML payload for stanza.

    This object can be used for any stanza payload.
    It doesn't decode the XML element, but instead keeps it in the ElementTree
    format.

    :Ivariables:
        - `xml_element_name`: qualified name of the wrapped element
        - `element`: the wrapped element
    :Types:
        - `xml_element_name`: `unicode`
        - `element`: :etree:`ElementTree.Element`
    """
    def __init__(self, data):
        if isinstance(data, StanzaPayload):
            data = data.as_xml()
        if not isinstance(data, ElementClass):
            raise TypeError("ElementTree.Element required")
        self.xml_element_name = data.tag
        self.element = data

    @classmethod
    def from_xml(cls, element):
        return cls(element)

    def as_xml(self):
        return self.element

    @property
    def handler_key(self):
        """Return `xml_element_name` as the extra key for stanza
        handlers."""
        return self.xml_element_name class VersionPayload(StanzaPayload):
    """Software version (XEP-0092) stanza payload.

    :Ivariables:
        - `name`: software name
        - `version`: software version
        - `os_name`: operating system name

    :Types:
        - `name`: `unicode`
        - `version`: `unicode`
        - `os_name`: `unicode`
    """
    def __init__(self, name = None, version = None, os_name = None):
        self.name = name
        self.version = version
        self.os_name = os_name

    @classmethod
    def from_xml(cls, element):
        name = None
        version = None
        os_name = None
        for child in element:
            if child.tag == NAME_TAG:
                name = child.text
            elif child.tag == VERSION_TAG:
                version = child.text
            elif child.tag == OS_TAG:
                os_name = child.text
        return cls(name, version, os_name)

    def as_xml(self):
        element = ElementTree.Element(QUERY_TAG)
        if self.name is None and self.version is None and self.os_name is None:
            return element
        sub = ElementTree.SubElement(element, NAME_TAG)
        sub.text = self.name
        sub = ElementTree.SubElement(element, VERSION_TAG)
        sub.text = self.version
        if self.os_name:
            sub = ElementTree.SubElement(element, OS_TAG)
            sub.text = self.os_name
        return element class VersionProvider(XMPPFeatureHandler):
    """Provides the Software version (XEP-0092) service.

    Handles incoming software version queries with values from:

        - :r:`software_name setting`
        - :r:`software_version setting`
        - :r:`software_os setting`
    """
    # pylint: disable=R0903
    def __init__(self, settings = None):
        self.settings = settings if settings else XMPPSettings()

    @iq_get_stanza_handler(VersionPayload)
    def handle_version_iq_get(self, stanza):
        """Handler <iq type="get"/> for a software version query."""
        payload = VersionPayload(name = self.settings["software_name"],
                                 version = self.settings["software_version"],
                                 os_name = self.settings["software_os"]
                                 )
        response = stanza.make_result_response()
        response.set_payload(payload)
        return response def request_software_version(stanza_processor, target_jid, callback,
                                                    error_callback = None):
    """Request software version information from a remote entity.

    When a valid response is received the `callback` will be handled
    with a `VersionPayload` instance as its only argument. The object will
    provide the requested infromation.

    In case of error stanza received or invalid response the `error_callback`
    (if provided) will be called with the offending stanza (which can
    be ``<iq type='error'/>`` or ``<iq type='result'>``) as its argument.

    The same function will be called on timeout, with the argument set to
    `None`.

    :Parameters:
        - `stanza_processor`: a object used to send the query and handle
          response. E.g. a `pyxmpp2.client.Client` instance
        - `target_jid`: the JID of the entity to query
        - `callback`: function to be called with a valid response
        - `error_callback`: function to be called on error
    :Types:
        - `stanza_processor`: `StanzaProcessor`
        - `target_jid`: `JID`
    """
    stanza = Iq(to_jid = target_jid, stanza_type = "get")
    payload = VersionPayload()
    stanza.set_payload(payload)
    def wrapper(stanza):
        """Wrapper for the user-provided `callback` that extracts the payload
        from stanza received."""
        payload = stanza.get_payload(VersionPayload)
        if payload is None:
            if error_callback:
                error_callback(stanza)
            else:
                logger.warning("Invalid version query response.")
        else:
            callback(payload)
    stanza_processor.set_response_handlers(stanza, wrapper, error_callback)
    stanza_processor.send(stanza) def load_tests(loader, tests, pattern):
    """Use default test list, just remove the classes which names start with
    '_'."""
    # pylint: disable=W0613
    suite = filter_tests(tests)
    return suite def setup_logging():
    """Set up logging for the tests.

    Log level used depends on number of '-v' in sys.argv
    """
    # pylint: disable=W0603
    global logging_ready
    if logging_ready:
        return
    if sys.argv.count("-v") > 2:
        logging.basicConfig(level=logging.DEBUG)
    elif sys.argv.count("-v") == 2:
        logging.basicConfig(level=logging.INFO)
    else:
        logging.basicConfig(level=logging.ERROR)
    logging_ready = True
import unittest
import platform
import pyxmpp2.version
from pyxmpp2.etree import ElementTree
from pyxmpp2.iq import Iq
from pyxmpp2.jid import JID
from pyxmpp2.stanzaprocessor import StanzaProcessor
from pyxmpp2.settings import XMPPSettings
from pyxmpp2.stanzapayload import XMLPayload
from pyxmpp2.ext.version import VersionPayload, VersionProvider
from pyxmpp2.ext.version import request_software_version
from pyxmpp2.test._support import load_tests, setup_logging
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# pylint: disable=C0111






IQ1 = '''<iq type="get" id="1" xmlns="jabber:client">
<query xmlns="jabber:iq:version"/>
</iq>'''

IQ2 = '''<iq type="response" id="1" xmlns="jabber:client">
<query xmlns="jabber:iq:version">
  <name>NAME</name>
  <version>VERSION</version>
  <os>OS</os>
</query>
</iq>'''

class TestVersionPayload(unittest.TestCase):
    def test_parse_empty(self):
        element = ElementTree.XML(IQ1)
        payload = VersionPayload.from_xml(element[0])
        self.assertIsNone(payload.name)
        self.assertIsNone(payload.version)
        self.assertIsNone(payload.os_name)

    def test_parse_full(self):
        element = ElementTree.XML(IQ2)
        payload = VersionPayload.from_xml(element[0])
        self.assertEqual(payload.name, 'NAME')
        self.assertEqual(payload.version, 'VERSION')
        self.assertEqual(payload.os_name, 'OS')

    def test_build_empty(self):
        payload = VersionPayload()
        self.assertIsNone(payload.name)
        self.assertIsNone(payload.version)
        self.assertIsNone(payload.os_name)
        element = payload.as_xml()
        self.assertEqual(element.tag, "{jabber:iq:version}query")
        self.assertEqual(len(element), 0)

class Processor(StanzaProcessor):
    def __init__(self, handlers):
        StanzaProcessor.__init__(self)
        self.setup_stanza_handlers(handlers, "post-auth")
        self.stanzas_sent = []
    def send(self, stanza):
        self.stanzas_sent.append(stanza)

class TestVersionProvider(unittest.TestCase):
    def test_defaults(self):
        provider = VersionProvider()
        processor = Processor([provider])
        stanza = Iq(ElementTree.XML(IQ1))
        processor.uplink_receive(stanza)
        self.assertEqual(len(processor.stanzas_sent), 1)
        response = processor.stanzas_sent[0]
        self.assertIsInstance(response, Iq)
        self.assertEqual(response.stanza_type, "result")
        payload = response.get_payload(VersionPayload)
        self.assertIsInstance(payload, VersionPayload)
        self.assertEqual(payload.name, "PyXMPP2")
        self.assertEqual(payload.version, pyxmpp2.version.version)
        expected = u" ".join((platform.system(), platform.release(),
                                                        platform.machine()))
        self.assertEqual(payload.os_name, expected)

    def test_custom(self):
        settings = XMPPSettings({
                        "software_name": "NAME",
                        "software_version": "VERSION",
                        "software_os": "OS",
                            })
        provider = VersionProvider(settings)
        processor = Processor([provider])
        stanza = Iq(ElementTree.XML(IQ1))
        processor.uplink_receive(stanza)
        self.assertEqual(len(processor.stanzas_sent), 1)
        response = processor.stanzas_sent[0]
        self.assertIsInstance(response, Iq)
        self.assertEqual(response.stanza_type, "result")
        payload = response.get_payload(VersionPayload)
        self.assertIsInstance(payload, VersionPayload)
        self.assertEqual(payload.name, "NAME")
        self.assertEqual(payload.version, "VERSION")
        self.assertEqual(payload.os_name, "OS")

    def test_bad_request(self):
        provider = VersionProvider()
        processor = Processor([provider])
        stanza = Iq(ElementTree.XML(IQ2))
        stanza.stanza_type = 'set'
        processor.uplink_receive(stanza)
        self.assertEqual(len(processor.stanzas_sent), 1)
        response = processor.stanzas_sent[0]
        self.assertIsInstance(response, Iq)
        self.assertEqual(response.stanza_type, "error")
        self.assertEqual(response.error.condition.tag,
                    "{urn:ietf:params:xml:ns:xmpp-stanzas}service-unavailable")

class TestVersionRequest(unittest.TestCase):
    def test_request(self):
        payload_received = []
        errors_received = []
        def callback(payload):
            payload_received.append(payload)
        def error_callback(stanza):
            errors_received.append(stanza)
        processor = Processor([])

        request_software_version(processor, JID("test@example.org"),