class ElementClass:
    def __subclasshook__(cls, other):
def element_to_unicode(element): class BadRequestProtocolError(ProtocolError):
    """Raised when invalid stanza is processed and 'bad-request' error should
    be reported."""
    def __init__(self, message):
        ProtocolError.__init__(self, "bad-request", message) class JIDMalformedProtocolError(ProtocolError, JIDError):
    """Raised when invalid JID is encountered."""
    def __init__(self, message):
        JIDError.__init__(self)
        ProtocolError.__init__(self, "jid-malformed", message) 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 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 def payload_factory(element):
    """Return a specialized `StanzaPayload` object for given element.
    """
    return payload_class_for_element_name(element.tag).from_xml(element) def payload_class_for_element_name(element_name):
    """Return a payload class for given element name."""
    logger.debug(" looking up payload class for element: {0!r}".format(
                                                                element_name))
    logger.debug("  known: {0!r}".format(STANZA_PAYLOAD_CLASSES))
    if element_name in STANZA_PAYLOAD_CLASSES:
        return STANZA_PAYLOAD_CLASSES[element_name]
    else:
        return XMLPayload def serialize(element):
    """Serialize an XMPP element.

    Utility function for debugging or logging.

        :Parameters:
            - `element`: the element to serialize
        :Types:
            - `element`: :etree:`ElementTree.Element`

        :Return: serialized element
        :Returntype: `unicode`
    """
    if getattr(_THREAD, "serializer", None) is None:
        _THREAD.serializer = XMPPSerializer("jabber:client")
        _THREAD.serializer.emit_head(None, None)
    return _THREAD.serializer.emit_stanza(element) STANZA_NAMESPACES = (STANZA_CLIENT_NS, STANZA_SERVER_NS) STANZA_CLIENT_NS = "jabber:client" XML_LANG_QNAME = XML_QNP + "lang" class StanzaErrorElement(ErrorElement):
    """Stanza error element.

    :Ivariables:
        - `error_type`: 'type' of the error, one of: 'auth', 'cancel',
          'continue', 'modify', 'wait'
    :Types:
        - `error_type`: `unicode`
    """
    error_qname = STANZA_CLIENT_QNP + "error"
    text_qname = STANZA_CLIENT_QNP + "text"
    cond_qname_prefix = STANZA_ERROR_QNP
    def __init__(self, element_or_cond, text = None, language = None,
                                                            error_type = None):
        """Initialize an StanzaErrorElement object.

        :Parameters:
            - `element_or_cond`: XML <error/> element to decode or an error
              condition name or element.
            - `text`: optional description to override the default one
            - `language`: RFC 3066 language tag for the description
            - `error_type`: 'type' of the error, one of: 'auth', 'cancel',
              'continue', 'modify', 'wait'
        :Types:
            - `element_or_cond`: :etree:`ElementTree.Element` or `unicode`
            - `text`: `unicode`
            - `language`: `unicode`
            - `error_type`: `unicode`
        """
        self.error_type = None
        if isinstance(element_or_cond, basestring):
            if element_or_cond not in STANZA_ERRORS:
                raise ValueError(u"Bad error condition")
        elif element_or_cond.tag.startswith(u"{"):
            namespace = element_or_cond.tag[1:].split(u"}", 1)[0]
            if namespace not in STANZA_NAMESPACES:
                raise ValueError(u"Bad error namespace {0!r}".format(namespace))
            self.error_qname = u"{{{0}}}error".format(namespace)
            self.text_qname = u"{{{0}}}text".format(namespace)
        else:
            raise ValueError(u"Bad error namespace - no namespace")
        ErrorElement.__init__(self, element_or_cond, text, language)
        if error_type is not None:
            self.error_type = error_type
        if self.condition.tag in STANZA_ERRORS_Q:
            cond = self.condition.tag
        else:
            cond = UNDEFINED_STANZA_CONDITION
        if not self.error_type:
            self.error_type = STANZA_ERRORS_Q[cond][1]

    def _from_xml(self, element):
        """Initialize an ErrorElement object from an XML element.

        :Parameters:
            - `element`: XML element to be decoded.
        :Types:
            - `element`: :etree:`ElementTree.Element`
        """
        ErrorElement._from_xml(self, element)
        error_type = element.get(u"type")
        if error_type:
            self.error_type = error_type

    def get_message(self):
        """Get the standard English message for the error.

        :return: the error message.
        :returntype: `unicode`"""
        cond = self.condition_name
        if cond in STANZA_ERRORS:
            return STANZA_ERRORS[cond][0]
        else:
            return None

    def as_xml(self, stanza_namespace = None): # pylint: disable-msg=W0221
        """Return the XML error representation.

        :Parameters:
            - `stanza_namespace`: namespace URI of the containing stanza
        :Types:
            - `stanza_namespace`: `unicode`

        :returntype: :etree:`ElementTree.Element`"""
        if stanza_namespace:
            self.error_qname = "{{{0}}}error".format(stanza_namespace)
            self.text_qname = "{{{0}}}text".format(stanza_namespace)
        result = ErrorElement.as_xml(self)
        result.set("type", self.error_type)
        return result class StanzaPayload:
    """Abstract base class for stanza payload objects.

    Subclasses are used to encapsulate stanza payload data
    and to reference payload type in stanza handlers or when
    requesting particular payload from a stanza.
    """
    # pylint: disable=W0232
    __metaclass__ = ABCMeta

    @abstractclassmethod
    def from_xml(cls, element):
        """Create a `cls` instance from an XML element.

        :Parameters:
            - `element`: the XML element
        :Types:
            - `element`: :etree:`ElementTree.Element`
        """
        # pylint: disable=E0213
        raise NotImplementedError

    @abstractmethod
    def as_xml(self):
        """Return the XML representation of the payload.

        :returntype: :etree:`ElementTree.Element`
        """
        raise NotImplementedError

    def copy(self):
        """Return a deep copy of self."""
        return deepcopy(self)

    @property
    def handler_key(self):
        """Defines a key which may be used when registering handlers
        for stanzas with this payload."""
        # pylint: disable-msg=R0201
        return None
