# Path: ejtp/client.py
# class Client(object):
#     def __init__(self, router, interface, encryptor_cache = None, make_jack = True):
#         '''
#             encryptor_get should be a function that accepts an argument "iface"
#             and returns an encryptor prototype (2-element list, like ["rotate", 5]).
#         '''
#         self.interface = (interface and py_address(interface)) or interface
#         self.router = router
#         if hasattr(self.router, "_loadclient"):
#             self.router._loadclient(self)
#         self.encryptor_cache = encryptor_cache or identity.IdentityCache()
#         if make_jack:
#             jacks.make(router, interface)
# 
#     def send(self, msg):
#         # Send frame to router
#         self.router.recv(msg)
# 
#     def relay(self, msg):
#         '''
#         Relay unwrapped frame. For basic clients, this is just self.send, but
#         it's a very convenient spot to override stuff in more complex subclasses.
#         '''
#         self.send(msg)
# 
#     def route(self, msg):
#         # Recieve frame from router (will be type 'r' or 's', which contains message)
#         logger.debug("Client routing frame: %s", repr(msg))
#         if isinstance(msg, frame.address.ReceiverCategory):
#             if msg.address != self.interface:
#                 self.relay(msg)
#             else:
#                 self.route( msg.unpack(self.encryptor_cache) )
#         elif isinstance(msg, frame.address.SenderCategory):
#             self.route( msg.unpack(self.encryptor_cache) )
#         elif isinstance(msg, frame.json.JSONFrame):
#             self.rcv_callback(msg, self)
#         else:
#             raise TypeError("Unknown frame type", msg)
# 
#     def rcv_callback(self, msg, client_obj):
#         logger.info("Client %r recieved from %r: %r", client_obj.interface, msg.sender, msg)
# 
#     def write(self, addr, txt, wrap_sender=True):
#         # Write and send a frame to addr
#         self.owrite([addr], txt, wrap_sender)
# 
#     def owrite(self, hoplist, msg, wrap_sender=True):
#         # Write a frame and send through a list of addresses
#         # The "o" is for onion routing
#         if wrap_sender:
#             msg = self.wrap_sender(msg)
#         # Onion routing magic
#         for address in reversed(hoplist):
#             ident = self.encryptor_cache[str_address(address)]
#             msg = frame.encrypted.construct(ident, msg.content)
#         self.send(msg)
# 
#     def owrite_json(self, hoplist, data, wrap_sender=True):
#         msg = frame.json.construct(data)
#         self.owrite(hoplist, msg, wrap_sender)
# 
#     def write_json(self, addr, data, wrap_sender=True):
#         self.owrite_json([addr], data, wrap_sender)
# 
#     def wrap_sender(self, msg):
#         # Encapsulate a message within a sender frame
#         return frame.signed.construct(self.identity, msg.content)
# 
#     @property
#     def identity(self):
#         return self.encryptor_cache[self.interface]
# 
#     # Encryption
# 
#     def encryptor_get(self, address):
#         address = str_address(address)
#         return make(self.encryptor_cache[address].encryptor)
# 
#     def encryptor_set(self, address, encryptor):
#         address = py_address(address)
#         if not address in self.encryptor_cache:
#             dummy_ident = identity.Identity(None, encryptor, address)
#             self.encryptor_cache.update_ident(dummy_ident)
#         else:
#             self.encryptor_cache[address].encryptor = list(encryptor)
# 
#     def sign(self, obj):
#         '''
#         Make a signature with this client's interface.
#         '''
#         strdata = hashfunc(strict(obj))
#         return self.encryptor_get(self.interface).flip().encrypt(strdata)
# 
#     def sig_verify(self, obj, signer, sig):
#         '''
#         Verify a signature.
#         '''
#         strdata = hashfunc(strict(obj))
#         return self.encryptor_get(signer).flip().decrypt(sig).toString() == strdata
# 
# Path: ejtp/util/hasher.py
# @StringDecorator()
# @StringDecorator(args=False, ret=True, strict=True)
# def strict(obj=None):
#     ''' Convert an object into a strict JSON string '''
#     if isinstance(obj, bool) or obj==None or isinstance(obj, int):
#         return json.dumps(obj)
#     if isinstance(obj, String):
#         return json.dumps(obj.export())
#     if isinstance(obj, RawData):
#         obj = tuple(obj)
#     if isinstance(obj, list) or isinstance(obj, tuple):
#         return "[%s]" % ",".join([strict(x).export() for x in obj])
#     if isinstance(obj, dict):
#         strdict = {}
#         for key in obj:
#             strdict[key] = obj[key]
#         keys = sorted(strdict.keys())
#         return "{%s}" % ",".join([strict(key).export()+":"+strict(strdict[key]).export() for key in keys])


import re
import logging
from persei import String
from ejtp.client import Client
from ejtp.util.hasher import strict
from ejtp.address import *
        try:
            interface = self.get_args(data, 'interface')[0]
        except KeyError:
            return # Error already handled

        # Find and destroy client holding that interface
        if self.router.client(interface) != None:
            self.router.kill_client(interface)
        else:
            return self.error(self.controller, 503, data)

        self.success(data)

    def success(self, data):
        logger.info("SUCCESFUL COMMAND %s", strict(data).export())
        self.write_json(self.controller, {
            'type':'ejtpd-success',
            'command': data,
        })

    def error(self, target, code, details=None):
        msg = errorcodes[code]
        logger.error("CLIENT ERROR #%d %s %s", code, msg, (details and strict(details) or String()).export())
        self.write_json(target, {
            'type':'ejtpd-error',
            'code': code,
            'msg':  msg,
            'details': details,
        })


