#!/usr/bin/env bash # Вшивает адрес сервера и публичный ключ в клон RustDesk перед сборкой. # Патчит две константы в сабмодуле hbb_common: # pub const RENDEZVOUS_SERVERS: &[&str] = &["rs-ny.rustdesk.com"]; # pub const RS_PUB_KEY: &str = "OeVu..."; # # Значения берутся из окружения (см. config/server.env.example): # RUSTDESK_SERVER — хост(ы) hbbs. Несколько — через запятую. Порт по умолчанию 21116, # нестандартный указывать как host:port. # RUSTDESK_PUB_KEY — публичный ключ сервера (содержимое id_ed25519.pub, base64). # RUSTDESK_API_SERVER — (опц.) URL API-сервера (RustDesk Server Pro). Пусто — пропустить. # # Использование: RUSTDESK_SERVER=... RUSTDESK_PUB_KEY=... ./scripts/preconfigure.sh <путь-к-клону-rustdesk> set -euo pipefail SRC="${1:?укажи путь к клону rustdesk первым аргументом}" CFG="$SRC/libs/hbb_common/src/config.rs" [ -f "$CFG" ] || { echo "ОШИБКА: не найден $CFG — сабмодуль hbb_common не инициализирован?" >&2; exit 1; } : "${RUSTDESK_SERVER:?не задан RUSTDESK_SERVER}" : "${RUSTDESK_PUB_KEY:?не задан RUSTDESK_PUB_KEY}" # Собираем Rust-массив &["a", "b"] из запятой-разделённого списка серверов. IFS=',' read -ra SRV <<< "$RUSTDESK_SERVER" ARR="" for s in "${SRV[@]}"; do s="$(echo -n "$s" | tr -d '[:space:]')" [ -n "$s" ] && ARR="${ARR}\"${s}\", " done ARR="&[${ARR%, }]" # Экранируем спецсимволы sed-замены (& | \) в подставляемых значениях. esc() { printf '%s' "$1" | sed -e 's/[\\&|]/\\&/g'; } ARR_ESC="$(esc "$ARR")" KEY_ESC="$(esc "$RUSTDESK_PUB_KEY")" # Патч RENDEZVOUS_SERVERS sed -i -E "s|^pub const RENDEZVOUS_SERVERS: &\[&str\] = .*;|pub const RENDEZVOUS_SERVERS: \&[\&str] = ${ARR_ESC};|" "$CFG" # Патч RS_PUB_KEY sed -i -E "s|^pub const RS_PUB_KEY: &str = .*;|pub const RS_PUB_KEY: \&str = \"${KEY_ESC}\";|" "$CFG" # Проверяем, что патч применился (значения не печатаем целиком — только факт). grep -q "^pub const RENDEZVOUS_SERVERS: &\[&str\] = &\[\"" "$CFG" || { echo "ОШИБКА: не удалось пропатчить RENDEZVOUS_SERVERS" >&2; exit 1; } if grep -q "OeVuKk5nlHiXp" "$CFG"; then echo "ОШИБКА: RS_PUB_KEY остался дефолтным RustDesk" >&2; exit 1 fi echo "[preconfigure] RENDEZVOUS_SERVERS -> ${#SRV[@]} сервер(ов), RS_PUB_KEY вшит." # ───────────────────────────────────────────────────────────────────────────── # Custom-client: сервер/ключ ВИДНЫ и ЗАЛОЧЕНЫ в настройках (override-settings) + # опц. preset (постоянный) пароль в HARD_SETTINGS. Механизм RustDesk load_custom_client() # читает ПОДПИСАННЫЙ custom.txt (приватного ключа их генератора у нас нет) -> патчим # загрузчик под встроенный НЕподписанный конфиг (src/custom_client.json, include_str!), # что работает и на Windows, и на Android. Отключить: RUSTDESK_NO_CUSTOM_CLIENT=1. # ───────────────────────────────────────────────────────────────────────────── if [ "${RUSTDESK_NO_CUSTOM_CLIENT:-}" != "1" ]; then # Рабочий python (на Windows git-bash python3 = Store-заглушка -> берём python). PY=""; for p in python3 python; do "$p" -c 'import hashlib,base64,json,secrets' 2>/dev/null && { PY="$p"; break; }; done [ -n "$PY" ] || { echo "ОШИБКА: не найден рабочий python для custom-client" >&2; exit 1; } FIRST_SRV="$(echo -n "${SRV[0]}" | tr -d '[:space:]')" LOCK_PW="${RUSTDESK_LOCK_PASSWORD:-Y}" # залочить смену preset-пароля (по умолчанию да) "$PY" - "$SRC/src/custom_client.json" "$FIRST_SRV" "$RUSTDESK_PUB_KEY" "${RUSTDESK_API_SERVER:-}" "${RUSTDESK_PRESET_PASSWORD:-}" "$LOCK_PW" <<'PYEOF' import sys, json, hashlib, base64, secrets out, server, key, api, preset_pw, lock_pw = sys.argv[1:7] override = {"custom-rendezvous-server": server, "relay-server": server, "key": key} if api: override["api-server"] = api cfg = {"override-settings": override} if preset_pw: override["verification-method"] = "use-both-passwords" # постоянный + временный пароль if lock_pw == "Y": override["disable-change-permanent-password"] = "Y" salt = secrets.token_hex(16) h1 = hashlib.sha256(preset_pw.encode() + salt.encode()).digest() # = compute_permanent_password_h1 cfg["password"] = "00" + base64.b64encode(h1).decode() # HBBS preset storage: "00"+b64(h1) cfg["salt"] = salt with open(out, "w", encoding="utf-8") as f: json.dump(cfg, f, ensure_ascii=False) print("[preconfigure] custom_client.json: сервер виден+залочен, preset-пароль=" + ("да" if preset_pw else "нет")) PYEOF # Патч common.rs: встроенный неподписанный загрузчик (идемпотентно; клон сбрасывается перед каждой сборкой). CM="$SRC/src/common.rs" if ! grep -q "read_custom_client_unsigned" "$CM"; then sed -i 's|pub fn load_custom_client() {|pub fn load_custom_client() {\n read_custom_client_unsigned(include_str!("custom_client.json"));|' "$CM" grep -q 'read_custom_client_unsigned(include_str' "$CM" || { echo "ОШИБКА: не удалось внедрить вызов в load_custom_client" >&2; exit 1; } cat >> "$CM" <<'RUSTEOF' // [rustdesk-custom] Встроенный НЕподписанный custom-client конфиг (src/custom_client.json). pub fn read_custom_client_unsigned(json: &str) { let json = json.trim(); if json.is_empty() || json == "{}" { return; } let Ok(mut data) = serde_json::from_str::>(json) else { log::error!("Failed to parse embedded custom client config"); return; }; if let Some(app_name) = data.remove("app-name") { if let Some(app_name) = app_name.as_str() { *config::APP_NAME.write().unwrap() = app_name.to_owned(); } } let mut map_display_settings = HashMap::new(); for s in keys::KEYS_DISPLAY_SETTINGS { map_display_settings.insert(s.replace("_", "-"), s); } let mut map_local_settings = HashMap::new(); for s in keys::KEYS_LOCAL_SETTINGS { map_local_settings.insert(s.replace("_", "-"), s); } let mut map_settings = HashMap::new(); for s in keys::KEYS_SETTINGS { map_settings.insert(s.replace("_", "-"), s); } let mut buildin_settings = HashMap::new(); for s in keys::KEYS_BUILDIN_SETTINGS { buildin_settings.insert(s.replace("_", "-"), s); } if let Some(default_settings) = data.remove("default-settings") { read_custom_client_advanced_settings( default_settings, &map_display_settings, &map_local_settings, &map_settings, &buildin_settings, false, ); } if let Some(overwrite_settings) = data.remove("override-settings") { read_custom_client_advanced_settings( overwrite_settings, &map_display_settings, &map_local_settings, &map_settings, &buildin_settings, true, ); } for (k, v) in data { if let Some(v) = v.as_str() { config::HARD_SETTINGS.write().unwrap().insert(k, v.to_owned()); }; } } RUSTEOF echo "[preconfigure] common.rs пропатчен (встроенный custom-client загрузчик)." fi fi