From a172d89c8c0a4e2047af94ba49755ab9d907f364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20Mu=CC=88ller=20zum=20Hagen?= Date: Sat, 17 Mar 2018 23:55:00 +0100 Subject: [PATCH 1/5] Fix some html/css mistakes. --- webserver/www/blockly.html | 1 - webserver/www/css/main.css | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/webserver/www/blockly.html b/webserver/www/blockly.html index 2498338..0b04d71 100644 --- a/webserver/www/blockly.html +++ b/webserver/www/blockly.html @@ -16,7 +16,6 @@ - diff --git a/webserver/www/css/main.css b/webserver/www/css/main.css index 79bc612..c4606b7 100644 --- a/webserver/www/css/main.css +++ b/webserver/www/css/main.css @@ -22,7 +22,7 @@ select { font-size: 18px; width: 100%; padding: 10px; - padding: 0 10px 0 10px 0; + padding: 0 10px 0 10px; border: 0 !important; /* needed */ -webkit-appearance: none; From 698d612fd7a14fa6e2583d16ce8b7fff0e739aa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20Mu=CC=88ller=20zum=20Hagen?= Date: Sun, 18 Mar 2018 13:26:35 +0100 Subject: [PATCH 2/5] Log messages poll prototype implementation Polling of the log messages is prototyped and needs some clean up. --- webserver/HSHttpServer.py | 221 +--------------------- webserver/HSLogMessage.py | 30 +++ webserver/HSRequestHandler.py | 236 ++++++++++++++++++++++++ webserver/HSTerm.py | 23 +-- webserver/www/blockly.html | 22 ++- webserver/www/editor.html | 21 +++ webserver/www/js/webserver_functions.js | 20 +- webserver/www/poll.html | 40 ++++ 8 files changed, 373 insertions(+), 240 deletions(-) create mode 100644 webserver/HSLogMessage.py create mode 100644 webserver/HSRequestHandler.py create mode 100644 webserver/www/poll.html diff --git a/webserver/HSHttpServer.py b/webserver/HSHttpServer.py index 3720259..13637c4 100644 --- a/webserver/HSHttpServer.py +++ b/webserver/HSHttpServer.py @@ -1,216 +1,15 @@ #!/usr/bin/python - -import SimpleHTTPServer from BaseHTTPServer import HTTPServer +from SocketServer import ThreadingMixIn import os -import json -import subprocess from HSTerm import HSTerm +from HSRequestHandler import HSRequestHandler +ThreadingMixIn.daemon_threads = True -class HSRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): - - def do_AUTHHEAD(self): - self.send_response(401) - self.send_header('WWW-Authenticate', 'Basic realm="HackerSchool"') - self.send_header('Content-type', 'text/html') - self.end_headers() - - def do_GET(self): - ''' Present frontpage with user authentication. ''' - if self.headers.get('Authorization') is None: - self.do_AUTHHEAD() - self.wfile.write('no auth header received') - - elif self.headers.get('Authorization') == 'Basic ' + str(self.server._authorization_key): - SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) - - else: - self.do_AUTHHEAD() - self.wfile.write('not authenticated') - - def do_POST(self): - ''' Present frontpage with user authentication. ''' - if self.headers.get('Authorization') is None: - self.do_AUTHHEAD() - self.wfile.write('no auth header received') - self.wfile.close() - - elif self.headers.get('Authorization') == 'Basic ' + str(self.server._authorization_key): - - content_length = int(self.headers['Content-Length']) - post_data = self.rfile.read(content_length) - - # Clear the exec file. - HSTerm.clear_exec() - - # Set the filename to the exec file anyway. - filename = HSTerm.exec_filename() - - if self.path.startswith('/__FILE_ACCESS__?'): - try: - # Extract the variables and handle the file access - variables = HSHttpServer.get_dict_from_url(self.path) - - HSTerm.term('FileAccess: %s' % variables['command']) - - if variables['command'] == 'save': - # Store all incomming data into the file. - HSTerm.term('Save file %s' % variables['filename']) - savename = HSHttpServer._CODE_ROOT + '/' + variables['filename'] - with open(savename, 'wb') as file: - file.write(post_data) - HSTerm.term_exec('File %s saved.' % savename) - HSTerm.term_exec('%d bytes written.' % content_length) - self.sendFile(200, filename) - - elif variables['command'] == 'execute': - # Execute the code from RAM when the content is smaller than 1k - HSTerm.term('Execute from RAM.') - - post_data = post_data.decode('utf-8') - - # Execute the give data. - try: - exec(post_data, globals()) - self.sendFile(200, filename) - except Exception as e: - HSTerm.term_exec('Error: %s' % str(e)) - self.sendFile(500, filename) - - elif variables['command'] == 'load': - # Return the file. - filename = self.server._CODE_ROOT + '/' + variables['filename'] - self.sendFile(200, filename) - - else: - # The given command is not known. - self.sendFile(404, '404.html') - - except Exception as e: - HSTerm.term_exec('Internal Error:\n%s' % str(e)) - self.sendFile(500, filename) - - elif self.path.startswith('/__COMMAND__'): - try: - # Convert the json data into a key/value dictionary. - args = json.loads(post_data.decode('utf-8')) - - HSTerm.term('Command: "%s"' % args['command']) - - # Save the new password key only when the oldkey is the same with the current. - if args['command'] == 'save_password': - if (args['oldKey'] == self.server._authorization_key and args['newKey'] != ''): - - HSTerm.term('Store the new password') - # Store the password. - self.server._authorization_key = args['newKey'] - with open('password', 'w') as file: - file.write(self.server._authorization_key) - - HSTerm.term_exec('New password written.') - - else: - HSTerm.term('auth key: %s' % self.server._authorization_key) - HSTerm.term('old key: %s' % args['oldKey']) - HSTerm.term('new key: %s' % args['newKey']) - HSTerm.term_exec('Error: Could not store the password.') - HSTerm.term_exec('The current Password is not the same!') - self.sendFile(200, filename) - - # Get the list of all available code files. - elif args['command'] == 'get_file_list': - - data = {} - data['files'] = os.listdir(self.server._CODE_ROOT) - data['files'].sort() - json_dump = json.dumps(data) - - self.send_response(200) - self.send_header('Content-type', 'text/html') - self.end_headers() - self.wfile.write(json_dump) - - # Get the version of this project. - elif args['command'] == 'get_version': - - data = {} - data['version'] = self.server._version - json_dump = json.dumps(data) - - self.send_response(200) - self.send_header('Content-type', 'text/html') - self.end_headers() - self.wfile.write(json_dump) - - # Run the file which is saved on the device - elif args['command'] == 'run': - run_file = self.server._CODE_ROOT + '/' + args['filename'] - try: - exec(open(run_file).read(), globals()) - except Exception as e: - HSTerm.term_exec('Error: %s' % str(e)) - self.sendFile(200, filename) - - # Update all files from the project. - elif args['command'] == 'update': - - # Run the update script and save the content to send. - bash = subprocess.Popen(['sh', self.server._UPDATE_FILE, '-c'], stdout=subprocess.PIPE) - data = bash.communicate()[0] - - self.send_response(200) - self.send_header('Content-type', 'text/html') - self.end_headers() - self.wfile.write(data) - self.wfile.write(b'\r\n') - self.wfile.write(b'\r\n') - self.wfile.flush(); - - # Reboot only if there is an update. - if bash.returncode == 1: - self.wfile.write('\nThe system will be updated / reboot and is available in a few seconds.\n\n\n') - self.wfile.write(b'\r\n') - self.wfile.write(b'\r\n') - self.wfile.flush(); - subprocess.check_output(['sh', self.server._UPDATE_FILE, '-u', '-r']) - - else: - # The given command is not known. - self.sendFile(404, '404.html') - - except Exception as e: - HSTerm.term_exec('Internal Error:\n%s' % str(e)) - self.sendFile(500, filename) - - else: - self.do_AUTHHEAD() - self.wfile.write('Invalid credentials') - - def send_error(self, code, message): - self.send_response(code) - self.send_header('Content-type', 'text/html') - self.end_headers() - f = open('404.html', 'rb') - self.wfile.write(f.read()) - f.close() - - def sendFile(self, response, filename): - if not os.path.exists(filename): - filename = os.path.join(self.server._DOCUMENT_ROOT, '404.hml') - response = 404 - - self.send_response(response) - self.send_header('Content-type', 'text/html') - self.end_headers() - f = open(filename, 'rb') - self.wfile.write(f.read()) - f.close() - - -class HSHttpServer(HTTPServer): +class HSHttpServer(ThreadingMixIn, HTTPServer): """ This implementation is a simple HTTP Server which works on Windows, Linux and macOS with different Browser. """ @@ -264,15 +63,3 @@ def start(self): except KeyboardInterrupt: pass self.server_close() - - @staticmethod - def get_dict_from_url(url): - """ - Get a dict of all variables from an url. - """ - variables = url.split('?')[1] - variables = variables.replace('&', '","') - variables = variables.replace('=', '":"') - variables = '{"%s"}' % variables - variables = json.loads(variables) - return variables diff --git a/webserver/HSLogMessage.py b/webserver/HSLogMessage.py new file mode 100644 index 0000000..c4f78e3 --- /dev/null +++ b/webserver/HSLogMessage.py @@ -0,0 +1,30 @@ +#!/usr/bin/python + +import time +import threading + +class HSLogMessage(): + + def __init__(self): + self.data = '' + self.event = threading.Event() + self.lock = threading.Lock() + self.event.clear() + + def wait(self): + """ + Wait until a new message is set and return the data of it. + """ + self.event.wait() + return self.data + + def post(self, data): + """ + Save the data and set/clear the event to post them. + Sleep for 100 milliseconds to give the browser a chance to receive them. + """ + with self.lock: + self.data = data + self.event.set() + self.event.clear() + time.sleep(0.1) diff --git a/webserver/HSRequestHandler.py b/webserver/HSRequestHandler.py new file mode 100644 index 0000000..43f29ab --- /dev/null +++ b/webserver/HSRequestHandler.py @@ -0,0 +1,236 @@ +#!/usr/bin/python + +import SimpleHTTPServer +import os +import json +import subprocess +import time + +from HSTerm import HSTerm + +class HSRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): + + def do_AUTHHEAD(self): + self.send_response(401) + self.send_header('WWW-Authenticate', 'Basic realm="HackerSchool"') + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write('not authenticated') + self.wfile.close() + + def do_GET(self): + """ + Present frontpage with user authentication. + """ + if self.headers.get('Authorization') is None: + self.do_AUTHHEAD() + + elif self.headers.get('Authorization') == 'Basic ' + str(self.server._authorization_key): + + SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) + + else: + self.do_AUTHHEAD() + + def do_POST(self): + """ + Present frontpage with user authentication. + """ + if self.headers.get('Authorization') is None: + self.do_AUTHHEAD() + + elif self.headers.get('Authorization') == 'Basic ' + str(self.server._authorization_key): + + content_length = int(self.headers['Content-Length']) + post_data = self.rfile.read(content_length) + + if self.path.startswith('/__FILE_ACCESS__?'): + try: + # Extract the variables and handle the file access + variables = self.get_dict_from_url(self.path) + + HSTerm.term('FileAccess: %s' % variables['command']) + + if variables['command'] == 'save': + # Store all incomming data into the file. + HSTerm.term('Save file %s' % variables['filename']) + savename = HSHttpServer._CODE_ROOT + '/' + variables['filename'] + with open(savename, 'wb') as file: + file.write(post_data) + HSTerm.term_exec('File %s saved.' % savename) + HSTerm.term_exec('%d bytes written.' % content_length) + + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write('Done ...') + + elif variables['command'] == 'execute': + # Execute the code from RAM when the content is smaller than 1k + HSTerm.term('Execute from RAM.') + + post_data = post_data.decode('utf-8') + + # Execute the given data. + try: + HSTerm.term_exec('Run ...\n') + exec(post_data, globals()) + HSTerm.term_exec('\n... Done\n\n') + except Exception as e: + HSTerm.term_exec('Error: %s' % str(e)) + + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write('Done ...') + + elif variables['command'] == 'load': + # Return the file. + filename = self.server._CODE_ROOT + '/' + variables['filename'] + + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + f = open(filename, 'rb') + self.wfile.write(f.read()) + f.close() + + else: + # The given command is not known. + self.send_response(404) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write('Command not known.') + + except Exception as e: + HSTerm.term_exec('Internal Error:\n%s' % str(e)) + self.send_response(500) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write('Done ...') + + elif self.path.startswith('/__COMMAND__'): + + try: + # Convert the json data into a key/value dictionary. + args = json.loads(post_data.decode('utf-8')) + + HSTerm.term('Command: "%s"' % args['command']) + + # Save the new password key only when the oldkey is the same with the current. + if args['command'] == 'save_password': + if (args['oldKey'] == self.server._authorization_key and args['newKey'] != ''): + + HSTerm.term('Store the new password') + # Store the password. + self.server._authorization_key = args['newKey'] + with open('password', 'w') as file: + file.write(self.server._authorization_key) + + HSTerm.term_exec('New password written.') + + else: + HSTerm.term('auth key: %s' % self.server._authorization_key) + HSTerm.term('old key: %s' % args['oldKey']) + HSTerm.term('new key: %s' % args['newKey']) + HSTerm.term_exec('Error: Could not store the password.') + HSTerm.term_exec('The current Password is not the same!') + + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write('Done ...') + + # Get the list of all available code files. + elif args['command'] == 'get_file_list': + + data = {} + data['files'] = os.listdir(self.server._CODE_ROOT) + data['files'].sort() + json_dump = json.dumps(data) + + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write(json_dump) + + # Get the version of this project. + elif args['command'] == 'get_version': + + data = {} + data['version'] = self.server._version + json_dump = json.dumps(data) + + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write(json_dump) + + # Run the file which is saved on the device + elif args['command'] == 'run': + run_file = self.server._CODE_ROOT + '/' + args['filename'] + try: + exec(open(run_file).read(), globals()) + except Exception as e: + HSTerm.term_exec('Error: %s' % str(e)) + + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write('Done ...') + + # Update all files from the project. + elif args['command'] == 'update': + + # Run the update script and save the content to send. + bash = subprocess.Popen(['sh', self.server._UPDATE_FILE, '-c'], stdout=subprocess.PIPE) + data = bash.communicate()[0] + + HSTerm.term_exec(data) + + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write('Done ...') + + # Reboot only if there is an update. + if bash.returncode == 1: + HSTerm.term_exec('\nThe system will be updated / reboot and is available in a few seconds.\n\n\n') + subprocess.check_output(['sh', self.server._UPDATE_FILE, '-u', '-r']) + + else: + # The given command is not known. + self.send_response(404) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write('Command not known.') + + except Exception as e: + HSTerm.term_exec('Internal Error:\n%s' % str(e)) + + self.send_response(500) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write('Internal Error.') + + elif self.path == '/poll': + message = HSTerm.get_message_wait() + + self.send_response(200) + self.send_header('Content-type', 'text/plain') + self.end_headers() + self.wfile.write(message) + + else: + self.do_AUTHHEAD() + + def get_dict_from_url(self, url): + """ + Get a dict of all variables from an url. + """ + variables = url.split('?')[1] + variables = variables.replace('&', '","') + variables = variables.replace('=', '":"') + variables = '{"%s"}' % variables + variables = json.loads(variables) + return variables diff --git a/webserver/HSTerm.py b/webserver/HSTerm.py index 06cdf84..369c4d6 100644 --- a/webserver/HSTerm.py +++ b/webserver/HSTerm.py @@ -1,5 +1,9 @@ #!/usr/bin/python +from HSLogMessage import HSLogMessage + +global log_message +log_message = HSLogMessage() class HSTerm: @@ -10,25 +14,18 @@ def term(message): """ print(message) - @staticmethod - def clear_exec(): - """ - Clear the exec file to write new content into it. - """ - with open('exec_file.txt', 'w') as file: - file.write('') - @staticmethod def term_exec(message): """ Write a line into the exec file and append a html and normal line break. """ - with open('exec_file.txt', 'a') as file: - file.write(str(message) + '\n') + # with open('exec_file.txt', 'a') as file: + # file.write(str(message) + '\n') + log_message.post(str(message)) @staticmethod - def exec_filename(): + def get_message_wait(): """ - Return the exec filename. + Wait for a new message and return ist """ - return 'exec_file.txt' + return log_message.wait() diff --git a/webserver/www/blockly.html b/webserver/www/blockly.html index 0b04d71..d4b83cf 100644 --- a/webserver/www/blockly.html +++ b/webserver/www/blockly.html @@ -24,6 +24,24 @@ + + + @@ -48,6 +66,9 @@ + + +
@@ -75,7 +96,6 @@

Console Log

diff --git a/webserver/www/editor.html b/webserver/www/editor.html index 29af4e3..71e4ce2 100644 --- a/webserver/www/editor.html +++ b/webserver/www/editor.html @@ -17,6 +17,24 @@ + + + @@ -41,6 +59,9 @@ + + +