import os
import urllib.parse

import eventlet
import eventlet.green.socket
# eventlet.monkey_patch()
import eventlet.websocket
import eventlet.wsgi
import wspty.pipe
from flask import Flask, request, redirect
from wspty.EchoTerminal import EchoTerminal
from wspty.EncodedTerminal import EncodedTerminal
from wspty.WebsocketBinding import WebsocketBinding

import config


def make_app():
    app = Flask(__name__)
    app.static_folder = get_static_folder()
    print("Serving static files from: " + app.static_folder)

    @app.route('/')
    def index():
        newurl = b'/static/index.html'
        if request.query_string:
            newurl = newurl + b'?' + request.query_string
        return redirect(newurl)
    return app


def parse_query(qstr):
    return {k: v[0] for k, v in urllib.parse.parse_qs(qstr).items()}


def debug(s):
    app.logger.debug(s)


class TerminalFactory:
    def __init__(self, args_dict, allow_unsafe=False):
        self.kind = args_dict['kind']
        self.hostname = args_dict.get('hostname', 'localhost')
        self.port = int(args_dict.get('port', '22'))
        self.username = args_dict.get('username')
        self.password = args_dict.get('password')
        self.term = args_dict.get('term')
        self.encoding = args_dict.get('encoding', 'utf8')
        self.allow_unsafe = allow_unsafe

    def create_binary(self):
        if self.kind == 'ssh':
            from wspty.SshTerminal import SshTerminal
            return SshTerminal(
                self.hostname, self.port, self.username, self.password, self.term
            )
        if self.kind == 'raw':
            from wspty.SocketTerminal import SocketTerminal
            sock = eventlet.green.socket.socket()
            ip = eventlet.green.socket.gethostbyname(self.hostname)
            sock.connect((ip, self.port))
            return SocketTerminal(sock)
        if self.kind == 'echo':
            return EchoTerminal()
        if self.kind == 'prompt':
            if not self.allow_unsafe:
                raise Exception("kind {} is disabled".format(self.kind))
            from wspty import PromptTerminal
            return PromptTerminal.os_terminal()
        raise NotImplemented('kind: {}'.format(self.kind))

    def create(self):
        return EncodedTerminal(self.create_binary(), self.encoding)


class DefaultRootApp:
    def __init__(self):
        self._app_handle_wssh = eventlet.websocket.WebSocketWSGI(self.handle_wssh)
        self.allow_unsafe = False

    def handle_wssh(self, ws):
        debug('Creating terminal with remote {remote}'.format(
            remote=ws.environ.get('REMOTE_ADDR'),
        ))

        ws_binding = WebsocketBinding(ws)
        query = parse_query(ws.environ.get('QUERY_STRING', ''))
        terminal = None
        try:
            kind, terminal = self.create_terminal(query)
            ws_binding.send('Connected to %s\r\n' % (kind,))
            wspty.pipe.pipe(ws_binding, terminal)
        except BaseException as e:
            ws_binding.send_error(e)
            raise
        finally:
            if terminal:
                terminal.close()

        debug('Closing terminal normally with remote {remote}'.format(
            remote=ws.environ.get('REMOTE_ADDR'),
        ))
        return ''

    def create_terminal(self, obj):
        factory = TerminalFactory(obj, self.allow_unsafe)
        return factory.kind, factory.create()

    def handler(self, env, *args):
        route = env["PATH_INFO"]
        if route == '/wssh':
            return self._app_handle_wssh(env, *args)
        else:
            return app(env, *args)


def make_parser():
    import argparse
    parser = argparse.ArgumentParser(description='Websocket Terminal server')
    parser.add_argument('-l', '--listen', default='', help='Listen on interface (default all)')
    parser.add_argument('-p', '--port', default=5002, type=int, help='Listen on port')
    parser.add_argument('--unsafe', action='store_true', help='Allow unauthenticated connections to local machine')
    return parser


def start(interface, port, root_app_handler):
    conn = (interface, port)
    listener = eventlet.listen(conn)
    print('listening on {0}:{1}'.format(*conn))
    try:
        eventlet.wsgi.server(listener, root_app_handler)
    except KeyboardInterrupt:
        pass


