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 Message(Stanza):
    """<message /> stanza class.
    """
    # pylint: disable-msg=R0902,R0904
    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,
                            subject = None, body = None, thread = None):
        """Initialize a `Message` object.

        :Parameters:
            - `element`: XML element of this stanza.
            - `from_jid`: sender JID.
            - `to_jid`: recipient JID.
            - `stanza_type`: staza type: one of: "normal", "chat", "headline",
              "error", "groupchat"
            - `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
            - `subject`: message subject,
            - `body`: message body.
            - `thread`: message thread id.
        :Types:
            - `element`: :etree:`ElementTree.Element`
            - `from_jid`: `JID`
            - `to_jid`: `JID`
            - `stanza_type`: `unicode`
            - `stanza_id`: `unicode`
            - `error`: `pyxmpp.error.StanzaErrorElement`
            - `error_cond`: `unicode`
            - `subject`: `unicode`
            - `body`: `unicode`
            - `thread`: `unicode`
            - `language`: `unicode`
        """
        # pylint: disable-msg=R0913
        self._subject = None
        self._body = None
        self._thread = None
        if element is None:
            element = "message"
        elif not isinstance(element, ElementClass):
            raise TypeError("Couldn't make Message 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 != "message":
            raise ValueError("The element is not <message/>")

        self._subject_tag = self._ns_prefix + "subject"
        self._body_tag = self._ns_prefix + "body"
        self._thread_tag = self._ns_prefix + "thread"

        if self._element is not None:
            self._decode_subelements()

        if subject is not None:
            self.subject = subject
        if body is not None:
            self.body = body
        if thread is not None:
            self.thread = thread

    def _decode_subelements(self):
        """Decode the stanza subelements."""
        for child in self._element:
            if child.tag == self._subject_tag:
                self._subject = child.text
            elif child.tag == self._body_tag:
                self._body = child.text
            elif child.tag == self._thread_tag:
                self._thread = child.text

    def as_xml(self):
        """Return the XML stanza representation.

        Always return an independent copy of the stanza XML representation,
        which can be freely modified without affecting the stanza.

        :returntype: :etree:`ElementTree.Element`"""
        result = Stanza.as_xml(self)
        if self._subject:
            child = ElementTree.SubElement(result, self._subject_tag)
            child.text = self._subject
        if self._body:
            child = ElementTree.SubElement(result, self._body_tag)
            child.text = self._body
        if self._thread:
            child = ElementTree.SubElement(result, self._thread_tag)
            child.text = self._thread
        return result

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

        :returntype: `Message`"""
        result = Message(None, self.from_jid, self.to_jid,
                        self.stanza_type, self.stanza_id, self.error,
                        self._return_path(), self._subject, self._body,
                                                            self._thread)
        for payload in self._payload:
            result.add_payload(payload.copy())
        return result

    @property
    def subject(self): # pylint: disable-msg=E0202
        """Message subject.

        :Returntype: `unicode`
        """
        return self._subject

    @subject.setter # pylint: disable-msg=E1101
    def subject(self, subject): # pylint: disable-msg=E0202,E0102,C0111
        self._subject = unicode(subject)
        self._dirty = True

    @property
    def body(self): # pylint: disable-msg=E0202
        """Message body.

        :Returntype: `unicode`
        """
        return self._body

    @body.setter # pylint: disable-msg=E1101
    def body(self, body): # pylint: disable-msg=E0202,E0102,C0111
        self._body = unicode(body)
        self._dirty = True

    @property
    def thread(self): # pylint: disable-msg=E0202
        """Thread id.

        :Returntype: `unicode`
        """
        return self._thread

    @thread.setter # pylint: disable-msg=E1101
    def thread(self, thread): # pylint: disable-msg=E0202,E0102,C0111
        self._thread = unicode(thread)
        self._dirty = True

    def make_error_response(self, cond):
        """Create error response for any non-error message stanza.

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

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

        if self.stanza_type == "error":
            raise ValueError("Errors may not be generated in response"
                                                                " to errors")

        msg = Message(stanza_type = "error", from_jid = self.to_jid,
                        to_jid = self.from_jid, stanza_id = self.stanza_id,
                        error_cond = cond,
                        subject = self._subject, body = self._body,
                        thread = self._thread)

        if self._payload is None:
            self.decode_payload()
        for payload in self._payload:
            msg.add_payload(payload.copy())

        return msg class Presence(Stanza):
    """<presence /> stanza.

    """
    # pylint: disable-msg=R0902,R0904
    element_name = "presence"
    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,
                            show = None, status = None, priority = None):
        """Initialize a `Presence` object.

        :Parameters:
            - `element`: XML element
            - `from_jid`: sender JID.
            - `to_jid`: recipient JID.
            - `stanza_type`: staza type: one of: None, "available",
              "unavailable", "subscribe", "subscribed", "unsubscribe",
              "unsubscribed" or "error". "available" is automatically changed to
              None.
            - `stanza_id`: stanza id -- value of stanza's "id" attribute
            - `language`: default language for the stanza content
            - `show`: "show" field of presence stanza. One of: None, "away",
              "xa", "dnd", "chat".
            - `status`: descriptive text for the presence stanza.
            - `priority`: presence priority.
            - `error_cond`: error condition name. Ignored if `stanza_type` is
              not "error"
        :Types:
            - `element`: :etree:`ElementTree.Element`
            - `from_jid`: `JID`
            - `to_jid`: `JID`
            - `stanza_type`: `unicode`
            - `stanza_id`: `unicode`
            - `language`: `unicode`
            - `show`: `unicode`
            - `status`: `unicode`
            - `priority`: `int`
            - `error_cond`: `unicode`
        """
        # pylint: disable-msg=R0913
        self._show = None
        self._status = None
        self._priority = 0
        if element is None:
            element = "presence"
        elif not isinstance(element, ElementClass):
            raise TypeError("Couldn't make Presence from " + repr(element))

        if stanza_type is not None and stanza_type not in PRESENCE_TYPES:
            raise ValueError("Bad presence type")
        elif stanza_type == 'available':
            stanza_type = None

        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 != "presence":
            raise ValueError("The element is not <presence />")

        self._show_tag = self._ns_prefix + "show"
        self._status_tag = self._ns_prefix + "status"
        self._priority_tag = self._ns_prefix + "priority"

        if self._element is not None:
            self._decode_subelements()

        if show is not None:
            self.show = show
        if status is not None:
            self.status = status
        if priority is not None:
            self.priority = priority

    def _decode_subelements(self):
        """Decode the stanza subelements."""
        for child in self._element:
            if child.tag == self._show_tag:
                self._show = child.text
            elif child.tag == self._status_tag:
                self._status = child.text
            elif child.tag == self._priority_tag:
                try:
                    self._priority = int(child.text.strip())
                    if self._priority < -128 or self._priority > 127:
                        raise ValueError
                except ValueError:
                    raise BadRequestProtocolError(
                                            "Presence priority not an integer")

    def as_xml(self):
        """Return the XML stanza representation.

        Always return an independent copy of the stanza XML representation,
        which can be freely modified without affecting the stanza.

        :returntype: :etree:`ElementTree.Element`"""
        result = Stanza.as_xml(self)
        if self._show:
            child = ElementTree.SubElement(result, self._show_tag)
            child.text = self._show
        if self._status:
            child = ElementTree.SubElement(result, self._status_tag)
            child.text = self._status
        if self._priority:
            child = ElementTree.SubElement(result, self._priority_tag)
            child.text = unicode(self._priority)
        return result

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

        :returntype: `Presence`"""
        result = Presence(None, self.from_jid, self.to_jid,
                        self.stanza_type, self.stanza_id, self.error,
                        self._return_path(),
                        self._show, self._status, self._priority)
        if self._payload is None:
            self.decode_payload()
        for payload in self._payload:
            result.add_payload(payload.copy())
        return result

    @property
    def show(self): # pylint: disable-msg=E0202
        """Presence status type.

        :returntype: `unicode`
        """
        return self._show

    @show.setter # pylint: disable-msg=E1101
    def show(self, show): # pylint: disable-msg=E0202,E0102,C0111
        self._show = unicode(show)
        self._dirty = True

    @property
    def status(self): # pylint: disable-msg=E0202
        """Presence status message.

        :returntype: `unicode`
        """
        return self._status

    @status.setter # pylint: disable-msg=E1101
    def status(self, status): # pylint: disable-msg=E0202,E0102,C0111
        self._status = unicode(status)
        self._dirty = True

    @property
    def priority(self): # pylint: disable-msg=E0202
        """Presence priority.

        :returntype: `unicode`
        """
        return self._priority

    @priority.setter # pylint: disable-msg=E1101
    def priority(self, priority): # pylint: disable-msg=E0202,E0102,C0111
        priority = int(priority)
        if priority < -128 or priority > 127:
            raise ValueError("Priority must be in the (-128, 128) range")
        self._priority = priority
        self._dirty = True

    def make_accept_response(self):
        """Create "accept" response for the "subscribe" / "subscribed" /
        "unsubscribe" / "unsubscribed" presence stanza.

        :return: new stanza.
        :returntype: `Presence`
        """
        if self.stanza_type not in ("subscribe", "subscribed",
                                                "unsubscribe", "unsubscribed"):
            raise ValueError("Results may only be generated for 'subscribe',"
                "'subscribed','unsubscribe' or 'unsubscribed' presence")
        stanza = Presence(stanza_type = ACCEPT_RESPONSES[self.stanza_type],
                            from_jid = self.to_jid, to_jid = self.from_jid,
                                                    stanza_id = self.stanza_id)
        return stanza

    def make_deny_response(self):
        """Create "deny" response for the "subscribe" / "subscribed" /
        "unsubscribe" / "unsubscribed" presence stanza.

        :return: new presence stanza.
        :returntype: `Presence`
        """
        if self.stanza_type not in ("subscribe", "subscribed",
                                                "unsubscribe", "unsubscribed"):
            raise ValueError("Results may only be generated for 'subscribe',"
                "'subscribed','unsubscribe' or 'unsubscribed' presence")
        stanza = Presence(stanza_type = DENY_RESPONSES[self.stanza_type],
                            from_jid = self.to_jid, to_jid = self.from_jid,
                                                    stanza_id = self.stanza_id)
        return stanza

    def make_error_response(self, cond):
        """Create error response for the any non-error presence stanza.

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

        :return: new presence stanza.
        :returntype: `Presence`
        """

        if self.stanza_type == "error":
            raise ValueError("Errors may not be generated in response"
                                                                " to errors")

        stanza = Presence(stanza_type = "error", from_jid = self.from_jid,
                            to_jid = self.to_jid, stanza_id = self.stanza_id,
                            status = self._status, show = self._show,
                            priority = self._priority, error_cond = cond)

        if self._payload is None:
            self.decode_payload()

        for payload in self._payload:
            stanza.add_payload(payload)

        return stanza class Client(StanzaProcessor, TimeoutHandler, EventHandler):
    """Base class for an XMPP-IM client.

    Joins the `MainLoop`, `ClientStream` and some basic handlers together,
    so a client application needs only to add its handlers.

    :Ivariables:
        - `jid`: configured JID of the client (current full JID is avialable as
          ``self.stream.jid``).
        - `main_loop`: the main loop object
        - `settings`: configuration settings
        - `handlers`: stream and main loop handlers provided via the
          constructor
        - `stream`: the stream object when connected
        - `roster_client`: the roster interface object
        - `_ml_handlers`: list of handlers installed by this object to at the
          main loop
    :Types:
        - `jid`: `jid.JID`
        - `main_loop`: `main_loop.interfaces.MainLoop`
        - `settings`: `XMPPSettings`
        - `stream`: `clientstream.ClientStream`
        - `roster_client`: `RosterClient`
    """
    # pylint: disable=R0902,R0904
    def __init__(self, jid, handlers, settings = None, main_loop = None):
        """Initialize a Client object.

        :Parameters:
            - `jid`: user JID for the connection.
            - `settings`: client settings.
            - `main_loop`: Main event loop to attach to. If None, a loop
              will be created.
        :Types:
            - `jid`: `jid.JID`
            - `settings`: `settings.XMPPSettings`
            - `main_loop`: `main_loop.interfaces.MainLoop`
        """
        self._ml_handlers = []
        self.jid = jid
        self.settings = settings if settings else XMPPSettings()
        StanzaProcessor.__init__(self, self.settings[u"default_stanza_timeout"])
        self.handlers = handlers
        self._base_handlers = self.base_handlers_factory()
        self.roster_client = self.roster_client_factory()
        self._base_handlers += [self.roster_client]
        self._ml_handlers += list(handlers) + self._base_handlers + [self]
        _move_session_handler(self._ml_handlers)
        if main_loop is not None:
            self.main_loop = main_loop
            for handler in self._ml_handlers:
                self.main_loop.add_handler(handler)
        else:
            self.main_loop = main_loop_factory(settings, self._ml_handlers)
        self.stream = None

    def __del__(self):
        for handler in self._ml_handlers:
            self.main_loop.remove_handler(handler)
        self._ml_handlers = []

    @property
    def roster(self):
        """Current roster.

        Shortcut for ``self.roster_client.roster``.
        """
        with self.lock:
            if self.roster_client is not None:
                return self.roster_client.roster
            else:
                return None

    def connect(self):
        """Schedule a new XMPP c2s connection.
        """
        with self.lock:
            if self.stream:
                logger.debug("Closing the previously used stream.")
                self._close_stream()

            transport = TCPTransport(self.settings)

            addr = self.settings["server"]
            if addr:
                service = None
            else:
                addr = self.jid.domain
                service = self.settings["c2s_service"]

            transport.connect(addr, self.settings["c2s_port"], service)
            handlers = self._base_handlers[:]
            handlers += self.handlers + [self]
            self.clear_response_handlers()
            self.setup_stanza_handlers(handlers, "pre-auth")
            stream = ClientStream(self.jid, self, handlers, self.settings)
            stream.initiate(transport)
            self.main_loop.add_handler(transport)
            self.main_loop.add_handler(stream)
            self._ml_handlers += [transport, stream]
            self.stream = stream
            self.uplink = stream

    def disconnect(self):
        """Gracefully disconnect from the server."""
        with self.lock:
            if self.stream:
                if self.settings[u"initial_presence"]:
                    self.send(Presence(stanza_type = "unavailable"))
                self.stream.disconnect()

    def close_stream(self):
        """Close the stream immediately.
        """
        with self.lock:
            self._close_stream()

    def _close_stream(self):
        """Same as `close_stream` but with the `lock` acquired.
        """
        self.stream.close()
        if self.stream.transport in self._ml_handlers:
            self._ml_handlers.remove(self.stream.transport)
            self.main_loop.remove_handler(self.stream.transport)
        self.stream = None
        self.uplink = None

    def run(self, timeout = None):
        """Call the main loop.

        Convenience wrapper for ``self.main_loop.loop``
        """
        self.main_loop.loop(timeout)

    @event_handler(AuthenticatedEvent)
    def _stream_authenticated(self, event):
        """Handle the `AuthenticatedEvent`.
        """
        with self.lock:
            if event.stream != self.stream:
                return
            self.me = event.stream.me
            self.peer = event.stream.peer
            handlers = self._base_handlers[:]
            handlers += self.handlers + [self]
            self.setup_stanza_handlers(handlers, "post-auth")

    @event_handler(AuthorizedEvent)
    def _stream_authorized(self, event):
        """Handle the `AuthorizedEvent`.
        """
        with self.lock:
            if event.stream != self.stream:
                return
            self.me = event.stream.me
            self.peer = event.stream.peer
            presence = self.settings[u"initial_presence"]
            if presence:
                self.send(presence)

    @event_handler(DisconnectedEvent)
    def _stream_disconnected(self, event):
        """Handle stream disconnection event.
        """
        with self.lock:
            if event.stream != self.stream:
                return
            if self.stream is not None and event.stream == self.stream:
                if self.stream.transport in self._ml_handlers:
                    self._ml_handlers.remove(self.stream.transport)
                    self.main_loop.remove_handler(self.stream.transport)
                self.stream = None
                self.uplink = None

    @timeout_handler(1)
    def regular_tasks(self):
        """Do some housekeeping (cache expiration, timeout handling).

        This method should be called periodically from the application's
        main loop.

        :Return: suggested delay (in seconds) before the next call to this
                                                                    method.
        :Returntype: `int`
        """
        with self.lock:
            ret = self._iq_response_handlers.expire()
            if ret is None:
                return 1
            else:
                return min(1, ret)

    def base_handlers_factory(self):
        """Default base client handlers factory.

        Subclasses can provide different behaviour by overriding this.

        :Return: list of handlers
        """
        tls_handler = StreamTLSHandler(self.settings)
        sasl_handler = StreamSASLHandler(self.settings)
        session_handler = SessionHandler()
        binding_handler = ResourceBindingHandler(self.settings)
        return [tls_handler, sasl_handler, binding_handler, session_handler]

    def roster_client_factory(self):
        """Creates the `RosterClient` instance for the `roster_client`
        attribute.

        Subclasses can provide different behaviour by overriding this. The
        overriding method can return `None` if no roster client is needed.

        :Return: `RosterClient`
        """
        return RosterClient(self.settings) 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 Resolver:
class XMPPTransport:
class StanzaRoute:
class XMPPFeatureHandler:
class StanzaPayload:
class StreamFeatureHandled(object):
class StreamFeatureNotHandled(object):
class StreamFeatureHandler:
class _NO_CHANGE(object):
    def resolve_srv(self, domain, service, protocol, callback):
    def resolve_address(self, hostname, callback, allow_cname = True):
    def set_target(self, stream):
    def send_stream_head(self, stanza_namespace, stream_from, stream_to,
                        stream_id = None, version = u'1.0', language = None):
    def restart(self):
    def send_stream_tail(self):
    def send_element(self, element):
    def is_connected(self):
    def disconnect(self):
    def auth_properties(self):
    def send(self, stanza):
    def uplink_receive(self, stanza):
def feature_uri(uri):
    def decorator(class_):
def _iq_handler(iq_type, payload_class, payload_key, usage_restriction):
    def decorator(func):
def iq_get_stanza_handler(payload_class, payload_key = None,
                                            usage_restriction = "post-auth"):
def iq_set_stanza_handler(payload_class, payload_key = None,
                                            usage_restriction = "post-auth"):
def _stanza_handler(element_name, stanza_type, payload_class, payload_key,
                                                            usage_restriction):
    def decorator(func):
def message_stanza_handler(stanza_type = None, payload_class = None,
                        payload_key = None, usage_restriction = "post-auth"):
def presence_stanza_handler(stanza_type = None, payload_class = None,
                        payload_key = None, usage_restriction = "post-auth"):
    def from_xml(cls, element):
    def as_xml(self):
    def copy(self):
    def handler_key(self):
def payload_element_name(element_name):
    def decorator(klass):
    def __init__(self, feature_name, mandatory = False):
    def __repr__(self):
    def __str__(self):
    def __init__(self, feature_name, mandatory = False):
    def __repr__(self):
    def __str__(self):
    def handle_stream_features(self, stream, features):
    def make_stream_features(self, stream, features):
def stream_element_handler(element_name, usage_restriction = None):
    def decorator(func):
    def __str__(self):
    def __repr__(self):
NO_CHANGE = _NO_CHANGE() class AuthorizedEvent(StreamEvent):
    """Event raised after stream authentication and authorization is complete.
    Usually it happens after SASL authentication and XMPP resource binding.

    Default action: none

    :Ivariables:
        - `authorized_jid`: JID just authorized to use the stream
    :Types:
        - `authorized_jid`: `pyxmpp2.jid.JID`
    """
    def __init__(self, authorized_jid):
        self.authorized_jid = authorized_jid
    def __unicode__(self):
        return u"Authorized: {0}".format(self.authorized_jid) class DisconnectedEvent(StreamEvent):
    """Emitted when the stream is disconnected. No more stanzas will
    be received and no more stanzas can be sent via this stream.

    :Ivariables:
        - `peer`: peer name
    :Types:
        - `peer`: `pyxmpp2.jid.JID`
    """
    def __init__(self, peer):
        self.peer = peer
    def __unicode__(self):
        return u"Disconnected from {0}".format(self.peer) class XMPPFeatureHandler:
    """Base class for objects handling incoming stanzas.

    The subclasses should provide methods decorated with one of:

      - `iq_get_stanza_handler`: for methods handling ``<iq type="get"/>``
        stanzas.
      - `iq_set_stanza_handler`: for methods handling ``<iq type="set"/>``
        stanzas.
      - `message_stanza_handler`: for methods handling ``<message />`` stanzas
      - `presence_stanza_handler`: for methods handling ``<presence />``
        stanzas

    :Ivariables:
        - `stanza_processor`: a stanza processor where this object was
          registered most recently (injected by `StanzaProcessor`)
    :Types:
        - `stanza_processor`: `StanzaProcessor`
    """
    # pylint: disable-msg=W0232,R0903
    __metaclass__ = ABCMeta
    stanza_processor = None def presence_stanza_handler(stanza_type = None, payload_class = None,
                        payload_key = None, usage_restriction = "post-auth"):
    """Method decorator generator for decorating <presence/>
    stanza handler methods in `XMPPFeatureHandler` subclasses.

    :Parameters:
        - `payload_class`: payload class expected
        - `stanza_type`: expected value of the 'type' attribute of the stanza.
        - `payload_key`: payload class specific filtering key
        - `usage_restriction`: optional usage restriction: "pre-auth" or
          "post-auth"
    :Types:
        - `payload_class`: subclass of `StanzaPayload`
        - `stanza_type`: `unicode`
        - `usage_restriction`: `unicode`
    """
    return _stanza_handler("presence", stanza_type, payload_class, payload_key,
                                                            usage_restriction) def message_stanza_handler(stanza_type = None, payload_class = None,
                        payload_key = None, usage_restriction = "post-auth"):
    """Method decorator generator for decorating <message/>
    stanza handler methods in `XMPPFeatureHandler` subclasses.

    :Parameters:
        - `payload_class`: payload class expected
        - `stanza_type`: expected value of the 'type' attribute of the stanza.
          `None` means all types except 'error'
        - `payload_key`: payload class specific filtering key
        - `usage_restriction`: optional usage restriction: "pre-auth" or
          "post-auth"
    :Types:
        - `payload_class`: subclass of `StanzaPayload`
        - `stanza_type`: `unicode`
        - `usage_restriction`: `unicode`
    """
    if stanza_type is None:
        stanza_type = "normal"
    return _stanza_handler("message", stanza_type, payload_class, payload_key,
                                                            usage_restriction) 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
import sys
import logging
import argparse
from getpass import getpass
from pyxmpp2.jid import JID
from pyxmpp2.message import Message
from pyxmpp2.presence import Presence
from pyxmpp2.client import Client
from pyxmpp2.settings import XMPPSettings
from pyxmpp2.interfaces import EventHandler, event_handler, QUIT
from pyxmpp2.streamevents import AuthorizedEvent, DisconnectedEvent
from pyxmpp2.interfaces import XMPPFeatureHandler
from pyxmpp2.interfaces import presence_stanza_handler, message_stanza_handler
from pyxmpp2.ext.version import VersionProvider
#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
An 'echo bot' – simple client that just confirms any presence subscriptions
and echoes incoming messages.
"""



