Add profile support to BackTunnel with defaults, named remotes, and seamless integration across CLI, GUI, and documentation
This commit is contained in:
@@ -10,6 +10,75 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Config search order: user → system → packaged example
|
||||
CONFIG_USER="${XDG_CONFIG_HOME:-$HOME/.config}/backtunnel/profiles.ini"
|
||||
CONFIG_SYS="/etc/backtunnel/profiles.ini"
|
||||
CONFIG_PKG="/usr/share/backtunnel/profiles.ini"
|
||||
|
||||
# resolve CONFIG_FILE to first existing
|
||||
if [[ -f "$CONFIG_USER" ]]; then
|
||||
CONFIG_FILE="$CONFIG_USER"
|
||||
elif [[ -f "$CONFIG_SYS" ]]; then
|
||||
CONFIG_FILE="$CONFIG_SYS"
|
||||
else
|
||||
CONFIG_FILE="$CONFIG_PKG"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2317 # invoked later at runtime; ShellCheck can't see call path
|
||||
ini_get() { # ini_get SECTION KEY -> value
|
||||
local sec="$1" key="$2"
|
||||
awk -v s="[""$sec""]" -v k="$key" '
|
||||
$0==s {ok=1; next}
|
||||
/^\[/ {ok=0}
|
||||
ok && $0 ~ /^[[:alnum:]_.-]+[[:space:]]*=/ {
|
||||
line=$0
|
||||
sub(/[[:space:]]*=[[:space:]]*/, "=", line)
|
||||
split(line,a,"=")
|
||||
if (a[1]==k) {
|
||||
val=substr(line, index(line,"=")+1)
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/,"",val)
|
||||
print val
|
||||
exit
|
||||
}
|
||||
}' "${CONFIG_FILE}" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2317
|
||||
profile_expand_remote() { # "@name" -> user@host, otherwise pass through
|
||||
local in="$1"
|
||||
if [[ "$in" == @* ]]; then
|
||||
local name="${in#@}" user host
|
||||
user="$(ini_get "$name" user)"
|
||||
host="$(ini_get "$name" host)"
|
||||
if [[ -n "$user" && -n "$host" ]]; then
|
||||
printf '%s@%s\n' "$user" "$host"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
printf '%s\n' "$in"
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2317
|
||||
profile_apply_defaults() { # set globals if unset; named overrides default
|
||||
local name="$1" v
|
||||
# defaults
|
||||
v="$(ini_get default tunnel_port)"; [[ -n "$v" && "${TUNNEL_PORT}" == "2222" ]] && TUNNEL_PORT="$v"
|
||||
v="$(ini_get default local_ssh_port)"; [[ -n "$v" && "${LOCAL_SSH_PORT}" == "22" ]] && LOCAL_SSH_PORT="$v"
|
||||
v="$(ini_get default invite_mount)"; [[ -n "$v" && "${INVITE_MOUNT}" == "/mnt/remote-rssh" ]] && INVITE_MOUNT="$v"
|
||||
v="$(ini_get default invite)"; [[ "${v,,}" == "true" ]] && INVITE=true
|
||||
v="$(ini_get default qr)"; [[ "${v,,}" == "true" ]] && QR=true
|
||||
if [[ -z "$DURATION" ]]; then
|
||||
v="$(ini_get default duration)"; [[ -n "$v" ]] && DURATION="$v"
|
||||
fi
|
||||
# named section
|
||||
if [[ -n "$name" ]]; then
|
||||
v="$(ini_get "$name" tunnel_port)"; [[ -n "$v" ]] && TUNNEL_PORT="$v"
|
||||
v="$(ini_get "$name" local_ssh_port)"; [[ -n "$v" ]] && LOCAL_SSH_PORT="$v"
|
||||
v="$(ini_get "$name" invite_mount)"; [[ -n "$v" ]] && INVITE_MOUNT="$v"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
TUNNEL_PORT=2222 # remote-side port exposed via -R
|
||||
LOCAL_SSH_PORT=22 # local sshd port to forward to
|
||||
DURATION="" # required: e.g. 30m, 2h, 1d
|
||||
@@ -61,6 +130,15 @@ shift 5 || true
|
||||
[[ "$KW1" != "with" ]] && usage
|
||||
[[ "$KW2" != "for" ]] && usage
|
||||
|
||||
# Apply [default] + named profile (if REMOTE is @name) before flag overrides
|
||||
profile_name=""
|
||||
if [[ "$REMOTE" == @* ]]; then
|
||||
profile_name="${REMOTE#@}"
|
||||
fi
|
||||
profile_apply_defaults "$profile_name"
|
||||
# Expand @name -> user@host for actual ssh use
|
||||
REMOTE="$(profile_expand_remote "$REMOTE")"
|
||||
|
||||
# --- optional flags ---
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
|
||||
@@ -1,45 +1,96 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2025. LUXIM d.o.o., Slovenia - Matjaž Mozetič.
|
||||
|
||||
# GUI wrapper for BackTunnel "Share" action (Dolphin service menu)
|
||||
# Prompts for parameters via kdialog and launches backtunnel-share in Konsole.
|
||||
# Prompts for parameters via kdialog and launches backtunnel-share in a terminal.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# --- Selection from Dolphin ---
|
||||
LOG="/tmp/backtunnel-share-gui.$UID.log"
|
||||
exec > >(tee -a "$LOG") 2>&1
|
||||
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PATH:-}"
|
||||
|
||||
FOLDER="${1:-}"
|
||||
if [[ -z "$FOLDER" ]]; then
|
||||
kdialog --error "No folder selected." || true
|
||||
echo "No folder argument passed from service menu." >>"$LOG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Defaults ---
|
||||
# Defaults
|
||||
REMOTE_DEFAULT="user@vps.example.com"
|
||||
DURATION_DEFAULT="2h"
|
||||
TPORT_DEFAULT="2222"
|
||||
LPORT_DEFAULT="22"
|
||||
INVITE_MOUNT_DEFAULT="/mnt/remote-rssh"
|
||||
|
||||
# --- Prompts ---
|
||||
REMOTE="$(kdialog --inputbox "Remote (user@host or user:host):" "$REMOTE_DEFAULT")" || exit 1
|
||||
DUR="$(kdialog --combobox "Share duration" 30m 2h 6h 1d 2d --editable "$DURATION_DEFAULT")" || exit 1
|
||||
TPORT="$(kdialog --inputbox "Tunnel port on remote:" "$TPORT_DEFAULT")" || exit 1
|
||||
LPORT="$(kdialog --inputbox "Local SSH port to expose:" "$LPORT_DEFAULT")" || exit 1
|
||||
# Profiles (search order: user -> system -> packaged fallback)
|
||||
PROFILES_USER="${XDG_CONFIG_HOME:-$HOME/.config}/backtunnel/profiles.ini"
|
||||
PROFILES_SYS="/etc/backtunnel/profiles.ini"
|
||||
PROFILES_PKG="/usr/share/backtunnel/profiles.ini"
|
||||
if [[ -f "$PROFILES_USER" ]]; then PROFILES_FILE="$PROFILES_USER"
|
||||
elif [[ -f "$PROFILES_SYS" ]]; then PROFILES_FILE="$PROFILES_SYS"
|
||||
else PROFILES_FILE="$PROFILES_PKG"
|
||||
fi
|
||||
|
||||
INV="" # explicit if-then; avoids SC2015 ("A && B || C is not if-then-else")
|
||||
# If profiles exist, offer a profile picker and prefill dialogs
|
||||
if [[ -f "$PROFILES_FILE" ]]; then
|
||||
mapfile -t profs < <(awk '/^\[/{gsub(/^\[|\]$/,"",$1); if ($1!="default") print $1}' "$PROFILES_FILE")
|
||||
if (( ${#profs[@]} )); then
|
||||
choice="$(kdialog --combobox "Choose profile (or Cancel for manual)" "${profs[@]}")" || choice=""
|
||||
if [[ -n "$choice" ]]; then
|
||||
getval() { awk -v s="[""$choice""]" -v k="$1" '
|
||||
$0==s{ok=1; next} /^\[/{ok=0}
|
||||
ok && $0 ~ /^[[:alnum:]_.-]+[[:space:]]*=/ {
|
||||
line=$0; sub(/[[:space:]]*=[[:space:]]*/, "=", line)
|
||||
split(line,a,"="); if(a[1]==k){val=substr(line,index(line,"=")+1); gsub(/^[[:space:]]+|[[:space:]]+$/,"",val); print val; exit}
|
||||
}' "$PROFILES_FILE" 2>/dev/null; }
|
||||
u="$(getval user)"; h="$(getval host)"
|
||||
if [[ -n "$u" && -n "$h" ]]; then
|
||||
REMOTE_DEFAULT="$u@$h"
|
||||
fi
|
||||
v="$(getval tunnel_port)"; [[ -n "$v" ]] && TPORT_DEFAULT="$v"
|
||||
v="$(getval local_ssh_port)"; [[ -n "$v" ]] && LPORT_DEFAULT="$v"
|
||||
v="$(getval invite_mount)"; [[ -n "$v" ]] && INVITE_MOUNT_DEFAULT="$v"
|
||||
# optional: default duration from [default]
|
||||
vd="$(awk -v s="[default]" -v k="duration" '
|
||||
$0==s{ok=1; next} /^\[/{ok=0}
|
||||
ok && $0 ~ /^[[:alnum:]_.-]+[[:space:]]*=/ {
|
||||
line=$0; sub(/[[:space:]]*=[[:space:]]*/, "=", line)
|
||||
split(line,a,"="); if(a[1]==k){val=substr(line,index(line,"=")+1); gsub(/^[[:space:]]+|[[:space:]]+$/,"",val); print val; exit}
|
||||
}' "$PROFILES_FILE" 2>/dev/null)"
|
||||
[[ -n "$vd" ]] && DURATION_DEFAULT="$vd"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Dialogs (Cancel returns 1; treat as benign exit)
|
||||
REMOTE="$(kdialog --inputbox "Remote (user@host or user:host):" "$REMOTE_DEFAULT")" || exit 0
|
||||
DUR="$(kdialog --combobox "Share duration" 30m 2h 6h 1d 2d --editable "$DURATION_DEFAULT")" || exit 0
|
||||
TPORT="$(kdialog --inputbox "Tunnel port on remote:" "$TPORT_DEFAULT")" || exit 0
|
||||
LPORT="$(kdialog --inputbox "Local SSH port to expose:" "$LPORT_DEFAULT")" || exit 0
|
||||
|
||||
INV="" # set to "-i" if chosen
|
||||
if kdialog --yesno "Print invite line for chat?"; then
|
||||
INV="-i"
|
||||
fi
|
||||
|
||||
QR=""
|
||||
QR="" # set to "--qr" if chosen
|
||||
if kdialog --yesno "Show QR code for the invite?"; then
|
||||
QR="--qr"
|
||||
fi
|
||||
|
||||
MP="$(kdialog --inputbox "Suggested mount point in invite:" "$INVITE_MOUNT_DEFAULT")" || exit 1
|
||||
MP="$(kdialog --inputbox "Suggested mount point in invite:" "$INVITE_MOUNT_DEFAULT")" || exit 0
|
||||
|
||||
# --- Run share in Konsole (keeps session open) ---
|
||||
exec konsole --noclose -e backtunnel-share \
|
||||
"$FOLDER" with "$REMOTE" for "$DUR" \
|
||||
-p "$TPORT" -l "$LPORT" $INV $QR --invite-mount "$MP"
|
||||
# Build command safely as an array; append optional flags conditionally (SC2206-safe)
|
||||
cmd=( backtunnel-share "$FOLDER" with "$REMOTE" for "$DUR" -p "$TPORT" -l "$LPORT" --invite-mount "$MP" )
|
||||
if [[ -n "$INV" ]]; then cmd+=("$INV"); fi
|
||||
if [[ -n "$QR" ]]; then cmd+=("$QR"); fi
|
||||
|
||||
if command -v konsole >/dev/null 2>&1; then
|
||||
exec konsole --noclose -e "${cmd[@]}"
|
||||
elif command -v xterm >/dev/null 2>&1; then
|
||||
exec xterm -hold -e "${cmd[@]}"
|
||||
else
|
||||
nohup "${cmd[@]}" >>"$LOG" 2>&1 &
|
||||
kdialog --msgbox "Sharing started in background.\nSee log: $LOG"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user