From 95eeffdd8cc3443add1ce2c739d0c413ac502015 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Mon, 11 Apr 2022 10:58:45 +0200 Subject: src: shorter import --- src/app.py | 3 ++- src/server.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/app.py b/src/app.py index 5af55af..b0c4672 100755 --- a/src/app.py +++ b/src/app.py @@ -44,6 +44,7 @@ import cgi from collections import namedtuple from concurrent.futures import ThreadPoolExecutor from enum import Enum +from http import HTTPStatus import http.server import json import os @@ -66,7 +67,7 @@ def split_by(xs, sep): class Response: - DEFAULT_STATUS = http.server.HTTPStatus.OK + DEFAULT_STATUS = HTTPStatus.OK @staticmethod def body_from_json(body): diff --git a/src/server.py b/src/server.py index 75723bd..9ffe6f1 100755 --- a/src/server.py +++ b/src/server.py @@ -9,6 +9,7 @@ # custom URLs. See that file for the reasons behind this. import argparse +from http import HTTPStatus import http.server import os import sys @@ -38,7 +39,7 @@ class RequestHandler(http.server.SimpleHTTPRequestHandler): response = request.process() response.write_to_request_handler(self) except: - status = http.server.HTTPStatus.INTERNAL_SERVER_ERROR + status = HTTPStatus.INTERNAL_SERVER_ERROR response = Response(traceback.format_exc(), status) response.write_to_request_handler(self) return -- cgit v1.2.3 From ce5bb34296429bd2401bb51c01522dd0c1bfbf83 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Mon, 11 Apr 2022 11:02:59 +0200 Subject: app.py: print HTTP status when CGIing --- src/app.py | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/app.py b/src/app.py index b0c4672..b83666e 100755 --- a/src/app.py +++ b/src/app.py @@ -90,6 +90,7 @@ class Response: self.write_body_as_cgi_script() def write_headers_as_cgi_script(self): + print(f'{self.status.value} {self.status.phrase}') for name, val in self.headers(): print(f'{name}: {val}') print() -- cgit v1.2.3 From 1a3a8f1ec191d3811730fd149521abcb2205a018 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Mon, 11 Apr 2022 11:36:31 +0200 Subject: support disallowing power requests --- src/app.py | 37 ++++++++++++++++++++++++++++++++----- src/server.py | 6 ++++++ 2 files changed, 38 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/app.py b/src/app.py index b83666e..7533df7 100755 --- a/src/app.py +++ b/src/app.py @@ -40,12 +40,10 @@ # no more. import abc -import cgi from collections import namedtuple from concurrent.futures import ThreadPoolExecutor from enum import Enum from http import HTTPStatus -import http.server import json import os import pwd @@ -53,6 +51,8 @@ import shlex import socket import subprocess from subprocess import DEVNULL, PIPE, STDOUT +import traceback +import urllib.parse def split_by(xs, sep): @@ -510,6 +510,17 @@ def systemd_users(): return show_users(list_users()) +def cgi_one_value(params, name, default=None): + values = params.get(name, []) + if not values: + if default is None: + raise ValueError(f'must have at least one value: {name}') + return default + if len(values) > 1: + raise ValueError(f'multiple values are not supported: {name}') + return values[0] + + class Request(Enum): STATUS = 'status' TOP = 'top' @@ -525,22 +536,38 @@ class Request(Enum): raise ValueError('HTTP path must start with a forward slash /') return Request(path[1:]) + @staticmethod + def from_query_string(qs): + params = urllib.parse.parse_qs(qs, strict_parsing=True) + request = cgi_one_value(params, 'what') + request = Request(request) + request.disable_power = int(cgi_one_value(params, 'disable_power', '0')) + return request + def process(self): if self is Request.STATUS: return Status().complete() if self is Request.TOP: return Top().complete() + + if self in [Request.REBOOT, Request.POWEROFF] and self.disable_power: + return Response(None, HTTPStatus.FORBIDDEN) if self is Request.REBOOT: return Reboot().complete() if self is Request.POWEROFF: return Poweroff().complete() + raise NotImplementedError(f'unknown request: {self}') def process_cgi_request(): - params = cgi.FieldStorage() - what = params['what'].value - Request(what).process().write_as_cgi_script() + try: + request = Request.from_query_string(os.environ['QUERY_STRING']) + request.process().write_as_cgi_script() + except: + status = HTTPStatus.INTERNAL_SERVER_ERROR + response = Response(traceback.format_exc(), status) + response.write_as_cgi_script() def main(): diff --git a/src/server.py b/src/server.py index 9ffe6f1..a0fa7c4 100755 --- a/src/server.py +++ b/src/server.py @@ -30,9 +30,12 @@ def default_html_dir(): class RequestHandler(http.server.SimpleHTTPRequestHandler): + ARGS = None + def do_GET(self): try: request = Request.from_http_path(self.path) + request.disable_power = RequestHandler.ARGS.disable_power except ValueError: return super().do_GET() try: @@ -60,6 +63,8 @@ def parse_args(args=None): parser.add_argument('-p', '--port', metavar='PORT', type=int, default=DEFAULT_PORT, help='set port number') + parser.add_argument('-n', '--disable-power', action='store_true', + help='disable reboot/poweroff requests') parser.add_argument('-d', '--dir', metavar='DIR', default=default_html_dir(), help='HTML directory path') @@ -68,6 +73,7 @@ def parse_args(args=None): def main(args=None): args = parse_args(args) + RequestHandler.ARGS = args # It's a failsafe; the script is not allowed to serve a random current # working directory. -- cgit v1.2.3