class EchoBot(EventHandler, XMPPFeatureHandler):
    """Echo Bot implementation."""
    def __init__(self, my_jid, settings):
        version_provider = VersionProvider(settings)
        self.client = Client(my_jid, [self, version_provider], settings)

    def run(self):
        """Request client connection and start the main loop."""
        self.client.connect()
        self.client.run()

    def disconnect(self):
        """Request disconnection and let the main loop run for a 2 more
        seconds for graceful disconnection."""
        self.client.disconnect()
        self.client.run(timeout = 2)

    @presence_stanza_handler("subscribe")
    def handle_presence_subscribe(self, stanza):
        logging.info(u"{0} requested presence subscription"
                                                    .format(stanza.from_jid))
        presence = Presence(to_jid = stanza.from_jid.bare(),
                                                    stanza_type = "subscribe")
        return [stanza.make_accept_response(), presence]

    @presence_stanza_handler("subscribed")
    def handle_presence_subscribed(self, stanza):
        logging.info(u"{0!r} accepted our subscription request"
                                                    .format(stanza.from_jid))
        return True

    @presence_stanza_handler("unsubscribe")
    def handle_presence_unsubscribe(self, stanza):
        logging.info(u"{0} canceled presence subscription"
                                                    .format(stanza.from_jid))
        presence = Presence(to_jid = stanza.from_jid.bare(),
                                                    stanza_type = "unsubscribe")
        return [stanza.make_accept_response(), presence]

    @presence_stanza_handler("unsubscribed")
    def handle_presence_unsubscribed(self, stanza):
        logging.info(u"{0!r} acknowledged our subscrption cancelation"
                                                    .format(stanza.from_jid))
        return True

    @message_stanza_handler()
    def handle_message(self, stanza):
        """Echo every non-error ``<message/>`` stanza.
        
        Add "Re: " to subject, if any.
        """
        if stanza.subject:
            subject = u"Re: " + stanza.subject
        else:
            subject = None
        msg = Message(stanza_type = stanza.stanza_type,
                        from_jid = stanza.to_jid, to_jid = stanza.from_jid,
                        subject = subject, body = stanza.body,
                        thread = stanza.thread)
        return msg

    @event_handler(DisconnectedEvent)
    def handle_disconnected(self, event):
        """Quit the main loop upon disconnection."""
        return QUIT
    
    @event_handler()
    def handle_all(self, event):
        """Log all events."""
        logging.info(u"-- {0}".format(event))

def main():
    """Parse the command-line arguments and run the bot."""
    parser = argparse.ArgumentParser(description = 'XMPP echo bot',
                                    parents = [XMPPSettings.get_arg_parser()])
    parser.add_argument('jid', metavar = 'JID', 
                                        help = 'The bot JID')
    parser.add_argument('--debug',
                        action = 'store_const', dest = 'log_level',
                        const = logging.DEBUG, default = logging.INFO,
                        help = 'Print debug messages')
    parser.add_argument('--quiet', const = logging.ERROR,
                        action = 'store_const', dest = 'log_level',
                        help = 'Print only error messages')
    parser.add_argument('--trace', action = 'store_true',
                        help = 'Print XML data sent and received')

    args = parser.parse_args()

    settings = XMPPSettings({