#!/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 <esp-wifi-connect component dir>")
|
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())
|