#!/usr/bin/env bash set -euo pipefail # Minimal KDE UI wrapper for Saikyo OS license install/status. # Uses kdialog and pkexec. if ! command -v kdialog >/dev/null 2>&1; then echo "ERROR: kdialog not found" >&2 exit 1 fi TITLE="SAIKYO OS — лицензия" ICON="/usr/share/icons/hicolor/512x512/apps/saikyo-activation-logo.png" human_duration() { local s="${1:-0}" if [[ -z "${s}" || ! "${s}" =~ ^[0-9]+$ ]]; then printf '%s' "—" return 0 fi local d h m d=$((s/86400)) h=$(((s%86400)/3600)) m=$(((s%3600)/60)) if [[ "$d" -gt 0 ]]; then printf '%sd %sh %sm' "$d" "$h" "$m" elif [[ "$h" -gt 0 ]]; then printf '%sh %sm' "$h" "$m" else printf '%sm' "$m" fi } parse_kv() { # Reads key=value lines from stdin and exports them as shell vars. local line k v while IFS= read -r line; do [[ -n "${line}" ]] || continue case "${line}" in *=*) k=${line%%=*} v=${line#*=} k=${k//-/_} case "${k}" in status|trial_days|trial_remaining_seconds|valid_until|expired|org_id|org_name|tier|machine_id|signature) printf -v "$k" '%s' "$v" ;; esac ;; esac done } get_status_fields() { status=""; trial_days=""; trial_remaining_seconds=""; valid_until=""; expired=""; org_id=""; org_name=""; tier=""; machine_id=""; signature="" local out out=$(/usr/sbin/saikyo-license status 2>/dev/null || true) parse_kv <<<"${out}" } status_summary() { get_status_fields local state="UNKNOWN" local rem="—" if [[ "${status:-}" == "trial" ]]; then state="TRIAL" rem=$(human_duration "${trial_remaining_seconds:-0}") elif [[ "${status:-}" == "missing" ]]; then state="MISSING" rem="—" else if [[ "${expired:-}" == "yes" ]]; then state="EXPIRED" else # If we have a valid_until and not expired=yes, treat as active (even if signature says invalid we still show it). if [[ -n "${valid_until:-}" ]]; then state="ACTIVE" else state="UNKNOWN" fi fi if [[ -n "${valid_until:-}" ]]; then local now exp_s now=$(date -u +%s 2>/dev/null || echo "") exp_s=$(date -u -d "${valid_until}" +%s 2>/dev/null || echo "") if [[ -n "${now}" && -n "${exp_s}" && "${exp_s}" =~ ^[0-9]+$ && "${now}" =~ ^[0-9]+$ && "${exp_s}" -gt "${now}" ]]; then rem=$(human_duration $((exp_s - now))) else rem="—" fi fi fi local org="—" [[ -n "${org_name:-}" ]] && org="${org_name}" local tier_txt="—" [[ -n "${tier:-}" ]] && tier_txt="${tier}" local until_txt="—" [[ -n "${valid_until:-}" ]] && until_txt="${valid_until}" local sig_txt="—" [[ -n "${signature:-}" ]] && sig_txt="${signature}" printf '%s\n' \ "Статус: ${state}" \ "Осталось: ${rem}" \ "Организация: ${org}" \ "Тариф: ${tier_txt}" \ "Действует до: ${until_txt}" \ "Подпись: ${sig_txt}" } has_valid_license() { # saikyo-license verify returns 0 only for a valid, installed license /usr/sbin/saikyo-license verify >/dev/null 2>&1 } show_status() { kdialog --title "$TITLE" --icon "$ICON" --msgbox "$(status_summary)" } install_license() { local json sig json=$(kdialog --title "$TITLE" --getopenfilename "$HOME" "license.json|license.json (*.json)" 2>/dev/null || true) [[ -n "${json}" ]] || return 0 sig=$(kdialog --title "$TITLE" --getopenfilename "$HOME" "license.sig|license.sig (*.sig)" 2>/dev/null || true) [[ -n "${sig}" ]] || return 0 # Run privileged install local cmd cmd=(pkexec /usr/sbin/saikyo-license install "$json" "$sig") local result result=$(${cmd[@]} 2>&1) || { kdialog --title "$TITLE" --icon "$ICON" --error "Ошибка установки лицензии:\n\n${result}" return 1 } kdialog --title "$TITLE" --icon "$ICON" --msgbox "Лицензия установлена.\n\n${result}" show_status } install_response_code() { local code code=$(kdialog --title "$TITLE" --icon "$ICON" --inputbox "SAIKYO OS оффлайн активация\n\nВставьте response-код (SAI-RC / SAI-RESP2 / SAI-RESP)" "" 2>/dev/null || true) [[ -n "${code}" ]] || return 0 local result result=$(pkexec /usr/sbin/saikyo-license install-code "${code}" 2>&1) || { kdialog --title "$TITLE" --icon "$ICON" --error "Ошибка оффлайн активации:\n\n${result}" return 1 } kdialog --title "$TITLE" --icon "$ICON" --msgbox "Лицензия установлена.\n\n${result}" show_status } show_files_info() { kdialog --title "$TITLE" --icon "$ICON" --msgbox "Файлы лицензии:\n\n/etc/saikyo/license/license.json\n/etc/saikyo/license/license.sig\n/etc/saikyo/license/trial_start (если trial)\n\nКлючи проверки:\n/usr/share/saikyo-os/license/saikyo-license-pub.pem\n/usr/share/saikyo-os/license/saikyo-license-ed25519-pub.pem" 2>/dev/null || true } show_diagnostics() { local out out=$( (/usr/sbin/saikyo-license status 2>&1 || true; echo; /usr/sbin/saikyo-license verify 2>&1 || true) | sed -n '1,200p' ) kdialog --title "$TITLE" --icon "$ICON" --msgbox "Диагностика (первые 200 строк):\n\n${out}" 2>/dev/null || true } show_machine_id() { local mid mid=$(cat /etc/machine-id 2>/dev/null || true) if [[ -z "${mid}" ]]; then kdialog --title "$TITLE" --icon "$ICON" --error "Не удалось прочитать /etc/machine-id" return 1 fi kdialog --title "$TITLE" --icon "$ICON" --msgbox "Идентификатор машины (machine-id):\n\n${mid}" } activate_online() { local token local server server="https://saikyo-os.ru/license" token=$(kdialog --title "$TITLE" --icon "$ICON" --inputbox "SAIKYO OS активация\n\nВведите токен активации" "" 2>/dev/null || true) [[ -n "${token}" ]] || return 0 local result result=$(pkexec /usr/sbin/saikyo-license activate --token "${token}" --server "${server}" 2>&1) || { kdialog --title "$TITLE" --icon "$ICON" --error "Ошибка онлайн-активации:\n\n${result}" return 1 } kdialog --title "$TITLE" --icon "$ICON" --msgbox "Онлайн-активация выполнена.\n\n${result}" show_status } open_portal() { if command -v xdg-open >/dev/null 2>&1; then xdg-open "https://saikyo-os.ru/license" >/dev/null 2>&1 || true fi } force_activation() { # Non-closable UX (as much as possible): closing/canceling just re-opens. # The only exit is a successful activation (valid license). if has_valid_license; then exit 0 fi while true; do if has_valid_license; then kdialog --title "$TITLE" --icon "$ICON" --msgbox "Лицензия активирована. Спасибо." exit 0 fi kdialog --title "$TITLE" --icon "$ICON" --msgbox "SAIKYO OS активация\n\nДля продолжения работы необходимо активировать лицензию.\n\nНажмите OK, чтобы ввести токен." 2>/dev/null || true if activate_online; then : else # Show error handled inside activate_online; loop. : fi done } if [[ "${1:-}" == "--force-activate" ]]; then shift || true force_activation fi while true; do header=$(status_summary) action=$(kdialog --title "$TITLE" --icon "$ICON" --menu "${header}\n\nДействия:" \ status "Показать статус" \ activate "Онлайн-активация/продление (токен)" \ offline "Оффлайн активация (response-код)" \ install "Установить лицензию (license.json + license.sig)" \ portal "Открыть портал" \ files "Где лежат файлы/ключи" \ diag "Диагностика" \ machine-id "Показать machine-id" \ exit "Выход" \ 2>/dev/null || true) case "$action" in status) show_status;; activate) activate_online;; offline) install_response_code;; install) install_license;; portal) open_portal;; files) show_files_info;; diag) show_diagnostics;; machine-id) show_machine_id;; exit|"") exit 0;; esac done