kde-dolphin-ftp-sharing-tab/scripts/dolphin-ftp-share

331 lines
10 KiB
Bash
Executable File

#!/bin/bash
# dolphin-ftp-share - Manage FTP folder shares for Dolphin with Pure-FTPd
#
# Each user gets a personal FTP root with only their shared folders.
# Virtual FTP users (PureDB) are created automatically.
# Permissions (ro/rw) are enforced via bind mount options.
#
# Usage:
# dolphin-ftp-share setup Initial Pure-FTPd setup
# dolphin-ftp-share info List all shares
# dolphin-ftp-share add <name> <path> <acl> Add/update a share
# dolphin-ftp-share delete <name> Remove a share
# dolphin-ftp-share passwd <username> <password> Set FTP password
set -e
SHARE_DIR="${HOME}/.local/share/dolphin-ftp-shares"
FTP_ROOT="${HOME}/.local/share/dolphin-ftp-root"
PASSWD_FILE="/etc/pure-ftpd/pureftpd.passwd"
PDB_FILE="/etc/pure-ftpd/pureftpd.pdb"
CALLER_UID=$(id -u)
CALLER_GID=$(id -g)
# Update home directory for an existing virtual FTP user.
ensure_virtual_user() {
local username="$1"
local user_root="$FTP_ROOT/users/$username"
mkdir -p "$user_root"
if sudo pure-pw show "$username" -f "$PASSWD_FILE" >/dev/null 2>&1; then
sudo pure-pw usermod "$username" \
-d "$user_root" \
-f "$PASSWD_FILE" 2>/dev/null
fi
}
rebuild_db() {
sudo pure-pw mkdb "$PDB_FILE" -f "$PASSWD_FILE"
}
# Unmount a share from all user roots
unmount_share() {
local name="$1"
if [ -d "$FTP_ROOT/users" ]; then
for user_dir in "$FTP_ROOT/users"/*/; do
[ -d "$user_dir" ] || continue
local mp="${user_dir}${name}"
if mountpoint -q "$mp" 2>/dev/null; then
sudo umount "$mp"
fi
rmdir "$mp" 2>/dev/null || true
done
fi
}
cmd_setup() {
echo "Setting up Pure-FTPd for Dolphin FTP sharing..."
mkdir -p "$FTP_ROOT/users"
# Enable PureDB authentication
echo "$PDB_FILE" | sudo tee /etc/pure-ftpd/conf/PureDB >/dev/null
sudo ln -sf /etc/pure-ftpd/conf/PureDB /etc/pure-ftpd/auth/50pure
# Chroot all users to their FTP home
echo "yes" | sudo tee /etc/pure-ftpd/conf/ChrootEveryone >/dev/null
# Disable anonymous access — only authenticated virtual users
echo "yes" | sudo tee /etc/pure-ftpd/conf/NoAnonymous >/dev/null
# Remove system auth so only PureDB users can log in
sudo rm -f /etc/pure-ftpd/auth/65unix /etc/pure-ftpd/auth/70pam
# Create empty password DB if needed
sudo touch "$PASSWD_FILE"
sudo pure-pw mkdb "$PDB_FILE" -f "$PASSWD_FILE"
# Enable logging
echo "yes" | sudo tee /etc/pure-ftpd/conf/VerboseLog >/dev/null
# Create sudoers rule for passwordless mount/umount/pure-pw
# Use SUDO_USER to get the real user (not root) when run with sudo
local REAL_USER="${SUDO_USER:-$USER}"
local REAL_HOME
REAL_HOME=$(getent passwd "$REAL_USER" | cut -d: -f6)
local REAL_FTP_ROOT="${REAL_HOME}/.local/share/dolphin-ftp-root"
sudo tee /etc/sudoers.d/dolphin-ftp-share >/dev/null <<EOF
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/mount --bind *
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/mount -o remount?ro?bind *
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/umount ${REAL_FTP_ROOT}/*
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/pure-pw *
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/tee /etc/pure-ftpd/*
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/tee -a /etc/pure-ftpd/*
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/rm -f /etc/pure-ftpd/conf/PassivePortRange
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/sed -i *pureftpd.passwd*
${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/systemctl restart pure-ftpd
EOF
sudo chmod 440 /etc/sudoers.d/dolphin-ftp-share
sudo systemctl restart pure-ftpd
echo ""
echo "Setup complete!"
echo "Share folders via Dolphin: right-click folder → Properties → FTP tab"
echo "Default FTP password = username. Change with:"
echo " dolphin-ftp-share passwd <user> <newpassword>"
}
cmd_info() {
[ -d "$SHARE_DIR" ] || exit 0
for conf in "$SHARE_DIR"/*.conf; do
[ -f "$conf" ] || continue
name=$(basename "$conf" .conf)
echo "[$name]"
cat "$conf"
echo ""
done
}
cmd_add() {
local name="$1"
local path="$2"
local acl="$3"
if [ -z "$name" ] || [ -z "$path" ]; then
echo "Error: name and path are required" >&2
exit 1
fi
mkdir -p "$SHARE_DIR" "$FTP_ROOT/users"
# Unmount any existing mounts for this share (handles permission changes)
unmount_share "$name"
# Save config
cat > "$SHARE_DIR/$name.conf" <<EOF
path=$path
user_acl=$acl
EOF
# Process per-user ACL: "user1:ro,user2:rw"
if [ -n "$acl" ]; then
IFS=',' read -ra entries <<< "$acl"
for entry in "${entries[@]}"; do
local username="${entry%%:*}"
local perm="${entry##*:}"
local user_root="$FTP_ROOT/users/$username"
local mount_point="$user_root/$name"
ensure_virtual_user "$username"
mkdir -p "$mount_point"
# Bind mount the shared folder into user's personal FTP root
sudo mount --bind "$path" "$mount_point"
# Read-only: remount with ro flag
if [ "$perm" = "ro" ]; then
sudo mount -o remount,ro,bind "$mount_point"
fi
done
fi
rebuild_db
echo "Share '$name' added: $path"
}
cmd_delete() {
local name="$1"
if [ -z "$name" ]; then
echo "Error: name is required" >&2
exit 1
fi
unmount_share "$name"
rm -f "$SHARE_DIR/$name.conf"
# Clean up
rmdir "$SHARE_DIR" 2>/dev/null || true
echo "Share '$name' deleted"
}
cmd_userexists() {
local username="$1"
if [ -z "$username" ]; then
exit 1
fi
if sudo pure-pw show "$username" -f "$PASSWD_FILE" >/dev/null 2>&1; then
exit 0
else
exit 1
fi
}
cmd_passwd() {
local username="$1"
local password="$2"
if [ -z "$username" ] || [ -z "$password" ]; then
echo "Error: username and password are required" >&2
exit 1
fi
local user_root="$FTP_ROOT/users/$username"
mkdir -p "$user_root"
# Generate MD5 password hash
local hash
hash=$(openssl passwd -1 "$password")
if sudo pure-pw show "$username" -f "$PASSWD_FILE" >/dev/null 2>&1; then
# User exists — update password in-place
sudo sed -i "s|^${username}:[^:]*:|${username}:${hash}:|" "$PASSWD_FILE"
echo "Password updated for '$username'"
else
# User doesn't exist — add entry to passwd file
# Format: username:hash:uid:gid:gecos:home:uploadbw:downloadbw:uploadratio:downloadratio:maxconn:filesquota:sizequota:allowedlocalip:allowedclientip:timerestrictions
echo "${username}:${hash}:${CALLER_UID}:${CALLER_GID}::${user_root}::::::::::::" | \
sudo tee -a "$PASSWD_FILE" >/dev/null
echo "Created FTP user '$username'"
fi
rebuild_db
}
cmd_deluser() {
local username="$1"
if [ -z "$username" ]; then
echo "Error: username is required" >&2
exit 1
fi
# Unmount all shares from this user's root first
local user_root="$FTP_ROOT/users/$username"
if [ -d "$user_root" ]; then
for mp in "$user_root"/*/; do
[ -d "$mp" ] || continue
if mountpoint -q "$mp" 2>/dev/null; then
sudo umount "$mp"
fi
rmdir "$mp" 2>/dev/null || true
done
rmdir "$user_root" 2>/dev/null || true
fi
# Remove user entry from passwd file
if sudo pure-pw show "$username" -f "$PASSWD_FILE" >/dev/null 2>&1; then
sudo sed -i "/^${username}:/d" "$PASSWD_FILE"
rebuild_db
echo "FTP user '$username' deleted"
else
echo "FTP user '$username' does not exist"
exit 1
fi
}
cmd_serverconfig() {
local action="$1"
case "$action" in
show)
# Output current mode and ports
if [ -f /etc/pure-ftpd/conf/PassivePortRange ]; then
local range
range=$(cat /etc/pure-ftpd/conf/PassivePortRange)
echo "mode=passive"
echo "passive_start=${range%% *}"
echo "passive_end=${range##* }"
else
echo "mode=active"
fi
;;
active)
# Remove passive port range → only active mode
sudo rm -f /etc/pure-ftpd/conf/PassivePortRange
sudo systemctl restart pure-ftpd
echo "Server set to active mode"
;;
passive)
local start_port="$2"
local end_port="$3"
if [ -z "$start_port" ] || [ -z "$end_port" ]; then
echo "Error: start and end port required" >&2
exit 1
fi
echo "${start_port} ${end_port}" | sudo tee /etc/pure-ftpd/conf/PassivePortRange >/dev/null
sudo systemctl restart pure-ftpd
echo "Server set to passive mode (ports ${start_port}-${end_port})"
;;
*)
echo "Usage: dolphin-ftp-share serverconfig {show|active|passive <start> <end>}" >&2
exit 1
;;
esac
}
case "${1:-}" in
setup) cmd_setup ;;
info) cmd_info ;;
add) cmd_add "$2" "$3" "${4:-}" ;;
delete) cmd_delete "$2" ;;
userexists) cmd_userexists "$2" ;;
passwd) cmd_passwd "$2" "$3" ;;
deluser) cmd_deluser "$2" ;;
serverconfig) cmd_serverconfig "$2" "$3" "$4" ;;
*)
echo "Usage: dolphin-ftp-share {setup|info|add|delete|userexists|passwd|deluser|serverconfig}" >&2
echo "" >&2
echo "Commands:" >&2
echo " setup Initial Pure-FTPd setup" >&2
echo " info List all shares" >&2
echo " add <name> <path> <acl> Add/update a share" >&2
echo " delete <name> Remove a share" >&2
echo " userexists <username> Check if FTP user exists" >&2
echo " passwd <username> <password> Set/create FTP password" >&2
echo " deluser <username> Delete FTP user (not system user)" >&2
echo " serverconfig show Show current server config" >&2
echo " serverconfig active Set active mode" >&2
echo " serverconfig passive <start> <end> Set passive mode with port range" >&2
exit 1
;;
esac