340 lines
13 KiB
PHP
340 lines
13 KiB
PHP
<?php
|
||
define('_SMTP_HOST', 'localhost');
|
||
|
||
define('_SMTPAuth', false);
|
||
//define('_SMTP_USER', 'ms1inscription@progiweb.ca');
|
||
//define('_SMTP_PASSWORD', 'airstream911');
|
||
define('_SMTP_PORT', 25);
|
||
|
||
use PHPMailer\PHPMailer\PHPMailer;
|
||
use PHPMailer\PHPMailer\SMTP;
|
||
use PHPMailer\PHPMailer\Exception;
|
||
|
||
include_once APPLICATION_PATH . 'plugins/PHPMailer-6.5.1/src/Exception.php';
|
||
include_once APPLICATION_PATH . 'plugins/PHPMailer-6.5.1/src/PHPMailer.php';
|
||
include_once APPLICATION_PATH . 'plugins/PHPMailer-6.5.1/src/SMTP.php';
|
||
include_once APPLICATION_PATH . 'php/class.Html2Text.php';
|
||
/**
|
||
* Cette fonction envoi un courriel avec PHP mailer
|
||
* @author Jean-Sébastien Proulx
|
||
* @version 2.0
|
||
* @date 2021-06-10
|
||
* @return bool
|
||
*/
|
||
function fxSendMail($strSubject, $strContent, $strMailTo, $strNameTo, $strMailFrom, $strNameFrom, $smtp = 1, $html = 1, $externe = 0, $attach = '', $blnGoDaddy = false, $strLangue ='fr') {
|
||
global $objDatabase, $vClient, $vEmail,$vDomaine,$vRepertoireFichiers;
|
||
$blnReturn = false;
|
||
$arrReturn = array(
|
||
'state' => 'error',
|
||
'message' => ''
|
||
);
|
||
|
||
//Instantiation and passing `true` enables exceptions
|
||
$objMail = new PHPMailer(true);
|
||
|
||
try {
|
||
//Server settings
|
||
$objMail->SMTPDebug = SMTP::DEBUG_OFF;
|
||
|
||
$objMail->isSMTP(); //Send using SMTP
|
||
$objMail->Host = _SMTP_HOST;
|
||
$objMail->SMTPAuth = false; //Enable SMTP authentication
|
||
// $objMail->Username = _SMTP_USER;
|
||
//$objMail->Password = _SMTP_PASSWORD;
|
||
//$objMail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS; //Enable TLS encryption; `PHPMailer::ENCRYPTION_SMTPS` encouraged
|
||
$objMail->Port = _SMTP_PORT;
|
||
$objMail->CharSet = "UTF-8";
|
||
$objMail->SMTPOptions = array(
|
||
'ssl' => array(
|
||
'verify_peer' => false,
|
||
'verify_peer_name' => false,
|
||
'allow_self_signed' => true
|
||
)
|
||
);
|
||
if ($strLangue == 'fr') {
|
||
//To load the French version
|
||
$objMail->setLanguage('fr');
|
||
}
|
||
|
||
//Recipients
|
||
$objMail->setFrom($vEmail, fxRemoveHtml($vClient));
|
||
$objMail->AddAddress($strMailTo, $strNameTo);
|
||
|
||
if ($strMailFrom != $vEmail) {
|
||
$objMail->addReplyTo($strMailFrom, $strNameFrom);
|
||
} else {
|
||
//$objMail->addReplyTo($vEmail, fxRemoveHtml($vClient));
|
||
}
|
||
|
||
//Content
|
||
$objMail->isHTML(true);
|
||
$objMail->Subject = $strSubject;
|
||
|
||
$strContent = email_embed_images_as_cid($strContent, $objMail, [
|
||
'base_path' => $vDomaine . $vRepertoireFichiers, // si tu as des src relatifs
|
||
'max_bytes' => 1024*1024
|
||
]);
|
||
|
||
$objMail->Body = $strContent;
|
||
$objHTML = new \Html2Text\Html2Text($strContent);
|
||
$objMail->AltBody = $objHTML->getText();
|
||
|
||
$objMail->send();
|
||
$blnReturn = true;
|
||
$arrReturn['state'] = 'success';
|
||
$arrReturn['message'] = "Message was sent.";
|
||
} catch (Exception $e) {
|
||
$arrReturn['message'] = "Message could not be sent. Mailer Error: {$objMail->ErrorInfo}";
|
||
}
|
||
|
||
// Log the result
|
||
$sqlInsert = "
|
||
INSERT INTO logs_mails
|
||
SET
|
||
log_subject = '" . $objDatabase->fxEscape($strSubject) . "',
|
||
log_content = '" . $objDatabase->fxEscape($strContent) . "',
|
||
log_mail_to = '" . $objDatabase->fxEscape($strMailTo) . "',
|
||
log_name_to = '" . $objDatabase->fxEscape($strNameTo) . "',
|
||
log_mail_from = '" . $objDatabase->fxEscape($strMailFrom) . "',
|
||
log_name_from = '" . $objDatabase->fxEscape($strNameFrom) . "',
|
||
log_status = '" . $objDatabase->fxEscape($arrReturn['state']) . "',
|
||
log_message = '" . $objDatabase->fxEscape($arrReturn['message']) . "'
|
||
";
|
||
$qryInsert = $objDatabase->fxQuery($sqlInsert);
|
||
|
||
return $blnReturn;
|
||
}
|
||
// fonction envoi un courriel (JSP)
|
||
// param : sujet, corps du message, courriel destinataire, nom destinataire, courriel expéditeur, nom expéditeur, smtp (oui/non), html (oui/non)
|
||
// externe (oui/non) -> appel fxSendMail provient d'un autre site (ie, Boutique Premiere) (AJOUT: AL, Juin 2015)
|
||
// retourne : vrai ou faux
|
||
function fxSendMailOld($subject, $body, $to_mail, $to_name, $from_mail, $from_name, $smtp, $html, $externe = 0, $attach = '', $blnGoDaddy = false) {
|
||
$blnGoDaddy = filter_var(fxGetVariable('mailGD'), FILTER_VALIDATE_BOOLEAN);
|
||
$error = 0;
|
||
$strError = '';
|
||
$arrSmtp = $GLOBALS['strDebug'] = [];
|
||
require_once 'mailer/PHPMailer.php';
|
||
require_once 'mailer/SMTP.php';
|
||
require_once 'mailer/Exception.php';
|
||
require_once 'php/Html2Text.php';
|
||
$mail = new PHPMailer();
|
||
|
||
$mail->IsSMTP();
|
||
$mail->SMTPAuth = true;
|
||
|
||
if ($blnGoDaddy) {
|
||
$mail->Host = "smtpout.secureserver.net";
|
||
$mail->Port = 25;
|
||
$mail->Username = "sendms1@progiweb.us";
|
||
$mail->Password = "airstream911";
|
||
} else {
|
||
$mail->Host = "pop.progiweb.ca";
|
||
$mail->Port = 25;
|
||
$mail->Username = "ms1inscription@progiweb.ca";
|
||
$mail->Password = "airstream911";
|
||
}
|
||
|
||
$arrSmtp['host'] = $mail->Host;
|
||
$arrSmtp['port'] = $mail->Port;
|
||
$arrSmtp['username'] = $mail->Username;
|
||
$arrSmtp['password'] = $mail->Password;
|
||
|
||
if ($externe == 0) {
|
||
$mail->SetFrom($GLOBALS['vEmail'], fxRemoveHtml($GLOBALS['vClient']));
|
||
// $from_mail="ms1inscription@progiweb.ca";
|
||
// $mail->SetFrom($from_mail, fxRemoveHtml($GLOBALS['vClient']));
|
||
$mail->Subject = fxRemoveHtml($GLOBALS['vClient']) . " | " . $subject;
|
||
}
|
||
|
||
if ($externe == 1) {
|
||
$mail->SetFrom($from_mail, $from_name);
|
||
$mail->Subject = $from_name . " | " . $subject;
|
||
}
|
||
|
||
$mail->CharSet = "UTF-8";
|
||
$mail->AddReplyTo($from_mail, $from_name);
|
||
|
||
// vérifier s'il y a des fichiers à attacher
|
||
if ($attach != '') {
|
||
$mail->AddAttachment($attach);
|
||
}
|
||
|
||
$mail->isHTML(true);
|
||
$mail->Body = $body;
|
||
$html = new \Html2Text\Html2Text($body);
|
||
$mail->AltBody = $html->getText();
|
||
$mail->AddAddress($to_mail, $to_name);
|
||
$mail->SMTPDebug = 2;
|
||
$mail->Debugoutput = function($str, $level) { $GLOBALS['strDebug'][] = "debug level $level; message: $str\n"; };
|
||
|
||
if (!$mail->send()) {
|
||
$error = 1;
|
||
$strError = print_r($mail->ErrorInfo, true);
|
||
}
|
||
|
||
fxLogMail($to_mail, $to_name, $error, $strError, print_r($GLOBALS['strDebug'], true), print_r($arrSmtp, true), $subject, $body);
|
||
|
||
// Clear all addresses and attachments for next loop
|
||
$mail->ClearAddresses();
|
||
$mail->ClearAttachments();
|
||
|
||
if ($error == 0)
|
||
return true;
|
||
else {
|
||
//fxcreer_log("Erreur lors de l'envoi du mail. (TO: $to_mail, FROM $from_mail:, SUJET: $subject)");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
function fxLogMail($to_mail, $to_name, $error, $strError, $strDebug, $strSmtp, $subject, $body) {
|
||
global $objDatabase;
|
||
|
||
$sqlInsert = "INSERT INTO email_logs SET log_error = " . intval($error) . ", log_error_msg = '" . $objDatabase->fxEscape($strError) . "', log_smtp_connect = '" . $objDatabase->fxEscape($strSmtp) . "', log_smtp_debug = '" . $objDatabase->fxEscape($strDebug) . "', log_subject = '" . $objDatabase->fxEscape($subject) . "', log_message = '" . $objDatabase->fxEscape($body) . "', log_mailto = '" . $objDatabase->fxEscape($to_mail) . "', log_nameto = '" . $objDatabase->fxEscape($to_name) . "'";
|
||
$qryInsert = $objDatabase->fxQuery($sqlInsert);
|
||
|
||
return $qryInsert;
|
||
}
|
||
|
||
|
||
/**
|
||
* Convertit toutes les <img src="http(s)://..."> (et relatives) en images CID.
|
||
* Si une image est inaccessible ou trop lourde, on la retire pour éviter le "Disarmed".
|
||
*/
|
||
function email_embed_images_as_cid(string $html, PHPMailer $mail, array $opt = []): string
|
||
{
|
||
$maxBytes = $opt['max_bytes'] ?? 1024 * 1024; // 1 Mo max pour une image
|
||
|
||
// 0) Détecter si c'est vraiment du HTML
|
||
// Si strip_tags() ne change rien, on est en texte brut → on ne touche pas.
|
||
$isHtml = (strlen($html) !== strlen(strip_tags($html)));
|
||
if (!$isHtml) {
|
||
return $html;
|
||
}
|
||
|
||
// On travaille sur une copie
|
||
$originalHtml = $html;
|
||
|
||
// 1) Préparer un vrai document HTML pour DOMDocument
|
||
// - Si pas de <html>, on enveloppe dans <html><body>...
|
||
// - On force un <meta charset="UTF-8"> pour éviter que DOMDocument casse les accents.
|
||
$unwrapBody = false;
|
||
|
||
if (stripos($html, '<html') === false) {
|
||
// Fragment HTML (ex: juste un <table>, des <p>, etc.)
|
||
$html = '<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body>' . $html . '</body></html>';
|
||
$unwrapBody = true;
|
||
} else {
|
||
// Document HTML complet : on s'assure qu'il a un charset UTF-8
|
||
if (stripos($html, 'charset=') === false) {
|
||
$html = preg_replace(
|
||
'#<head([^>]*)>#i',
|
||
'<head$1><meta charset="UTF-8">',
|
||
$html,
|
||
1,
|
||
$count
|
||
);
|
||
if ($count === 0) {
|
||
// pas de <head>, on colle la meta au début
|
||
$html = '<meta charset="UTF-8">' . $html;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 2) Charger dans DOMDocument en UTF-8
|
||
libxml_use_internal_errors(true);
|
||
$doc = new DOMDocument('1.0', 'UTF-8');
|
||
$loaded = $doc->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||
libxml_clear_errors();
|
||
|
||
if (!$loaded) {
|
||
// Si DOMDocument n'arrive pas à parser, on ne casse rien → on renvoie l'original
|
||
return $originalHtml;
|
||
}
|
||
|
||
$xp = new DOMXPath($doc);
|
||
|
||
// 3) Traiter toutes les <img>
|
||
foreach ($xp->query('//img[@src]') as $img) {
|
||
/** @var DOMElement $img */
|
||
$src = trim($img->getAttribute('src'));
|
||
if ($src === '' || stripos($src, 'cid:') === 0 || stripos($src, 'data:') === 0) {
|
||
continue; // déjà safe ou inline
|
||
}
|
||
|
||
$binary = null;
|
||
$mime = null;
|
||
$tmpPath = null;
|
||
|
||
// a) src absolu http(s)
|
||
if (preg_match('#^https?://#i', $src)) {
|
||
$binary = @file_get_contents($src);
|
||
if ($binary === false || strlen($binary) === 0 || strlen($binary) > $maxBytes) {
|
||
// Image introuvable ou trop grosse → on enlève l'image
|
||
$img->parentNode->removeChild($img);
|
||
continue;
|
||
}
|
||
|
||
$finfo = new finfo(FILEINFO_MIME_TYPE);
|
||
$mime = $finfo->buffer($binary) ?: 'application/octet-stream';
|
||
if (strpos($mime, 'image/') !== 0) {
|
||
$img->parentNode->removeChild($img);
|
||
continue;
|
||
}
|
||
|
||
$ext = explode('/', $mime)[1] ?? 'bin';
|
||
$cid = 'img' . bin2hex(random_bytes(8));
|
||
$tmpPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $cid . '.' . $ext;
|
||
|
||
file_put_contents($tmpPath, $binary);
|
||
$mail->addEmbeddedImage($tmpPath, $cid, basename($tmpPath), 'base64', $mime);
|
||
$img->setAttribute('src', 'cid:' . $cid);
|
||
continue;
|
||
}
|
||
|
||
// b) src relatif (ex: "images/logo.png" ou "/images/logo.png")
|
||
$fsPath = null;
|
||
if ($src[0] === '/') {
|
||
$fsPath = rtrim($_SERVER['DOCUMENT_ROOT'] ?? '', '/') . $src;
|
||
} else {
|
||
$basePath = $opt['base_path'] ?? null;
|
||
$fsPath = $basePath ? rtrim($basePath, '/') . '/' . $src : null;
|
||
}
|
||
|
||
if ($fsPath && is_file($fsPath)) {
|
||
$mime = mime_content_type($fsPath) ?: 'image/png';
|
||
$cid = 'img' . bin2hex(random_bytes(8));
|
||
|
||
$mail->addEmbeddedImage($fsPath, $cid, basename($fsPath), 'base64', $mime);
|
||
$img->setAttribute('src', 'cid:' . $cid);
|
||
} else {
|
||
// Image introuvable → on enlève l’<img> pour éviter les "disarmed"
|
||
$img->parentNode->removeChild($img);
|
||
}
|
||
}
|
||
|
||
// 4) (Optionnel) retirer les <link> vers des polices externes (ex: Google Fonts)
|
||
foreach ($xp->query('//link[@href]') as $link) {
|
||
$href = $link->getAttribute('href');
|
||
if (stripos($href, 'fonts.googleapis.com') !== false) {
|
||
$link->parentNode->removeChild($link);
|
||
}
|
||
}
|
||
|
||
// 5) Récupérer le HTML final
|
||
if ($unwrapBody) {
|
||
// On avait enveloppé dans <body>...</body> → on ne renvoie que le contenu du body
|
||
$bodyList = $doc->getElementsByTagName('body');
|
||
if ($bodyList->length > 0) {
|
||
$body = $bodyList->item(0);
|
||
$result = '';
|
||
foreach ($body->childNodes as $child) {
|
||
$result .= $doc->saveHTML($child);
|
||
}
|
||
return $result;
|
||
}
|
||
}
|
||
|
||
// Cas document complet : on renvoie tout
|
||
return $doc->saveHTML();
|
||
}
|