Files
Linux-Server-Management-Too…/modules/email/email-diagnostics.sh
T
cschantz d2e5d3f940 Fix email diagnostics to search multiple log files for comprehensive results
The script now searches:
- /var/log/exim_mainlog (Exim delivery logs)
- /var/log/maillog (Dovecot auth + delivery)
- /var/log/messages (fallback)

This fixes the issue where only auth logs were found but actual
email deliveries were missed because they were in different log files.
Now properly separates delivery events from authentication events
across all log sources.
2025-12-31 19:09:10 -05:00

655 lines
23 KiB
Bash
Executable File

#!/bin/bash
################################################################################
# Email Account/Domain Diagnostics
################################################################################
# Purpose: Verify email is working for specific address or domain
# Shows proof of delivery or identifies why emails aren't working
################################################################################
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
source "$SCRIPT_DIR/lib/common-functions.sh"
source "$SCRIPT_DIR/lib/system-detect.sh"
source "$SCRIPT_DIR/lib/email-functions.sh"
show_banner "Email Diagnostics - Verify Email Delivery"
# Get mail log path
MAIL_LOG=$(get_mail_log_path)
if [ ! -f "$MAIL_LOG" ]; then
print_error "Mail log not found: $MAIL_LOG"
exit 1
fi
print_success "Using mail log: $MAIL_LOG"
echo ""
# Ask what to check
echo -e "${BOLD}What would you like to check?${NC}"
echo ""
echo " 1) Specific email address (e.g., user@example.com)"
echo " 2) Entire domain (e.g., example.com)"
echo ""
read -p "Enter choice [1]: " check_type
check_type=${check_type:-1}
# Get email/domain to check
echo ""
if [ "$check_type" = "2" ]; then
read -p "Enter domain to check (e.g., example.com): " target
search_pattern="@${target}"
check_label="domain $target"
else
read -p "Enter email address to check: " target
search_pattern="$target"
check_label="email $target"
fi
if [ -z "$target" ]; then
print_error "No email/domain provided"
exit 1
fi
# Time period to check
echo ""
echo "Check logs from:"
echo " 1) Last 1 hour"
echo " 2) Last 6 hours"
echo " 3) Last 24 hours (recommended)"
echo " 4) Last 48 hours"
echo " 5) Last week"
echo ""
read -p "Enter choice [3]: " time_choice
time_choice=${time_choice:-3}
case "$time_choice" in
1) hours=1 ;;
2) hours=6 ;;
3) hours=24 ;;
4) hours=48 ;;
5) hours=168 ;;
*) hours=24 ;;
esac
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
################################################################################
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
# 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
if [ ! -s "$TEMP_MATCHES" ]; then
print_error "NO EMAIL ACTIVITY FOUND for $check_label"
echo ""
echo "This means:"
echo " • No emails sent TO this $check_label"
echo " • No emails sent FROM this $check_label"
echo " • No delivery attempts logged"
echo ""
print_warning "Possible reasons:"
echo " 1. Email address/domain doesn't exist on this server"
echo " 2. No email activity in the last $hours hours"
echo " 3. Emails are going to a different mail server"
echo ""
# Check if domain exists
if [ "$check_type" = "2" ]; then
if grep -q "$target" /etc/localdomains 2>/dev/null || grep -q "$target" /etc/userdomains 2>/dev/null; then
print_info "Domain $target IS configured on this server"
else
print_warning "Domain $target NOT found in local domains"
fi
fi
rm -f "$TEMP_MATCHES" "$TEMP_AUTH" "$TEMP_ALL"
exit 0
fi
total_lines=$(wc -l < "$TEMP_MATCHES")
print_success "Found $total_lines log entries for $check_label"
echo ""
################################################################################
# Categorize activity
################################################################################
# Count different types first (sanitize to remove newlines)
delivered=$(grep -ci "=> .*$search_pattern\|delivered.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
delivered=$(echo "$delivered" | head -1 | tr -d '\n\r')
sent=$(grep -ci "<=.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
sent=$(echo "$sent" | head -1 | tr -d '\n\r')
bounced=$(grep -ci "bounce.*$search_pattern\|failed.*$search_pattern\|550.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || 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=$(echo "$deferred" | head -1 | tr -d '\n\r')
rejected=$(grep -ci "rejected.*$search_pattern\|denied.*$search_pattern\|blocked.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || 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=$(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=$(echo "$dkim_fail" | head -1 | tr -d '\n\r')
spam_rejected=$(grep -ci "spam.*$search_pattern\|SpamAssassin.*rejected.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || 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=$(echo "$greylist" | head -1 | tr -d '\n\r')
received=$(grep -ci "=> .*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || 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=$(echo "$auth_success" | head -1 | tr -d '\n\r')
################################################################################
# Quick Summary
################################################################################
print_header "QUICK SUMMARY"
echo ""
# Determine quick status
total_problems=$((bounced + rejected + spam_rejected))
total_email_activity=$((sent + received + delivered))
if [ "$total_email_activity" -gt 0 ] && [ "$total_problems" -eq 0 ]; then
print_success "Email appears to be working (${total_email_activity} email events, no problems)"
elif [ "$total_email_activity" -gt 0 ] && [ "$total_problems" -gt 0 ]; then
print_warning "Partial email issues (${total_email_activity} delivered, ${total_problems} failed)"
elif [ "$total_problems" -gt 0 ]; then
print_error "Email delivery problems detected (${total_problems} failures)"
elif [ "$auth_success" -gt 0 ] && [ "$total_email_activity" -eq 0 ]; then
print_info "Account active (${auth_success} logins) but no email traffic in last ${hours}h"
elif [ "$auth_failed" -gt 0 ]; then
print_warning "Failed login attempts detected (${auth_failed} attempts) - possible attack"
else
print_info "No activity detected in last ${hours} hours"
fi
echo ""
################################################################################
# Detailed Activity Breakdown
################################################################################
print_header "EMAIL DELIVERY ACTIVITY (last $hours hours)"
echo ""
# Always show main metrics for clarity
if [ "$received" -gt 0 ]; then
print_success "Received: $received emails (incoming TO this $check_label)"
else
echo "Received: 0 emails (no incoming mail)"
fi
if [ "$sent" -gt 0 ]; then
print_success "Sent: $sent emails (outgoing FROM this $check_label)"
else
echo "Sent: 0 emails (no outgoing mail)"
fi
if [ "$delivered" -gt 0 ]; then
print_success "Delivered: $delivered successful deliveries"
else
echo "Delivered: 0 (no completed deliveries logged)"
fi
echo ""
# Only show problems if they exist
has_problems=0
if [ "$bounced" -gt 0 ]; then
print_error "Bounced: $bounced emails bounced/failed"
has_problems=1
fi
if [ "$deferred" -gt 0 ]; then
print_warning "Deferred: $deferred emails temporarily delayed"
has_problems=1
fi
if [ "$rejected" -gt 0 ]; then
print_error "Rejected: $rejected emails blocked/rejected"
has_problems=1
fi
if [ "$spam_rejected" -gt 0 ]; then
print_error "Spam Rejected: $spam_rejected emails marked as spam"
has_problems=1
fi
if [ "$spf_fail" -gt 0 ]; then
print_warning "SPF Failures: $spf_fail authentication failures"
has_problems=1
fi
if [ "$dkim_fail" -gt 0 ]; then
print_warning "DKIM Failures: $dkim_fail signature failures"
has_problems=1
fi
if [ "$greylist" -gt 0 ]; then
print_info "Greylisted: $greylist emails temporarily delayed (normal anti-spam)"
fi
if [ $has_problems -eq 0 ]; then
print_success "No delivery problems detected"
fi
# Show email traffic patterns
if [ "$sent" -gt 0 ] || [ "$received" -gt 0 ]; then
echo ""
print_header "EMAIL TRAFFIC PATTERNS"
echo ""
# Top recipients (who this email is sending to)
if [ "$sent" -gt 0 ]; then
print_info "Top 5 recipients (emails sent TO):"
grep -i "<= .*$search_pattern" "$TEMP_MATCHES" | grep -oE "=> [^@]+@[^ ]+" | sed 's/=> //' | sort | uniq -c | sort -rn | head -5 | while read count recipient; do
echo " $recipient - $count emails"
done
echo ""
fi
# Top senders (who is sending to this email)
if [ "$received" -gt 0 ]; then
print_info "Top 5 senders (emails received FROM):"
grep -i "=> .*$search_pattern" "$TEMP_MATCHES" | grep -oE "<= [^@]+@[^ ]+" | sed 's/<= //' | sort | uniq -c | sort -rn | head -5 | while read count sender; do
echo " $sender - $count emails"
done
echo ""
fi
fi
# Show authentication summary
if [ -s "$TEMP_AUTH" ]; then
echo ""
print_header "MAILBOX ACCESS ACTIVITY (IMAP/POP3 Logins)"
echo ""
print_info "This shows if customer can access their mailbox (not email delivery)"
echo ""
if [ "$auth_success" -gt 0 ]; then
print_success "Successful Logins: $auth_success (password is correct, mailbox accessible)"
fi
if [ "$auth_failed" -gt 0 ]; then
print_error "Failed Logins: $auth_failed (WRONG PASSWORD or brute-force attack)"
# Extract IPs attempting failed logins
failed_ips=$(grep -i "auth failed\|Login aborted" "$TEMP_AUTH" | grep -oE "rip=[0-9.]+|from [0-9.]+" | sed 's/rip=//; s/from //' | sort | uniq -c | sort -rn)
if [ -n "$failed_ips" ]; then
echo ""
print_warning "IPs with failed login attempts:"
echo "$failed_ips" | while read count ip; do
echo " $ip - $count attempts"
done
fi
fi
fi
echo ""
################################################################################
# Account Status Check (for specific email addresses)
################################################################################
if [ "$check_type" != "2" ]; then
print_header "EMAIL ACCOUNT STATUS"
echo ""
# Extract username and domain from email
local_part=$(echo "$target" | cut -d'@' -f1)
domain_part=$(echo "$target" | cut -d'@' -f2)
# Check if email account exists in various locations
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
fi
# Check 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
if [ $account_found -eq 0 ] && [ -f "/var/mail/$local_part" ]; then
account_found=1
maildir="/var/mail/$local_part"
fi
# If successful logins exist, account must exist (even if we can't find the directory)
if [ $account_found -eq 0 ] && [ "$auth_success" -gt 0 ]; then
account_found=1
print_success "Email account EXISTS (confirmed by successful logins)"
print_warning "Note: Mailbox directory not found in standard locations"
elif [ $account_found -eq 1 ]; then
print_success "Email account EXISTS on this server"
# Show mailbox details if we found the directory
if [ -n "$maildir" ] && [ -d "$maildir" ]; then
disk_usage=$(du -sh "$maildir" 2>/dev/null | awk '{print $1}')
if [ -n "$disk_usage" ]; then
print_info "Mailbox size: $disk_usage"
fi
# Count messages
msg_count=$(find "$maildir" -type f -name "*:2,*" 2>/dev/null | wc -l)
if [ "$msg_count" -gt 0 ]; then
print_info "Messages stored: $msg_count"
fi
# Check for quota file
quota_file=$(find "$maildir" -name "maildirsize" 2>/dev/null | head -1)
if [ -f "$quota_file" ]; then
quota_info=$(head -1 "$quota_file" 2>/dev/null)
print_info "Quota: $quota_info"
fi
fi
else
print_warning "Email account NOT FOUND on this server"
echo ""
echo "This could mean:"
echo " • Account doesn't exist (typo in email address?)"
echo " • Account is on a different mail server"
echo " • Mail is handled by external service (Google, Office365, etc.)"
fi
# Check for forwarders
if [ -f "/etc/valiases/$domain_part" ]; then
forwarder=$(grep "^$local_part:" "/etc/valiases/$domain_part" 2>/dev/null)
if [ -n "$forwarder" ]; then
echo ""
print_info "Forwarder configured:"
echo " $forwarder"
fi
fi
echo ""
fi
################################################################################
# Show verdict
################################################################################
print_header "DIAGNOSTIC RESULT"
echo ""
# Determine overall status
if [ "$delivered" -gt 0 ] && [ "$bounced" -eq 0 ] && [ "$rejected" -eq 0 ] && [ "$spam_rejected" -eq 0 ]; then
print_success "EMAIL IS WORKING PROPERLY"
echo ""
echo "Evidence: $delivered successful deliveries in the last $hours hours"
echo "No bounces, rejections, or spam filtering detected"
echo ""
elif [ "$delivered" -gt 0 ] && [ "$bounced" -gt 0 ]; then
print_warning "EMAIL PARTIALLY WORKING"
echo ""
echo "Some emails delivered ($delivered) but some failed ($bounced)"
echo "Check bounce reasons below for details"
echo ""
elif [ "$bounced" -gt 0 ] || [ "$rejected" -gt 0 ] || [ "$spam_rejected" -gt 0 ]; then
print_error "EMAIL HAS DELIVERY PROBLEMS"
echo ""
echo "Emails are being rejected, bouncing, or filtered as spam"
echo "See details below for why"
echo ""
elif [ "$deferred" -gt 0 ]; then
print_warning "EMAIL DELAYED"
echo ""
echo "Emails are being deferred (temporary failures)"
echo "This usually resolves itself within minutes"
echo ""
elif [ "$sent" -gt 0 ] || [ "$received" -gt 0 ]; then
print_info "EMAIL ACTIVITY DETECTED"
echo ""
echo "Email traffic logged but no delivery confirmations in last $hours hours"
echo "This may be normal depending on email volume"
echo ""
elif [ "$auth_success" -gt 0 ] || [ "$auth_failed" -gt 0 ]; then
print_info "MAILBOX ACCESS ONLY"
echo ""
echo "No email delivery activity, only mailbox access (IMAP/POP3) detected"
echo "This means the account exists and is being checked, but no emails sent/received"
echo ""
if [ "$auth_success" -gt 0 ]; then
echo "Authentication is working (customer can access mailbox)"
fi
if [ "$auth_failed" -gt 0 ]; then
echo "Failed login attempts detected (see details below)"
fi
echo ""
else
print_warning "NO EMAIL ACTIVITY FOUND"
echo ""
echo "No email sending, receiving, or mailbox access in the last $hours hours"
echo "Account may be inactive or not used during this time period"
echo ""
fi
################################################################################
# Show recent activity samples
################################################################################
if [ "$delivered" -gt 0 ]; then
print_header "RECENT SUCCESSFUL DELIVERIES (last 5 from past $hours hours)"
echo ""
print_info "PROOF - These emails were delivered recently:"
echo ""
grep -i "=> .*$search_pattern\|delivered.*$search_pattern" "$TEMP_MATCHES" | tail -5 | while read line; do
# Extract timestamp if present
timestamp=$(echo "$line" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}|[A-Z][a-z]{2} [0-9]+ [0-9]{2}:[0-9]{2}:[0-9]{2}' | head -1)
if [ -n "$timestamp" ]; then
echo -e " ${GREEN}[$timestamp]${NC} $line"
else
echo " $line"
fi
done
echo ""
fi
if [ "$bounced" -gt 0 ]; then
print_header "RECENT BOUNCES/FAILURES (last 5 from past $hours hours)"
echo ""
print_warning "PROOF - These emails failed recently:"
echo ""
grep -i "bounce.*$search_pattern\|failed.*$search_pattern\|550.*$search_pattern" "$TEMP_MATCHES" | tail -5 | while read line; do
timestamp=$(echo "$line" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}|[A-Z][a-z]{2} [0-9]+ [0-9]{2}:[0-9]{2}:[0-9]{2}' | head -1)
if [ -n "$timestamp" ]; then
echo -e " ${RED}[$timestamp]${NC} $line"
else
echo " $line"
fi
done
echo ""
# Extract bounce reasons
print_header "COMMON BOUNCE REASONS"
echo ""
grep -i "bounce\|550\|fail" "$TEMP_MATCHES" | grep -oE "550 .*|User unknown|Mailbox.*full|Relay.*denied|blocked" | sort | uniq -c | sort -rn | head -5
echo ""
fi
if [ "$rejected" -gt 0 ]; then
print_header "RECENT REJECTIONS (last 5)"
echo ""
grep -i "rejected.*$search_pattern\|denied.*$search_pattern\|blocked.*$search_pattern" "$TEMP_MATCHES" | tail -5 | while read line; do
echo " $line"
done
echo ""
fi
if [ "$spam_rejected" -gt 0 ]; then
print_header "RECENT SPAM REJECTIONS (last 5)"
echo ""
grep -i "spam.*$search_pattern\|SpamAssassin.*rejected" "$TEMP_MATCHES" | tail -5 | while read line; do
timestamp=$(echo "$line" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}|[A-Z][a-z]{2} [0-9]+ [0-9]{2}:[0-9]{2}:[0-9]{2}' | head -1)
if [ -n "$timestamp" ]; then
echo -e " ${RED}[$timestamp]${NC} $line"
else
echo " $line"
fi
done
echo ""
fi
if [ "$greylist" -gt 0 ]; then
print_header "RECENT GREYLISTING EVENTS (last 5)"
echo ""
print_info "Note: Greylisting is temporary - emails usually deliver after retry"
echo ""
grep -i "greylist" "$TEMP_MATCHES" | tail -5 | while read line; do
echo " $line"
done
echo ""
fi
if [ "$spf_fail" -gt 0 ] || [ "$dkim_fail" -gt 0 ]; then
print_header "AUTHENTICATION ISSUES"
echo ""
if [ "$spf_fail" -gt 0 ]; then
echo "SPF failures detected:"
grep -i "SPF.*fail" "$TEMP_MATCHES" | head -3
echo ""
fi
if [ "$dkim_fail" -gt 0 ]; then
echo "DKIM failures detected:"
grep -i "DKIM.*fail" "$TEMP_MATCHES" | head -3
echo ""
fi
fi
################################################################################
# Recommendations
################################################################################
print_header "RECOMMENDATIONS"
echo ""
if [ "$bounced" -gt 0 ]; then
echo "To fix bounces:"
echo " 1. Verify the recipient email address is correct"
echo " 2. Check if recipient mailbox is full"
echo " 3. Ensure domain DNS is configured properly"
echo ""
fi
if [ "$rejected" -gt 0 ]; then
echo "To fix rejections:"
echo " 1. Check if server IP is blacklisted (use Blacklist Check tool)"
echo " 2. Verify SPF/DKIM/DMARC records are correct"
echo " 3. Check if recipient is blocking your domain"
echo ""
fi
if [ "$spf_fail" -gt 0 ]; then
echo "To fix SPF failures:"
echo " 1. Add this server's IP to domain's SPF record"
echo " 2. Use SPF/DKIM/DMARC Check tool for details"
echo ""
fi
if [ "$spam_rejected" -gt 0 ]; then
echo "To fix spam rejections:"
echo " 1. Check SpamAssassin score and adjust settings"
echo " 2. Verify email content isn't triggering spam filters"
echo " 3. Ensure proper SPF/DKIM authentication"
echo " 4. Check if sending domain has good reputation"
echo ""
fi
if [ "$greylist" -gt 0 ] && [ "$delivered" -eq 0 ]; then
echo "Greylisting delays:"
echo " • This is normal anti-spam behavior"
echo " • Emails should deliver after sender retries (5-15 minutes)"
echo " • No action needed unless emails never arrive"
echo ""
fi
if [ "$check_type" != "2" ] && [ $account_found -eq 0 ]; then
echo "Email account not found:"
echo " 1. Verify the email address is spelled correctly"
echo " 2. Check if domain DNS points to this server"
echo " 3. Create the email account if it should exist here"
echo " 4. Check if mail is handled by external service"
echo ""
fi
if [ "$auth_failed" -gt 0 ]; then
echo "Failed login attempts detected:"
echo " 1. Customer may be using wrong password"
echo " 2. Could be brute-force attack attempt"
echo " 3. Check IPs - block suspicious ones with firewall"
echo " 4. Advise customer to change password if compromised"
echo ""
fi
################################################################################
# Save full report
################################################################################
REPORT_FILE="/tmp/email_diag_${target//[@.]/_}_$(date +%Y%m%d_%H%M%S).txt"
cp "$TEMP_MATCHES" "$REPORT_FILE"
print_info "Full log saved to: $REPORT_FILE"
echo ""
# Cleanup
rm -f "$TEMP_MATCHES" "$TEMP_AUTH" "$TEMP_ALL"