1025 lines
39 KiB
Text
1025 lines
39 KiB
Text
<?php
|
|
/**
|
|
* SMTP Mailer WireMail module
|
|
*
|
|
* This module extends the WireMail base class and integrate the EmailMessage- and the SMTP-Library
|
|
* from M. Lemos (http://www.phpclasses.org/browse/author/1.html) into ProcessWire.
|
|
*
|
|
*
|
|
* @copyright Copyright (c) 2014 - 2021, Horst Nogajski
|
|
* @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License, version 2
|
|
* OR
|
|
* @license https://processwire.com/about/license/mit/
|
|
*
|
|
* ProcessWire 2.x & 3.x, Copyright 2020 by Ryan Cramer
|
|
* https://processwire.com
|
|
* https://processwire.com/about/license/mit/
|
|
*
|
|
**/
|
|
|
|
class WireMailSmtp extends WireMail implements Module, ConfigurableModule {
|
|
|
|
|
|
public static function getModuleInfo() {
|
|
return array(
|
|
'title' => 'Wire Mail SMTP',
|
|
'version' => '0.6.0',
|
|
'summary' => "Extends WireMail, uses SMTP protocol (plain | SSL | TLS), provides: to, cc, bcc, attachments, priority, disposition notification, bulksending, ...",
|
|
'href' => 'https://processwire.com/talk/topic/5704-module-wiremailsmtp/',
|
|
'author' => 'horst',
|
|
'singular' => false,
|
|
'autoload' => false
|
|
);
|
|
}
|
|
|
|
|
|
public static function getCryptoMethodsTLS() {
|
|
$validTlsCryptoMethods = array();
|
|
foreach(array(
|
|
'STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT', // since PHP 5.6.0
|
|
'STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT', // since PHP 5.6.0
|
|
'STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT', // since PHP 5.6.0
|
|
'STREAM_CRYPTO_METHOD_ANY_CLIENT', // since PHP 5.6.0
|
|
'STREAM_CRYPTO_METHOD_TLS_CLIENT', // BEFORE PHP 5.6.0
|
|
) as $item) {
|
|
if(defined($item) && constant($item)) $validTlsCryptoMethods[] = $item;
|
|
}
|
|
$a = array();
|
|
foreach($validTlsCryptoMethods as $value) $a[$value] = $value;
|
|
return $a;
|
|
}
|
|
|
|
public static function getCryptoMethodsSSL() {
|
|
return array();
|
|
}
|
|
|
|
/**
|
|
* Name of activity/error log files without extension (.txt)
|
|
*
|
|
*/
|
|
const LOG_FILENAME_ACTIVITY = 'wiremailsmtp_activity';
|
|
const LOG_FILENAME_ERROR = 'wiremailsmtp_errors';
|
|
const LOG_FILENAME_SENTLOG = 'wiremailsmtp_sentlog';
|
|
|
|
|
|
private $smtp = null;
|
|
private $maildata = array();
|
|
|
|
|
|
/**
|
|
* Mail properties
|
|
*
|
|
*/
|
|
protected $mail = array(
|
|
'to' => array(), // to addresses - associative: both key and value are email (to prevent dups)
|
|
'toName' => array(), // to names - associative: indexed by 'to' email address, may be blank/null for any email
|
|
'cc' => array(),
|
|
'ccName' => array(),
|
|
'bcc' => array(),
|
|
'from' => '',
|
|
'fromName' => '',
|
|
'priority' => '',
|
|
'dispositionNotification' => '',
|
|
'subject' => '',
|
|
'body' => '',
|
|
'bodyHTML' => '',
|
|
'addSignature' => null,
|
|
'attachments' => array(),
|
|
'header' => array(),
|
|
'sendSingle' => false,
|
|
'sendBulk' => false,
|
|
'useSentLog' => false,
|
|
'wrapText' => false
|
|
);
|
|
|
|
|
|
/**
|
|
* Default settings used by this module
|
|
*
|
|
* @return array
|
|
*/
|
|
static public function getDefaultData() {
|
|
return array(
|
|
'default_charset' => 'UTF-8',
|
|
'localhost' => '', // this computer address
|
|
'smtp_host' => '', // SMTP server address
|
|
'smtp_port' => 25, // SMTP server port
|
|
'smtp_ssl' => 0, // SMTP use SSL ?
|
|
'smtp_ssl_crypto_method' => '', // crypto method to use with SSL connections
|
|
'smtp_start_tls' => 0, // SMTP use START_TLS ?
|
|
'smtp_tls_crypto_method' => '', // crypto method to use with TLS connections
|
|
'smtp_user' => '', // SMTP user name
|
|
'smtp_password' => '', // SMTP password
|
|
'smtp_password2' => '', // SMTP password
|
|
'clear_smtp_password' => '', // SMTP password
|
|
'allow_without_authentication' => 0, // No user name and password required
|
|
'realm' => '', // Authentication realm or domain
|
|
'workstation' => '', // Workstation for NTLM authentication
|
|
'authentication_mechanism' => '', // SASL authentication mechanism
|
|
'smtp_debug' => 0, // debug smtp server communication?
|
|
'smtp_html_debug' => 0, // debug smtp server communication in HTML?
|
|
'sender_name' => '', // From: the senders name
|
|
'sender_email' => '', // From: the senders email address
|
|
'sender_reply' => '', // Reply-To: optional email address
|
|
'sender_errors_to' => '', // Errors-To: optional email address
|
|
'sender_signature' => '', // a Signature Text, like Contact Data and / or Confidentiality Notices
|
|
'sender_signature_html' => '', // a Signature Text in HTML, like Contact Data and / or Confidentiality Notices
|
|
'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
|
|
'extra_headers' => '', // optional Custom-Meta-Headers
|
|
'valid_recipients' => '', // email addresses of valid recipients. String that we convert to array at runtime.
|
|
'smtp_certificate' => 0 // allow or not self signed certificate (PHP >= 5.6)
|
|
);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* This method is intended for usage with e.g. Newsletter-Modules.
|
|
* You can hook into it if you want use alternative stores for it
|
|
*
|
|
* This resets the Log for sent emaildresses. It starts a new Session.
|
|
* Best usage should be interactively when setting up a new Newsletter.
|
|
*
|
|
*
|
|
* @return boolean true if Log is empty or false if it is not empty
|
|
*
|
|
*/
|
|
public function ___sentLogReset() {
|
|
$filename = wire('config')->paths->logs . self::LOG_FILENAME_SENTLOG . '.txt';
|
|
@touch($filename);
|
|
$res = file_put_contents($filename, '', LOCK_EX );
|
|
if(false===$res || 0!==$res || !file_exists($filename) || !is_readable($filename) || !is_writeable($filename)) {
|
|
$this->logError('Cannot reset Content of the SentLog: ' . $filename);
|
|
throw new WireException('You want to make usage of the SentLog-feature, but cannot reset Content of the SentLog: ' . basename($filename));
|
|
}
|
|
return 0===$res ? true : false;
|
|
}
|
|
|
|
|
|
/**
|
|
* This method is intended for usage with e.g. Newsletter-Modules.
|
|
* You can hook into it if you want use alternative stores for it
|
|
*
|
|
* This returns an array containing all emailaddresses
|
|
*
|
|
*
|
|
* @return array with emailaddresses as values
|
|
*
|
|
*/
|
|
public function ___sentLogGet() {
|
|
$filename = wire('config')->paths->logs . self::LOG_FILENAME_SENTLOG . '.txt';
|
|
@touch($filename);
|
|
if(!file_exists($filename) || !is_readable($filename) || !is_writeable($filename)) {
|
|
$this->logError('Cannot get content of the SentLog: ' . $filename);
|
|
throw new WireException('You want to make usage of the SentLog-feature, but cannot get content of the SentLog: ' . basename($filename));
|
|
}
|
|
$a = explode("\n", trim(file_get_contents($filename)));
|
|
$emailaddresses = array();
|
|
foreach($a as $e) {
|
|
if(trim($e)=='') continue;
|
|
$emailaddresses[] = trim($e);
|
|
}
|
|
return $emailaddresses;
|
|
}
|
|
|
|
|
|
/**
|
|
* This method is intended for usage with e.g. Newsletter-Modules.
|
|
* You can hook into it if you want use alternative stores for it
|
|
*
|
|
* Add a emailaddress to the SentLog
|
|
* If you have enabled the usage it is called automatically within
|
|
* the send() loop to store each successful sent emailaddress.
|
|
*
|
|
*
|
|
* @param string Must be a single email address
|
|
* @return boolean true or false
|
|
*
|
|
*/
|
|
public function ___sentLogAdd($emailaddress) {
|
|
$filename = wire('config')->paths->logs . self::LOG_FILENAME_SENTLOG . '.txt';
|
|
$data = trim(str_replace(array('<','>'), '', $emailaddress)) . "\n";
|
|
$res = file_put_contents($filename, $data, LOCK_EX + FILE_APPEND );
|
|
if(false===$res || strlen($data)!=$res) {
|
|
$this->logError('Cannot add emailaddress to the SentLog: ' . $filename);
|
|
throw new WireException('You want to make usage of the SentLog-feature, but cannot add emailaddress to the SentLog: ' . basename($filename));
|
|
}
|
|
return strlen($data) === $res ? true : false;
|
|
}
|
|
|
|
|
|
/**
|
|
* This method is intended for usage with e.g. Newsletter-Modules.
|
|
*
|
|
* If the send() method should make usage of the SentLog set it to true!
|
|
*
|
|
*
|
|
* @param boolean true or false
|
|
* @return $this
|
|
*
|
|
*/
|
|
public function useSentLog($useIt=true) {
|
|
$this->mail['useSentLog'] = (bool)$useIt;
|
|
return $this;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Bundle module settings back into an array for WireMailSmtpAdaptor
|
|
*
|
|
*/
|
|
public function getSettings() {
|
|
$siteconfig = is_array($this->wire('config')->wiremailsmtp) ? $this->wire('config')->wiremailsmtp : array();
|
|
$settings = array();
|
|
foreach(self::getDefaultData() as $key => $value) {
|
|
$k = $key;
|
|
$v = $this->$key;
|
|
if($key === 'valid_recipients') {
|
|
// convert multi-line textarea value to array of emails
|
|
$emails = array();
|
|
foreach(explode("\n", $this->valid_recipients) as $email) {
|
|
if(trim($email)=='') continue;
|
|
$emails[] = trim($email);
|
|
}
|
|
$settings[$key] = $emails;
|
|
// now check for settings in site/config.php that should override the default settings from the module config:
|
|
if(isset($siteconfig[$key])) $settings[$key] = $siteconfig[$key];
|
|
continue;
|
|
}
|
|
if($key === 'extra_headers') {
|
|
// convert multi-line textarea value to array of Key => Value pairs
|
|
$extraHeaders = array();
|
|
foreach(explode("\n", $this->get('extra_headers')) as $extraHeader) {
|
|
if(trim($extraHeader)=='') continue;
|
|
$tmp = explode('=', $extraHeader);
|
|
if(!is_array($tmp) || count($tmp)!=2) continue;
|
|
$extraHeaders[$tmp[0]] = $tmp[1];
|
|
}
|
|
$settings[$key] = $extraHeaders;
|
|
// now check for settings in site/config.php that should override the default settings from the module config:
|
|
if(isset($siteconfig[$key])) $settings[$key] = $siteconfig[$key];
|
|
continue;
|
|
}
|
|
$settings[$k] = $this->$key;
|
|
// now check for settings in site/config.php that should override the default settings from the module config:
|
|
if(isset($siteconfig[$k])) $settings[$k] = $siteconfig[$k];
|
|
}
|
|
return $settings;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Populate default settings
|
|
*
|
|
*/
|
|
public function __construct() {
|
|
$this->mail['header']['X-Mailer'] = "ProcessWire/" . $this->className();
|
|
foreach(self::getDefaultData() as $key => $value) {
|
|
$this->$key = $value;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialize the module and setup hooks
|
|
*
|
|
*/
|
|
public function init() {
|
|
require_once(wire('config')->paths->WireMailSmtp . 'WireMailSmtpAdaptor.php');
|
|
$this->smtp = new hnsmtp($this->getSettings());
|
|
}
|
|
|
|
|
|
// public function ready() {
|
|
// }
|
|
|
|
|
|
public function __destruct() {
|
|
if($this->smtp) $this->smtp->close();
|
|
unset($this->smtp);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Save activity message to log file
|
|
*
|
|
*/
|
|
public function logActivity($message) {
|
|
$this->log->save( self::LOG_FILENAME_ACTIVITY , $message);
|
|
}
|
|
|
|
|
|
/**
|
|
* Save error message to log file
|
|
*
|
|
*/
|
|
public function logError($message) {
|
|
$this->log->save( self::LOG_FILENAME_ERROR , $message);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Build a form allowing configuration of this Module
|
|
*
|
|
*/
|
|
static public function getModuleConfigInputfields(array $data) {
|
|
$localhost = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '');
|
|
$data = array_merge(self::getDefaultData(), array('localhost'=>$localhost), $data);
|
|
|
|
// // special handling for SMTP password
|
|
// // seen by @teppo's SwiftMailer
|
|
// if(isset($data['smtp_password2'])) {
|
|
// $data['smtp_password'] = $data['smtp_password2'];
|
|
// unset($data['smtp_password2'], $data['clear_smtp_password']);
|
|
// wire('modules')->saveModuleConfigData('WireMailSmtp', $data);
|
|
// }
|
|
// elseif(isset($data['clear_smtp_password']) && $data['clear_smtp_password']) {
|
|
// unset($data['smtp_password'], $data['smtp_password2'], $data['clear_smtp_password']);
|
|
// wire('modules')->saveModuleConfigData('WireMailSmtp', $data);
|
|
// }
|
|
// else {
|
|
// unset($data['smtp_password2'], $data['clear_smtp_password']);
|
|
// wire('modules')->saveModuleConfigData('WireMailSmtp', $data);
|
|
// }
|
|
|
|
require_once(dirname(__FILE__) . '/WireMailSmtpConfig.php');
|
|
$c = new WireMailSmtpConfig();
|
|
return $c->getConfig($data);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return a ready-to-use copy of the Adaptor
|
|
*
|
|
*/
|
|
public function getAdaptor() {
|
|
return $this;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Set the email cc address
|
|
*
|
|
* Each added email addresses appends to any addresses already supplied, unless
|
|
* you specify NULL as the email address, in which case it clears them all.
|
|
*
|
|
* @param string|array|null $email Specify any ONE of the following:
|
|
* 1. Single email address or "User Name <user@example.com>" string.
|
|
* 2. CSV string of #1.
|
|
* 3. Non-associative array of #1.
|
|
* 4. Associative array of (email => name)
|
|
* 5. NULL (default value, to clear out any previously set values)
|
|
* @param string $name Optionally provide a FROM name, applicable
|
|
* only when specifying #1 (single email) for the first argument.
|
|
* @return this
|
|
* @throws WireException if any provided emails were invalid
|
|
*
|
|
*/
|
|
public function cc($email = null, $name = null) {
|
|
|
|
if(is_null($email)) {
|
|
// clear existing values
|
|
$this->mail['cc'] = array();
|
|
$this->mail['ccName'] = array();
|
|
return $this;
|
|
}
|
|
|
|
$emails = is_array($email) ? $email : explode(',', $email);
|
|
|
|
foreach($emails as $key => $value) {
|
|
|
|
$toName = '';
|
|
if(is_string($key)) {
|
|
// associative array
|
|
// email provided as $key, and $toName as value
|
|
$toEmail = $key;
|
|
$toName = $value;
|
|
|
|
} else if(strpos($value, '<') !== false && strpos($value, '>') !== false) {
|
|
// toName supplied as: "User Name <user@example.com"
|
|
list($toEmail, $toName) = $this->extractEmailAndName($value);
|
|
|
|
} else {
|
|
// just an email address, possibly with name as a function arg
|
|
$toEmail = $value;
|
|
}
|
|
|
|
if(empty($toName)) $toName = $name; // use function arg if not overwritten
|
|
$toEmail = $this->sanitizeEmail($toEmail);
|
|
$this->mail['cc'][$toEmail] = $toEmail;
|
|
$this->mail['ccName'][$toEmail] = $this->sanitizeHeader($toName);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the email bcc address
|
|
*
|
|
* Each added email addresses appends to any addresses already supplied, unless
|
|
* you specify NULL as the email address, in which case it clears them all.
|
|
*
|
|
* @param string|array|null $email Specify any ONE of the following:
|
|
* 1. Single email address or "User Name <user@example.com>" string.
|
|
* 2. CSV string of #1.
|
|
* 3. Non-associative array of #1.
|
|
* 4. Associative array of (email => name)
|
|
* 5. NULL (default value, to clear out any previously set values)
|
|
* @param string $name Optionally provide a FROM name, applicable
|
|
* only when specifying #1 (single email) for the first argument.
|
|
* @return this
|
|
* @throws WireException if any provided emails were invalid
|
|
*
|
|
*/
|
|
public function bcc($email = null, $name = null) {
|
|
|
|
// a BCC Name isn't used, because BCC addresses by it's nature aren't kept in email messages
|
|
// we leave it here for compatibilty with TO and CC methods
|
|
|
|
if(is_null($email)) {
|
|
// clear existing values
|
|
$this->mail['bcc'] = array();
|
|
return $this;
|
|
}
|
|
|
|
$emails = is_array($email) ? $email : explode(',', $email);
|
|
|
|
foreach($emails as $key => $value) {
|
|
|
|
$toName = '';
|
|
if(is_string($key)) {
|
|
// associative array
|
|
// email provided as $key, and $toName as value
|
|
$toEmail = $key;
|
|
$toName = $value;
|
|
|
|
} else if(strpos($value, '<') !== false && strpos($value, '>') !== false) {
|
|
// toName supplied as: "User Name <user@example.com"
|
|
list($toEmail, $toName) = $this->extractEmailAndName($value);
|
|
|
|
} else {
|
|
// just an email address, possibly with name as a function arg
|
|
$toEmail = $value;
|
|
}
|
|
|
|
if(empty($toName)) $toName = $name; // use function arg if not overwritten
|
|
$toEmail = $this->sanitizeEmail($toEmail);
|
|
$this->mail['bcc'][$toEmail] = $toEmail;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the 'cc' name
|
|
*
|
|
* It is preferable to do this with the cc() method, but this is provided to ensure that
|
|
* all properties can be set with direct access, i.e. $mailer->toName = 'User Name';
|
|
*
|
|
* This sets the 'to name' for whatever the last added 'cc' email address was.
|
|
*
|
|
* @param string
|
|
* @return this
|
|
* @throws WireException if you attempt to set a toName before a to email.
|
|
*
|
|
*/
|
|
public function ccName($name) {
|
|
$emails = $this->mail['cc'];
|
|
if(!count($emails)) throw new WireException("Please set a 'cc' address before setting a name.");
|
|
$email = end($emails);
|
|
$this->mail['ccName'][$email] = $this->sanitizeHeader($name);
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the email from address
|
|
*
|
|
* @param string Must be a single email address or "User Name <user@example.com>" string.
|
|
* @param string|null An optional FROM name (same as setting/calling fromName)
|
|
* @return this
|
|
* @throws WireException if provided email was invalid
|
|
*
|
|
*/
|
|
public function from($email = '', $name = null) {
|
|
if(is_null($name)) list($email, $name) = $this->extractEmailAndName($email);
|
|
if(empty($email)) {
|
|
$email = $this->sender_email;
|
|
$name = $this->sender_name;
|
|
}
|
|
if($name) $this->mail['fromName'] = $this->sanitizeHeader($name);
|
|
$this->mail['from'] = $email;
|
|
return $this;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Set the email priority headers
|
|
*
|
|
* @param number 1 | 2 | 3 | 4 | 5 ( 1 = highest | 3 = normal / default | 5 = lowest )
|
|
* @return this
|
|
*
|
|
*/
|
|
public function priority($number) {
|
|
$this->mail['priority'] = $number;
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Request a Disposition Notification
|
|
*
|
|
* @return this
|
|
*
|
|
*/
|
|
public function dispositionNotification($request=true) {
|
|
$this->mail['dispositionNotification'] = (bool)$request;
|
|
return $this;
|
|
}
|
|
public function notification($request=true) {
|
|
return $this->dispositionNotification($request);
|
|
}
|
|
|
|
|
|
|
|
|
|
// I don't find the param names of the core class to be intuitive or descriptive in the right way.
|
|
// The "$value" in real is the "filename" of the attachment, and the "$filename"
|
|
// in real is an "alternative BASENAME"! This may lead to confusion.
|
|
//
|
|
// But I have to use it the exact same way as in the core class, because otherwise the
|
|
// systems raises PHP strict notices about the different param names. :(
|
|
// So please, for better understanding, read on the example of the next method "attachments()".
|
|
//
|
|
// old code: public function attachment($filename, $alternativeBasename = '')
|
|
/**
|
|
* Add attachment to the email,
|
|
* (conform with the core Wiremail update introduced in PW 3.0.36)
|
|
*
|
|
* @param $filename string, Full path and filename of file attachment, (no URL!)
|
|
* @param $alternativeBasename (optional) string, Optional different basename for file as it appears in the mail
|
|
* @return WireMailSmtp
|
|
*/
|
|
public function attachment($value, $filename = '') {
|
|
$a = array($value => $filename);
|
|
return $this->attachments($a);
|
|
}
|
|
/**
|
|
* @param array $values, array with keys (absolute pathes to filenames) and optional values (alternative basenames)
|
|
* example: array(
|
|
* '/an/absolute/path/to/file.typ' => 'alternative-basename.typ',
|
|
* '/another/path/to/anotherfile.typ' => '',
|
|
* // ...
|
|
* )
|
|
* @return WireMailSmtp
|
|
*/
|
|
public function attachments($values) {
|
|
// $value is the fullpath filename (!), filename is an optional alternativeBasename (!), the terms are this way in PW core WireMail
|
|
foreach($values as $filename => $alternativeBasename) {
|
|
$this->mail['attachments'][trim($filename)] = trim($alternativeBasename);
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
|
|
|
|
|
|
public function addSignature($add=true) {
|
|
$this->mail['addSignature'] = (bool)$add;
|
|
return $this;
|
|
}
|
|
|
|
public function wrapText($wrap=true) {
|
|
$this->mail['wrapText'] = (bool)$wrap;
|
|
return $this;
|
|
}
|
|
|
|
|
|
|
|
public function sendSingle($singleMail=true) {
|
|
$this->mail['sendSingle'] = (bool)$singleMail;
|
|
if((bool)$singleMail) {
|
|
$this->mail['sendBulk'] = false;
|
|
}
|
|
return $this;
|
|
}
|
|
public function single($singleMail=true) {
|
|
return $this->sendSingle($singleMail);
|
|
}
|
|
|
|
|
|
public function sendBulk($bulkMail=true) {
|
|
$this->mail['sendBulk'] = (bool)$bulkMail;
|
|
if((bool)$bulkMail) {
|
|
$this->mail['sendSingle'] = false;
|
|
}
|
|
return $this;
|
|
}
|
|
public function bulk($bulkMail=true) {
|
|
return $this->sendBulk($bulkMail);
|
|
}
|
|
|
|
|
|
|
|
|
|
public function ___send($debugServer = false) {
|
|
$this->smtp->setSender($this->mail['from'], $this->mail['fromName']);
|
|
if($this->mail['dispositionNotification']) $this->setNotification(); // must be called after setSender
|
|
$this->smtp->setCustomHeader($this->mail['header']);
|
|
if($this->mail['priority']!='') $this->setPriority($this->mail['priority']);
|
|
$this->setSubject($this->mail['subject']);
|
|
|
|
if($this->bodyHTML) {
|
|
// we use MultipartAlternative
|
|
$text = strlen($this->body) ? $this->body : strip_tags($this->bodyHTML);
|
|
$this->setTextAndHtmlBody($text, $this->bodyHTML, $this->mail['addSignature']);
|
|
} else {
|
|
// we use plaintext
|
|
$this->setTextBody($this->body, $this->mail['addSignature']);
|
|
}
|
|
|
|
|
|
foreach($this->mail['attachments'] as $value => $filename) {
|
|
$this->attachFile($value, $filename);
|
|
}
|
|
|
|
|
|
// check and optionally adjust setting for sendSingle
|
|
if($this->mail['sendSingle'] && count($this->to)>1) {
|
|
$this->mail['sendSingle'] = false;
|
|
$this->logError("You have set sendSingle to true, but also you have provided more than one TO-Recipient. We now change the sending method to send multiple messages!");
|
|
}
|
|
|
|
// send a single email an quit
|
|
if($this->mail['sendSingle']) {
|
|
|
|
// with sending only a single mail, we may also use cc and bcc recipients
|
|
// there should be only one TO recipients, but it is let to the user how he
|
|
// want handle this
|
|
foreach($this->to as $email) {
|
|
$name = $this->toName[$email];
|
|
$this->addRecipient($email, $name, 'to');
|
|
}
|
|
foreach($this->cc as $email) {
|
|
$name = $this->ccName[$email];
|
|
$this->addRecipient($email, $name, 'cc');
|
|
}
|
|
foreach($this->bcc as $email) {
|
|
$this->addRecipient($email, '', 'bcc');
|
|
}
|
|
|
|
// correct the recipient headers >>>
|
|
$tmpTO = $tmpCC = $tmpBCC = array();
|
|
foreach($this->maildata['recipients'] as $tmpRecipient) {
|
|
$tmpRecipientStr = strlen(trim($tmpRecipient['name'])) > 0 ? '"'. trim($tmpRecipient['name']) .'" ' : '';
|
|
$tmpRecipientStr .= '<'. trim($tmpRecipient['emailaddress']) .'>';
|
|
switch($tmpRecipient['type']) {
|
|
case 'to':
|
|
$tmpTO[$tmpRecipient['emailaddress']] = $tmpRecipientStr;
|
|
break;
|
|
case 'cc':
|
|
$tmpCC[$tmpRecipient['emailaddress']] = $tmpRecipientStr;
|
|
break;
|
|
case 'bcc':
|
|
$tmpBCC[$tmpRecipient['emailaddress']] = $tmpRecipientStr;
|
|
break;
|
|
}
|
|
}
|
|
$tmpTOstr = implode(', ', $tmpTO);
|
|
$tmpCCstr = implode(', ', $tmpCC);
|
|
$tmpBCCstr = implode(', ', $tmpBCC);
|
|
#$this->smtp->setHeader('To', trim($tmpTOstr));
|
|
if ($tmpCCstr) {
|
|
$this->smtp->setHeader('CC', trim($tmpCCstr));
|
|
}
|
|
if ($tmpBCCstr) {
|
|
$this->smtp->setHeader('BCC', trim($tmpBCCstr));
|
|
}
|
|
unset($tmpRecipient, $tmpRecipientStr, $tmpTO, $tmpTOstr, $tmpCC, $tmpCCstr, $tmpBCC, $tmpBCCstr);
|
|
// <<< correct the recipient headers
|
|
|
|
$maildata = '';
|
|
$ret = $this->smtp->send($debugServer, $debugServer, $maildata);
|
|
$this->maildata['send'] = $maildata;
|
|
return $ret ? 1 : 0;
|
|
|
|
}
|
|
|
|
// send multiple messages and quit
|
|
$numSent = 0;
|
|
$recipientsSuccess = array();
|
|
$recipientsFailed = array();
|
|
|
|
if($this->mail['useSentLog']) {
|
|
$sent = $this->sentLogGet();
|
|
if(!is_array($sent)) {
|
|
$this->logError('Cannot get content of the SentLog!');
|
|
throw new WireException('You want to make usage of the SentLog-feature, but cannot get content of the SentLog!');
|
|
}
|
|
$recipientsSuccess = $sent;
|
|
unset($sent);
|
|
}
|
|
if(count($this->to)>50) $this->mail['sendBulk'] = true;
|
|
if(count($this->to)>5 || $this->mail['sendBulk']) $this->smtp->SetBulkMail(1);
|
|
if($this->mail['sendBulk']) $this->smtp->SetHeader('Precedence', 'bulk');
|
|
foreach($this->bcc as $bcc) $this->addRecipient($bcc, '', 'bcc');
|
|
foreach($this->to as $to) {
|
|
if(in_array($to, $recipientsSuccess)) continue; // "Only one cross each" (Monty Python: The Life of Brian)
|
|
set_time_limit(intval(30));
|
|
|
|
$toName = $this->mail['toName'][$to];
|
|
$this->addRecipient($to, $toName, 'to');
|
|
|
|
$ret = $this->smtp->send($debugServer, $debugServer, $maildata);
|
|
|
|
if($ret) {
|
|
$recipientsSuccess[$to] = $to;
|
|
if($this->mail['useSentLog'] && ! $this->sentLogAdd($to)) {
|
|
$this->logError('Cannot add emailaddress to the SentLog: ' . SELF::LOG_FILENAME_SENTLOG . '.txt');
|
|
throw new WireException('You want to make usage of the SentLog-feature, but cannot add emailaddress to the SentLog: ' . SELF::LOG_FILENAME_SENTLOG . '.txt');
|
|
}
|
|
}
|
|
else {
|
|
$recipientsFailed[$to] = $to;
|
|
}
|
|
$numSent += $ret ? 1 : 0;
|
|
}
|
|
if(count($this->to)>5 || $this->mail['sendBulk']) $this->smtp->SetBulkMail(0);
|
|
$this->smtp->close();
|
|
|
|
if(!isset($maildata) && 0==$numSent) $maildata = 'no messages are sent';
|
|
$this->maildata['send'] = $maildata;
|
|
$this->maildata['recipientsSuccess'] = $recipientsSuccess;
|
|
$this->maildata['recipientsFailed'] = $recipientsFailed;
|
|
|
|
return $numSent;
|
|
}
|
|
|
|
|
|
/**
|
|
* This method is intended for verbose debug purposes!
|
|
* Use this instead of the regular send() method.
|
|
*
|
|
* @returns a debugDump in string format
|
|
* @param $outputMode: integer, 1 = echo HTML | 2 = echo PlainText | 3 return String | 4 = write into file
|
|
* @param $filename: string, path to writeable log filename, required for $outputMode = 4
|
|
*/
|
|
public function debugSend($outputMode = 1, $filename = '') {
|
|
ob_start();
|
|
$this->send(true);
|
|
$debugLog = "\n\n" . ob_get_clean();
|
|
ob_start();
|
|
$dump = '';
|
|
$dump .= $this->mvd(array('SETTINGS' => $this->getSettings()), $outputMode, $filename);
|
|
$dump .= $this->mvd(array('RESULT' => $this->getResult()), $outputMode, $filename);
|
|
$dump .= $this->mvd(array('ERRORS' => $this->getErrors()), $outputMode, $filename);
|
|
$dump .= $this->mvd(array('DEBUGLOG' => $debugLog), $outputMode, $filename);
|
|
$return = ob_get_clean();
|
|
if(1 == $outputMode || 2 == $outputMode) {
|
|
echo $return;
|
|
$dump = $return;
|
|
}
|
|
return str_replace(array($this->mvdWrap1(), $this->mvdWrap2(), "\n<!--", "\n-->"), '', $dump);
|
|
}
|
|
|
|
|
|
public function getResult() {
|
|
// Returns an array with all settings and content of the current email
|
|
return $this->maildata;
|
|
}
|
|
|
|
|
|
public function getErrors() {
|
|
// Returns an array of error messages, if they occurred.
|
|
// Returns blank array if no errors occurred.
|
|
// The module would call this after getImages() or testConnection() to
|
|
// see if it should display/log any error messages.
|
|
return (array)$this->smtp->getErrors();
|
|
}
|
|
|
|
|
|
public function testConnection() {
|
|
// Tests that the email settings work. This would be used by the module at
|
|
// config time to give the user a Yes or No as to whether their email settings
|
|
// are functional. Returns a boolean TRUE or FALSE.
|
|
return $this->smtp->testConnection();
|
|
}
|
|
|
|
|
|
// formatting for debug log output, used by debugSend()
|
|
private function mvd($v, $outputMode = 1, $filename = '') {
|
|
ob_start();
|
|
var_dump($v);
|
|
$content = ob_get_contents();
|
|
ob_end_clean();
|
|
|
|
$m = 0;
|
|
preg_match_all('#^(.*)=>#mU', $content, $stack);
|
|
$lines = $stack[1];
|
|
$indents = array_map('strlen', $lines);
|
|
if($indents) $m = max($indents) + 1;
|
|
$content = preg_replace_callback(
|
|
'#^(.*)=>\\n\s+(\S)#Um',
|
|
function($match) use ($m) {
|
|
return $match[1] . str_repeat(' ', ($m - strlen($match[1]) > 1 ? $m - strlen($match[1]) : 1)) . $match[2];
|
|
},
|
|
$content
|
|
);
|
|
$content = preg_replace('#^((\s*).*){$#m', "\\1\n\\2{", $content);
|
|
$content = str_replace(array('<pre>', '</pre>'), '', $content);
|
|
|
|
switch($outputMode) {
|
|
case 1:
|
|
// Output to Browser-Window
|
|
echo $this->mvdWrap1() . $content . $this->mvdWrap2();
|
|
break;
|
|
case 2:
|
|
// Output to Commandline-Window or to Browser as hidden comment
|
|
echo isset($_SERVER['HTTP_HOST']) ? "\n<!--\n{$content}\n-->\n" : "{$content}\n";
|
|
break;
|
|
case 3:
|
|
// Output into a StringVar
|
|
return $this->mvdWrap1() . $content . $this->mvdWrap2();
|
|
break;
|
|
case 4:
|
|
// Output into a file, if a valid filename is given and we have write access to it
|
|
@touch($filename);
|
|
if(is_writable($filename)) {
|
|
$content = str_replace(array('>','"',' '), array('>','"',''), strip_tags($content));
|
|
$res = file_put_contents($filename, $content, FILE_APPEND);
|
|
wireChmod($filename);
|
|
return $res === strlen($content);
|
|
}
|
|
return false;
|
|
break;
|
|
}
|
|
}
|
|
private function mvdWrap1() { return "<pre style=\"overflow:scroll !important; margin: 10px 20px; padding: 10px 10px 10px 10px; background-color:#F2F2F2; color:#000; border: 1px solid #333; font-family:'Hack', 'Source Code Pro', 'Lucida Console', 'Courier', monospace; font-size:12px; line-height:15px;\">"; }
|
|
private function mvdWrap2() { return "</pre>"; }
|
|
|
|
|
|
/* $type = ['To'|'CC'|'BCC'] */
|
|
protected function addRecipient($emailaddress, $name='', $type='To') {
|
|
if(!in_array(strtoupper($type), array('TO','CC','BCC'))) {
|
|
$type = 'To';
|
|
}
|
|
$emailaddress = str_replace(array('<', '>'), '', $emailaddress);
|
|
$this->maildata['recipients'][] = array('emailaddress'=>$emailaddress, 'name'=>$name, 'type'=>$type);
|
|
return $this->smtp->setEmailHeader($type, $emailaddress, $name);
|
|
}
|
|
|
|
protected function setSubject($text) {
|
|
$this->maildata['subject'] = (string)$text;
|
|
return $this->smtp->setHeader('Subject', (string)$text);
|
|
}
|
|
|
|
protected function setTextBody($text, $addSignature=null) {
|
|
$maildata = '';
|
|
if(!is_bool($addSignature)) {
|
|
if(in_array($this->send_sender_signature, array('1','2','3'))) {
|
|
switch($this->send_sender_signature) {
|
|
case '1': // only when explicitly called via API
|
|
$addSignature = false;
|
|
break;
|
|
case '2': // automaticaly when FROM = Sender Emailaddress
|
|
$from = strtolower(trim(str_replace(array('<','>'), '', $this->from)));
|
|
$sender = strtolower(trim(str_replace(array('<','>'), '', $this->sender_email)));
|
|
$addSignature = $from == $sender || '' == $from ? true : false;
|
|
break;
|
|
case '3': // automaticaly with _every_ Message
|
|
$addSignature = true;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
$addSignature = false;
|
|
}
|
|
}
|
|
$res = $this->smtp->setTextBody($text, $addSignature, $this->wrapText, $maildata);
|
|
$this->maildata['addSignature'] = $addSignature ? '1' : '0';
|
|
$this->maildata['textbody'] = $maildata;
|
|
return $res;
|
|
}
|
|
|
|
protected function setTextAndHtmlBody($text, $html, $addSignature=null) {
|
|
$maildata1 = $maildata2 = '';
|
|
if(!is_bool($addSignature)) {
|
|
if(in_array($this->send_sender_signature, array('1','2','3'))) {
|
|
switch($this->send_sender_signature) {
|
|
case '1': // only when explicitly called via API
|
|
$addSignature = false;
|
|
break;
|
|
case '2': // automaticaly when FROM = Sender Emailaddress
|
|
$from = strtolower(trim(str_replace(array('<','>'), '', $this->from)));
|
|
$sender = strtolower(trim(str_replace(array('<','>'), '', $this->sender_email)));
|
|
$addSignature = $from == $sender || '' == $from ? true : false;
|
|
break;
|
|
case '3': // automaticaly with _every_ Message
|
|
$addSignature = true;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
$addSignature = false;
|
|
}
|
|
}
|
|
$res = $this->smtp->setTextAndHtmlBody($text, $html, $addSignature, $this->wrapText, $maildata1, $maildata2);
|
|
$this->maildata['addSignature'] = $addSignature ? '1' : '0';
|
|
$this->maildata['textbody'] = $maildata1;
|
|
$this->maildata['htmlbody'] = $maildata2;
|
|
return $res;
|
|
}
|
|
|
|
// updated to PW 3.0.36 new attachment function with optional alternative basename
|
|
protected function attachFile($value, $filename = '') {
|
|
|
|
if(!file_exists($value) || !is_readable($value)) {
|
|
$this->logError('Error in $WireMailSmtp->attachFile($filename): Not existing or not readable file: ' . $value);
|
|
return false;
|
|
}
|
|
$attachment = array(
|
|
'FileName' => $value,
|
|
'Content-Type' => 'automatic/name',
|
|
'Disposition' => 'attachment'
|
|
);
|
|
if($filename) $attachment['Name'] = $filename; // optional alternative basename
|
|
$ret = $this->smtp->addAttachment($attachment);
|
|
if(!$ret) {
|
|
return false;
|
|
}
|
|
$this->maildata['attachments'][] = $value; // logging attachment filename
|
|
return true;
|
|
}
|
|
|
|
protected function setNotification() {
|
|
$maildata = '';
|
|
$ret = $this->smtp->setNotification($maildata);
|
|
if($ret) {
|
|
$this->maildata['notification'] = $maildata;
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
/* $priority = [ 1 | 2 | (3) | 4 | 5 ] */
|
|
protected function setPriority($priority=3) {
|
|
if($ret = (bool)$this->smtp->setPriority($priority)) {
|
|
$this->maildata['priority'] = $priority;
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Return instance of the installer class
|
|
*
|
|
*/
|
|
protected function getInstaller() {
|
|
#require_once(dirname(__FILE__) . '/WireMailSmtpInstall.php');
|
|
#return new WireMailSmtpInstall();
|
|
}
|
|
|
|
|
|
/**
|
|
* Perform installation
|
|
*
|
|
*/
|
|
public function ___install() {
|
|
#$this->getInstaller()->install(self::$defaultSettings);
|
|
}
|
|
|
|
|
|
/**
|
|
* Perform uninstall
|
|
*
|
|
*/
|
|
public function ___uninstall() {
|
|
#$this->getInstaller()->uninstall($this);
|
|
}
|
|
|
|
}
|
|
|
|
|