]+>$/', $value, $match)) { $value = trim((string) $match[1]); } if (is_email($value)) { $value = ''; } $value = wp_strip_all_tags($value); $value = str_replace(array("\r", "\n", '"', '\\'), ' ', $value); $value = trim(preg_replace('/\s+/', ' ', $value)); if ('' === $value) { $value = $fallback; } return sanitize_text_field($value); } } if (!function_exists('matewa_fallback_mail_normalise_address_header')) { function matewa_fallback_mail_normalise_address_header($header_name, $value) { $header_name = ucfirst(strtolower((string) $header_name)); $value = trim(str_replace(array("\r", "\n"), '', (string) $value)); $email = ''; if (preg_match('/<([^>]+)>/', $value, $match)) { $email = sanitize_email((string) $match[1]); } else { $email = sanitize_email($value); } if (!is_email($email) && 'From' === $header_name) { $email = matewa_fallback_mail_safe_sender_email(); } if (!is_email($email)) { return ''; } $name = matewa_fallback_mail_header_display_name($value, 'Website Leads'); if ('reply-to' === strtolower($header_name)) { return 'Reply-To: ' . $email; } return 'From: ' . $name . ' <' . $email . '>'; } } if (!function_exists('matewa_fallback_mail_normalise_headers')) { function matewa_fallback_mail_normalise_headers($headers) { if (is_string($headers)) { $headers = preg_split('/\r\n|\r|\n/', $headers); } $headers = is_array($headers) ? $headers : array(); $clean = array(); $has_content_type = false; $has_from = false; foreach ($headers as $header) { $header = trim((string) $header); if ('' === $header) { continue; } $header = trim(str_replace(array("\r", "\n"), '', $header)); if ('' === $header || false === strpos($header, ':')) { continue; } list($name, $value) = explode(':', $header, 2); $name = trim((string) $name); $value = trim((string) $value); $name_lower = strtolower($name); if ('content-type' === $name_lower) { $has_content_type = true; $clean[] = 'Content-Type: text/html; charset=UTF-8'; continue; } if ('from' === $name_lower || 'reply-to' === $name_lower) { $address_header = matewa_fallback_mail_normalise_address_header('from' === $name_lower ? 'From' : 'Reply-To', $value); if ('' !== $address_header) { $clean[] = $address_header; if ('from' === $name_lower) { $has_from = true; } } continue; } $safe_name = preg_replace('/[^A-Za-z0-9\-]/', '', $name); $safe_value = sanitize_text_field(wp_strip_all_tags($value)); if ('' !== $safe_name && '' !== $safe_value) { $clean[] = $safe_name . ': ' . $safe_value; } } if (!$has_content_type) { array_unshift($clean, 'Content-Type: text/html; charset=UTF-8'); } if (!$has_from) { $safe_from = matewa_fallback_mail_safe_sender_email(); if (is_email($safe_from)) { $options = function_exists('matewa_client_mail_delivery_get_options') ? matewa_client_mail_delivery_get_options() : array(); $name = matewa_fallback_mail_header_display_name((string) ($options['sender_name'] ?? ''), 'Website Leads'); $clean[] = 'From: ' . $name . ' <' . $safe_from . '>'; } } return array_values(array_unique($clean)); } } if (!function_exists('matewa_fallback_mail_build_payload')) { function matewa_fallback_mail_build_payload($context, $to, $subject, $message, $headers = array(), $meta = array()) { $to = sanitize_email((string) $to); $subject = sanitize_text_field((string) $subject); $message = wp_kses_post((string) $message); $headers = matewa_fallback_mail_normalise_headers($headers); $meta = is_array($meta) ? $meta : array(); $safe_meta = array(); foreach ($meta as $key => $value) { $safe_key = sanitize_key((string) $key); if ('' === $safe_key) { continue; } if (is_scalar($value)) { $safe_meta[$safe_key] = sanitize_text_field((string) $value); } } return array( 'id' => function_exists('wp_generate_uuid4') ? wp_generate_uuid4() : uniqid('matewa_mail_', true), 'context' => matewa_fallback_mail_sanitise_context($context), 'to' => $to, 'subject' => $subject, 'message' => $message, 'headers' => $headers, 'meta' => $safe_meta, 'created_at' => matewa_fallback_mail_now(), 'retries' => 0, 'last_error' => '', ); } } if (!function_exists('matewa_fallback_mail_queue_limit')) { function matewa_fallback_mail_queue_limit() { return 30; } } if (!function_exists('matewa_fallback_mail_get_queue')) { function matewa_fallback_mail_get_queue() { $queue = get_option(MATEWA_FALLBACK_MAIL_QUEUE_OPTION, array()); return is_array($queue) ? $queue : array(); } } if (!function_exists('matewa_fallback_mail_save_queue')) { function matewa_fallback_mail_save_queue(array $queue) { $limit = matewa_fallback_mail_queue_limit(); if (count($queue) > $limit) { $queue = array_slice($queue, -1 * $limit, null, true); } return update_option(MATEWA_FALLBACK_MAIL_QUEUE_OPTION, $queue, false); } } if (!function_exists('matewa_fallback_mail_queue_count')) { function matewa_fallback_mail_queue_count() { return count(matewa_fallback_mail_get_queue()); } } if (!function_exists('matewa_fallback_mail_store_failed')) { function matewa_fallback_mail_store_failed(array $payload, $error = '') { $queue = matewa_fallback_mail_get_queue(); $payload['last_error'] = sanitize_text_field((string) $error); $payload['failed_at'] = matewa_fallback_mail_now(); $payload['retries'] = absint($payload['retries'] ?? 0); $id = sanitize_key((string) ($payload['id'] ?? '')); if ('' === $id) { $id = function_exists('wp_generate_uuid4') ? wp_generate_uuid4() : uniqid('mail_', true); $payload['id'] = $id; } $queue[$id] = $payload; matewa_fallback_mail_save_queue($queue); return $id; } } if (!function_exists('matewa_fallback_mail_update_last_result')) { function matewa_fallback_mail_update_last_result(array $result) { $result['updated_at'] = matewa_fallback_mail_now(); update_option(MATEWA_FALLBACK_MAIL_LAST_RESULT_OPTION, $result, false); } } if (!function_exists('matewa_fallback_mail_get_last_result')) { function matewa_fallback_mail_get_last_result() { $result = get_option(MATEWA_FALLBACK_MAIL_LAST_RESULT_OPTION, array()); return is_array($result) ? $result : array(); } } if (!function_exists('matewa_fallback_mail_try_managed_sender')) { function matewa_fallback_mail_try_managed_sender(array $payload) { /** * Future server-side sender hook. * A later 247ChatMate relay module can return: * array('sent' => true, 'method' => '247chatmate_managed_sender') */ $result = apply_filters('matewa_fallback_mail_managed_sender_result', null, $payload); if (is_array($result)) { return array( 'sent' => !empty($result['sent']), 'method' => !empty($result['method']) ? sanitize_key((string) $result['method']) : '247chatmate_managed_sender', 'error' => !empty($result['error']) ? sanitize_text_field((string) $result['error']) : '', ); } return array( 'sent' => false, 'method' => '247chatmate_managed_sender_not_configured', 'error' => 'Managed sender module is not configured on this site.', ); } } if (!function_exists('matewa_fallback_mail_send_via_site_mail')) { function matewa_fallback_mail_send_via_site_mail(array $payload) { $last_mail_error = ''; $failure_hook = function($wp_error) use (&$last_mail_error) { if (is_wp_error($wp_error)) { $last_mail_error = $wp_error->get_error_message(); } }; add_action('wp_mail_failed', $failure_hook, 10, 1); $sent = wp_mail( $payload['to'], wp_specialchars_decode((string) $payload['subject'], ENT_QUOTES), (string) $payload['message'], matewa_fallback_mail_normalise_headers($payload['headers'] ?? array()) ); remove_action('wp_mail_failed', $failure_hook, 10); return array( 'sent' => (bool) $sent, 'method' => 'site_wp_mail_or_smtp', 'error' => $sent ? '' : ('' !== $last_mail_error ? $last_mail_error : 'wp_mail returned false.'), ); } } if (!function_exists('matewa_fallback_mail_deliver_payload')) { function matewa_fallback_mail_deliver_payload(array $payload) { $managed = matewa_fallback_mail_try_managed_sender($payload); if (!empty($managed['sent'])) { return array( 'sent' => true, 'method' => $managed['method'], 'error' => '', ); } $client_route = function_exists('matewa_client_mail_delivery_get_route') ? matewa_client_mail_delivery_get_route() : 'site_mail'; $client_result = array(); if ('client_smtp' === $client_route && function_exists('matewa_client_mail_delivery_send_via_smtp')) { $client_result = matewa_client_mail_delivery_send_via_smtp($payload); if (!empty($client_result['sent'])) { return array( 'sent' => true, 'method' => !empty($client_result['method']) ? sanitize_key((string) $client_result['method']) : 'client_smtp', 'error' => '', ); } $allow_site_fallback = function_exists('matewa_client_mail_delivery_allow_site_fallback') ? matewa_client_mail_delivery_allow_site_fallback() : true; if (!$allow_site_fallback) { return array( 'sent' => false, 'method' => !empty($client_result['method']) ? sanitize_key((string) $client_result['method']) : 'client_smtp_failed', 'error' => !empty($client_result['error']) ? sanitize_text_field((string) $client_result['error']) : 'Client SMTP route failed.', ); } } $site_mail = matewa_fallback_mail_send_via_site_mail($payload); if (!empty($site_mail['sent'])) { return array( 'sent' => true, 'method' => !empty($client_result) ? 'client_smtp_fallback_site_mail' : $site_mail['method'], 'error' => '', ); } $client_error = !empty($client_result['error']) ? sanitize_text_field((string) $client_result['error']) : ''; $site_error = !empty($site_mail['error']) ? sanitize_text_field((string) $site_mail['error']) : 'wp_mail returned false.'; $error = '' !== $client_error ? 'Client SMTP failed: ' . $client_error . ' Site mail failed: ' . $site_error : $site_error; return array( 'sent' => false, 'method' => !empty($client_result) ? 'client_smtp_then_site_mail_failed' : $site_mail['method'], 'error' => $error, ); } } if (!function_exists('matewa_fallback_mail_send')) { function matewa_fallback_mail_send($context, $to, $subject, $message, $headers = array(), $meta = array(), $store_on_fail = true) { $payload = matewa_fallback_mail_build_payload($context, $to, $subject, $message, $headers, $meta); $result = array( 'sent' => false, 'queued' => false, 'queue_id' => '', 'method' => '', 'error' => '', 'context' => $payload['context'], 'recipient' => $payload['to'], 'created_at' => $payload['created_at'], ); if (!is_email($payload['to'])) { $result['error'] = 'Invalid recipient email.'; matewa_fallback_mail_update_last_result($result); return $result; } $delivery = matewa_fallback_mail_deliver_payload($payload); if (!empty($delivery['sent'])) { $result['sent'] = true; $result['method'] = !empty($delivery['method']) ? sanitize_key((string) $delivery['method']) : 'mail_sent'; matewa_fallback_mail_update_last_result($result); return $result; } $result['method'] = !empty($delivery['method']) ? sanitize_key((string) $delivery['method']) : 'mail_failed'; $result['error'] = !empty($delivery['error']) ? sanitize_text_field((string) $delivery['error']) : 'Mail delivery failed.'; if ($store_on_fail) { $queue_id = matewa_fallback_mail_store_failed($payload, $result['error']); $result['queued'] = '' !== $queue_id; $result['queue_id'] = $queue_id; } matewa_fallback_mail_update_last_result($result); return $result; } } if (!function_exists('matewa_fallback_mail_retry_failed_queue')) { function matewa_fallback_mail_retry_failed_queue($limit = 10) { $queue = matewa_fallback_mail_get_queue(); $limit = max(1, min(25, absint($limit))); $attempted = 0; $sent = 0; $failed = 0; foreach ($queue as $id => $payload) { if ($attempted >= $limit) { break; } $payload = is_array($payload) ? $payload : array(); $attempted++; $payload['retries'] = absint($payload['retries'] ?? 0) + 1; $mail_result = function_exists('matewa_fallback_mail_deliver_payload') ? matewa_fallback_mail_deliver_payload($payload) : matewa_fallback_mail_send_via_site_mail($payload); if (!empty($mail_result['sent'])) { unset($queue[$id]); $sent++; continue; } $payload['last_error'] = sanitize_text_field((string) ($mail_result['error'] ?? 'wp_mail returned false.')); $payload['last_retry_at'] = matewa_fallback_mail_now(); $queue[$id] = $payload; $failed++; } matewa_fallback_mail_save_queue($queue); $result = array( 'sent' => 0 === $attempted ? false : (0 < $sent), 'queued' => 0 < count($queue), 'method' => 'selected_client_mail_route_retry', 'error' => 0 < $failed ? $failed . ' queued email(s) still failed.' : '', 'context' => 'retry_queue', 'attempted' => $attempted, 'retried' => $attempted, 'resent' => $sent, 'failed' => $failed, 'remaining' => count($queue), ); matewa_fallback_mail_update_last_result($result); return $result; } } if (!function_exists('matewa_fallback_mail_status_label')) { function matewa_fallback_mail_status_label() { $last = matewa_fallback_mail_get_last_result(); $queue_count = matewa_fallback_mail_queue_count(); if (!empty($last['sent'])) { return 0 < $queue_count ? 'Mail is sending. ' . $queue_count . ' queued email(s) still need retry.' : 'Mail route active — last test/send succeeded.'; } if (0 < $queue_count) { return 'Mail fallback queue active — ' . $queue_count . ' email(s) waiting for retry.'; } return 'Mail route ready. Send a test weekly summary to confirm delivery.'; } } if (!function_exists('matewa_fallback_mail_last_error_label')) { function matewa_fallback_mail_last_error_label() { $last = matewa_fallback_mail_get_last_result(); $error = isset($last['error']) ? sanitize_text_field((string) $last['error']) : ''; return $error; } } if (!function_exists('matewa_fallback_mail_handle_retry_request')) { function matewa_fallback_mail_handle_retry_request() { if (!is_admin() || !current_user_can('manage_options')) { return; } $page = isset($_GET['page']) ? sanitize_key(wp_unslash($_GET['page'])) : ''; if ('247chatmate' !== $page || empty($_POST['matewa_fallback_mail_action'])) { return; } check_admin_referer('matewa_fallback_mail_nonce', 'matewa_fallback_mail_nonce'); $action = sanitize_key(wp_unslash($_POST['matewa_fallback_mail_action'])); if ('retry_failed' !== $action) { wp_safe_redirect(admin_url('admin.php?page=247chatmate&matewa_saved_section=setup&matewa_scroll_target=lead_tools&matewa_fallback_mail_notice=unknown_action#matewa-setup-lead-tools')); exit; } $result = matewa_fallback_mail_retry_failed_queue(10); $notice = 0 < absint($result['resent'] ?? 0) ? 'retry_sent' : 'retry_none'; if (0 < absint($result['failed'] ?? 0)) { $notice = 'retry_failed'; } wp_safe_redirect(admin_url('admin.php?page=247chatmate&matewa_saved_section=setup&matewa_scroll_target=lead_tools&matewa_fallback_mail_notice=' . $notice . '#matewa-setup-lead-tools')); exit; } } if (!function_exists('matewa_fallback_mail_render_action_forms')) { function matewa_fallback_mail_render_action_forms() { ?>