#!/bin/bash ############################################################################# # User Manager Library # Dynamic user listing and management for cPanel/Plesk/InterWorx ############################################################################# # Source dependencies if [ -z "$TOOLKIT_BASE_DIR" ]; then SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/common-functions.sh" source "$SCRIPT_DIR/system-detect.sh" fi # Initialize temp session directory if not set if [ -z "$TEMP_SESSION_DIR" ]; then TEMP_SESSION_DIR="/tmp/server-toolkit-$$" mkdir -p "$TEMP_SESSION_DIR" 2>/dev/null fi ############################################################################# # USER LISTING (Control Panel Specific) ############################################################################# list_all_users() { case "$SYS_CONTROL_PANEL" in cpanel) list_cpanel_users ;; plesk) list_plesk_users ;; interworx) list_interworx_users ;; *) list_system_users ;; esac } # cPanel user listing list_cpanel_users() { if [ -d "/var/cpanel/users" ]; then ls /var/cpanel/users/ 2>/dev/null || true else # Fallback: parse /etc/trueuserdomains awk -F: '{print $2}' /etc/trueuserdomains 2>/dev/null | sort -u || true fi } # Plesk user listing list_plesk_users() { if command_exists mysql && [ -f /etc/psa/.psa.shadow ]; then mysql -Ns psa -e "SELECT login FROM sys_users WHERE type='user'" 2>/dev/null else # Fallback: list directories find /var/www/vhosts -maxdepth 1 -type d -printf "%f\n" 2>/dev/null | grep -v "^system$\|^default$\|^chroot$" fi } # InterWorx user listing list_interworx_users() { if [ -x "/usr/local/interworx/bin/listaccounts.pex" ]; then /usr/local/interworx/bin/listaccounts.pex --output user 2>/dev/null else # Fallback: parse InterWorx config find /home -maxdepth 1 -type d -name "*.conf" 2>/dev/null | xargs -I {} basename {} .conf fi } # System users (no control panel) list_system_users() { # List users with home directories in /home and valid shells awk -F: '$6 ~ /^\/home\// && $7 !~ /nologin|false/ {print $1}' /etc/passwd } ############################################################################# # USER INFORMATION ############################################################################# get_user_info() { local username="$1" case "$SYS_CONTROL_PANEL" in cpanel) get_cpanel_user_info "$username" ;; plesk) get_plesk_user_info "$username" ;; interworx) get_interworx_user_info "$username" ;; *) get_system_user_info "$username" ;; esac } # cPanel user info get_cpanel_user_info() { local username="$1" local user_file="/var/cpanel/users/${username}" if [ ! -f "$user_file" ]; then echo "USER_EXISTS=no" return 1 fi # Parse cPanel user file local primary_domain=$(grep "^DNS=" "$user_file" | cut -d= -f2) local email=$(grep "^CONTACTEMAIL=" "$user_file" | cut -d= -f2) # cPanel doesn't store HOMEDIR in user file - it's always /home/username local home_dir="/home/${username}" # Get addon/parked domains local all_domains=$(grep "^DNS" "$user_file" | cut -d= -f2 | tr '\n' ' ') # Get disk usage local disk_used=$(du -sh "$home_dir" 2>/dev/null | awk '{print $1}') echo "USER_EXISTS=yes" echo "USERNAME=$username" echo "PRIMARY_DOMAIN=$primary_domain" echo "ALL_DOMAINS=$all_domains" echo "EMAIL=$email" echo "HOME_DIR=$home_dir" echo "DISK_USED=$disk_used" } # Plesk user info get_plesk_user_info() { local username="$1" if ! command_exists mysql || [ ! -f /etc/psa/.psa.shadow ]; then echo "USER_EXISTS=no" return 1 fi local home_dir="/var/www/vhosts/${username}" local primary_domain=$(mysql -Ns psa -e "SELECT name FROM domains WHERE id IN (SELECT domain_id FROM sys_users WHERE login='$username')" 2>/dev/null | head -1) local disk_used=$(du -sh "$home_dir" 2>/dev/null | awk '{print $1}') echo "USER_EXISTS=yes" echo "USERNAME=$username" echo "PRIMARY_DOMAIN=$primary_domain" echo "HOME_DIR=$home_dir" echo "DISK_USED=$disk_used" } # InterWorx user info get_interworx_user_info() { local username="$1" local home_dir="/home/${username}" if [ ! -d "$home_dir" ]; then echo "USER_EXISTS=no" return 1 fi local disk_used=$(du -sh "$home_dir" 2>/dev/null | awk '{print $1}') echo "USER_EXISTS=yes" echo "USERNAME=$username" echo "HOME_DIR=$home_dir" echo "DISK_USED=$disk_used" } # System user info (no control panel) get_system_user_info() { local username="$1" local user_entry=$(getent passwd "$username") if [ -z "$user_entry" ]; then echo "USER_EXISTS=no" return 1 fi local home_dir=$(echo "$user_entry" | cut -d: -f6) local disk_used=$(du -sh "$home_dir" 2>/dev/null | awk '{print $1}') echo "USER_EXISTS=yes" echo "USERNAME=$username" echo "HOME_DIR=$home_dir" echo "DISK_USED=$disk_used" } ############################################################################# # USER DOMAINS ############################################################################# get_user_domains() { local username="$1" case "$SYS_CONTROL_PANEL" in cpanel) get_cpanel_user_domains "$username" ;; plesk) get_plesk_user_domains "$username" ;; interworx) get_interworx_user_domains "$username" ;; *) echo "" ;; esac } get_cpanel_user_domains() { local username="$1" # Primary domain (format: domain: user) grep ": ${username}$" /etc/trueuserdomains 2>/dev/null | cut -d: -f1 || true # Addon domains if [ -f "/etc/userdatadomains" ]; then grep "==${username}$" /etc/userdatadomains 2>/dev/null | cut -d: -f1 || true fi } get_plesk_user_domains() { local username="$1" if command_exists mysql && [ -f /etc/psa/.psa.shadow ]; then mysql -Ns psa -e "SELECT d.name FROM domains d JOIN sys_users u ON d.id=u.domain_id WHERE u.login='$username'" 2>/dev/null fi } get_interworx_user_domains() { local username="$1" if [ -x "/usr/local/interworx/bin/listaccounts.pex" ]; then /usr/local/interworx/bin/listaccounts.pex --user "$username" --output domain 2>/dev/null fi } ############################################################################# # USER DATABASES ############################################################################# get_user_databases() { local username="$1" case "$SYS_CONTROL_PANEL" in cpanel) get_cpanel_user_databases "$username" ;; plesk) get_plesk_user_databases "$username" ;; interworx) get_interworx_user_databases "$username" ;; *) # Try to find databases matching username pattern mysql -e "SHOW DATABASES" 2>/dev/null | grep "^${username}_" ;; esac } get_cpanel_user_databases() { local username="$1" # cPanel databases typically follow pattern: username_dbname mysql -e "SHOW DATABASES" 2>/dev/null | grep "^${username}_" || true } get_plesk_user_databases() { local username="$1" if command_exists mysql && [ -f /etc/psa/.psa.shadow ]; then mysql -Ns psa -e "SELECT db.name FROM data_bases db JOIN domains d ON db.dom_id=d.id JOIN sys_users u ON d.id=u.domain_id WHERE u.login='$username'" 2>/dev/null fi } get_interworx_user_databases() { local username="$1" # InterWorx databases typically follow pattern: username_dbname mysql -e "SHOW DATABASES" 2>/dev/null | grep "^${username}_" || true } ############################################################################# # USER LOG FILES ############################################################################# get_user_log_files() { local username="$1" local domains=$(get_user_domains "$username") case "$SYS_CONTROL_PANEL" in cpanel) for domain in $domains; do echo "${SYS_LOG_DIR}/${domain}" echo "${SYS_LOG_DIR}/${domain}-ssl_log" done ;; plesk) echo "/var/www/vhosts/${username}/statistics/logs/access_log" echo "/var/www/vhosts/${username}/statistics/logs/error_log" for domain in $domains; do echo "/var/www/vhosts/${domain}/statistics/logs/access_log" echo "/var/www/vhosts/${domain}/statistics/logs/error_log" done ;; interworx) for domain in $domains; do echo "/home/${username}/var/${domain}/logs/access_log" echo "/home/${username}/var/${domain}/logs/error_log" done ;; esac | grep -v "^$" | sort -u } ############################################################################# # USER SELECTION MENU ############################################################################# select_user_interactive() { local prompt="${1:-Select a user}" local users=($(list_all_users)) local total_users=${#users[@]} if [ $total_users -eq 0 ]; then print_error "No users found" >&2 return 1 fi # Build user info cache to avoid repeated get_user_domains calls declare -A user_primary_domain declare -A user_domain_count for user in "${users[@]}"; do local domains=$(get_user_domains "$user" 2>/dev/null | grep -v "^$") if [ -n "$domains" ]; then user_domain_count["$user"]=$(echo "$domains" | wc -l) user_primary_domain["$user"]=$(echo "$domains" | head -1) else user_domain_count["$user"]=0 user_primary_domain["$user"]="(no domains)" fi done # Send all display output to stderr so it shows on screen (not captured by $(...)) { echo "" print_section "$prompt" echo "" echo "Found $total_users user(s) on this server" echo "-------------------------------------------------------------------------------" # Auto-show list if 10 or fewer users if [ $total_users -le 10 ]; then echo "" for user in "${users[@]}"; do echo -e " ${GREEN}$user${NC} - ${user_primary_domain[$user]} (${user_domain_count[$user]} domains)" done fi echo "" echo "-------------------------------------------------------------------------------" echo "" echo "Options:" if [ $total_users -gt 10 ]; then echo " L - List all $total_users users" fi echo " S [text] - Search/filter users (e.g., 's pick' or 's example.com')" echo " [username] - Enter exact username" echo " A - All users (system-wide scan)" echo " 0 - Cancel/Go back" echo "" } >&2 read -p "Enter choice> " choice case "$choice" in [Aa]|ALL|all) echo "ALL" return 0 ;; [Ss]\ *|[Ss]) # Search mode local search_term="${choice#[Ss] }" # Remove 'S ' prefix search_term="${search_term#[Ss]}" # Remove 'S' if no space if [ -z "$search_term" ]; then # No search term provided, ask for it read -p "Enter search term (username or domain)> " search_term >&2 fi if [ -z "$search_term" ]; then print_error "No search term provided" >&2 return 1 fi # Search and build match array local -a matched_users local -a menu_items for user in "${users[@]}"; do # Case-insensitive partial match in username or domain if [[ "${user,,}" == *"${search_term,,}"* ]] || [[ "${user_primary_domain[$user],,}" == *"${search_term,,}"* ]]; then matched_users+=("$user") menu_items+=("$user - ${user_primary_domain[$user]} (${user_domain_count[$user]} domains)") fi done if [ ${#matched_users[@]} -eq 0 ]; then print_error "No users found matching '$search_term'" >&2 return 1 elif [ ${#matched_users[@]} -eq 1 ]; then # Single match - ask for confirmation { echo "" echo "Found 1 match: ${matched_users[0]} - ${user_primary_domain[${matched_users[0]}]} (${user_domain_count[${matched_users[0]}]} domains)" echo "" } >&2 read -p "Use this user? (Y/n)> " confirm >&2 if [[ ! $confirm =~ ^[Nn]$ ]]; then echo "${matched_users[0]}" return 0 else return 1 fi else # Multiple matches - show interactive arrow-key menu { echo "" echo "Found ${#matched_users[@]} matches for '$search_term'" echo "Use arrow keys (↑/↓) to navigate, Enter to select:" echo "" } >&2 PS3="Select user> " select option in "${menu_items[@]}" "Cancel"; do if [ "$option" = "Cancel" ]; then return 1 elif [ -n "$option" ]; then # Extract username from selected menu item (before the ' - ') local selected_user="${matched_users[$((REPLY-1))]}" echo "$selected_user" return 0 fi done >&2 fi ;; [Ll]|LIST|list) # Show full list { echo "" echo "Complete user list ($total_users users):" echo "-------------------------------------------------------------------------------" for user in "${users[@]}"; do echo -e " ${GREEN}$user${NC} - ${user_primary_domain[$user]} (${user_domain_count[$user]} domains)" done echo "-------------------------------------------------------------------------------" echo "" } >&2 # Ask again after showing list read -p "Enter username (or A for all, 0 to cancel)> " choice # Re-evaluate choice if [[ " ${users[@]} " =~ " ${choice} " ]]; then echo "$choice" return 0 elif [ "$choice" = "A" ] || [ "$choice" = "a" ]; then echo "ALL" return 0 elif [ "$choice" = "0" ]; then return 1 else print_error "User '$choice' not found" >&2 return 1 fi ;; 0|cancel|back) return 1 ;; "") print_error "No input provided" >&2 return 1 ;; *) # Check if it's an exact username match if [[ " ${users[@]} " =~ " ${choice} " ]]; then echo "$choice" return 0 fi # Not exact match print_error "User '$choice' not found" >&2 if [ $total_users -gt 10 ]; then echo " Tip: Type 'L' to list all users" >&2 fi return 1 ;; esac } ############################################################################# # USER PROCESSES ############################################################################# get_user_processes() { local username="$1" ps aux | grep "^${username}" | grep -v grep } get_user_top_processes() { local username="$1" local limit="${2:-10}" ps aux | grep "^${username}" | grep -v grep | sort -k3 -rn | head -n "$limit" } ############################################################################# # DATABASE HELPER FUNCTIONS ############################################################################# get_database_owner() { local db_name="$1" # Try to determine owner from database name prefix (common cPanel convention) # Database names are typically: username_dbname local prefix=$(echo "$db_name" | cut -d_ -f1) # Check if this prefix matches a user local users=$(list_all_users) for user in $users; do if [ "$user" = "$prefix" ]; then echo "$user" return 0 fi done # If no match, return unknown echo "unknown" } get_database_domain() { local db_name="$1" local owner=$(get_database_owner "$db_name") # Try to get primary domain for the owner if [ "$owner" != "unknown" ]; then get_user_domains "$owner" 2>/dev/null | head -1 else echo "" fi } ############################################################################# # WORDPRESS DETECTION ############################################################################# find_user_wordpress_sites() { local username="$1" local home_dir=$(get_user_info "$username" | grep "^HOME_DIR=" | cut -d= -f2) if [ -z "$home_dir" ] || [ ! -d "$home_dir" ]; then return 1 fi # Find wp-config.php files find "$home_dir" -name "wp-config.php" -type f 2>/dev/null | while read wp_config; do local wp_dir=$(dirname "$wp_config") local domain=$(basename "$(dirname "$wp_dir")" 2>/dev/null) # Try to get actual domain from wp-config local site_url=$(grep "WP_SITEURL\|WP_HOME" "$wp_config" | head -1 | grep -oP "https?://\K[^/'\"]+") if [ -n "$site_url" ]; then echo "${site_url}|${wp_dir}" else echo "${domain}|${wp_dir}" fi done } ############################################################################# # DISPLAY FUNCTIONS ############################################################################# show_user_summary() { local username="$1" print_section "User Summary: $username" # Get user info local user_info=$(get_user_info "$username") if echo "$user_info" | grep -q "USER_EXISTS=no"; then print_error "User does not exist" return 1 fi # Parse info local primary_domain=$(echo "$user_info" | grep "^PRIMARY_DOMAIN=" | cut -d= -f2) local home_dir=$(echo "$user_info" | grep "^HOME_DIR=" | cut -d= -f2) local disk_used=$(echo "$user_info" | grep "^DISK_USED=" | cut -d= -f2) # Display echo " Username: $username" [ -n "$primary_domain" ] && echo " Primary Domain: $primary_domain" echo " Home Directory: $home_dir" echo " Disk Usage: $disk_used" echo "" # Domains local domains=$(get_user_domains "$username") local domain_count=$(echo "$domains" | grep -v "^$" | wc -l) echo " Domains ($domain_count):" echo "$domains" | sed 's/^/ - /' echo "" # Databases local databases=$(get_user_databases "$username") local db_count=$(echo "$databases" | grep -v "^$" | wc -l) echo " Databases ($db_count):" echo "$databases" | sed 's/^/ - /' echo "" } show_all_users_summary() { print_section "All Users Summary" local users=($(list_all_users)) local total_users=${#users[@]} echo " Total Users: $total_users" echo "" printf " ${BOLD}%-20s %-30s %10s %10s${NC}\n" "Username" "Primary Domain" "Domains" "Databases" echo " ────────────────────────────────────────────────────────────────────" for user in "${users[@]}"; do local primary=$(get_user_domains "$user" | head -1) local domain_count=$(get_user_domains "$user" | grep -v "^$" | wc -l) local db_count=$(get_user_databases "$user" | grep -v "^$" | wc -l) printf " %-20s %-30s %10s %10s\n" "$user" "$primary" "$domain_count" "$db_count" done echo "" }