diff --git a/includes/steps/115-email.php b/includes/steps/115-email.php
index 75d4b82..ea57a8a 100644
--- a/includes/steps/115-email.php
+++ b/includes/steps/115-email.php
@@ -59,6 +59,7 @@ return new class extends WPH_Step {
public function render_extra(array $session_state): void {
$current = wp_get_current_user();
$default_to = $current && $current->user_email ? $current->user_email : get_option('admin_email');
+ $debug_data = $session_state['email_debug'] ?? null;
?>
+
+
+
+ SMTP debug trace — transport: · captured ago
+
+
+
+
SMTP debug was requested but no trace lines were captured — transport was . PHPMailer only emits trace output for the smtp transport.
+
finding('last_send', 'bad', 'Last test send', 'invalid address', 'The destination address was not a valid email.');
}
+ $debug_requested = !empty($input['debug']);
// Capture any wp_mail_failed error so we can surface the underlying reason.
$error_msg = '';
- $capture = function ($wp_error) use (&$error_msg) {
+ $capture_error = function ($wp_error) use (&$error_msg) {
if (is_object($wp_error) && method_exists($wp_error, 'get_error_message')) {
$error_msg = (string) $wp_error->get_error_message();
}
};
- add_action('wp_mail_failed', $capture);
+ add_action('wp_mail_failed', $capture_error);
+
+ // Optional: turn on PHPMailer SMTP debug for this one send. Only useful
+ // when transport is 'smtp' — for plain PHP mail() there is no wire
+ // conversation to capture.
+ $trace = [];
+ $transport = 'mail';
+ $debug_hook = null;
+ if ($debug_requested) {
+ $debug_hook = function ($phpmailer) use (&$trace, &$transport) {
+ $transport = isset($phpmailer->Mailer) ? (string) $phpmailer->Mailer : 'mail';
+ $phpmailer->SMTPDebug = 2; // 0=off, 1=client, 2=client+server, 3=connection, 4=low-level
+ $phpmailer->Debugoutput = function ($line, $level) use (&$trace) {
+ // Redact AUTH credentials — PHPMailer emits the base64'd username/password
+ // when SMTPDebug captures the AUTH LOGIN handshake.
+ if (stripos($line, 'AUTH') !== false || preg_match('/^[A-Za-z0-9+\/=]{16,}$/', trim($line))) {
+ $line = '[REDACTED — credential or response containing it]';
+ }
+ $trace[] = trim($line);
+ };
+ };
+ add_action('phpmailer_init', $debug_hook, 1000);
+ }
$host = parse_url(get_site_url(), PHP_URL_HOST) ?: 'site';
$subject = '[Site Healthcheck] Test from ' . $host . ' — ' . date('Y-m-d H:i');
@@ -104,24 +141,39 @@ return new class extends WPH_Step {
. "If you received this, wp_mail() is working from this site.";
$sent = wp_mail($to, $subject, $body);
- remove_action('wp_mail_failed', $capture);
+
+ remove_action('wp_mail_failed', $capture_error);
+ if ($debug_hook) remove_action('phpmailer_init', $debug_hook, 1000);
+
+ // Stash the debug trace on the session so the UI can render it as a
+ // collapsible block. Returning it inside the finding's detail would
+ // mangle the formatting.
+ $session = WPH_Session::current();
+ if ($session && $debug_requested) {
+ $data = $session->data();
+ $data['email_debug'] = [
+ 'captured_at' => time(),
+ 'transport' => $transport,
+ 'trace' => array_slice($trace, 0, 200), // cap to avoid bloating the option
+ ];
+ update_option(WPH_OPT_SESSION, $data, false);
+ }
$time = date('Y-m-d H:i');
if ($sent) {
- return $this->finding(
- 'last_send',
- 'ok',
- 'Last test send',
- 'sent to ' . $to . ' at ' . $time,
- 'wp_mail() returned true. Confirm receipt in the inbox (check spam too).'
- );
+ $detail = 'wp_mail() returned true — the transport accepted handoff. This does not guarantee inbox delivery; confirm receipt (check spam too).';
+ if ($debug_requested) {
+ $detail .= ' SMTP debug ' . ($transport === 'smtp' ? 'captured below — ' . count($trace) . ' line(s).' : 'requested but transport is "' . $transport . '" (no SMTP conversation to capture).');
+ }
+ return $this->finding('last_send', 'ok', 'Last test send', 'sent to ' . $to . ' at ' . $time, $detail);
}
return $this->finding(
'last_send',
'bad',
'Last test send',
'failed at ' . $time,
- $error_msg ?: 'wp_mail() returned false but no error was captured. Check the host\'s mail logs.'
+ ($error_msg ?: 'wp_mail() returned false but no error was captured. Check the host\'s mail logs.')
+ . ($debug_requested && $trace ? ' (debug trace below)' : '')
);
}
};