Files
claudetools/clients/cryoweave/web/cw-graph-mailer.php
Mike Swanson 5f30e1154a sync: auto-sync from GURU-5070 at 2026-06-23 07:57:32
Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-06-23 07:57:32
2026-06-23 07:59:40 -07:00

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 );