Untitled
#!/usr/bin/python3 import sys,re,os import struct,random,math,itertools import html import requests from datetime import date import time from bottle import route, run, get, post, request, response, abort, auth_basic # an ugly spell to get unbuffer stdout on python3 class Unbuffered(object): def __init__(self, stream): self.stream = stream def write(self, data): self.stream.write(data) self.stream.flush() def writelines(self, datas): self.stream.writelines(datas) self.stream.flush() def __getattr__(self, attr): return getattr(self.stream, attr) sys.stdout = Unbuffered(sys.stdout) if len(sys.argv)<2: print("BookHook web server") print("Syntax: %s <port> [group]" % sys.argv[0]) sys.exit(1) port = int(sys.argv[1]) group = None if len(sys.argv)>=3: group = int(sys.argv[2]) logfile = "log.txt" target_url_file = "target_url.dat" if group: target_url_file = f"target_url{group}.dat" def get_target_url(): try: with open(target_url_file) as f: r = f.read().strip() except FileNotFoundError: set_target_url("") return "" return r def set_target_url(url): with open(target_url_file,"w") as f: f.write(url.strip()) failure_mode = None failure_password = "dogs" general_user = "admin" general_pass = "ECE$%*" # Define a function to authenticate users def check_auth(username, password): # Replace this with your own authentication logic if username == general_user and password == general_pass: return True return False def iff(c,a,b): if c: return a else: return b def get_background(): return f"./static/bg{iff(group,group,0)}.gif" def get_title(): r = "Hypothetical Books: BookHook Manager" if group: r += f" - GROUP {group}" return r @get('/fail') @post('/fail') @auth_basic(check_auth) def fail(): global failure_mode r = f"<html><body style='background: url({get_background()});'>\n" r += f"<h1>{get_title()} - FAILURE SIMULATOR</h1>\n" if request.method == 'POST': new_failure_mode = request.forms.get('f') given_password = request.forms.get('p') if new_failure_mode=="None": new_failure_mode = None if given_password == failure_password: failure_mode = new_failure_mode else: r += "<div style='color:red; background-color: yellow;'>DON'T HACK MY WEBS!!!!!</div>\n" else: given_password = '' r += f"failure_mode = {failure_mode}<p>" r += f""" <form action='/fail' method=post> Password: <input type=password name=p value='{given_password}'><p> <input type=submit name=f value="None"> <input type=submit name=f value="changedxml"> <input type=submit name=f value="wrongclosetag"> <input type=submit name=f value="nodate"> <input type=submit name=f value="badxml"> <input type=submit name=f value="blank"> <input type=submit name=f value="get"> <input type=submit name=f value="chargen"> <input type=submit name=f value="slow10sec"> <input type=submit name=f value="slow10min"> <p> </form> <a href="/">Front page</a> """ r+= "</body></html>" return r @get('/') @get('/fancy') @auth_basic(check_auth) def root(): is_fancy = 'fancy' in request.url r = """<html><body> <style> #map { width: 600px; height: 400px; border: 1px #000 solid; } body { """ r += f"background-image: url({get_background()});" r += """ } </style> """ r += f"<h1>{get_title()}</h1>\n" r += """ Connects to our POS systems over RS-232 and turns sales into XML hooks! By Ronnie Hypothetical, copyright 2002 <p>""" r += f""" <H2>Update target URL</h2> <form method=post action=/settarget> Target URL: <input type=text style="width:80%;" name=target value="{get_target_url()}"><br> <input type=submit> </form> """ r += """ <H2>Simulate purchase</h2> This will send an XML post to the target URL describing the purchase below for the purposes of testing. <p><i>Meta-note: In our case, this will be the ONLY posts sent to your system, since we don't actually have cash registers selling books in real life.</i> <form method=post action=/simulate name=simulate> Date: <input type=text name=date> (Format must by YYYY-MM-DD, blank defaults to today)<br> <table><tr><th>ISBN<th>Qty<th>Unit price</tr> """ for i in range(iff(is_fancy,15,3)): r += f"<tr><td><input type=text name=isbn{i}><td><input type=text name=qty{i}><td><input type=text name=price{i}>\n" r+= """ </table> <Script> function definputs() { document.querySelector('input[name="date"]').value = "2002-09-17"; document.querySelector('input[name="isbn0"]').value = "0345409469"; document.querySelector('input[name="qty0"]').value = 1; document.querySelector('input[name="price0"]').value = 7.99; document.querySelector('input[name="isbn1"]').value = "978-0345376596"; document.querySelector('input[name="qty1"]').value = 2; document.querySelector('input[name="price1"]').value = 9.99; } </script> [ <a href="#" onclick="definputs();">Fill with dummy info</a> · <a href="#" onclick="document.forms['simulate'].reset();">Clear</a> ]<bR><br> <input type=submit value="Simulate purchase"> </form> """ r+= """ <h2>Info for future people</h2> This posts the sale as XML to the target URL. The <a href="/static/schema.xml">schema is here</a>. XML is the future! """ r += "\n\n<!-- <img src='static/IAMCOOL.jpg'>A picture of me! --></body></html>\n" return r @post('/settarget') @auth_basic(check_auth) def req_set_target(): new_target = request.forms.get('target') set_target_url(new_target) r = '<html><body>ok. redirecting...<meta http-equiv="refresh" content="2; url=/"></body></html>' return r # wraps a string in a file-like object that can have its reads delayed by a given number of seconds class SlowStringFile(object): def __init__(self, content, delay): self.content = content self.delay = delay def read(self, size=-1): time.sleep(self.delay) if size==-1: result = self.content self.content = '' else: result = self.content[:size] self.content = self.content[size:] return result def __iter__(self): return self def __next__(self): data = self.read() if data: return data else: raise StopIteration # a file-like object that generates random binary content class CharGen(object): def __init__(self): pass def read(self, size=1024*1024): return os.urandom(size) def __iter__(self): return self def __next__(self): data = self.read() if data: return data else: raise StopIteration @post('/simulate') @auth_basic(check_auth) def req_simulate(): i = 0 sale_date = request.forms.get("date","").strip() if sale_date=="": sale_date = date.today().strftime("%Y-%m-%d") if failure_mode == 'nodate': xml = "<sale>\n" else: xml = f'<sale date="{sale_date}">\n' if failure_mode == 'badxml': xml += "/>\n" while request.forms.get(f"isbn{i}","").strip() != "": xml += "<item>" if failure_mode == 'changedxml': # just rearranged and spaced, still valid xml += " <price>" + request.forms.get(f"price{i}","").strip() + "</price>\n" xml += "\t<qty>" + request.forms.get(f"qty{i}","").strip() + "</qty>\n\n" xml += " <isbn>" + request.forms.get(f"isbn{i}","").strip() + "</isbn>" else: xml += "<isbn>" + request.forms.get(f"isbn{i}","").strip() + "</isbn>" xml += "<qty>" + request.forms.get(f"qty{i}","").strip() + "</qty>" xml += "<price>" + request.forms.get(f"price{i}","").strip() + "</price>" xml += "</item>\n" i += 1 if failure_mode == 'wrongclosetag': xml += "</sales>\n" else: xml += "</sale>\n" if failure_mode=="blank": xml = "" url = get_target_url() r = "<html><body><style>pre { margin-left: 20px; background-color:#ddd; padding: 5px;}</style>\n" r += f"I will post the following to <u>{url}</u>: <pre>{html.escape(xml)}</pre>\n" if failure_mode=="slow10sec": xml = SlowStringFile(xml.encode('utf-8'),10) if failure_mode=="slow10min": xml = SlowStringFile(xml.encode('utf-8'),10*60) if failure_mode=='chargen': xml = CharGen() r += "<hr><p>Issuing request..." if failure_mode=="get": response = requests.get(url, data=xml, headers={'Content-Type': 'application/xml'}) else: response = requests.post(url, data=xml, headers={'Content-Type': 'application/xml'}) r += f"Got status code <u>{response.status_code}</u> and this response body:<pre>{html.escape(response.text)}</pre>\n" r += "<hr><a href=/>Go back</a>\n" r += "</body></html>" return r from bottle import static_file @route('/static/<filename>') @auth_basic(check_auth) def server_static(filename): return static_file(filename, root='./static/') run(host='0.0.0.0', port=port, debug=True, server='paste') # install paste with: sudo apt install python3-paste
Leave a Comment