import http.cookies import http.server import urllib.parse import config import database import generate_html import session class HTTPRequestHandler(http.server.BaseHTTPRequestHandler): server_version = 'Buranun/0.0' # TODO: Look into keepalive problems in links2 #protocol_version = 'HTTP/1.1' protocol_version = 'HTTP/1.0' def __redirect(self, path = '/', buranun_session = None): # Construct the URL to redirect to protocol = 'https' if config.ssl else 'http' host_port = config.outside_host if config.outside_port == '' else '%s:%s' % (config.outside_host, config.outside_port) url = '%s://%s%s%s' % (protocol, host_port, config.url_prefix, path) encoded = url.encode('utf-8') length = len(encoded) self.send_response(303) self.send_header('Location', url) self.send_header('Content-Type', 'text/plain; charset=utf-8') self.send_header('Content-Length', length) # TODO: Make the max-age more sensical sent_cookies = http.cookies.SimpleCookie() sent_cookies['buranun_session'] = buranun_session sent_cookies['buranun_session']['path'] = config.url_prefix if config.url_prefix != '' else '/' sent_cookies['buranun_session']['max-age'] = 60 sent_cookies['buranun_session']['secure'] = config.ssl sent_cookies['buranun_session']['httponly'] = True # Since http.cookies doesn't play nicely with http.server we need to do this manually self.flush_headers() self.wfile.write(sent_cookies.output().encode('utf-8') + b'\r\n') self.end_headers() self.wfile.write(encoded) def __send_html(self, html, *, status_code = 200): encoded = html.encode('utf-8') length = len(encoded) self.send_response(status_code) self.send_header('Content-Type', 'text/html; charset=utf-8') self.send_header('Content-Length', length) self.end_headers() self.wfile.write(encoded) def __send_404(self, path): html = generate_html.error_404(path) self.__send_html(html, status_code = 404) def do_POST(self): path = urllib.parse.unquote(self.path) path_components = [component for component in path.split('/') if component != ''] # Read the POST data post_data_length = int(self.headers['Content-Length']) post_data = bytearray() while len(post_data) < post_data_length: data = self.rfile.read(post_data_length - len(post_data)) post_data.extend(data) post_keys = urllib.parse.parse_qs(post_data.decode('utf-8'), keep_blank_values = True) if len(path_components) == 1 and path_components[0] == 'login': with database.connect() as db: username = post_keys['username'][0] password = post_keys['password'][0] userid = database.get_userid(db, username) password_correct = database.check_password(db, userid, password) if password_correct: self.__redirect(buranun_session = session.new_session(userid)) else: # TODO: Have it forward the user back to the page where they were at html = generate_html.login(self.path, retrying = True) self.__send_html(html) else: self.__send_404(path) def do_GET(self): # TODO: Do something with the session cookies_string = self.headers['cookie'] logged_in = False if cookies_string is not None: received_cookies = http.cookies.SimpleCookie() try: received_cookies.load(cookies_string) except http.cookies.CookieError: print('malformed cookies') if 'buranun_session' in received_cookies: sessionid = int(received_cookies['buranun_session'].value) print(sessionid) userid = session.get_userid(sessionid) if userid is not None: logged_in = True with database.connect() as db: print(userid, database.get_user_info(db, userid)) else: print('unknown session ID') else: print('No buranun_session') else: print('No cookies') if not logged_in: # Display page that tells user to login # TODO: Have it forward the user back to the page where they were at html = generate_html.login(self.path) self.__send_html(html) # Don't run rest of the function return path = urllib.parse.unquote(self.path) path_components = [component for component in path.split('/') if component != ''] if len(path_components) == 0: # Path of format / → index html = generate_html.index() self.__send_html(html) elif len(path_components) == 1: # Path of format /foo/ → board index board_name = path_components[0] html = generate_html.board(board_name) self.__send_html(html) else: # Path not understood, send 404 self.__send_404(path) def main(): # TODO: Don't hardcode config.load('buranun.conf') httpd = http.server.HTTPServer(('', config.port), HTTPRequestHandler) httpd.serve_forever() if __name__ == '__main__': main()