wangzhibo
2026-09-11 659c470eba542383987aad227b7f918100f131fa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#!/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())