from .etree import ElementTree, ElementClass
from .exceptions import BadRequestProtocolError, JIDMalformedProtocolError
from .jid import JID
from .stanzapayload import XMLPayload, payload_factory
from .stanzapayload import payload_class_for_element_name
from .xmppserializer import serialize
from .constants import STANZA_NAMESPACES, STANZA_CLIENT_NS, XML_LANG_QNAME
from .error import StanzaErrorElement
from .interfaces import StanzaPayload
import random
import weakref
from __future__ import absolute_import, division

__docformat__ = "restructuredtext en"



random.seed()

class Stanza(object):
    """Base class for all XMPP stanzas.

    :Ivariables:
        - `_payload`: the stanza payload
        - `_error`: error associated a stanza of type "error"
        - `_namespace`: namespace of this stanza element
        - `_return_path`: weak reference to the return route object
    :Types:
        - `_payload`: `list` of (`unicode`, `StanzaPayload`)
        - `_error`: `pyxmpp2.error.StanzaErrorElement`
        - `_namespace`: `unicode`
        - `_return_path`: weakref to `StanzaRoute`
    """
    # pylint: disable-msg=R0902
    element_name = "Unknown"
    def __init__(self, element, from_jid = None, to_jid = None,
                            stanza_type = None, stanza_id = None,
                            error = None, error_cond = None,
                            return_path = None, language = None):
        """Initialize a Stanza object.

        :Parameters:
            - `element`: XML element of this stanza, or element name for a new
              stanza. If element is given it must not be modified later,
              unless `decode_payload()` and `mark_dirty()` methods are called
              first (the element changes won't affec the stanza then).
            - `from_jid`: sender JID.
            - `to_jid`: recipient JID.
            - `stanza_type`: staza type: one of: "get", "set", "result" or
              "error".
            - `stanza_id`: stanza id -- value of stanza's "id" attribute. If
              not given, then unique for the session value is generated.
            - `error`: error object. Ignored if `stanza_type` is not "error".
            - `error_cond`: error condition name. Ignored if `stanza_type` is
              not "error" or `error` is not None.
            - `return_path`: route for sending responses to this stanza. Will
              be weakly referenced.
            - `language`: default language for the stanza content
        :Types:
            - `element`: `unicode` or :etree:`ElementTree.Element`
            - `from_jid`: `JID`
            - `to_jid`: `JID`
            - `stanza_type`: `unicode`
            - `stanza_id`: `unicode`
            - `error`: `pyxmpp.error.StanzaErrorElement`
            - `error_cond`: `unicode`
            - `return_path`: `StanzaRoute`
            - `language`: `unicode`
        """
        # pylint: disable-msg=R0913
        self._error = None
        self._from_jid = None
        self._to_jid = None
        self._stanza_type = None
        self._stanza_id = None
        self._language = language
        if isinstance(element, ElementClass):
            self._element = element
            self._dirty = False
            self._decode_attributes()
            if not element.tag.startswith("{"):
                raise ValueError("Element has no namespace")
            else:
                self._namespace, self.element_name = element.tag[1:].split("}")
                if self._namespace not in STANZA_NAMESPACES:
                    raise BadRequestProtocolError("Wrong stanza namespace")
            self._payload = None
        else:
            self._element = None
            self._dirty = True
            self.element_name = unicode(element)
            self._namespace = STANZA_CLIENT_NS
            self._payload = []

        self._ns_prefix = "{{{0}}}".format(self._namespace)
        self._element_qname = self._ns_prefix + self.element_name

        if from_jid is not None:
            self.from_jid = from_jid

        if to_jid is not None:
            self.to_jid = to_jid

        if stanza_type:
            self.stanza_type = stanza_type

        if stanza_id:
            self.stanza_id = stanza_id

        if self.stanza_type == "error":
            if error:
                self._error = StanzaErrorElement(error)
            elif error_cond:
                self._error = StanzaErrorElement(error_cond)
            else:
                self._decode_error()

        if return_path is not None:
            self._return_path = weakref.ref(return_path)

    def _decode_attributes(self):
        """Decode attributes of the stanza XML element
        and put them into the stanza properties."""
        try:
            from_jid = self._element.get('from')
            if from_jid:
                self._from_jid = JID(from_jid)
            to_jid = self._element.get('to')
            if to_jid:
                self._to_jid = JID(to_jid)
        except ValueError:

            raise JIDMalformedProtocolError