praiadeseselle/site/modules/WireMailSmtp/WireMailSmtpAdaptor.php

450 lines
17 KiB
PHP

<?php
/*******************************************************************************
* WireMailSmtp | hnsmtp
*
* @version - '0.6.4'
* @date - 2023/11/08
* @author - Horst Nogajski
* @licence - GNU GPL v2 - http://www.gnu.org/licenses/gpl-2.0.html
* @licence - MIT - https://processwire.com/about/license/mit/
********************************************************************************/
require_once( dirname(__FILE__) . '/smtp_classes/email_message.php' );
require_once( dirname(__FILE__) . '/smtp_classes/smtp_message.php' );
require_once( dirname(__FILE__) . '/smtp_classes/smtp.php' );
require_once( dirname(__FILE__) . '/smtp_classes/sasl.php' );
class hnsmtp {
private $module = null;
private $default_charset = 'UTF-8';
private $smtp_host = ''; /* SMTP server host name */
private $smtp_port = 25; /* SMTP server host port,
usually 25 but for use with SSL or TLS 587 */
private $smtp_ssl = 0; /* Establish secure connections using SSL */
private $smtp_ssl_crypto_method = ''; /* Define the crypto method to use with SSL */
private $smtp_start_tls = 0; /* Establish secure connections using START_TLS */
private $smtp_tls_crypto_method = ''; /* Define the crypto method to use with TLS */
private $localhost = ''; /* this computers address */
private $realm = ''; /* Authentication realm or domain */
private $workstation = ''; /* Workstation for NTLM authentication */
private $authentication_mechanism = ''; /* SASL authentication mechanism */
private $allow_without_authentication = 0; /* Server allows connection without authentication */
private $smtp_user = ''; /* Authentication user name */
private $smtp_password = ''; /* Authentication password */
private $smtp_debug = 0; /* Output debug information */
private $smtp_html_debug = 0; /* Debug information is in HTML */
private $sender_name = ''; // From: the senders name
private $sender_email = ''; // From: the senders email address
private $sender_reply = ''; // Reply-To: optional email address
private $sender_errors_to = ''; // Errors-To: optional email address
private $sender_signature = ''; // a Signature Text, like Contact Data and / or Confidentiality Notices
private $sender_signature_html = ''; // a Signature Text in HTML, like Contact Data and / or Confidentiality Notices
private $send_sender_signature = 1; // when the signature should be send: with every mail | only when the default Email is the sender | only when explicitly called via the API
private $extra_headers = array(); // optional Custom-Meta-Headers
private $valid_recipients = array(); /* SenderEmailAddresses wich are allowed to
receive Messages */
private $smtp_certificate = false; // @flydev: https://processwire.com/talk/topic/5704-wiremailsmtp/page-5#entry113290
private $aValidVars = null;
private $emailMessage = null;
private $connected = null;
private $errors = array();
private $from = '';
private $fromName = '';
public function testConnection($debug = false) {
$res = $this->connect($debug);
$this->close();
return $res;
}
public function connect($debug = false) {
$this->connected = (($this->errors[] = $this->emailMessage->StartSendingMessage()) == '') ? true : false;
return $this->connected;
}
public function close() {
if(!isset($this->emailMessage)) {
return null;
}
$res = $this->emailMessage->ResetConnection('') == '' ? true : false;
$this->connected = false;
return $res;
}
public function getErrors() {
$a = array();
foreach( $this->errors as $e ) {
if($e=='') {
continue;
}
$a[] = $e;
}
$this->errors = $a;
return $this->errors;
}
protected function set_var_val( $k, $v ) {
if(!isset($this->aValidVars[$k])) {
return;
}
switch($k) {
case 'send_sender_signature':
case 'smtp_port':
$this->$k = intval($v);
break;
case 'smtp_certificate':
case 'smtp_ssl':
case 'smtp_start_tls':
case 'smtp_debug':
case 'smtp_html_debug':
case 'allow_without_authentication':
if(is_bool($v)) {
$this->$k = $v==true ? 1 : 0;
}
elseif(is_int($v)) {
$this->$k = $v==1 ? 1 : 0;
}
elseif(is_string($v) && in_array($v, array('1','on','On','ON','true','TRUE'))) {
$this->$k = 1;
}
elseif(is_string($v) && in_array($v, array('0','off','Off','OFF','false','FALSE'))) {
$this->$k = 0;
}
else {
$this->$k = 0;
}
break;
case 'smtp_tls_crypto_method':
$availableTLSmethods = WireMailSmtp::getCryptoMethodsTLS();
if(is_string($v) && isset($v, $availableTLSmethods)) {
$this->$k = $v;
}
break;
case 'smtp_ssl_crypto_method':
$availableSSLmethods = WireMailSmtp::getCryptoMethodsSSL();
if(is_string($v) && isset($v, $availableSSLmethods)) {
$this->$k = $v;
}
break;
case 'authentication_mechanism':
$this->authentication_mechanism = $v;
break;
case 'valid_recipients':
case 'extra_headers';
$this->$k = is_array($v) || is_string($v) ? (array)$v : array();
break;
default:
if(in_array($k, array('smtp_host', 'smtp_user', 'smtp_password',
'localhost', 'workstation', 'realm',
'sender_name', 'sender_email', 'sender_reply',
'sender_errors_to', 'sender_signature', 'sender_signature_html',
'default_charset'
))
) {
$this->$k = strval($v);
}
}
}
public function __construct($aConfig = null) {
if(!is_array($aConfig)) {
return;
}
$this->aValidVars = get_class_vars(__CLASS__);
foreach($aConfig as $k => $v) {
$this->set_var_val($k, $v);
}
foreach($this->valid_recipients as $k=>$v) {
$this->valid_recipients[$k] = str_replace(array('<','>'), '', strtolower(trim($v)));
}
// start SMTP-Mail
$this->emailMessage = new smtp_message_class();
// SMTP Server Authentication
$this->emailMessage->default_charset = $this->default_charset;
$this->emailMessage->localhost = $this->localhost;
$this->emailMessage->smtp_host = $this->smtp_host;
$this->emailMessage->smtp_port = $this->smtp_port;
$this->emailMessage->smtp_ssl = $this->smtp_ssl;
$this->emailMessage->smtp_ssl_crypto_method = $this->smtp_ssl_crypto_method;
$this->emailMessage->smtp_start_tls = $this->smtp_start_tls;
$this->emailMessage->smtp_tls_crypto_method = $this->smtp_tls_crypto_method;
$this->emailMessage->smtp_user = $this->smtp_user;
$this->emailMessage->smtp_password = $this->smtp_password;
$this->emailMessage->allow_without_authentication = $this->allow_without_authentication;
$this->emailMessage->smtp_certificate = $this->smtp_certificate;
// advanced SMTP Server Settings
$this->emailMessage->smtp_realm = $this->realm;
$this->emailMessage->smtp_workstation = $this->workstation;
$this->emailMessage->smtp_authentication_mechanism = $this->authentication_mechanism;
// Debug on / off
$this->emailMessage->smtp_debug = $this->smtp_debug;
$this->emailMessage->smtp_html_debug = $this->smtp_html_debug;
}
public function __destruct() {
if( $this->connected ) {
$this->close();
}
unset($this->emailMessage);
}
protected function logError($msg) {
$this->module = wire('modules')->get('WireMailSmtp');
$this->module->logError($msg);
}
protected function logActivity($msg) {
$this->module = wire('modules')->get('WireMailSmtp');
$this->module->logActivity($msg);
}
public function setSender($from='', $fromName='') {
$genericEmail = isset($this->localhost) ? 'processwire@' . $this->localhost : false;
$sender = strlen($from)>0 ? $from : $this->sender_email;
if(empty($sender) && false!==$genericEmail) {
$sender = $genericEmail; // fallback to a generic email address
}
$this->isValidEmailadress($sender); // if it is not a valid Emailaddress a Error is thrown
$senderName = strlen($fromName)>0 ? $fromName : $this->sender_name;
$this->from = $sender;
$this->fromName = $senderName;
if($sender==$this->sender_email) {
// we use the defaults from module config
$replyTo = isset($this->sender_reply) && strlen($this->sender_reply)>0 ? $this->sender_reply : $this->sender_email;
$errorsTo = isset($this->sender_errors_to) && strlen($this->sender_errors_to)>0 ? $this->sender_errors_to : $this->sender_email;
}
else {
$replyTo = $genericEmail!=$sender ? $sender : ''; // we don't want get replys to the generic emailaddress
$errorsTo = '';
}
$this->setEmailHeader('from', $sender, $senderName);
if(''!=$replyTo) $this->setEmailHeader('reply', $replyTo);
if(''!=$errorsTo) $this->setEmailHeader('errors', $errorsTo);
}
public function setCustomHeader($header) {
$extra_headers = (isset($this->extra_headers) && is_array($this->extra_headers) && 0<count($this->extra_headers)) ? $this->extra_headers : array();
$headers = array_merge($extra_headers, $header);
foreach($headers as $k=>$v) {
$this->setHeader($k, $v);
}
}
public function setTextBody($text, $addSignature, $wrapText, &$maildata) {
if($addSignature===true && isset($this->sender_signature) && is_string($this->sender_signature) && strlen(trim($this->sender_signature))>0) {
$text .= "\r\n\r\n" . $this->sender_signature;
}
$text = $wrapText ? $this->emailMessage->WrapText($text) : (string)$text;
$maildata = $text;
$ret = $this->emailMessage->AddQuotedPrintableTextPart($text);
if($ret=='') {
return true;
}
$this->logError('Error in '.__CLASS__.'::'.__FUNCTION__.' : ' . $ret);
return false;
}
public function setTextAndHtmlBody($text, $html, $addSignature, $wrapText, &$maildata1, &$maildata2) {
if($addSignature===true && isset($this->sender_signature) && is_string($this->sender_signature) && strlen(trim($this->sender_signature))>0) {
$text .= "\r\n\r\n--\r\n" . $this->sender_signature;
}
if($addSignature===true && isset($this->sender_signature_html) && is_string($this->sender_signature_html) && strlen(trim($this->sender_signature_html))>0) {
// we first need to check if there is a </body> end tag in the html-markup
if(preg_match('</body>', $html)) {
$html = str_replace("</body>", "\r\n\r\n" . $this->sender_signature_html . "\r\n</body>", $html);
} else {
$html .= "\r\n\r\n" . $this->sender_signature_html . "\r\n";
}
}
$maildata1 = $text = $wrapText ? $this->emailMessage->WrapText($text) : (string)$text;
$maildata2 = $html = $wrapText ? $this->emailMessage->WrapText($html) : (string)$html;
// create Alternative-Multipart
$html_part = $text_part = $alternative_part = 0;
$this->emailMessage->CreateQuotedPrintableTextPart($text, 'UTF-8', $text_part);
$this->emailMessage->CreateQuotedPrintableHTMLPart($html, 'UTF-8', $html_part);
$alternative_parts = array( $text_part, $html_part );
#$this->emailMessage->CreateAlternativeMultipart($alternative_parts, $alternative_part);
$ret = $this->emailMessage->AddAlternativeMultipart($alternative_parts);
if($ret=='') {
return true;
}
$this->logError('Error in '.__CLASS__.'::'.__FUNCTION__.' : ' . $ret);
return false;
}
public function addAttachment($attachment) {
$ret = $this->emailMessage->AddFilePart($attachment);
if($ret=='') {
return true;
}
$this->logError('Error in '.__CLASS__.'::'.__FUNCTION__.' : ' . $ret);
return false;
}
public function setNotification(&$maildata) {
if(!isset($this->from) || strlen(trim($this->from))==0) {
return false;
}
$email = $this->bundleEmailAndName($this->from, $this->fromName);
$maildata = $email;
return $this->setHeader('Disposition-Notification-To', $email);
}
public function setPriority($priority=3) {
$priority = intval($priority);
if(!in_array($priority, array(1, 2, 3, 4, 5))) {
$this->logError('Error in '.__CLASS__.'::'.__FUNCTION__.' : ' . "($priority)");
return false;
}
$priorities = array(
5 => array('5 (Lowest)', 'Low', 'Low'),
4 => array('4 (Low)', 'Low', 'Low'),
3 => array('3 (Normal)', 'Normal', 'Normal'),
2 => array('2 (High)', 'High', 'High'),
1 => array('1 (Highest)', 'High', 'High')
);
$ret = 0;
$ret += $this->setHeader('X-Priority', $priorities[$priority][0]) ? 1 : 0;
$ret += $this->setHeader('X-MSMail-Priority', $priorities[$priority][1]) ? 1 : 0;
$ret += $this->setHeader('Importance', $priorities[$priority][2]) ? 1 : 0;
return 3==$ret ? true : false;
}
public function send($debugServer=false, $htmlDebug=false, &$maildata='') {
if($debugServer) $this->emailMessage->smtp_debug = 1;
if($htmlDebug) $this->emailMessage->smtp_html_debug = 1;
if(!$this->connect()) {
$this->logError('Error in '.__CLASS__.'::'.__FUNCTION__.' : cannot connect to smtp-server!');
return false;
}
$this->emailMessage->SetHeader("Date", gmdate("D, j M Y H:i:s \G\M\T"));
$ret = $this->emailMessage->Send();
if($ret=='') {
$maildata = 'success';
return true;
}
$this->logError('Error in '.__CLASS__.'::'.__FUNCTION__.' : ' . $ret);
$maildata = (string)$ret;
return false;
}
public function setEmailHeader($type, $address, $name='') {
$address = str_replace(array('<','>'), '', $address);
if(!$this->isValidEmailadress($address)) {
$this->logError('Error in '.__CLASS__.'::'.__FUNCTION__.' : No valid E-mailadress: '.$address);
return false;
}
$type = strtolower($type);
$valid_types = array('to'=>'To','from'=>'From','cc'=>'CC','bcc'=>'BCC','reply-to'=>'Reply-To','reply'=>'Reply-To','errors-to'=>'Errors-To','errors'=>'Errors-To','error'=>'Errors-To');
if(!in_array($type,array_keys($valid_types))) {
$this->logError('Error in '.__CLASS__.'::'.__FUNCTION__.' : No valid Headertype: '.$type);
return false;
}
if(strpos($type,'reply')!==false) {
$this->emailMessage->SetHeader('Return-Path',$address);
}
$ret = $this->emailMessage->SetEncodedEmailHeader($valid_types[$type], $address, $name);
if($ret!='') {
$this->logError('Error in '.__CLASS__.'::'.__FUNCTION__.' : ' . $ret);
return false;
}
return true;
}
public function setHeader($headername,$content) {
$ret = $this->emailMessage->SetEncodedHeader($headername,$content);
if($ret!='') {
$this->logError('Error in '.__CLASS__.'::'.__FUNCTION__.' : ' . $ret);
return false;
}
return true;
}
public function isValidEmailadress($email) {
$email = strtolower(trim($email));
$clean = wire('sanitizer')->email($email);
if($email != $clean) throw new WireException("Invalid email address");
return true;
}
protected function bundleEmailAndName($email, $name) {
$email = strtolower(trim($email));
$clean = wire('sanitizer')->email($email);
if(!strlen($name)) return $email;
$name = wire('sanitizer')->emailHeader($name);
if(strpos($name, ',') !== false) {
// name contains a comma, so quote the value
$name = str_replace('"', '', $name); // remove existing quotes
$name = '"' . $name . '"'; // surround w/quotes
}
return "$name <$email>";
}
public function setBulkMail($bulk) {
return $this->emailMessage->setBulkMail($bulk);
}
} // END class hnsmtp