# Path: txtorcon/interface.py
# class ICircuitContainer(Interface):
#     """
#     An interface that contains a bunch of Circuit objects and can look
#     them up by id.
#     """
# 
#     def find_circuit(circ_id):
#         ":return: a circuit for the cird_id, or exception."
# 
#     def close_circuit(circuit, **kwargs):
#         """
#         Close a circuit.
#         :return: a Deferred which callbacks when the closing process
#         is started (not necessarily finished inside Tor).
#         """
# 
#     # FIXME do we need an IStreamContainer that Stream instances get?
#     # (Currently, they get an ICircuitContainer...)
#     def close_stream(stream, **kwargs):
#         """
#         Close a stream.
#         :return: a Deferred which callbacks when the closing process
#         is started (not necessarily finished inside Tor).
#         """
# 
# class IStreamListener(Interface):
#     """
#     Notifications about changes to a :class:`txtorcon.Stream`.
# 
#     If you wish for your listener to be added to *all* new streams,
#     see :meth:`txtorcon.TorState.add_stream_listener`.
#     """
# 
#     def stream_new(stream):
#         "a new stream has been created"
# 
#     def stream_succeeded(stream):
#         "stream has succeeded"
# 
#     def stream_attach(stream, circuit):
#         "the stream has been attached to a circuit"
# 
#     def stream_detach(stream, **kw):
#         """
#         the stream has been detached from its circuit
# 
#         :param kw:
#             provides any flags for this event, which will include at
#             least REASON (but may include anything). See control-spec.
#         """
# 
#     def stream_closed(stream, **kw):
#         """
#         stream has been closed (won't be in controller's list anymore).
# 
#         :param kw:
#             provides any flags for this event, which will include at
#             least REASON (but may include anything). See control-spec.
#         """
# 
#     def stream_failed(stream, **kw):
#         """
#         stream failed for some reason (won't be in controller's list anymore).
# 
#         :param kw:
#             a dict of all the flags for the stream failure; see
#             control-spec but these will include REASON and sometimes
#             REMOTE_REASON (if the remote Tor closed the
#             connection). Both an all-uppercase and all-lowercase
#             version of each keyword is supplied (by the library; Tor
#             provides all-uppercase only). Others may include
#             BUILD_FLAGS, PURPOSE, HS_STATE, REND_QUERY, TIME_CREATED
#             (or anything else).
#         """
# 
# Path: txtorcon/util.py
# def find_keywords(args, key_filter=lambda x: not x.startswith("$")):
#     """
#     This splits up strings like name=value, foo=bar into a dict. Does NOT deal
#     with quotes in value (e.g. key="value with space" will not work
# 
#     By default, note that it takes OUT any key which starts with $ (i.e. a
#     single dollar sign) since for many use-cases the way Tor encodes nodes
#     with "$hash=name" looks like a keyword argument (but it isn't). If you
#     don't want this, override the "key_filter" argument to this method.
# 
#     :param args: a list of strings, each with one key=value pair
# 
#     :return:
#         a dict of key->value (both strings) of all name=value type
#         keywords found in args.
#     """
#     filtered = [x for x in args if '=' in x and key_filter(x.split('=')[0])]
#     return dict(x.split('=', 1) for x in filtered)
# 
# def maybe_ip_addr(addr):
#     """
#     Tries to return an IPAddress, otherwise returns a string.
# 
#     TODO consider explicitly checking for .exit or .onion at the end?
#     """
# 
#     if six.PY2 and isinstance(addr, str):
#         addr = unicode(addr)  # noqa
#     try:
#         return ipaddress.ip_address(addr)
#     except ValueError:
#         pass
#     return str(addr)


from twisted.python import log
from twisted.internet import defer
from txtorcon.interface import ICircuitContainer, IStreamListener
from txtorcon.util import find_keywords, maybe_ip_addr
            if self.source_addr != '(Tor_internal)':
                self.source_addr = maybe_ip_addr(self.source_addr)
            self.source_port = int(kw['SOURCE_ADDR'][last_colon + 1:])

        self.state = args[1]
        # XXX why not using the state-machine stuff? ;)
        if self.state in ['NEW', 'NEWRESOLVE', 'SUCCEEDED']:
            if self.target_host is None:
                last_colon = args[3].rfind(':')
                self.target_host = args[3][:last_colon]
                self.target_port = int(args[3][last_colon + 1:])
                # target_host is often an IP address (newer tors? did
                # this change?) so we attempt to look it up in our
                # AddrMap and make it a name no matter what.
                if self._addrmap:
                    try:
                        h = self._addrmap.find(self.target_host)
                        self.target_host = h.name
                    except KeyError:
                        pass

            self.target_port = int(self.target_port)
            if self.state == 'NEW':
                if self.circuit is not None:
                    log.err(RuntimeError("Weird: circuit valid in NEW"))
                self._notify('stream_new', self)
            else:
                self._notify('stream_succeeded', self)

        elif self.state == 'REMAP':

