diff --git a/modules/email/email-diagnostics.sh b/modules/email/email-diagnostics.sh index 930ec88..600aba6 100755 --- a/modules/email/email-diagnostics.sh +++ b/modules/email/email-diagnostics.sh @@ -14,6 +14,13 @@ source "$SCRIPT_DIR/lib/email-functions.sh" show_banner "Email Diagnostics - Verify Email Delivery" +# Cleanup temporary files on script exit +cleanup() { + rm -f /tmp/email_diag_$$.txt /tmp/email_auth_$$.txt /tmp/email_all_$$.txt \ + /tmp/email_bounces_$$.txt /tmp/email_blacklists_$$.txt /tmp/email_blacklists_filtered_$$.txt 2>/dev/null +} +trap cleanup EXIT INT TERM + # Get mail log path MAIL_LOG=$(get_mail_log_path) if [ ! -f "$MAIL_LOG" ]; then @@ -124,15 +131,6 @@ echo "" print_info "Analyzing $check_label for last $hours hours..." echo "" -# Calculate time cutoff -if date --version 2>&1 | grep -q "GNU"; then - # GNU date - cutoff_time=$(date -d "$hours hours ago" +"%Y-%m-%d %H:%M:%S") -else - # BSD date (macOS) - cutoff_time=$(date -v-${hours}H +"%Y-%m-%d %H:%M:%S") -fi - ################################################################################ # Analysis ################################################################################ @@ -141,32 +139,14 @@ TEMP_MATCHES="/tmp/email_diag_$$.txt" TEMP_AUTH="/tmp/email_auth_$$.txt" TEMP_ALL="/tmp/email_all_$$.txt" -# Search multiple log files for comprehensive results -# Check Exim logs (email delivery) -if [ -f "/var/log/exim_mainlog" ]; then - grep -i "$search_pattern" /var/log/exim_mainlog >> "$TEMP_ALL" 2>/dev/null || true -fi - -# Check maillog (Dovecot auth + some delivery) -if [ -f "/var/log/maillog" ]; then - grep -i "$search_pattern" /var/log/maillog >> "$TEMP_ALL" 2>/dev/null || true -fi - -# Check messages log (fallback) -if [ -f "/var/log/messages" ]; then - grep -i "$search_pattern" /var/log/messages >> "$TEMP_ALL" 2>/dev/null || true -fi - -# If we found nothing, fall back to the detected mail log -if [ ! -s "$TEMP_ALL" ]; then - grep -i "$search_pattern" "$MAIL_LOG" > "$TEMP_ALL" 2>/dev/null || true -fi +# Use the detected mail log path (system-specific) +grep -iF "$search_pattern" "$MAIL_LOG" > "$TEMP_ALL" 2>/dev/null || true # Separate authentication events (IMAP/POP3 logins) grep -E "imap-login|pop3-login|dovecot.*Login|Logged in|Disconnected" "$TEMP_ALL" > "$TEMP_AUTH" 2>/dev/null || true # Get only email delivery events (exclude auth logs) -grep -v "imap-login\|pop3-login\|dovecot.*Login\|Logged in\|Disconnected" "$TEMP_ALL" > "$TEMP_MATCHES" 2>/dev/null || true +grep -vE "imap-login|pop3-login|dovecot.*Login|Logged in|Disconnected" "$TEMP_ALL" > "$TEMP_MATCHES" 2>/dev/null || true if [ ! -s "$TEMP_MATCHES" ]; then print_error "NO EMAIL ACTIVITY FOUND for $check_label" @@ -182,9 +162,26 @@ if [ ! -s "$TEMP_MATCHES" ]; then echo " 3. Emails are going to a different mail server" echo "" - # Check if domain exists + # Check if domain exists (control-panel aware) if [ "$check_type" = "2" ]; then - if grep -q "$target" /etc/localdomains 2>/dev/null || grep -q "$target" /etc/userdomains 2>/dev/null; then + domain_found=0 + + # cPanel domains + if [ -f "/etc/localdomains" ] && grep -qF "$target" /etc/localdomains 2>/dev/null; then + domain_found=1 + fi + + # Plesk domains (from /var/www/vhosts/*/conf/httpd.include) + if [ $domain_found -eq 0 ] && [ -d "/var/www/vhosts/$target" ]; then + domain_found=1 + fi + + # InterWorx domains (from /var/www/html/DOMAIN structure) + if [ $domain_found -eq 0 ] && [ -d "/var/www/html/$target" ]; then + domain_found=1 + fi + + if [ $domain_found -eq 1 ]; then print_info "Domain $target IS configured on this server" else print_warning "Domain $target NOT found in local domains" @@ -204,33 +201,33 @@ echo "" ################################################################################ # Count different types first (sanitize to remove newlines) -delivered=$(grep -ci "=> .*$search_pattern\|delivered.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0) +delivered=$(grep -F "$search_pattern" "$TEMP_MATCHES" 2>/dev/null | grep -ci "=>\|delivered" || echo 0) delivered=$(echo "$delivered" | head -1 | tr -d '\n\r') -sent=$(grep -ci "<=.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0) +sent=$(grep -F "$search_pattern" "$TEMP_MATCHES" 2>/dev/null | grep -ci "<=" || echo 0) sent=$(echo "$sent" | head -1 | tr -d '\n\r') # Only count actual email bounces, not auth failures or successful deliveries -bounced=$(grep -i "$search_pattern" "$TEMP_MATCHES" | grep -v "authenticator failed\|Authentication failed\|saved mail to\|=>" | grep -ci "550\|551\|552\|553\|554\|bounced\|Mail delivery failed\|** " 2>/dev/null || echo 0) +bounced=$(grep -F "$search_pattern" "$TEMP_MATCHES" 2>/dev/null | grep -v "authenticator failed\|Authentication failed\|saved mail to\|=>" | grep -ci "550\|551\|552\|553\|554\|bounced\|Mail delivery failed\|** " || echo 0) bounced=$(echo "$bounced" | head -1 | tr -d '\n\r') -deferred=$(grep -ci "deferred.*$search_pattern\|retry.*$search_pattern\|temporarily rejected" "$TEMP_MATCHES" 2>/dev/null || echo 0) +deferred=$(grep -F "$search_pattern" "$TEMP_MATCHES" 2>/dev/null | grep -ci "deferred\|retry\|temporarily rejected" || echo 0) deferred=$(echo "$deferred" | head -1 | tr -d '\n\r') -rejected=$(grep -i "$search_pattern" "$TEMP_MATCHES" | grep -v "authenticator failed\|Authentication failed" | grep -ci "rejected RCPT\|rejected.*relay.*denied\|rejected.*spam" 2>/dev/null || echo 0) +rejected=$(grep -F "$search_pattern" "$TEMP_MATCHES" 2>/dev/null | grep -v "authenticator failed\|Authentication failed" | grep -ci "rejected RCPT\|rejected.*relay.*denied\|rejected.*spam" || echo 0) rejected=$(echo "$rejected" | head -1 | tr -d '\n\r') -spf_fail=$(grep -ci "SPF.*fail.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0) +spf_fail=$(grep -F "$search_pattern" "$TEMP_MATCHES" 2>/dev/null | grep -ci "SPF.*fail" || echo 0) spf_fail=$(echo "$spf_fail" | head -1 | tr -d '\n\r') -dkim_fail=$(grep -ci "DKIM.*fail.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0) +dkim_fail=$(grep -F "$search_pattern" "$TEMP_MATCHES" 2>/dev/null | grep -ci "DKIM.*fail" || echo 0) dkim_fail=$(echo "$dkim_fail" | head -1 | tr -d '\n\r') # Only count actually rejected spam, not spam delivered to spam folder -spam_rejected=$(grep -i "$search_pattern" "$TEMP_MATCHES" | grep -i "spam" | grep -ci "rejected\|blocked\|denied" 2>/dev/null || echo 0) +spam_rejected=$(grep -F "$search_pattern" "$TEMP_MATCHES" 2>/dev/null | grep -i "spam" | grep -ci "rejected\|blocked\|denied" || echo 0) spam_rejected=$(echo "$spam_rejected" | head -1 | tr -d '\n\r') -greylist=$(grep -ci "greylist.*$search_pattern\|greylisted.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0) +greylist=$(grep -F "$search_pattern" "$TEMP_MATCHES" 2>/dev/null | grep -ci "greylist\|greylisted" || echo 0) greylist=$(echo "$greylist" | head -1 | tr -d '\n\r') -received=$(grep -ci "=> .*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0) +received=$(grep -F "$search_pattern" "$TEMP_MATCHES" 2>/dev/null | grep -ci "=> " || echo 0) received=$(echo "$received" | head -1 | tr -d '\n\r') # Count authentication events auth_failed=$(grep -ci "auth failed\|Login aborted\|authentication failed" "$TEMP_AUTH" 2>/dev/null || echo 0) auth_failed=$(echo "$auth_failed" | head -1 | tr -d '\n\r') -auth_success=$(grep -ci "Logged in\|Login:.*user=.*$search_pattern" "$TEMP_AUTH" 2>/dev/null || echo 0) +auth_success=$(grep -F "$search_pattern" "$TEMP_AUTH" 2>/dev/null | grep -ci "Logged in" || echo 0) auth_success=$(echo "$auth_success" | head -1 | tr -d '\n\r') ################################################################################ @@ -338,7 +335,7 @@ if [ "$sent" -gt 0 ] || [ "$received" -gt 0 ]; then # Top recipients (delivery recipients from emails in TEMP_MATCHES) if [ "$sent" -gt 0 ] || [ "$delivered" -gt 0 ]; then print_info "Top 5 recipients (emails delivered TO):" - grep -oE "=> [^@]+@[^ ]+" "$TEMP_MATCHES" | sed 's/=> //' | sort | uniq -c | sort -rn | head -5 | while read count recipient; do + grep -F "$search_pattern" "$TEMP_MATCHES" 2>/dev/null | grep -oE "=> [^@]+@[^ ]+" | sed 's/=> //' | sort | uniq -c | sort -rn | head -5 | while read count recipient; do [ -n "$count" ] && echo " $recipient - $count emails" done echo "" @@ -347,7 +344,7 @@ if [ "$sent" -gt 0 ] || [ "$received" -gt 0 ]; then # Top senders (who is sending emails in TEMP_MATCHES) if [ "$sent" -gt 0 ]; then print_info "Top 5 senders (emails sent FROM):" - grep -oE "<= [^@]+@[^ ]+" "$TEMP_MATCHES" | sed 's/<= //' | sort | uniq -c | sort -rn | head -5 | while read count sender; do + grep -F "$search_pattern" "$TEMP_MATCHES" 2>/dev/null | grep -oE "<= [^@]+@[^ ]+" | sed 's/<= //' | sort | uniq -c | sort -rn | head -5 | while read count sender; do [ -n "$count" ] && echo " $sender - $count emails" done echo "" @@ -399,21 +396,25 @@ if [ "$check_type" != "2" ]; then account_found=0 maildir="" - # Check cPanel mail directory - if [ -d "/home/*/mail/$domain_part/$local_part" ]; then - maildir=$(find /home/*/mail/$domain_part/$local_part -maxdepth 0 -type d 2>/dev/null | head -1) - if [ -n "$maildir" ]; then - account_found=1 - fi + # Try cPanel mail directory (common path) + if [ -d "/home/$domain_part/mail/$domain_part/$local_part" ] 2>/dev/null; then + maildir="/home/$domain_part/mail/$domain_part/$local_part" + account_found=1 fi - # Check Plesk mail directory + # Try Plesk mail directory if [ $account_found -eq 0 ] && [ -d "/var/qmail/mailnames/$domain_part/$local_part" ]; then account_found=1 maildir="/var/qmail/mailnames/$domain_part/$local_part" fi - # Check generic /var/mail + # Try InterWorx mail directory + if [ $account_found -eq 0 ] && [ -d "/home/$local_part/domains/$domain_part/mail/$local_part" ]; then + account_found=1 + maildir="/home/$local_part/domains/$domain_part/mail/$local_part" + fi + + # Try generic /var/mail if [ $account_found -eq 0 ] && [ -f "/var/mail/$local_part" ]; then account_found=1 maildir="/var/mail/$local_part" @@ -456,9 +457,23 @@ if [ "$check_type" != "2" ]; then echo " • Mail is handled by external service (Google, Office365, etc.)" fi - # Check for forwarders + # Check for forwarders (control-panel aware) + forwarder_found=0 + + # cPanel forwarders (in /etc/valiases) if [ -f "/etc/valiases/$domain_part" ]; then - forwarder=$(grep "^$local_part:" "/etc/valiases/$domain_part" 2>/dev/null) + forwarder=$(grep -F "^$local_part:" "/etc/valiases/$domain_part" 2>/dev/null) + if [ -n "$forwarder" ]; then + echo "" + print_info "Forwarder configured:" + echo " $forwarder" + forwarder_found=1 + fi + fi + + # Plesk forwarders (in /var/qmail/alias/.qmail-DOMAIN-user) + if [ $forwarder_found -eq 0 ] && [ -f "/var/qmail/alias/.qmail-$domain_part-$local_part" ]; then + forwarder=$(cat "/var/qmail/alias/.qmail-$domain_part-$local_part" 2>/dev/null | head -1) if [ -n "$forwarder" ]; then echo "" print_info "Forwarder configured:" @@ -551,7 +566,7 @@ if [ "$delivered" -gt 0 ]; then else echo " $line" fi - done < <(grep -i "=> .*$search_pattern\|delivered.*$search_pattern" "$TEMP_MATCHES" | tail -5) + done < <(grep -F "$search_pattern" "$TEMP_MATCHES" | grep -i "=>\|delivered" | tail -5) echo "" fi @@ -561,7 +576,7 @@ if [ "$bounced" -gt 0 ]; then # Get all bounce lines TEMP_BOUNCES="/tmp/email_bounces_$$.txt" - grep -i "$search_pattern" "$TEMP_MATCHES" | grep -v "authenticator failed\|Authentication failed\|saved mail to\|=>" | grep -i "550\|551\|552\|553\|554\|bounced\|Mail delivery failed\|** " > "$TEMP_BOUNCES" 2>/dev/null + grep -F "$search_pattern" "$TEMP_MATCHES" 2>/dev/null | grep -v "authenticator failed\|Authentication failed\|saved mail to\|=>" | grep -i "550\|551\|552\|553\|554\|bounced\|Mail delivery failed\|** " > "$TEMP_BOUNCES" 2>/dev/null # Categorize failures (sanitize counts to remove newlines) recipient_unknown=$(grep -ci "user unknown\|No such user\|does not exist\|recipient rejected\|Recipient address rejected\|550.*User" "$TEMP_BOUNCES" 2>/dev/null || echo 0) @@ -586,7 +601,7 @@ if [ "$bounced" -gt 0 ]; then if [ "$recipient_unknown" -gt 0 ]; then print_error " Recipient doesn't exist: $recipient_unknown emails" echo " Reason: Email address is invalid or doesn't exist on recipient server" - grep -i "user unknown\|No such user\|does not exist\|recipient rejected\|550.*User" "$TEMP_BOUNCES" | head -2 | while read line; do + grep -iE "user unknown|No such user|does not exist|recipient rejected|550.*User" "$TEMP_BOUNCES" | head -2 | while read line; do echo " Example: $(echo "$line" | grep -oE '[^ ]+@[^ ,]+' | head -1)" done echo "" @@ -595,7 +610,7 @@ if [ "$bounced" -gt 0 ]; then if [ "$mailbox_full" -gt 0 ]; then print_warning " Mailbox full: $mailbox_full emails" echo " Reason: Recipient's mailbox has exceeded storage quota" - grep -i "mailbox.*full\|quota.*exceeded\|552" "$TEMP_BOUNCES" | head -2 | while read line; do + grep -iE "mailbox.*full|quota.*exceeded|552" "$TEMP_BOUNCES" | head -2 | while read line; do echo " Example: $(echo "$line" | grep -oE '[^ ]+@[^ ,]+' | head -1)" done echo ""