#!/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 Add/update a share # dolphin-ftp-share delete Remove a share # dolphin-ftp-share passwd 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 < " } 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" <&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 }" >&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 Add/update a share" >&2 echo " delete Remove a share" >&2 echo " userexists Check if FTP user exists" >&2 echo " passwd Set/create FTP password" >&2 echo " deluser 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 Set passive mode with port range" >&2 exit 1 ;; esac