# Path: txtorcon/interface.py
# class ICircuitContainer(Interface):
#     """
#     An interface that contains a bunch of Circuit objects and can look
#     them up by id.
#     """
# 
#     def find_circuit(circ_id):
#         ":return: a circuit for the cird_id, or exception."
# 
#     def close_circuit(circuit, **kwargs):
#         """
#         Close a circuit.
#         :return: a Deferred which callbacks when the closing process
#         is started (not necessarily finished inside Tor).
#         """
# 
#     # FIXME do we need an IStreamContainer that Stream instances get?
#     # (Currently, they get an ICircuitContainer...)
#     def close_stream(stream, **kwargs):
#         """
#         Close a stream.
#         :return: a Deferred which callbacks when the closing process
#         is started (not necessarily finished inside Tor).
#         """
# 
# class IStreamListener(Interface):
#     """
#     Notifications about changes to a :class:`txtorcon.Stream`.
# 
#     If you wish for your listener to be added to *all* new streams,
#     see :meth:`txtorcon.TorState.add_stream_listener`.
#     """
# 
#     def stream_new(stream):
#         "a new stream has been created"
# 
#     def stream_succeeded(stream):
#         "stream has succeeded"
# 
#     def stream_attach(stream, circuit):
#         "the stream has been attached to a circuit"
# 
#     def stream_detach(stream, **kw):
#         """
#         the stream has been detached from its circuit
# 
#         :param kw:
#             provides any flags for this event, which will include at
#             least REASON (but may include anything). See control-spec.
#         """
# 
#     def stream_closed(stream, **kw):
#         """
#         stream has been closed (won't be in controller's list anymore).
# 
#         :param kw:
#             provides any flags for this event, which will include at
#             least REASON (but may include anything). See control-spec.
#         """
# 
#     def stream_failed(stream, **kw):
#         """
#         stream failed for some reason (won't be in controller's list anymore).
# 
#         :param kw:
#             a dict of all the flags for the stream failure; see
#             control-spec but these will include REASON and sometimes
#             REMOTE_REASON (if the remote Tor closed the
#             connection). Both an all-uppercase and all-lowercase
#             version of each keyword is supplied (by the library; Tor
#             provides all-uppercase only). Others may include
#             BUILD_FLAGS, PURPOSE, HS_STATE, REND_QUERY, TIME_CREATED
#             (or anything else).
#         """
# 
# Path: txtorcon/util.py
# def find_keywords(args, key_filter=lambda x: not x.startswith("$")):
#     """
#     This splits up strings like name=value, foo=bar into a dict. Does NOT deal
#     with quotes in value (e.g. key="value with space" will not work
# 
#     By default, note that it takes OUT any key which starts with $ (i.e. a
#     single dollar sign) since for many use-cases the way Tor encodes nodes
#     with "$hash=name" looks like a keyword argument (but it isn't). If you
#     don't want this, override the "key_filter" argument to this method.
# 
#     :param args: a list of strings, each with one key=value pair
# 
#     :return:
#         a dict of key->value (both strings) of all name=value type
#         keywords found in args.
#     """
#     filtered = [x for x in args if '=' in x and key_filter(x.split('=')[0])]
#     return dict(x.split('=', 1) for x in filtered)
# 
# def maybe_ip_addr(addr):
#     """
#     Tries to return an IPAddress, otherwise returns a string.
# 
#     TODO consider explicitly checking for .exit or .onion at the end?
#     """
# 
#     if six.PY2 and isinstance(addr, str):
#         addr = unicode(addr)  # noqa
#     try:
#         return ipaddress.ip_address(addr)
#     except ValueError:
#         pass
#     return str(addr)


# Path: txtorcon/stream.py
from twisted.python import log
from twisted.internet import defer
from txtorcon.interface import ICircuitContainer, IStreamListener
from txtorcon.util import find_keywords, maybe_ip_addr

            if self.source_addr != '(Tor_internal)':
                self.source_addr = maybe_ip_addr(self.source_addr)
            self.source_port = int(kw['SOURCE_ADDR'][last_colon + 1:])

        self.state = args[1]
        # XXX why not using the state-machine stuff? ;)
        if self.state in ['NEW', 'NEWRESOLVE', 'SUCCEEDED']:
            if self.target_host is None:
                last_colon = args[3].rfind(':')
                self.target_host = args[3][:last_colon]
                self.target_port = int(args[3][last_colon + 1:])
                # target_host is often an IP address (newer tors? did
                # this change?) so we attempt to look it up in our
                # AddrMap and make it a name no matter what.
                if self._addrmap:
                    try:
                        h = self._addrmap.find(self.target_host)
                        self.target_host = h.name
                    except KeyError:
                        pass

            self.target_port = int(self.target_port)
            if self.state == 'NEW':
                if self.circuit is not None:
                    log.err(RuntimeError("Weird: circuit valid in NEW"))
                self._notify('stream_new', self)
            else:
                self._notify('stream_succeeded', self)

        elif self.state == 'REMAP':

            self.target_addr = maybe_ip_addr(args[3][:args[3].rfind(':')])