# -*- coding: utf-8 -*- """钉住两件事:中转地址默认必须是空的,以及老版本硬塞的地址要被清掉。 == 为什么需要 == 用户报过:「安卓端为什么还是默认填的我的中转站,以后改成手动填」。 根因是**老版本 1.5.0 把作者的域名硬编码成默认值**: private static final String DEFAULT_RELAY_URL = "https://relay.xxmxxs.top"; 第一次运行就存进了 SharedPreferences。装新 APK 不会清偏好, 所以看起来像"默认还是我的中转站"。 这条规矩(**对外发布的软件里不能内置作者自己的中转站地址**) 踩过一次就会再踩 —— 用户明确说过"这个中转站我就自己用,不对外开放"。 所以用断言钉死。 覆盖: 一、默认值必须是空串 二、源码里不能有任何"写死的中转域名默认值" 三、必须有迁移逻辑,且迁移清单里包含那个历史域名 四、设置页存在(用户要求各种设置收在一起) 五、其它客户端(Windows / 服务端)的默认也得是空 """ from __future__ import annotations import re import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent WATCH = (ROOT / "android" / "src" / "com" / "lanremote" / "watch" / "MainActivity.java") passed = 0 failed = 0 def check(name, ok, detail=""): global passed, failed if ok: passed += 1 print(" [PASS] " + name) else: failed += 1 print(" [FAIL] " + name + ((" —— " + str(detail)) if detail else "")) def main() -> int: print("=" * 60) print(" 中转地址默认必须为空(对外软件不能内置作者的服务器)") print("=" * 60) check("MainActivity.java 存在", WATCH.is_file(), WATCH) if not WATCH.is_file(): return 1 src = WATCH.read_text(encoding="utf-8") # ---------------------------------------------------------- 一 print(" ---- 一、默认值是空 ----") m = re.search(r'DEFAULT_RELAY_URL\s*=\s*"([^"]*)"', src) check("能找到 DEFAULT_RELAY_URL", bool(m), "找不到定义") if m: value = m.group(1) check("默认值是空串", value == "", "实际是 %r —— 对外软件不能内置中转地址" % value) # ---------------------------------------------------------- 二 print(" ---- 二、没有写死的中转域名默认值 ----") # 找所有 "xxx = "https://某个域名"" 形式的赋值(非注释) hard = [] for i, line in enumerate(src.splitlines(), 1): s = line.strip() if s.startswith("//") or s.startswith("*") or s.startswith("/*"): continue for m2 in re.finditer(r'"https?://[a-z0-9.\-]+\.[a-z]{2,}[^"]*"', s): # 允许出现在迁移清单里(那是用来"清掉"的,不是默认值) if "LEGACY_BAKED_IN_RELAYS" in src[ max(0, src.find(s) - 400):src.find(s) + 400]: continue hard.append((i, m2.group(0))) # 更准的做法:把迁移清单那一段排除掉再看 cleaned = re.sub( r"private static final String\[\] LEGACY_BAKED_IN_RELAYS.*?\};", "", src, flags=re.S) cleaned = re.sub(r"//.*", "", cleaned) # 去行注释 cleaned = re.sub(r"/\*.*?\*/", "", cleaned, flags=re.S) # 去块注释 leftovers = [] for i, line in enumerate(cleaned.splitlines(), 1): for m3 in re.finditer(r'"https?://[a-z0-9.\-]+\.[a-z]{2,}[^"]*"', line): leftovers.append((i, m3.group(0))) check("除了迁移清单,没有别处写死中转域名", not leftovers, "还在这些地方:%s" % leftovers[:4]) # ---------------------------------------------------------- 三 print(" ---- 三、有迁移逻辑,能清掉老版本硬塞的值 ----") check("有 migrateLegacyRelay()", "migrateLegacyRelay" in src, "找不到迁移函数") check("启动时会调用它", re.search(r"^\s*migrateLegacyRelay\(\);\s*$", src, re.M) is not None, "定义了但没调用 = 迁移不生效") check("迁移清单里有那个历史域名", "relay.xxmxxs.top" in src, "清单里没包含历史域名") check("迁移是 remove 而不是覆盖成别的固定值", re.search(r'\.remove\("relay_url"\)', src) is not None, "应该是 remove,不能换成另一个硬编码值") check("只清完全相等的老值(用户手填的不能动)", "equalsIgnoreCase" in src, "没看到相等判断") # ---------------------------------------------------------- 四 print(" ---- 四、设置页存在 ----") for needle, note in (("buildSettingsPanel", "设置页构建"), ("showSettingsPanel", "打开展示"), ("settingsPanel", "面板字段"), ("showOnly(settingsPanel)", "面板纳入 showOnly(防漏藏)")): check(note, needle in src, "找不到 %s" % needle) # ---------------------------------------------------------- 五 print(" ---- 五、其它客户端的默认也得是空 ----") win = (ROOT / "windows" / "lanremote" / "server.py").read_text( encoding="utf-8") m4 = re.search(r'DEFAULT_RELAY_URL\s*=\s*"([^"]*)"', win) check("Windows 被控端默认是空", bool(m4) and m4.group(1) == "", m4.group(1) if m4 else "找不到") run = (ROOT / "windows" / "run_agent.py").read_text(encoding="utf-8") # run_agent 里不该有写死的公网域名(除了注释) run_clean = re.sub(r"#.*", "", run) run_clean = re.sub(r'""".*?"""', "", run_clean, flags=re.S) hits = re.findall(r'"https?://[a-z0-9.\-]+\.(?:top|com|cn|net)[^"]*"', run_clean) check("Windows 端源码里没有写死的中转域名", not hits, "还有:%s" % hits[:3]) print() print("=" * 60) print(" 通过 %d 项,失败 %d 项" % (passed, failed)) return 0 if failed == 0 else 1 if __name__ == "__main__": sys.exit(main())