100 lines
4.0 KiB
PHP
100 lines
4.0 KiB
PHP
<?php
|
|
/*
|
|
Plugin Name: CryoWeave M365 Graph Mailer
|
|
Description: Routes all wp_mail() through Microsoft Graph as noreply@cryoweave.com (app-only client credentials) so site/contact-form mail is DMARC-aligned. Falls back to default wp_mail on any failure. Deployed by ClaudeTools 2026-06-23.
|
|
Version: 1.0
|
|
*/
|
|
if ( ! defined( 'ABSPATH' ) ) { exit; }
|
|
|
|
define( 'CW_MAILER_TENANT', '44705a37-b5d8-4bb1-882d-e18775612ada' );
|
|
define( 'CW_MAILER_CLIENT', '4003c79e-d4ac-4265-ba36-da783d89ee4d' );
|
|
define( 'CW_MAILER_SECRET', '__SECRET__' );
|
|
define( 'CW_MAILER_FROM', 'noreply@cryoweave.com' );
|
|
define( 'CW_MAILER_FROMNAME', 'CryoWeave' );
|
|
|
|
function cw_graph_token() {
|
|
$tok = get_transient( 'cw_graph_token' );
|
|
if ( $tok ) { return $tok; }
|
|
$resp = wp_remote_post(
|
|
'https://login.microsoftonline.com/' . CW_MAILER_TENANT . '/oauth2/v2.0/token',
|
|
array(
|
|
'timeout' => 20,
|
|
'body' => array(
|
|
'client_id' => CW_MAILER_CLIENT,
|
|
'client_secret' => CW_MAILER_SECRET,
|
|
'scope' => 'https://graph.microsoft.com/.default',
|
|
'grant_type' => 'client_credentials',
|
|
),
|
|
)
|
|
);
|
|
if ( is_wp_error( $resp ) ) { return false; }
|
|
$j = json_decode( wp_remote_retrieve_body( $resp ), true );
|
|
if ( empty( $j['access_token'] ) ) { return false; }
|
|
set_transient( 'cw_graph_token', $j['access_token'], max( 60, intval( isset( $j['expires_in'] ) ? $j['expires_in'] : 3600 ) - 120 ) );
|
|
return $j['access_token'];
|
|
}
|
|
|
|
add_filter( 'pre_wp_mail', function ( $null, $atts ) {
|
|
$to = isset( $atts['to'] ) ? $atts['to'] : array();
|
|
$subject = isset( $atts['subject'] ) ? $atts['subject'] : '';
|
|
$message = isset( $atts['message'] ) ? $atts['message'] : '';
|
|
$headers = isset( $atts['headers'] ) ? $atts['headers'] : array();
|
|
|
|
$cc = array(); $bcc = array(); $replyto = array(); $ctype = 'Text';
|
|
if ( ! is_array( $headers ) ) {
|
|
$headers = explode( "\n", str_replace( "\r\n", "\n", $headers ) );
|
|
}
|
|
foreach ( (array) $headers as $h ) {
|
|
if ( strpos( $h, ':' ) === false ) { continue; }
|
|
list( $k, $v ) = array_map( 'trim', explode( ':', $h, 2 ) );
|
|
$kl = strtolower( $k );
|
|
if ( 'cc' === $kl ) { $cc = array_merge( $cc, array_map( 'trim', explode( ',', $v ) ) ); }
|
|
elseif ( 'bcc' === $kl ) { $bcc = array_merge( $bcc, array_map( 'trim', explode( ',', $v ) ) ); }
|
|
elseif ( 'reply-to' === $kl ) { $replyto[] = $v; }
|
|
elseif ( 'content-type' === $kl && stripos( $v, 'html' ) !== false ) { $ctype = 'HTML'; }
|
|
}
|
|
if ( ! is_array( $to ) ) { $to = array_map( 'trim', explode( ',', $to ) ); }
|
|
|
|
$addr = function ( $a ) {
|
|
if ( preg_match( '/<([^>]+)>/', $a, $m ) ) { return trim( $m[1] ); }
|
|
return trim( $a );
|
|
};
|
|
$rcpt = function ( $list ) use ( $addr ) {
|
|
$o = array();
|
|
foreach ( (array) $list as $a ) {
|
|
$e = $addr( $a );
|
|
if ( $e ) { $o[] = array( 'emailAddress' => array( 'address' => $e ) ); }
|
|
}
|
|
return $o;
|
|
};
|
|
|
|
$token = cw_graph_token();
|
|
if ( ! $token ) { return $null; } // fall back to default wp_mail
|
|
|
|
$payload = array(
|
|
'message' => array(
|
|
'subject' => $subject,
|
|
'body' => array( 'contentType' => $ctype, 'content' => $message ),
|
|
'from' => array( 'emailAddress' => array( 'address' => CW_MAILER_FROM, 'name' => CW_MAILER_FROMNAME ) ),
|
|
'toRecipients' => $rcpt( $to ),
|
|
),
|
|
'saveToSentItems' => false,
|
|
);
|
|
if ( $cc ) { $payload['message']['ccRecipients'] = $rcpt( $cc ); }
|
|
if ( $bcc ) { $payload['message']['bccRecipients'] = $rcpt( $bcc ); }
|
|
if ( $replyto ) { $payload['message']['replyTo'] = $rcpt( $replyto ); }
|
|
|
|
$resp = wp_remote_post(
|
|
'https://graph.microsoft.com/v1.0/users/' . rawurlencode( CW_MAILER_FROM ) . '/sendMail',
|
|
array(
|
|
'timeout' => 25,
|
|
'headers' => array( 'Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json' ),
|
|
'body' => wp_json_encode( $payload ),
|
|
)
|
|
);
|
|
if ( is_wp_error( $resp ) ) { return $null; } // fall back
|
|
$code = wp_remote_retrieve_response_code( $resp );
|
|
if ( 202 === intval( $code ) ) { return true; } // handled
|
|
return $null; // anything else: fall back to default wp_mail
|
|
}, 10, 2 );
|