Add profile support to BackTunnel with defaults, named remotes, and seamless integration across CLI, GUI, and documentation
This commit is contained in:
101
Makefile
Normal file
101
Makefile
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
# BackTunnel convenience Makefile (for first-time users and simple installs)
|
||||||
|
|
||||||
|
PREFIX ?= /usr
|
||||||
|
BINDIR := $(PREFIX)/bin
|
||||||
|
MANDIR := $(PREFIX)/share/man
|
||||||
|
KIO_SM := $(PREFIX)/share/kio/servicemenus
|
||||||
|
KSVC5 := $(PREFIX)/share/kservices5/ServiceMenus
|
||||||
|
APPDIR := $(PREFIX)/share/applications
|
||||||
|
BCOMP := $(PREFIX)/share/bash-completion/completions
|
||||||
|
|
||||||
|
# Config paths for user init
|
||||||
|
XDG_CONFIG_HOME ?= $(HOME)/.config
|
||||||
|
BT_CFG_DIR := $(XDG_CONFIG_HOME)/backtunnel
|
||||||
|
BT_CFG_FILE := $(BT_CFG_DIR)/profiles.ini
|
||||||
|
BT_CFG_EXAMPLE := docs/profiles.ini.example
|
||||||
|
|
||||||
|
.PHONY: all init install uninstall refresh man check shellcheck help
|
||||||
|
|
||||||
|
all: help
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "BackTunnel Makefile"
|
||||||
|
@echo " make init # Copy example profiles.ini for current user (first run)"
|
||||||
|
@echo " make install # Install scripts, man page, service menus (root)"
|
||||||
|
@echo " make uninstall # Remove installed files (root)"
|
||||||
|
@echo " make refresh # Refresh KDE/desktop caches (root)"
|
||||||
|
@echo " make man # View man page locally (man ./man/backtunnel.1)"
|
||||||
|
@echo " make check # Basic bash syntax check"
|
||||||
|
@echo " make shellcheck # ShellCheck (if installed)"
|
||||||
|
|
||||||
|
# --- First-time user init (no root needed) ---
|
||||||
|
init:
|
||||||
|
@mkdir -p "$(BT_CFG_DIR)"
|
||||||
|
@if [ -f "$(BT_CFG_FILE)" ]; then \
|
||||||
|
echo "profiles.ini already exists at $(BT_CFG_FILE) — skipping."; \
|
||||||
|
else \
|
||||||
|
if [ -f "$(BT_CFG_EXAMPLE)" ]; then \
|
||||||
|
cp "$(BT_CFG_EXAMPLE)" "$(BT_CFG_FILE)"; \
|
||||||
|
echo "Created $(BT_CFG_FILE) from $(BT_CFG_EXAMPLE)."; \
|
||||||
|
else \
|
||||||
|
echo "Example file $(BT_CFG_EXAMPLE) not found."; exit 1; \
|
||||||
|
fi \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Install/uninstall (root or DESTDIR) ---
|
||||||
|
install:
|
||||||
|
@install -Dm755 scripts/backtunnel-share "$(DESTDIR)$(BINDIR)/backtunnel-share"
|
||||||
|
@install -Dm755 scripts/backtunnel-access "$(DESTDIR)$(BINDIR)/backtunnel-access"
|
||||||
|
@install -Dm755 scripts/backtunnel-share-gui "$(DESTDIR)$(BINDIR)/backtunnel-share-gui"
|
||||||
|
@install -Dm644 man/backtunnel.1 "$(DESTDIR)$(MANDIR)/man1/backtunnel.1"
|
||||||
|
@install -Dm644 completions/backtunnel.bash "$(DESTDIR)$(BCOMP)/backtunnel-share"
|
||||||
|
@install -Dm644 completions/backtunnel.bash "$(DESTDIR)$(BCOMP)/backtunnel-access"
|
||||||
|
@install -Dm644 servicemenus/backtunnel_share.desktop "$(DESTDIR)$(KIO_SM)/backtunnel_share.desktop"
|
||||||
|
@install -Dm644 servicemenus/backtunnel_access.desktop "$(DESTDIR)$(KIO_SM)/backtunnel_access.desktop"
|
||||||
|
# Plasma 5 legacy path (harmless if unused)
|
||||||
|
@install -Dm644 servicemenus/backtunnel_share.desktop "$(DESTDIR)$(KSVC5)/backtunnel_share.desktop"
|
||||||
|
@install -Dm644 servicemenus/backtunnel_access.desktop "$(DESTDIR)$(KSVC5)/backtunnel_access.desktop"
|
||||||
|
# Optional desktop launcher if present
|
||||||
|
@if [ -f desktop/backtunnel.desktop ]; then \
|
||||||
|
install -Dm644 desktop/backtunnel.desktop "$(DESTDIR)$(APPDIR)/backtunnel.desktop"; \
|
||||||
|
fi
|
||||||
|
@$(MAKE) refresh
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
@rm -f "$(DESTDIR)$(BINDIR)/backtunnel-share" \
|
||||||
|
"$(DESTDIR)$(BINDIR)/backtunnel-access" \
|
||||||
|
"$(DESTDIR)$(BINDIR)/backtunnel-share-gui"
|
||||||
|
@rm -f "$(DESTDIR)$(MANDIR)/man1/backtunnel.1"
|
||||||
|
@rm -f "$(DESTDIR)$(BCOMP)/backtunnel-share" \
|
||||||
|
"$(DESTDIR)$(BCOMP)/backtunnel-access"
|
||||||
|
@rm -f "$(DESTDIR)$(KIO_SM)/backtunnel_share.desktop" \
|
||||||
|
"$(DESTDIR)$(KIO_SM)/backtunnel_access.desktop"
|
||||||
|
@rm -f "$(DESTDIR)$(KSVC5)/backtunnel_share.desktop" \
|
||||||
|
"$(DESTDIR)$(KSVC5)/backtunnel_access.desktop"
|
||||||
|
@rm -f "$(DESTDIR)$(APPDIR)/backtunnel.desktop" || true
|
||||||
|
@$(MAKE) refresh
|
||||||
|
|
||||||
|
# --- Cache refreshers (no-op if tools missing) ---
|
||||||
|
refresh:
|
||||||
|
@command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database -q || true
|
||||||
|
@if command -v kbuildsycoca6 >/dev/null 2>&1; then \
|
||||||
|
kbuildsycoca6 --noincremental >/dev/null 2>&1 || true; \
|
||||||
|
elif command -v kbuildsycoca5 >/dev/null 2>&1; then \
|
||||||
|
kbuildsycoca5 --noincremental >/dev/null 2>&1 || true; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Dev helpers ---
|
||||||
|
man:
|
||||||
|
@man ./man/backtunnel.1
|
||||||
|
|
||||||
|
check:
|
||||||
|
@bash -n scripts/backtunnel-share
|
||||||
|
@bash -n scripts/backtunnel-access
|
||||||
|
@bash -n scripts/backtunnel-share-gui
|
||||||
|
@echo "bash -n OK."
|
||||||
|
|
||||||
|
shellcheck:
|
||||||
|
@command -v shellcheck >/dev/null 2>&1 || { echo "ShellCheck not installed"; exit 1; }
|
||||||
|
@shellcheck scripts/backtunnel-share
|
||||||
|
@shellcheck scripts/backtunnel-access
|
||||||
|
@shellcheck scripts/backtunnel-share-gui
|
||||||
12
README.md
12
README.md
@@ -58,6 +58,18 @@ backtunnel-access /path/to/folder from remoteuser:remotehost [options]
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 📁 Profiles (named remotes)
|
||||||
|
|
||||||
|
BackTunnel supports **profiles** to simplify connections. Instead of typing
|
||||||
|
`user@host -p PORT -l PORT …` every time, you can define defaults and named remotes in:
|
||||||
|
|
||||||
|
📖 Example config: see [docs/profiles.ini.example](docs/profiles.ini.example)
|
||||||
|
|
||||||
|
System-wide default: /etc/backtunnel/profiles.ini (admins can edit)
|
||||||
|
Packaged example: /usr/share/backtunnel/profiles.ini
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 🧰 Dolphin Service Menus
|
## 🧰 Dolphin Service Menus
|
||||||
|
|
||||||
Two context actions for Dolphin:
|
Two context actions for Dolphin:
|
||||||
|
|||||||
13
docs/profiles.ini.example
Normal file
13
docs/profiles.ini.example
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[default]
|
||||||
|
duration=2h
|
||||||
|
tunnel_port=2222
|
||||||
|
local_ssh_port=22
|
||||||
|
invite_mount=~/remote-rssh
|
||||||
|
invite=true
|
||||||
|
qr=false
|
||||||
|
|
||||||
|
[myvps]
|
||||||
|
user=me
|
||||||
|
host=vps.example.com
|
||||||
|
tunnel_port=4422
|
||||||
|
invite_mount=~/remote-rssh
|
||||||
118
man/backtunnel.1
118
man/backtunnel.1
@@ -1,51 +1,53 @@
|
|||||||
.TH backtunnel 1 "September 2025" "1.2" "BackTunnel – Reverse SSH Sharing Toolkit"
|
.TH backtunnel 1 "September 2025" "1.2" "BackTunnel – Reverse SSH Sharing Toolkit"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
backtunnel-share, backtunnel-access \- Secure reverse SSH folder sharing and access
|
backtunnel-share, backtunnel-access \- Secure reverse SSH folder sharing and access (with profiles)
|
||||||
|
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B backtunnel-share
|
.B backtunnel-share
|
||||||
/path/to/folder with remoteuser:remotehost for <duration> [options]
|
/path/to/folder with {remoteuser:remotehost|remoteuser@remotehost|@profilename} for <duration> [options]
|
||||||
|
|
||||||
.B backtunnel-access
|
.B backtunnel-access
|
||||||
/path/to/folder from remoteuser:remotehost [options]
|
/path/to/folder from {remoteuser:remotehost|remoteuser@remotehost} [options]
|
||||||
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
\fBbacktunnel-share\fR starts a reverse SSH tunnel from the local (sharing) machine to a remote, reachable host.
|
\fBbacktunnel-share\fR starts a reverse SSH tunnel from the local (sharing) machine to a remote,
|
||||||
The tunnel exposes the local sshd (typically port 22) on a loopback port on the remote host using \fBssh -R\fR.
|
reachable host. The tunnel exposes the local sshd (typically port 22) on a loopback port on the
|
||||||
The sharing ends automatically after the given \fIduration\fR via \fBtimeout\fR.
|
remote host using \fBssh -R\fR. Sharing ends automatically after the given \fIduration\fR via \fBtimeout\fR.
|
||||||
|
Profiles can be used to simplify the remote specification and default options.
|
||||||
|
|
||||||
With the \fB--invite\fR option, \fBbacktunnel-share\fR prints a ready-to-copy access command for the remote user,
|
\fBbacktunnel-access\fR mounts the shared folder from the remote side using \fBsshfs\fR by connecting
|
||||||
which can be pasted directly into a chat or terminal. The invite can also be rendered as a QR code or written to a file.
|
to \fBlocalhost:<port>\fR on the remote host (the port exposed by \fBbacktunnel-share\fR).
|
||||||
|
|
||||||
\fBbacktunnel-access\fR mounts the shared folder from the remote side using \fBsshfs\fR by connecting to \fBlocalhost:<port>\fR on the remote host
|
|
||||||
(the port exposed by \fBbacktunnel-share\fR).
|
|
||||||
|
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.SS backtunnel-share options
|
.SS backtunnel-share options
|
||||||
.TP
|
.TP
|
||||||
.B -p, --tunnel-port <PORT>
|
.B -p, --tunnel-port <PORT>
|
||||||
Remote port to bind with \fB-R\fR (default: 2222).
|
Remote port to bind with \fB-R\fR (default: 2222). May be provided via profile.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B -l, --local-ssh-port <PORT>
|
.B -l, --local-ssh-port <PORT>
|
||||||
Local sshd port to forward to (default: 22).
|
Local sshd port to forward to (default: 22). May be provided via profile.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B -i, --invite
|
.B -i, --invite
|
||||||
Print a ready-to-copy invite command for the remote user.
|
Print a ready-to-copy access command for the remote side.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B --invite-mount <PATH>
|
.B --invite-mount <PATH>
|
||||||
Mount point suggested in the invite (default: /mnt/remote-rssh).
|
Suggested mount point included in the invite text (default: /mnt/remote-rssh). May be provided via profile.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B --invite-file <FILE>
|
.B --invite-file <FILE>
|
||||||
Also write the invite text (including unmount hint) to the given file.
|
Write the invite text (including unmount hint) to the given file.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B --qr
|
.B --qr
|
||||||
Render the invite as a terminal QR code (requires \fBqrencode\fR).
|
Additionally render the invite as a QR code (requires \fBqrencode\fR).
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B -h, --help
|
||||||
|
Show usage.
|
||||||
|
|
||||||
.SS backtunnel-access options
|
.SS backtunnel-access options
|
||||||
.TP
|
.TP
|
||||||
@@ -65,9 +67,65 @@ Path to share (server) or to mount (client).
|
|||||||
.B remoteuser:remotehost
|
.B remoteuser:remotehost
|
||||||
Or \fBremoteuser@remotehost\fR. The remote host that accepts the initial SSH connection.
|
Or \fBremoteuser@remotehost\fR. The remote host that accepts the initial SSH connection.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B @profilename
|
||||||
|
A named profile that expands to \fBuser@host\fR and may also supply defaults for \fB--tunnel-port\fR,
|
||||||
|
\fB--local-ssh-port\fR, \fB--invite-mount\fR, and \fIduration\fR. See \fBPROFILES\fR.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B <duration>
|
.B <duration>
|
||||||
Time to keep the share active. Accepts \fBn\fR\fIs\fR|\fIm\fR|\fIh\fR|\fId\fR forms, e.g. 30m, 2h, 1d. Passed to \fBtimeout\fR.
|
Time to keep the share active. Accepts \fBn\fR\fIs\fR|\fIm\fR|\fIh\fR|\fId\fR forms, e.g. 30m, 2h, 1d (passed to \fBtimeout\fR).
|
||||||
|
May be provided via the \fB[default]\fR profile; the positional \fI<duration>\fR takes precedence.
|
||||||
|
|
||||||
|
.SH PROFILES
|
||||||
|
BackTunnel can read defaults and named remote definitions from (searched in order):
|
||||||
|
.P
|
||||||
|
\fB~/.config/backtunnel/profiles.ini\fR (per-user)
|
||||||
|
.br
|
||||||
|
\fB/etc/backtunnel/profiles.ini\fR (system-wide default)
|
||||||
|
.br
|
||||||
|
\fB/usr/share/backtunnel/profiles.ini\fR (packaged example/fallback)
|
||||||
|
.P
|
||||||
|
Two kinds of sections are recognized:
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B [default]
|
||||||
|
Global defaults applied unless overridden by a named profile or CLI flags:
|
||||||
|
.IP
|
||||||
|
\fBduration\fR=2h
|
||||||
|
.br
|
||||||
|
\fBtunnel_port\fR=2222
|
||||||
|
.br
|
||||||
|
\fBlocal_ssh_port\fR=22
|
||||||
|
.br
|
||||||
|
\fBinvite_mount\fR=~/remote-rssh
|
||||||
|
.br
|
||||||
|
\fBinvite\fR=true|false
|
||||||
|
.br
|
||||||
|
\fBqr\fR=true|false
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B [name]
|
||||||
|
A named profile providing at least \fBuser\fR and \fBhost\fR, and optional overrides:
|
||||||
|
.IP
|
||||||
|
\fBuser\fR=alice
|
||||||
|
.br
|
||||||
|
\fBhost\fR=vps.example.com
|
||||||
|
.br
|
||||||
|
\fBtunnel_port\fR=4422
|
||||||
|
.br
|
||||||
|
\fBlocal_ssh_port\fR=22
|
||||||
|
.br
|
||||||
|
\fBinvite_mount\fR=/mnt/remote-rssh
|
||||||
|
|
||||||
|
.P
|
||||||
|
To use a profile, replace the remote with \fB@name\fR, e.g.:
|
||||||
|
.P
|
||||||
|
.nf
|
||||||
|
backtunnel-share /path/to/folder with @workvps for 1d
|
||||||
|
.fi
|
||||||
|
.P
|
||||||
|
Command-line options \fBalways override\fR values from profiles.
|
||||||
|
|
||||||
.SH EXAMPLES
|
.SH EXAMPLES
|
||||||
.TP
|
.TP
|
||||||
@@ -81,14 +139,9 @@ Share for 1 day, using custom ports:
|
|||||||
/home/user/docs with alice:vps.example.com for 1d -p 4422 -l 2222
|
/home/user/docs with alice:vps.example.com for 1d -p 4422 -l 2222
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
Share for 2 hours and print an invite:
|
Share using a profile, then override port:
|
||||||
.B backtunnel-share
|
.B backtunnel-share
|
||||||
/home/user/docs with alice@vps.example.com for 2h -i
|
/home/user/docs with @workvps for 6h -p 5500
|
||||||
|
|
||||||
.TP
|
|
||||||
Share with QR invite:
|
|
||||||
.B backtunnel-share
|
|
||||||
/home/user/docs with alice@vps.example.com for 2h -i --qr
|
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
Mount with default port and mount point:
|
Mount with default port and mount point:
|
||||||
@@ -103,12 +156,21 @@ Mount with custom port and mount point:
|
|||||||
.SH NOTES
|
.SH NOTES
|
||||||
By default, \fBssh -R\fR binds to 127.0.0.1 on the remote side, limiting access to local users on the remote machine.
|
By default, \fBssh -R\fR binds to 127.0.0.1 on the remote side, limiting access to local users on the remote machine.
|
||||||
The client connects to \fBlocalhost:<port>\fR from the remote host.
|
The client connects to \fBlocalhost:<port>\fR from the remote host.
|
||||||
|
Ensure the sharing host's sshd provides a valid SFTP subsystem for sshfs.
|
||||||
|
|
||||||
The invite feature is intended for convenience: copy-paste the printed command into chat for the remote user.
|
.SH FILES
|
||||||
Unmount with \fBfusermount -u <mountpoint>\fR after use.
|
.TP
|
||||||
|
\fB~/.config/backtunnel/profiles.ini\fR
|
||||||
|
Per-user configuration file containing defaults and named profiles.
|
||||||
|
.TP
|
||||||
|
\fB/etc/backtunnel/profiles.ini\fR
|
||||||
|
System-wide default profiles (editable by admins).
|
||||||
|
.TP
|
||||||
|
\fB/usr/share/backtunnel/profiles.ini\fR
|
||||||
|
Packaged example/fallback used when user/system config is absent.
|
||||||
|
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
ssh(1), sshfs(1), timeout(1), autossh(1), qrencode(1), fusermount(1)
|
ssh(1), sshfs(1), timeout(1), autossh(1), fusermount(1)
|
||||||
|
|
||||||
.SH AUTHOR
|
.SH AUTHOR
|
||||||
Matjaž Mozetič
|
Matjaž Mozetič
|
||||||
|
|||||||
@@ -10,6 +10,75 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
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
|
TUNNEL_PORT=2222 # remote-side port exposed via -R
|
||||||
LOCAL_SSH_PORT=22 # local sshd port to forward to
|
LOCAL_SSH_PORT=22 # local sshd port to forward to
|
||||||
DURATION="" # required: e.g. 30m, 2h, 1d
|
DURATION="" # required: e.g. 30m, 2h, 1d
|
||||||
@@ -61,6 +130,15 @@ shift 5 || true
|
|||||||
[[ "$KW1" != "with" ]] && usage
|
[[ "$KW1" != "with" ]] && usage
|
||||||
[[ "$KW2" != "for" ]] && 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 ---
|
# --- optional flags ---
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
|
|||||||
@@ -1,45 +1,96 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Copyright (c) 2025. LUXIM d.o.o., Slovenia - Matjaž Mozetič.
|
|
||||||
|
|
||||||
# GUI wrapper for BackTunnel "Share" action (Dolphin service menu)
|
# 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
|
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:-}"
|
FOLDER="${1:-}"
|
||||||
if [[ -z "$FOLDER" ]]; then
|
if [[ -z "$FOLDER" ]]; then
|
||||||
kdialog --error "No folder selected." || true
|
kdialog --error "No folder selected." || true
|
||||||
|
echo "No folder argument passed from service menu." >>"$LOG"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- Defaults ---
|
# Defaults
|
||||||
REMOTE_DEFAULT="user@vps.example.com"
|
REMOTE_DEFAULT="user@vps.example.com"
|
||||||
DURATION_DEFAULT="2h"
|
DURATION_DEFAULT="2h"
|
||||||
TPORT_DEFAULT="2222"
|
TPORT_DEFAULT="2222"
|
||||||
LPORT_DEFAULT="22"
|
LPORT_DEFAULT="22"
|
||||||
INVITE_MOUNT_DEFAULT="/mnt/remote-rssh"
|
INVITE_MOUNT_DEFAULT="/mnt/remote-rssh"
|
||||||
|
|
||||||
# --- Prompts ---
|
# Profiles (search order: user -> system -> packaged fallback)
|
||||||
REMOTE="$(kdialog --inputbox "Remote (user@host or user:host):" "$REMOTE_DEFAULT")" || exit 1
|
PROFILES_USER="${XDG_CONFIG_HOME:-$HOME/.config}/backtunnel/profiles.ini"
|
||||||
DUR="$(kdialog --combobox "Share duration" 30m 2h 6h 1d 2d --editable "$DURATION_DEFAULT")" || exit 1
|
PROFILES_SYS="/etc/backtunnel/profiles.ini"
|
||||||
TPORT="$(kdialog --inputbox "Tunnel port on remote:" "$TPORT_DEFAULT")" || exit 1
|
PROFILES_PKG="/usr/share/backtunnel/profiles.ini"
|
||||||
LPORT="$(kdialog --inputbox "Local SSH port to expose:" "$LPORT_DEFAULT")" || exit 1
|
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
|
if kdialog --yesno "Print invite line for chat?"; then
|
||||||
INV="-i"
|
INV="-i"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
QR=""
|
QR="" # set to "--qr" if chosen
|
||||||
if kdialog --yesno "Show QR code for the invite?"; then
|
if kdialog --yesno "Show QR code for the invite?"; then
|
||||||
QR="--qr"
|
QR="--qr"
|
||||||
fi
|
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) ---
|
# Build command safely as an array; append optional flags conditionally (SC2206-safe)
|
||||||
exec konsole --noclose -e backtunnel-share \
|
cmd=( backtunnel-share "$FOLDER" with "$REMOTE" for "$DUR" -p "$TPORT" -l "$LPORT" --invite-mount "$MP" )
|
||||||
"$FOLDER" with "$REMOTE" for "$DUR" \
|
if [[ -n "$INV" ]]; then cmd+=("$INV"); fi
|
||||||
-p "$TPORT" -l "$LPORT" $INV $QR --invite-mount "$MP"
|
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