from Meh import Config, Option, ExceptionInConfigError from Highway import Server, Route, DummyPipe import Logging import os import subprocess import re import pwd import platform from subprocess import Popen, PIPE from _thread import start_new_thread from wsgiref.simple_server import make_server from ws4py.server.wsgirefserver import WSGIServer, WebSocketWSGIRequestHandler from ws4py.server.wsgiutils import WebSocketWSGIApplication from bottle.bottle import route, run, static_file @route("/") def index(): return static_file("index.html", root="Shared/dashb0ard") @route("/static/") def static(filepath): return static_file(filepath, root="Shared/dashb0ard/static") class Info(Route): def run(self, data, handler): handler.send({"routes" : list(handler.routes.keys())}, "info") class Subscribe(Route): EDITOR = 1 WALLABY = 2 WEB = 3 CHANNELS = [EDITOR, WALLABY, WEB] def run(self, data, handler): if type(data) is dict: if "channel" in data: if data["channel"] in Subscribe.CHANNELS: handler.channel = data["channel"] if handler.debug: Logging.info("'%s:%i' has identified as a %s client." % (handler.address, handler.port, "Editor" if handler.channel == Subscribe.EDITOR else "Controller" if handler.channel == Subscribe.WALLABY else "Web" if handler.channel == Subscribe.WEB else "Unknown")) if "name" in data: handler.name = data["name"] handler.routes["peers"].push_changes(handler) class WhoAmI(Route): def run(self, data, handler): handler.send({"id" : handler.id_, "user" : pwd.getpwuid(os.getuid()).pw_name}, handler.reverse_routes[self]) class Peers(Route): """ {"subscribe" : [1, 2]} {"unsubscribe" : [1, 2]} {"channels" : [1, 2]} """ def __init__(self): self.subscriptions = {} def run(self, data, handler): for event in ("subscribe", "unsubscribe", "channels"): if event in data: channels = [] for channel in data[event]: if channel in Subscribe.CHANNELS: channels.append(channel) if event == "unsubscribe": for channel in channels: self.unsubscribe(handler, channel) else: if event == "subscribe": for channel in channels: self.subscribe(handler, channel) # Send on channels and on subscribe self.send_connected_peers(handler, channels) def send_connected_peers(self, handler, channels): out = {} peers = handler.peers for peer_id in peers: # Only check for type inclusion if check_type is True peer = peers[peer_id] if peer.channel in channels: if peer is not handler: out[peer_id] = {"name" : peer.name, "address" : peer.address, "port" : peer.port, "channel" : peer.channel} handler.send(out, handler.reverse_routes[self]) def subscribe(self, handler, channel): if handler not in self.subscriptions: self.subscriptions[handler] = [channel] else: if channel not in self.subscriptions[handler]: self.subscriptions[handler].append(channel) def unsubscribe(self, handler, channel): if handler in self.subscriptions: if channel in self.subscriptions[handler]: del self.subscriptions[handler][self.subscriptions[handler].index(channel)] def unsubscribe_all(self, handler): if handler in self.subscriptions: del self.subscriptions[handler] def push_changes(self, handler): out = {} to_unsubscribe = [] peers = handler.peers for handler_ in self.subscriptions: try: self.send_connected_peers(handler_, self.subscriptions[handler_]) except RuntimeError: to_unsubscribe.append(handler_) for handler in to_unsubscribe: self.unsubscribe_all(handler) class Handler(Server): def setup(self, routes, websockets, debug=False): super().setup(routes, websockets, debug=debug) self.channel = None self.name = "Unknown" def ready(self): if self.debug: Logging.info("Handler for '%s:%d' ready." % (self.address, self.port)) def closed(self, code, reason): if self.debug: Logging.info("'%s:%d' disconnected." % (self.address, self.port)) self.routes["peers"].push_changes(self) def folder_validator(folder): if not os.path.isdir(folder): try: os.mkdir(folder) except OSError: return False return True CONFIG_PATH = "server.cfg" config = Config() config.add(Option("fl0w_address", ("127.0.0.1", 3077))) config.add(Option("behem0th_address", ("127.0.0.1", 3078))) config.add(Option("dashb0ard_address", ("127.0.0.1", 8080))) config.add(Option("debug", True, validator=lambda x: True if True or False else False)) config.add(Option("path", "Content", validator=folder_validator)) try: config = config.load(CONFIG_PATH) except (IOError, ExceptionInConfigError): config.dump(CONFIG_PATH) config = config.load(CONFIG_PATH) #compile = Compile(config.source_path, config.binary_path) server = make_server(config.fl0w_address[0], config.fl0w_address[1], server_class=WSGIServer, handler_class=WebSocketWSGIRequestHandler, app=None) server.initialize_websockets_manager() server.set_app(WebSocketWSGIApplication(handler_cls=Handler, handler_args={"debug" : config.debug, "websockets" : server.manager.websockets, "routes" : {"info" : Info(), "whoami" : WhoAmI(), "subscribe" : Subscribe(), "hostname" : DummyPipe(), "processes" : DummyPipe(), "peers" : Peers(), "sensor" : DummyPipe(), "identify" : DummyPipe(), "std_stream" : DummyPipe(), "stop_programs" : DummyPipe(), "shutdown" : DummyPipe(), "reboot" : DummyPipe(), "output" : DummyPipe()}})) try: Logging.header("Server loop starting.") start_new_thread(run, (), {"host" : config.dashb0ard_address[0], "port" : config.dashb0ard_address[1], "quiet" : True}) Logging.info("Starting dashb0ard on 'http://%s:%d'" % (config.dashb0ard_address[0], config.dashb0ard_address[1])) Logging.info("Starting fl0w on 'ws://%s:%d'" % (config.fl0w_address[0], config.fl0w_address[1])) server.serve_forever() except KeyboardInterrupt: Logging.header("Gracefully shutting down server.") server.server_close() Logging.success("Server shutdown successful.")