#!/bin/sh set -eu INSTALL_BIN_DIR="${LEMANA_VPN_BIN_DIR:-$HOME/bin}" CONFIG_DIR="${LEMANA_VPN_CONFIG_DIR:-$HOME/.config/lemana-vpn}" OC_CONFIG_DIR="${OPENCONNECT_LITE_CONFIG_DIR:-$HOME/.config/openconnect-lite}" OC_VENV="${LEMANA_VPN_OC_VENV:-$HOME/.local/pipx/venvs/openconnect-lite}" DNS_CLEANUP="${LEMANA_VPN_DNS_CLEANUP:-/usr/local/sbin/lemana-vpn-dns-cleanup}" USERNAME="${LEMANA_VPN_USERNAME:-60103293}" APP_DIR="${LEMANA_VPN_APP_DIR:-$HOME/Applications/LemanaVPN.app}" LAUNCH_AGENT="$HOME/Library/LaunchAgents/ru.dokops.LemanaVPN.plist" DRY_RUN=0 KEEP_CONFIG=0 KEEP_APP=0 REMOVE_KEYCHAIN=0 REMOVE_TOUCHID_HELPER=0 REMOVE_OPENCONNECT_LITE=0 if [ -t 1 ] && [ -z "${NO_COLOR:-}" ] && [ "${TERM:-}" != "dumb" ]; then C_RESET="$(printf '\033[0m')" C_BOLD="$(printf '\033[1m')" C_DIM="$(printf '\033[2m')" C_RED="$(printf '\033[31m')" C_GREEN="$(printf '\033[32m')" C_YELLOW="$(printf '\033[33m')" C_BLUE="$(printf '\033[34m')" C_CYAN="$(printf '\033[36m')" else C_RESET="" C_BOLD="" C_DIM="" C_RED="" C_GREEN="" C_YELLOW="" C_BLUE="" C_CYAN="" fi if [ "${LEMANA_VPN_NO_EMOJI:-0}" = "1" ]; then E_STEP=">" E_INFO="i" E_OK="+" E_WARN="!" E_SKIP="-" E_ERROR="x" else E_STEP="➡️" E_INFO="ℹ️" E_OK="✅" E_WARN="⚠️" E_SKIP="⏭️" E_ERROR="❌" fi usage() { cat <<'USAGE' Usage: sh uninstall.sh [options] Options: --keep-config Keep ~/.config/lemana-vpn --keep-app Keep ~/Applications/LemanaVPN.app and LaunchAgent --remove-keychain Remove VPN-related Keychain entries --remove-touchid-helper Remove ~/bin/keychain-fingerprint --remove-openconnect-lite Remove pipx openconnect-lite after patch rollback --dry-run Print actions without changing files -h, --help Show this help Default uninstall restores openconnect-lite patch backup, removes Lemana VPN scripts/config/sudoers/zsh aliases, and keeps shared package dependencies. USAGE } while [ "$#" -gt 0 ]; do case "$1" in --keep-config) KEEP_CONFIG=1 ;; --keep-app) KEEP_APP=1 ;; --remove-keychain) REMOVE_KEYCHAIN=1 ;; --remove-touchid-helper) REMOVE_TOUCHID_HELPER=1 ;; --remove-openconnect-lite) REMOVE_OPENCONNECT_LITE=1 ;; --dry-run) DRY_RUN=1 ;; -h|--help) usage exit 0 ;; *) echo "Unknown option: $1" >&2 usage >&2 exit 1 ;; esac shift done log() { printf '%s\n' "$*" } color_line() { color="$1" shift printf '%s%s%s\n' "$color" "$*" "$C_RESET" } log_step() { color_line "$C_BOLD$C_CYAN" "$E_STEP $*" } log_info() { color_line "$C_BLUE" "$E_INFO $*" } log_detail() { color_line "$C_DIM" " $*" } log_ok() { color_line "$C_GREEN" "$E_OK $*" } log_warn() { color_line "$C_YELLOW" "$E_WARN $*" } log_skip() { color_line "$C_DIM" "$E_SKIP $*" } die() { printf '%s%s ERROR: %s%s\n' "$C_RED" "$E_ERROR" "$*" "$C_RESET" >&2 exit 1 } run() { if [ "$DRY_RUN" -eq 1 ]; then printf '+' for arg in "$@"; do printf ' %s' "$arg" done printf '\n' return 0 fi "$@" } find_webengine_process() { if [ -n "${LEMANA_VPN_WEBENGINE_PROCESS:-}" ]; then printf '%s\n' "$LEMANA_VPN_WEBENGINE_PROCESS" return 0 fi find "$OC_VENV/lib" -path '*/site-packages/openconnect_lite/browser/webengine_process.py' -print -quit 2>/dev/null || true } restore_openconnect_lite_patch() { backup="$CONFIG_DIR/patch-backups/webengine_process.py.before-lemana-vpn" wep="$(find_webengine_process)" log_step "Проверяю runtime-патчи openconnect-lite" log_detail "Если Lemana VPN менял webengine_process.py, перед удалением возвращаем исходник из backup." if [ ! -f "$backup" ]; then log_skip "No openconnect-lite patch backup found; patch rollback skipped." return 0 fi if [ -z "$wep" ] || [ ! -f "$wep" ]; then log_warn "openconnect-lite source not found; patch rollback skipped." return 0 fi log_info "Restoring openconnect-lite source from patch backup" run cp "$backup" "$wep" log_ok "openconnect-lite patch rollback completed" } remove_zshrc_block() { zshrc="$HOME/.zshrc" if [ ! -f "$zshrc" ]; then log_skip "~/.zshrc not found; shell aliases skipped." return 0 fi tmp="$(mktemp)" if [ "$DRY_RUN" -eq 1 ]; then printf '+ update %s aliases\n' "$zshrc" rm -f "$tmp" return 0 fi awk ' /^# >>> lemana-vpn$/ { skip=1; next } /^# <<< lemana-vpn$/ { skip=0; next } skip != 1 { print } ' "$zshrc" > "$tmp" mv "$tmp" "$zshrc" log_ok "Shell aliases removed from $zshrc" } remove_keychain_entries() { if [ "$REMOVE_KEYCHAIN" -ne 1 ]; then log_skip "Keychain cleanup disabled; LDAP/TOTP and Bitwarden cached entries are kept." return 0 fi log_step "Удаляю VPN-записи из macOS Keychain" log_detail "Удаляются только записи openconnect-lite для пользователя $USERNAME и кэш Bitwarden Lemana VPN." if [ "$DRY_RUN" -eq 1 ]; then run security delete-generic-password -s openconnect-lite -a "$USERNAME" run security delete-generic-password -s openconnect-lite -a "totp/$USERNAME" run security delete-generic-password -s vpn-lemanapro -a bw-session run security delete-generic-password -s vpn-lemanapro -a bw-master else security delete-generic-password -s openconnect-lite -a "$USERNAME" >/dev/null 2>&1 || true security delete-generic-password -s openconnect-lite -a "totp/$USERNAME" >/dev/null 2>&1 || true security delete-generic-password -s vpn-lemanapro -a bw-session >/dev/null 2>&1 || true security delete-generic-password -s vpn-lemanapro -a bw-master >/dev/null 2>&1 || true fi log_ok "Keychain cleanup completed" } main() { [ "$(uname -s)" = "Darwin" ] || die "This uninstaller supports macOS only" log_step "Начинаю удаление Lemana VPN" log_detail "По умолчанию удаляются скрипты, sudoers, DNS wrapper, config, aliases и приложение." log_detail "Shared-зависимости Homebrew не удаляются; openconnect-lite удаляется только с --remove-openconnect-lite." restore_openconnect_lite_patch log_step "Удаляю CLI-скрипты" log_detail "Убираю vpn-lemanapro.sh и локальный uninstall helper из $INSTALL_BIN_DIR." run rm -f "$INSTALL_BIN_DIR/vpn-lemanapro.sh" run rm -f "$INSTALL_BIN_DIR/uninstall-lemana-vpn.sh" log_ok "CLI scripts removed" if [ "$REMOVE_TOUCHID_HELPER" -eq 1 ]; then log_info "Removing Touch ID helper: $INSTALL_BIN_DIR/keychain-fingerprint" run rm -f "$INSTALL_BIN_DIR/keychain-fingerprint" else log_skip "Touch ID helper kept; use --remove-touchid-helper to remove it." fi log_step "Удаляю sudoers и DNS cleanup wrapper" log_detail "macOS может запросить sudo-пароль, потому что эти файлы принадлежат root." run sudo rm -f /etc/sudoers.d/lemana-vpn-openconnect /etc/sudoers.d/lemana-vpn-dns run sudo rm -f "$DNS_CLEANUP" log_ok "sudoers and DNS cleanup wrapper removed" if [ "$KEEP_APP" -eq 0 ]; then log_step "Удаляю Menu Bar app" log_detail "Сначала останавливаю уже запущенное LemanaVPN, затем отключаю LaunchAgent и удаляю $APP_DIR." if [ "$DRY_RUN" -eq 0 ]; then if pgrep -x LemanaVPN >/dev/null 2>&1; then log_info "Stopping running LemanaVPN process" killall LemanaVPN >/dev/null 2>&1 || true sleep 1 fi launchctl unload "$LAUNCH_AGENT" >/dev/null 2>&1 || true else printf '+ killall LemanaVPN # if running\n' printf '+ launchctl unload %s\n' "$LAUNCH_AGENT" fi run rm -f "$LAUNCH_AGENT" run rm -rf "$APP_DIR" log_ok "Menu Bar app removed" else log_skip "Menu Bar app kept because --keep-app is set." fi log_step "Удаляю shell aliases" log_detail "Из ~/.zshrc удаляется только блок между # >>> lemana-vpn и # <<< lemana-vpn." remove_zshrc_block log_step "Удаляю openconnect-lite config" log_detail "Удаляется профиль SSO, который был создан установщиком Lemana VPN." run rm -f "$OC_CONFIG_DIR/config.toml" log_ok "openconnect-lite config removed" if [ "$KEEP_CONFIG" -eq 0 ]; then log_step "Удаляю Lemana VPN config" log_detail "Удаляется $CONFIG_DIR, включая backup runtime-патчей после их отката." run rm -rf "$CONFIG_DIR" log_ok "Lemana VPN config removed" else log_skip "Lemana VPN config kept because --keep-config is set." fi remove_keychain_entries if [ "$REMOVE_OPENCONNECT_LITE" -eq 1 ]; then if command -v pipx >/dev/null 2>&1; then log_step "Удаляю openconnect-lite из pipx" log_detail "Это опционально: пакет может использоваться не только Lemana VPN." run pipx uninstall openconnect-lite log_ok "openconnect-lite removed from pipx" else log_warn "pipx not found; openconnect-lite package removal skipped." fi else log_skip "openconnect-lite kept; use --remove-openconnect-lite to uninstall it from pipx." fi log_ok "Done." } main "$@"