def start_default(interface, port, allow_unsafe=False, root_app_cls=DefaultRootApp):
    root_app = root_app_cls()
    root_app.allow_unsafe = allow_unsafe
    start(interface, port, root_app.handler)


def main():
    args = make_parser().parse_args()
    start_default(args.listen, args.port, args.unsafe)


def get_static_folder():
    path_root = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../client')
    path_root = os.path.join(path_root, config.CLIENT_DIR)
    return os.path.abspath(path_root)


app = make_app()

if __name__ == '__main__':
    main()

[((19, 10, 19, 25), 'flask.Flask', 'Flask', ({(19, 16, 19, 24): '__name__'}, {}), '(__name__)', False, 'from flask import Flask, request, redirect\n'), ((119, 13, 119, 77), 'argparse.ArgumentParser', 'argparse.ArgumentParser', (), '', False, 'import argparse\n'), ((128, 15, 128, 36), 'eventlet.listen', 'eventlet.listen', ({(128, 31, 128, 35): 'conn'}, {}), '(conn)', False, 'import eventlet\n'), ((149, 16, 149, 58), 'os.path.join', 'os.path.join', ({(149, 29, 149, 38): 'path_root', (149, 40, 149, 57): 'config.CLIENT_DIR'}, {}), '(path_root, config.CLIENT_DIR)', False, 'import os\n'), ((150, 11, 150, 37), 'os.path.abspath', 'os.path.abspath', ({(150, 27, 150, 36): 'path_root'}, {}), '(path_root)', False, 'import os\n'), ((28, 15, 28, 31), 'flask.redirect', 'redirect', ({(28, 24, 28, 30): 'newurl'}, {}), '(newurl)', False, 'from flask import Flask, request, redirect\n'), ((78, 32, 78, 82), 'eventlet.websocket.WebSocketWSGI', 'eventlet.websocket.WebSocketWSGI', ({(78, 65, 78, 81): 'self.handle_wssh'}, {}), '(self.handle_wssh)', False, 'import eventlet\n'), ((86, 21, 86, 41), 'wspty.WebsocketBinding.WebsocketBinding', 'WebsocketBinding', ({(86, 38, 86, 40): 'ws'}, {}), '(ws)', False, 'from wspty.WebsocketBinding import WebsocketBinding\n'), ((131, 8, 131, 56), 'eventlet.wsgi.server', 'eventlet.wsgi.server', ({(131, 29, 131, 37): 'listener', (131, 39, 131, 55): 'root_app_handler'}, {}), '(listener, root_app_handler)', False, 'import eventlet\n'), ((54, 19, 56, 13), 'wspty.SshTerminal.SshTerminal', 'SshTerminal', ({(55, 16, 55, 29): 'self.hostname', (55, 31, 55, 40): 'self.port', (55, 42, 55, 55): 'self.username', (55, 57, 55, 70): 'self.password', (55, 72, 55, 81): 'self.term'}, {}), '(self.hostname, self.port, self.username, self.password, self.term)', False, 'from wspty.SshTerminal import SshTerminal\n'), ((59, 19, 59, 49), 'eventlet.green.socket.socket', 'eventlet.green.socket.socket', ({}, {}), '()', False, 'import eventlet\n'), ((60, 17, 60, 67), 'eventlet.green.socket.gethostbyname', 'eventlet.green.socket.gethostbyname', ({(60, 53, 60, 66): 'self.hostname'}, {}), '(self.hostname)', False, 'import eventlet\n'), ((62, 19, 62, 39), 'wspty.SocketTerminal.SocketTerminal', 'SocketTerminal', ({(62, 34, 62, 38): 'sock'}, {}), '(sock)', False, 'from wspty.SocketTerminal import SocketTerminal\n'), ((64, 19, 64, 33), 'wspty.EchoTerminal.EchoTerminal', 'EchoTerminal', ({}, {}), '()', False, 'from wspty.EchoTerminal import EchoTerminal\n'), ((69, 19, 69, 47), 'wspty.PromptTerminal.os_terminal', 'PromptTerminal.os_terminal', ({}, {}), '()', False, 'from wspty import PromptTerminal\n'), ((148, 45, 148, 71), 'os.path.realpath', 'os.path.realpath', ({(148, 62, 148, 70): '__file__'}, {}), '(__file__)', False, 'import os\n')]