# Path: ejtp/client.py
# class Client(object):
#     def __init__(self, router, interface, encryptor_cache = None, make_jack = True):
#         '''
#             encryptor_get should be a function that accepts an argument "iface"
#             and returns an encryptor prototype (2-element list, like ["rotate", 5]).
#         '''
#         self.interface = (interface and py_address(interface)) or interface
#         self.router = router
#         if hasattr(self.router, "_loadclient"):
#             self.router._loadclient(self)
#         self.encryptor_cache = encryptor_cache or identity.IdentityCache()
#         if make_jack:
#             jacks.make(router, interface)
# 
#     def send(self, msg):
#         # Send frame to router
#         self.router.recv(msg)
# 
#     def relay(self, msg):
#         '''
#         Relay unwrapped frame. For basic clients, this is just self.send, but
#         it's a very convenient spot to override stuff in more complex subclasses.
#         '''
#         self.send(msg)
# 
#     def route(self, msg):
#         # Recieve frame from router (will be type 'r' or 's', which contains message)
#         logger.debug("Client routing frame: %s", repr(msg))
#         if isinstance(msg, frame.address.ReceiverCategory):
#             if msg.address != self.interface:
#                 self.relay(msg)
#             else:
#                 self.route( msg.unpack(self.encryptor_cache) )
#         elif isinstance(msg, frame.address.SenderCategory):
#             self.route( msg.unpack(self.encryptor_cache) )
#         elif isinstance(msg, frame.json.JSONFrame):
#             self.rcv_callback(msg, self)
#         else:
#             raise TypeError("Unknown frame type", msg)
# 
#     def rcv_callback(self, msg, client_obj):
#         logger.info("Client %r recieved from %r: %r", client_obj.interface, msg.sender, msg)
# 
#     def write(self, addr, txt, wrap_sender=True):
#         # Write and send a frame to addr
#         self.owrite([addr], txt, wrap_sender)
# 
#     def owrite(self, hoplist, msg, wrap_sender=True):
#         # Write a frame and send through a list of addresses
#         # The "o" is for onion routing
#         if wrap_sender:
#             msg = self.wrap_sender(msg)
#         # Onion routing magic
#         for address in reversed(hoplist):
#             ident = self.encryptor_cache[str_address(address)]
#             msg = frame.encrypted.construct(ident, msg.content)
#         self.send(msg)
# 
#     def owrite_json(self, hoplist, data, wrap_sender=True):
#         msg = frame.json.construct(data)
#         self.owrite(hoplist, msg, wrap_sender)
# 
#     def write_json(self, addr, data, wrap_sender=True):
#         self.owrite_json([addr], data, wrap_sender)
# 
#     def wrap_sender(self, msg):
#         # Encapsulate a message within a sender frame
#         return frame.signed.construct(self.identity, msg.content)
# 
#     @property
#     def identity(self):
#         return self.encryptor_cache[self.interface]
# 
#     # Encryption
# 
#     def encryptor_get(self, address):
#         address = str_address(address)
#         return make(self.encryptor_cache[address].encryptor)
# 
#     def encryptor_set(self, address, encryptor):
#         address = py_address(address)
#         if not address in self.encryptor_cache:
#             dummy_ident = identity.Identity(None, encryptor, address)
#             self.encryptor_cache.update_ident(dummy_ident)
#         else:
#             self.encryptor_cache[address].encryptor = list(encryptor)
# 
#     def sign(self, obj):
#         '''
#         Make a signature with this client's interface.
#         '''
#         strdata = hashfunc(strict(obj))
#         return self.encryptor_get(self.interface).flip().encrypt(strdata)
# 
#     def sig_verify(self, obj, signer, sig):
#         '''
#         Verify a signature.
#         '''
#         strdata = hashfunc(strict(obj))
#         return self.encryptor_get(signer).flip().decrypt(sig).toString() == strdata
# 
# Path: ejtp/util/hasher.py
# @StringDecorator()
# @StringDecorator(args=False, ret=True, strict=True)
# def strict(obj=None):
#     ''' Convert an object into a strict JSON string '''
#     if isinstance(obj, bool) or obj==None or isinstance(obj, int):
#         return json.dumps(obj)
#     if isinstance(obj, String):
#         return json.dumps(obj.export())
#     if isinstance(obj, RawData):
#         obj = tuple(obj)
#     if isinstance(obj, list) or isinstance(obj, tuple):
#         return "[%s]" % ",".join([strict(x).export() for x in obj])
#     if isinstance(obj, dict):
#         strdict = {}
#         for key in obj:
#             strdict[key] = obj[key]
#         keys = sorted(strdict.keys())
#         return "{%s}" % ",".join([strict(key).export()+":"+strict(strdict[key]).export() for key in keys])


# Path: ejtp/applications/daemon.py
import re
import logging
from persei import String
from ejtp.client import Client
from ejtp.util.hasher import strict
from ejtp.address import *

        try:
            interface = self.get_args(data, 'interface')[0]
        except KeyError:
            return # Error already handled

        # Find and destroy client holding that interface
        if self.router.client(interface) != None:
            self.router.kill_client(interface)
        else:
            return self.error(self.controller, 503, data)

        self.success(data)

    def success(self, data):
        logger.info("SUCCESFUL COMMAND %s", strict(data).export())
        self.write_json(self.controller, {
            'type':'ejtpd-success',
            'command': data,
        })

    def error(self, target, code, details=None):
        msg = errorcodes[code]
        logger.error("CLIENT ERROR #%d %s %s", code, msg, (details and strict(details) or String()).export())
        self.write_json(target, {
            'type':'ejtpd-error',
            'code': code,
            'msg':  msg,
            'details': details,
        })


class ControllerClient(Client):