From 26273fdf4e31b4ac818fd6c75a3996df60c3836d Mon Sep 17 00:00:00 2001 From: Patrick Di Fazio Date: Thu, 25 Dec 2025 21:26:13 +0100 Subject: [PATCH] unified style and added error handling --- src/handler.py | 169 ++++++------ src/templates/html/login_error.html | 117 +++++++-- src/templates/html/login_form.html | 155 ++++++++++- src/templates/html/phpmyadmin.html | 383 ++++++++++------------------ src/templates/html/robots.txt | 2 + src/templates/html/wp_login.html | 157 ++++++------ 6 files changed, 553 insertions(+), 430 deletions(-) diff --git a/src/handler.py b/src/handler.py index 6a4b969..81f48fa 100644 --- a/src/handler.py +++ b/src/handler.py @@ -212,85 +212,99 @@ class Handler(BaseHTTPRequestHandler): time.sleep(1) - self.send_response(200) - self.send_header('Content-type', 'text/html') - self.end_headers() - self.wfile.write(html_templates.login_error().encode()) + try: + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write(html_templates.login_error().encode()) + except BrokenPipeError: + # Client disconnected before receiving response, ignore silently + pass + except Exception as e: + # Log other exceptions but don't crash + print(f"[ERROR] Failed to send response to {client_ip}: {str(e)}") def serve_special_path(self, path: str) -> bool: """Serve special paths like robots.txt, API endpoints, etc.""" - if path == '/robots.txt': - self.send_response(200) - self.send_header('Content-type', 'text/plain') - self.end_headers() - self.wfile.write(html_templates.robots_txt().encode()) - return True - - if path in ['/credentials.txt', '/passwords.txt', '/admin_notes.txt']: - self.send_response(200) - self.send_header('Content-type', 'text/plain') - self.end_headers() - if 'credentials' in path: - self.wfile.write(credentials_txt().encode()) - else: - self.wfile.write(passwords_txt().encode()) - return True - - if path in ['/users.json', '/api_keys.json', '/config.json']: - self.send_response(200) - self.send_header('Content-type', 'application/json') - self.end_headers() - if 'users' in path: - self.wfile.write(users_json().encode()) - elif 'api_keys' in path: - self.wfile.write(api_keys_json().encode()) - else: - self.wfile.write(api_response('/api/config').encode()) - return True - - if path in ['/admin', '/admin/', '/admin/login', '/login']: - self.send_response(200) - self.send_header('Content-type', 'text/html') - self.end_headers() - self.wfile.write(html_templates.login_form().encode()) - return True - - # WordPress login page - if path in ['/wp-login.php', '/wp-login', '/wp-admin', '/wp-admin/']: - self.send_response(200) - self.send_header('Content-type', 'text/html') - self.end_headers() - self.wfile.write(html_templates.wp_login().encode()) - return True - - if path in ['/wp-content/', '/wp-includes/'] or 'wordpress' in path.lower(): - self.send_response(200) - self.send_header('Content-type', 'text/html') - self.end_headers() - self.wfile.write(html_templates.wordpress().encode()) - return True - - if 'phpmyadmin' in path.lower() or path in ['/pma/', '/phpMyAdmin/']: - self.send_response(200) - self.send_header('Content-type', 'text/html') - self.end_headers() - self.wfile.write(html_templates.phpmyadmin().encode()) - return True - - if path.startswith('/api/') or path.startswith('/api') or path in ['/.env']: - self.send_response(200) - self.send_header('Content-type', 'application/json') - self.end_headers() - self.wfile.write(api_response(path).encode()) - return True - - if path in ['/backup/', '/uploads/', '/private/', '/admin/', '/config/', '/database/']: - self.send_response(200) - self.send_header('Content-type', 'text/html') - self.end_headers() - self.wfile.write(directory_listing(path).encode()) - return True + try: + if path == '/robots.txt': + self.send_response(200) + self.send_header('Content-type', 'text/plain') + self.end_headers() + self.wfile.write(html_templates.robots_txt().encode()) + return True + + if path in ['/credentials.txt', '/passwords.txt', '/admin_notes.txt']: + self.send_response(200) + self.send_header('Content-type', 'text/plain') + self.end_headers() + if 'credentials' in path: + self.wfile.write(credentials_txt().encode()) + else: + self.wfile.write(passwords_txt().encode()) + return True + + if path in ['/users.json', '/api_keys.json', '/config.json']: + self.send_response(200) + self.send_header('Content-type', 'application/json') + self.end_headers() + if 'users' in path: + self.wfile.write(users_json().encode()) + elif 'api_keys' in path: + self.wfile.write(api_keys_json().encode()) + else: + self.wfile.write(api_response('/api/config').encode()) + return True + + if path in ['/admin', '/admin/', '/admin/login', '/login']: + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write(html_templates.login_form().encode()) + return True + + # WordPress login page + if path in ['/wp-login.php', '/wp-login', '/wp-admin', '/wp-admin/']: + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write(html_templates.wp_login().encode()) + return True + + if path in ['/wp-content/', '/wp-includes/'] or 'wordpress' in path.lower(): + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write(html_templates.wordpress().encode()) + return True + + if 'phpmyadmin' in path.lower() or path in ['/pma/', '/phpMyAdmin/']: + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write(html_templates.phpmyadmin().encode()) + return True + + if path.startswith('/api/') or path.startswith('/api') or path in ['/.env']: + self.send_response(200) + self.send_header('Content-type', 'application/json') + self.end_headers() + self.wfile.write(api_response(path).encode()) + return True + + if path in ['/backup/', '/uploads/', '/private/', '/admin/', '/config/', '/database/']: + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write(directory_listing(path).encode()) + return True + except BrokenPipeError: + # Client disconnected, ignore silently + pass + except Exception as e: + print(f"[ERROR] Failed to serve special path {path}: {str(e)}") + pass return False @@ -306,6 +320,8 @@ class Handler(BaseHTTPRequestHandler): try: stats = self.tracker.get_stats() self.wfile.write(generate_dashboard(stats).encode()) + except BrokenPipeError: + pass except Exception as e: print(f"Error generating dashboard: {e}") return @@ -337,6 +353,9 @@ class Handler(BaseHTTPRequestHandler): if Handler.counter < 0: Handler.counter = self.config.canary_token_tries + except BrokenPipeError: + # Client disconnected, ignore silently + pass except Exception as e: print(f"Error generating page: {e}") diff --git a/src/templates/html/login_error.html b/src/templates/html/login_error.html index 8e4974d..3727c20 100644 --- a/src/templates/html/login_error.html +++ b/src/templates/html/login_error.html @@ -1,29 +1,108 @@ - + - Login Failed + + + Error -
-

Admin Login

-
ERROR: Invalid username or password.
-
- - - -
-

Forgot your password?

+
+

⚠ Error

+ +
+ Login Failed. Please try again. +
+ +

If the problem persists, please contact support.

+ + + + ← Back to Home
\ No newline at end of file diff --git a/src/templates/html/login_form.html b/src/templates/html/login_form.html index d39ede6..247355e 100644 --- a/src/templates/html/login_form.html +++ b/src/templates/html/login_form.html @@ -1,25 +1,156 @@ - + + + Admin Login -