#!/usr/bin/env python3 """Idempotent patches for 78/esp-wifi-connect captive portal. The managed component is gitignored; this script is applied at CMake configure so Android/WeChat locale codes (zh_CN) and socket exhaustion from captive DNS hijack (WeChat /mmtls) do not break the config page. """ from __future__ import annotations import re import sys from pathlib import Path HTML_MARKER = "xiaozhi-portal-lang-patch" HTTPD_MARKER = "xiaozhi-portal-httpd-patch" def _normalize_ws(text: str) -> str: return text.replace("\r\n", "\n") def patch_html(path: Path) -> bool: raw = _normalize_ws(path.read_text(encoding="utf-8")) original = raw if HTML_MARKER not in raw: raw, n = re.subn( r"function getSupportedLanguage\(lang\) \{\s*" r"// First try exact match", "function getSupportedLanguage(lang) {\n" f" // {HTML_MARKER}\n" " if (!lang) { return 'en-US'; }\n" " lang = String(lang).replace(/_/g, '-');\n" " // First try exact match", raw, count=1, ) if n == 0: print(f"warn: could not patch getSupportedLanguage in {path}") if "getSupportedLanguage(langParam ||" not in raw: raw, n = re.subn( r"const savedLang = langParam \|\|\s*" r"localStorage\.getItem\('preferred_language'\) \|\|\s*" r"getSupportedLanguage\(browserLang\);", "const savedLang = getSupportedLanguage(langParam ||\n" " localStorage.getItem('preferred_language') ||\n" " browserLang);", raw, count=1, ) if n == 0: print(f"warn: could not patch savedLang in {path}") if raw == original: print(f"already patched {path}") return False path.write_text(raw, encoding="utf-8", newline="\n") print(f"patched {path}") return True def patch_httpd(path: Path) -> bool: raw = _normalize_ws(path.read_text(encoding="utf-8")) if HTTPD_MARKER in raw: print(f"already patched {path}") return False raw2, n = re.subn( r"httpd_config_t config = HTTPD_DEFAULT_CONFIG\(\);\s*" r"config\.max_uri_handlers = 24;\s*" r"config\.uri_match_fn = httpd_uri_match_wildcard;", "httpd_config_t config = HTTPD_DEFAULT_CONFIG();\n" " config.max_uri_handlers = 24;\n" f" config.lru_purge_enable = true; // {HTTPD_MARKER}\n" " config.uri_match_fn = httpd_uri_match_wildcard;", raw, count=1, ) if n == 0: print(f"warn: could not patch httpd config in {path}") return False path.write_text(raw2, encoding="utf-8", newline="\n") print(f"patched {path}") return True def main() -> int: if len(sys.argv) != 2: print("usage: patch_wifi_connect.py ") return 2 root = Path(sys.argv[1]) if not root.is_dir(): print(f"error: not a directory: {root}") return 1 html = root / "assets" / "wifi_configuration.html" cc = root / "wifi_configuration_ap.cc" if html.is_file(): patch_html(html) else: print(f"warn: missing {html}") if cc.is_file(): patch_httpd(cc) else: print(f"warn: missing {cc}") return 0 if __name__ == "__main__": raise SystemExit(main())