#!/bin/bash ############################################################################# # Unified Domain/User Discovery Library # Abstracts control panel differences for consistent domain/user enumeration ############################################################################# # Source dependencies if [ -z "$TOOLKIT_BASE_DIR" ]; then SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" [ -f "$SCRIPT_DIR/common-functions.sh" ] && source "$SCRIPT_DIR/common-functions.sh" || { echo "ERROR: common-functions.sh not found" >&2; return 1; } [ -f "$SCRIPT_DIR/system-detect.sh" ] && source "$SCRIPT_DIR/system-detect.sh" || { echo "ERROR: system-detect.sh not found" >&2; return 1; } fi # Source control panel helpers if available if [ "$SYS_CONTROL_PANEL" = "plesk" ]; then # Try LIB_DIR first, then SCRIPT_DIR if [ -n "$LIB_DIR" ] && [ -f "$LIB_DIR/plesk-helpers.sh" ]; then source "$LIB_DIR/plesk-helpers.sh" elif [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/plesk-helpers.sh" ]; then source "$SCRIPT_DIR/plesk-helpers.sh" fi fi ############################################################################# # DOMAIN DISCOVERY (Control Panel Agnostic) ############################################################################# # List all domains on the server # Returns: One domain per line list_all_domains() { case "$SYS_CONTROL_PANEL" in cpanel) # cPanel: /etc/userdomains maps domains to users if [ -f /etc/userdomains ]; then awk -F': ' '{print $1}' /etc/userdomains | grep -v "^\*" | sort -u else # Fallback: scan /var/cpanel/users/ for user_file in /var/cpanel/users/*; do [ -f "$user_file" ] && grep "^DNS=" "$user_file" | cut -d'=' -f2 done | sort -u fi ;; plesk) # Use plesk_list_domains if available, otherwise fallback if type plesk_list_domains >/dev/null 2>&1; then plesk_list_domains else # Fallback: scan vhosts directory ls -1 /var/www/vhosts/ 2>/dev/null | \ grep -v "^system$\|^chroot$\|^\.skel$\|^default$\|^fs$" | \ grep -v "^\." || true fi ;; interworx) # InterWorx: nodeworx CLI or directory scan if command_exists nodeworx; then nodeworx -u -n -c Siteworx -a list 2>/dev/null | tail -n +2 | awk '{print $2}' else # Fallback: scan /chroot/home/*/var/ find /chroot/home/*/var/* -maxdepth 0 -type d 2>/dev/null | \ awk -F'/' '{print $(NF)}' | sort -u fi ;; *) # Standalone: scan common web directories { find /var/www/html/*/public_html -maxdepth 0 -type d 2>/dev/null | awk -F'/' '{print $(NF-1)}' find /home/*/public_html -maxdepth 0 -type d 2>/dev/null | awk -F'/' '{print $(NF-1)}' find /var/www/*/public_html -maxdepth 0 -type d 2>/dev/null | awk -F'/' '{print $(NF-1)}' } | sort -u ;; esac } # Get document root for a domain # Usage: get_domain_docroot DOMAIN # Returns: /path/to/document/root get_domain_docroot() { local domain="$1" [ -z "$domain" ] && return 1 case "$SYS_CONTROL_PANEL" in cpanel) # cPanel: Get user from domain, then home dir local user=$(grep "^${domain}:" /etc/userdomains 2>/dev/null | awk -F': ' '{print $2}') if [ -n "$user" ]; then # Check if it's the main domain or addon local user_data="/var/cpanel/users/$user" if [ -f "$user_data" ]; then local main_domain=$(grep "^DNS=" "$user_data" | cut -d'=' -f2) if [ "$domain" = "$main_domain" ]; then echo "/home/$user/public_html" else # Addon domain or subdomain local docroot=$(grep -A1 "^${domain}:" /var/cpanel/userdata/$user/*.yaml 2>/dev/null | \ grep "documentroot:" | head -1 | awk '{print $2}') [ -n "$docroot" ] && echo "$docroot" || echo "/home/$user/public_html/$domain" fi fi fi ;; plesk) plesk_get_docroot "$domain" ;; interworx) # InterWorx: /chroot/home/USER/var/DOMAIN/html local user=$(find /chroot/home/*/var/$domain -maxdepth 0 -type d 2>/dev/null | awk -F'/' '{print $4}' | head -1) [ -n "$user" ] && echo "/chroot/home/$user/var/$domain/html" ;; *) # Standalone: common patterns for path in \ "/var/www/html/$domain/public_html" \ "/var/www/$domain/public_html" \ "/home/$domain/public_html" \ "/var/www/html/$domain" \ "/var/www/$domain" \ "/home/$domain/html"; do [ -d "$path" ] && echo "$path" && return 0 done ;; esac } # Get log directory for a domain # Usage: get_domain_logdir DOMAIN # Returns: /path/to/logs get_domain_logdir() { local domain="$1" [ -z "$domain" ] && return 1 case "$SYS_CONTROL_PANEL" in cpanel) # cPanel: /var/log/apache2/domlogs/ or /usr/local/apache/domlogs/ if [ -d "/var/log/apache2/domlogs" ]; then echo "/var/log/apache2/domlogs" elif [ -d "/usr/local/apache/domlogs" ]; then echo "/usr/local/apache/domlogs" fi ;; plesk) plesk_get_logdir "$domain" ;; interworx) # InterWorx: /chroot/home/USER/var/DOMAIN/logs local user=$(find /chroot/home/*/var/$domain -maxdepth 0 -type d 2>/dev/null | awk -F'/' '{print $4}' | head -1) [ -n "$user" ] && [ -d "/chroot/home/$user/var/$domain/logs" ] && \ echo "/chroot/home/$user/var/$domain/logs" ;; *) # Standalone: common log locations for logdir in \ "/var/log/httpd" \ "/var/log/apache2" \ "/var/log/nginx"; do [ -d "$logdir" ] && echo "$logdir" && return 0 done ;; esac } # Get access log path for a domain # Usage: get_domain_access_log DOMAIN [ssl] # Returns: /path/to/access_log get_domain_access_log() { local domain="$1" local ssl="${2:-}" local logdir case "$SYS_CONTROL_PANEL" in cpanel) logdir=$(get_domain_logdir "$domain") [ -z "$logdir" ] && return 1 if [ "$ssl" = "ssl" ]; then echo "$logdir/${domain}-ssl_log" else echo "$logdir/${domain}" fi ;; plesk) plesk_get_access_log "$domain" "$ssl" ;; interworx) logdir=$(get_domain_logdir "$domain") [ -z "$logdir" ] && return 1 echo "$logdir/access_log" ;; *) # Standalone: guess based on web server if [ "$SYS_WEB_SERVER" = "nginx" ]; then echo "/var/log/nginx/access.log" else echo "/var/log/httpd/access_log" fi ;; esac } # Get error log path for a domain # Usage: get_domain_error_log DOMAIN # Returns: /path/to/error_log get_domain_error_log() { local domain="$1" local logdir case "$SYS_CONTROL_PANEL" in cpanel) logdir=$(get_domain_logdir "$domain") [ -z "$logdir" ] && return 1 echo "$logdir/${domain}-error_log" ;; plesk) plesk_get_error_log "$domain" ;; interworx) logdir=$(get_domain_logdir "$domain") [ -z "$logdir" ] && return 1 echo "$logdir/error_log" ;; *) # Standalone: guess based on web server if [ "$SYS_WEB_SERVER" = "nginx" ]; then echo "/var/log/nginx/error.log" else echo "/var/log/httpd/error_log" fi ;; esac } # Get all log file paths (access and error logs for all domains) # Returns: One log file path per line get_all_log_files() { case "$SYS_CONTROL_PANEL" in cpanel) find /var/log/apache2/domlogs /usr/local/apache/domlogs -type f 2>/dev/null | \ grep -E '\.(log|_log)$|^[^.]+$' ;; plesk) # Check both old and new log locations { find /var/www/vhosts/system/*/logs -type f 2>/dev/null find /var/www/vhosts/*/logs -type f 2>/dev/null | grep -v "/system/" } | sort -u ;; interworx) find /chroot/home/*/var/*/logs -type f 2>/dev/null ;; *) find /var/log/httpd /var/log/apache2 /var/log/nginx -type f 2>/dev/null | \ grep -E '\.(log|_log)$|access|error' ;; esac } ############################################################################# # USER/OWNER DISCOVERY ############################################################################# # Get owner/user for a domain # Usage: get_domain_owner DOMAIN # Returns: username get_domain_owner() { local domain="$1" [ -z "$domain" ] && return 1 case "$SYS_CONTROL_PANEL" in cpanel) grep "^${domain}:" /etc/userdomains 2>/dev/null | awk -F': ' '{print $2}' ;; plesk) plesk_get_owner "$domain" ;; interworx) find /chroot/home/*/var/$domain -maxdepth 0 -type d 2>/dev/null | \ awk -F'/' '{print $4}' | head -1 ;; *) # Standalone: check directory ownership local docroot=$(get_domain_docroot "$domain") [ -n "$docroot" ] && stat -c "%U" "$docroot" 2>/dev/null ;; esac } # List all users (control panel accounts) # Returns: One username per line list_all_users() { case "$SYS_CONTROL_PANEL" in cpanel) ls -1 /var/cpanel/users/ 2>/dev/null ;; plesk) if plesk_cli_available; then plesk_exec bin user --list 2>/dev/null else # Fallback: unique owners from vhosts ls -1 /var/www/vhosts/ 2>/dev/null | \ grep -v "^system$\|^chroot$\|^\.skel$\|^default$\|^fs$" | \ while read -r domain; do [ -d "/var/www/vhosts/$domain" ] && stat -c "%U" "/var/www/vhosts/$domain" 2>/dev/null done | sort -u fi ;; interworx) ls -1 /chroot/home/ 2>/dev/null ;; *) # Standalone: users with /home directories ls -1 /home/ 2>/dev/null | while read -r user; do [ -d "/home/$user" ] && echo "$user" done ;; esac } ############################################################################# # PHP-FPM DISCOVERY ############################################################################# # Get PHP-FPM pool socket for a domain # Usage: get_domain_fpm_socket DOMAIN # Returns: /path/to/php-fpm.sock get_domain_fpm_socket() { local domain="$1" [ -z "$domain" ] && return 1 case "$SYS_CONTROL_PANEL" in cpanel) # cPanel: EA-PHP sockets in /opt/cpanel/ea-phpXX/root/usr/var/run/ local user=$(get_domain_owner "$domain") [ -z "$user" ] && return 1 # Find socket for this user find /opt/cpanel/ea-php*/root/usr/var/run/ -name "*${user}*" -type s 2>/dev/null | head -1 ;; plesk) plesk_get_fpm_socket "$domain" ;; interworx) # InterWorx: check /chroot/home/USER/var/DOMAIN/ local user=$(get_domain_owner "$domain") [ -z "$user" ] && return 1 find /chroot/home/$user/var/$domain/ -name "*.sock" -type s 2>/dev/null | head -1 ;; *) # Standalone: common socket locations find /var/run/php-fpm /run/php-fpm -name "*.sock" -type s 2>/dev/null | head -1 ;; esac } # Get all PHP-FPM pool sockets # Returns: One socket path per line get_all_fpm_sockets() { case "$SYS_CONTROL_PANEL" in cpanel) find /opt/cpanel/ea-php*/root/usr/var/run/ -name "*.sock" -type s 2>/dev/null ;; plesk) plesk_list_fpm_sockets ;; interworx) find /chroot/home/*/var/*/ -name "*.sock" -type s 2>/dev/null ;; *) find /var/run/php-fpm /run/php-fpm -name "*.sock" -type s 2>/dev/null ;; esac } ############################################################################# # DATABASE DISCOVERY ############################################################################# # List all databases for a domain # Usage: get_domain_databases DOMAIN # Returns: One database name per line get_domain_databases() { local domain="$1" [ -z "$domain" ] && return 1 case "$SYS_CONTROL_PANEL" in cpanel) local user=$(get_domain_owner "$domain") [ -z "$user" ] && return 1 # cPanel databases are prefixed with username_ mysql -e "SHOW DATABASES;" 2>/dev/null | grep "^${user}_" ;; plesk) plesk_list_domain_databases "$domain" ;; interworx) local user=$(get_domain_owner "$domain") [ -z "$user" ] && return 1 # InterWorx uses username prefix mysql -e "SHOW DATABASES;" 2>/dev/null | grep "^${user}_" ;; *) # Standalone: just list all non-system databases mysql -e "SHOW DATABASES;" 2>/dev/null | \ grep -v "Database\|information_schema\|performance_schema\|mysql\|sys" ;; esac } ############################################################################# # UTILITY FUNCTIONS ############################################################################# # Check if a domain exists on the server # Usage: domain_exists DOMAIN domain_exists() { local domain="$1" [ -z "$domain" ] && return 1 case "$SYS_CONTROL_PANEL" in cpanel) grep -q "^${domain}:" /etc/userdomains 2>/dev/null ;; plesk) plesk_domain_exists "$domain" ;; interworx) find /chroot/home/*/var/$domain -maxdepth 0 -type d 2>/dev/null | grep -q . ;; *) local docroot=$(get_domain_docroot "$domain") [ -n "$docroot" ] && [ -d "$docroot" ] ;; esac } # Get all domains with their document roots as TSV # Format: DOMAIN\t/path/to/docroot list_domains_with_docroots() { local domain docroot while IFS= read -r domain; do docroot=$(get_domain_docroot "$domain") [ -n "$docroot" ] && echo -e "$domain\t$docroot" done < <(list_all_domains) } # Export all functions export -f list_all_domains export -f get_domain_docroot export -f get_domain_logdir export -f get_domain_access_log export -f get_domain_error_log export -f get_all_log_files export -f get_domain_owner export -f list_all_users export -f get_domain_fpm_socket export -f get_all_fpm_sockets export -f get_domain_databases export -f domain_exists export -f list_domains_with_docroots