diff --git a/e107_admin/mailout.php b/e107_admin/mailout.php
index c56ee005f..0f811ea54 100644
--- a/e107_admin/mailout.php
+++ b/e107_admin/mailout.php
@@ -2,7 +2,7 @@
/*
* e107 website system
*
- * Copyright (C) 2008-2010 e107 Inc (e107.org)
+ * Copyright (C) 2008-2013 e107 Inc (e107.org)
* Released under the terms and conditions of the
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
*
@@ -399,6 +399,7 @@ switch ($action)
case 'sent' :
case 'pending' :
case 'held' :
+ case 'mailshowtemplate' :
if (isset($_POST['etrigger_ecolumns']))
{
$mailAdmin->mailbodySaveColumnPref($action);
@@ -554,6 +555,10 @@ switch ($action)
$mailAdmin->showEmailList($action, -1, -1);
break;
+ case 'mailshowtemplate' : // Show the templated email
+ $mailAdmin->showEmailTemplate($mailId);
+ break;
+
case 'maildelete' : // NOTE:: need to set previous page in form
$mailAdmin->showDeleteConfirm($mailId, $pageMode);
break;
@@ -679,7 +684,14 @@ function saveMailPrefs(&$emessage)
if (!in_array($_POST['mailer'], array('smtp', 'sendmail', 'php'))) $_POST['mailer'] = 'php';
$temp['mailer'] = $_POST['mailer'];
// Allow qmail as an option as well - works much as sendmail
- if ((strpos($_POST['sendmail'],'sendmail') !== FALSE) || (strpos($_POST['sendmail'],'qmail') !== FALSE)) $temp['sendmail'] = $e107->tp->toDB($_POST['sendmail']);
+ if ((strpos($_POST['sendmail'],'sendmail') !== FALSE) || (strpos($_POST['sendmail'],'qmail') !== FALSE))
+ {
+ $temp['sendmail'] = $e107->tp->toDB($_POST['sendmail']);
+ }
+ else
+ {
+ $temp['sendmail'] = '';
+ }
$temp['smtp_server'] = $e107->tp->toDB($_POST['smtp_server']);
$temp['smtp_username'] = $e107->tp->toDB($_POST['smtp_username']);
$temp['smtp_password'] = $e107->tp->toDB($_POST['smtp_password']);
diff --git a/e107_admin/notify.php b/e107_admin/notify.php
index 6431e5beb..0d8e1b74c 100644
--- a/e107_admin/notify.php
+++ b/e107_admin/notify.php
@@ -2,7 +2,7 @@
/*
* e107 website system
*
- * Copyright (C) 2008-2009 e107 Inc (e107.org)
+ * Copyright (C) 2008-2013 e107 Inc (e107.org)
* Released under the terms and conditions of the
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
*
diff --git a/e107_handlers/mail.php b/e107_handlers/mail.php
index ba174b544..d790db040 100644
--- a/e107_handlers/mail.php
+++ b/e107_handlers/mail.php
@@ -1,886 +1,947 @@
-SetLanguage(CORE_LC) - e.g. 'en', 'br'
-5. Logging:
- - Use rolling log for errors - error string(s) available - sort out entry
- - Look at support of some other logging options
-9. Make sure SMTPDebug can be set (TRUE/FALSE)
-12. Check support for port number - ATM we just override for SSL. Looks as if phpmailer can take it from end of server link.
-13. Possibly strip bbcode from plain text mailings - best done by caller?
-18. Note object iteration - may be useful for dump of object state
-19. Consider overriding error handler
-20. Look at using new prefs structure
-21. Should we always send an ID?
-22. Force singleton so all mail sending flow controlled a bit
-
-
-Tested so far (with PHP4 version)
-------------
-SMTP send (standard)
-Return receipts
-text or mixed
-replyto
-priority field
-TLS to googlemail (use TLS)
-
-
-Notes if problems
------------------
-1. Attachment adding call had dirname() added round path.
-2. There are legacy and new methods for generating a multi-part body (HTML + plain text). Only the new method handles inline images.
- - Currently uses the new method (which is part of phpmailer)
-
-
-General notes
--------------
-1. Can specify a comma-separated list of smtp servers - presumably all require the same login credentials
-2. qmail can be used (if available) by selecting sendmail, and setting the sendmail path to that for qmail instead
-3. phpmailer does trim() on passed parameters where needed - so we don't need to.
-4. phpmailer has its own list of MIME types.
-5. Attachments - note method available for passing string attachments
- - AddStringAttachment($string,$filename,$encoding,$type)
-6. Several email address-related methods can accept two comma-separated strings, one for addresses and one for related names
-7. Its possible to send a text-only email by passing an array of parameters including 'send_html' = FALSE
-8. For bulk emailing, must call the 'allSent()' method when complete to ensure SMTP mailer is properly closed.
-9. For sending through googlemail (and presumably gmail), use TLS
-10. Note that the 'add_html_header' option adds only the DOCTYPE bits - not the
.... section
-
-
-Possible Enhancements
----------------------
-1. Support other fields:
- ContentType
- Encoding - ???. Defaults to 8-bit
-
-
-
-Preferences used:
- $pref['mailer'] - connection type - SMTP, sendmail etc
-
- $pref['mail_options'] - NEW - general mailing options
- textonly - if true, defaults to plain text emails
- hostname=text - used in Message ID and received headers, and default Helo string. (Otherwise server-related default used)
-
- $pref['mail_log_options'] - NEW. Logging options (also used in mailout_process). Comma-separated list of values
- 1 - logenable - numeric value 0..3 controlling logging to a text file
- 2 - add_email - if '1', the detail of the email is logged as well
-
- $pref['smtp_server'] |
- $pref['smtp_username'] | Server details. USed for POP3 server if POP before SMTP authorisation
- $pref['smtp_password'] |
- $pref['smtp_keepalive'] - deprecated in favour of option - flag
- $pref['smtp_pop3auth'] - deprecated in favour of option - POP before SMTP authorisation flag
- $pref['smtp_options'] - NEW - comma separated list:
- keepalive - If active, bulk email send keeps the SMTP connection open, closing it every $pref['mail_pause'] emails
- useVERP - formats return path to facilitate bounce processing
- secure=[TLS|SSL] - enable secure authorisation by TLS or SSL
- pop3auth - enable POP before SMTP authorisation
- helo=text - Alternative Helo string
-
- $pref['sendmail'] - path to sendmail
-
- $pref['mail_pause'] - number of emails to send before pause
- $pref['mail_pausetime'] - time to pause
-
- $pref['mail_bounce_email'] - 'reply to' address
- $pref['mail_bounce_pop3']
- $pref['mail_bounce_user']
- $pref['mail_bounce_pass']
-
-Usage
-=====
-1. Create new object of the correct class
-2. Set up everything - to/from/email etc
-3. Call create_connection()
-4. Call send_mail()
-+----------------------------------------------------------------------------+
-*/
-
-if (!defined('e107_INIT')) { exit; }
-
-
-//define('MAIL_DEBUG',TRUE);
-//define('LOG_CALLER', TRUE);
-
-require_once(e_HANDLER.'phpmailer/class.phpmailer.php');
-
-// Directory for log (if enabled)
-define('MAIL_LOG_PATH',e_LOG);
-
-class e107Email extends PHPMailer
-{
- private $general_opts = array();
- private $logEnable = 0; // 0 = log disabled, 1 = 'dry run' (debug and log, no send). 2 = 'log all' (send, and log result)
- private $logHandle = FALSE; // Save handle of log file if opened
-
- private $localUseVerp = FALSE; // Use our own variable - PHPMailer one doesn't work with all mailers
- private $save_bouncepath = ''; // Used with VERP
-
- private $add_email = 0; // 1 includes email detail in log (if logging enabled, of course)
- private $allow_html = 1; // Flag for HTML conversion - '1' = default, FALSE = disable, TRUE = force.
- private $add_HTML_header = FALSE; // If TRUE, inserts a standard HTML header at the front of the HTML part of the email (set FALSE for BC)
- private $SendCount = 0; // Keep track of how many emails sent since last SMTP open/connect (used for SMTP KeepAlive)
- private $TotalSent = 0; // Info might be of interest
- private $TotalErrors = 0; // Count errors in sending emails
- private $pause_amount = 10; // Number of emails to send before pausing/resetting (or closing if SMTPkeepAlive set)
- private $pause_time = 1; // Time to pause after sending a block of emails
- private $templateOption = array();
- public $legacyBody = FALSE; // TRUE enables legacy conversion of plain text body to HTML in HTML emails
- public $template = "email"; // Choice of email, notify or mailout
-
-/**
- * Constructor sets up all the global options, and sensible defaults - it should be the only place the prefs are accessed
- *
- * @var array $overrides - array of values which override mail-related prefs. Key is the same as the corresponding pref.
- * @return none
- */
- public function __construct($overrides = FALSE)
- {
- parent::__construct(FALSE); // Parent constructor - no exceptions for now
-
- $e107 = e107::getInstance();
- global $pref;
-
- //Load up Email Templates
- include(e107::coreTemplatePath('email','front'));
- $this->templateOption['email'] = array('header'=>$EMAIL_HEADER,'footer'=>$EMAIL_FOOTER);
- $this->templateOption['notify'] = array('header'=>$NOTIFY_HEADER,'footer'=>$NOTIFY_FOOTER);
- $this->templateOption['mailout'] = array('header'=>$MAILOUT_HEADER,'footer'=>$MAILOUT_FOOTER);
-
- $this->CharSet = 'utf-8';
- $this->SetLanguage(CORE_LC);
-
- if (($overrides === FALSE) || !is_array($overrides))
- {
- $overrides = array();
- }
-
- foreach (array('mailer', 'smtp_server', 'smtp_username', 'smtp_password', 'sendmail', 'siteadminemail', 'siteadmin', 'smtp_pop3auth') as $k)
- {
- if (!isset($overrides[$k])) $overrides[$k] = $pref[$k];
- }
- $this->pause_amount = varset($pref['mail_pause'], 10);
- $this->pause_time = varset($pref['mail_pausetime'], 1);
-
- if (varsettrue($pref['mail_options'])) $this->general_opts = explode(',',$pref['mail_options'],'');
- if (defined('MAIL_DEBUG')) echo 'Mail_options: '.$pref['mail_options'].' Count: '.count($this->general_opts).'
';
- foreach ($this->general_opts as $k => $v)
- {
- $v = trim($v);
- $this->general_opts[$k] = $v;
- if (strpos($v,'hostname') === 0)
- {
- list(,$this->HostName) = explode('=',$v);
- if (defined('MAIL_DEBUG')) echo "Host name set to: {$this->HostName}
";
- }
- }
-
- list($this->logEnable,$this->add_email) = explode(',',varset($pref['mail_log_options'],'0,0'));
-
- switch ($overrides['mailer'])
- {
- case 'smtp' :
- $smtp_options = array();
- $temp_opts = explode(',',varset($pref['smtp_options'],''));
- if (varsettrue($overrides ['smtp_pop3auth'])) $temp_opts[] = 'pop3auth'; // Legacy option - remove later
- if (varsettrue($pref['smtp_keepalive'])) $temp_opts[] = 'keepalive'; // Legacy option - remove later
- foreach ($temp_opts as $k=>$v)
- {
- if (strpos($v,'=') !== FALSE)
- {
- list($v,$k) = explode('=',$v,2);
- $smtp_options[trim($v)] = trim($k);
- }
- else
- {
- $smtp_options[trim($v)] = TRUE; // Simple on/off option
- }
- }
- unset($temp_opts);
-
- $this->IsSMTP(); // Enable SMTP functions
- if (varsettrue($smtp_options['helo'])) $this->Helo = $smtp_options['helo'];
-
- if (isset($smtp_options['pop3auth'])) // We've made sure this is set
- { // Need POP-before-SMTP authorisation
- require_once(e_HANDLER.'phpmailer/class.pop3.php');
- $pop = new POP3();
- $pop->Authorise($overrides['smtp_server'], 110, 30, $overrides['smtp_username'], $overrides['smtp_password'], 1);
- }
-
- $this->Mailer = 'smtp';
- $this->localUseVerp = isset($smtp_options['useVERP']);
- if (isset($smtp_options['secure']))
- {
- switch ($smtp_options['secure'])
- {
- case 'TLS' :
- $this->SMTPSecure = 'tls';
- $this->Port = 465; // Can also use port 587, and maybe even 25
- break;
- case 'SSL' :
- $this->SMTPSecure = 'ssl';
- $this->Port = 465;
- break;
- default :
- if (defined('MAIL_DEBUG')) echo "Invalid option: {$smtp_options['secure']}
";
- }
- }
- $this->SMTPKeepAlive = varset($smtp_options['keepalive'],FALSE); // ***** Control this
- $this->Host = $overrides['smtp_server'];
- if($overrides['smtp_username'] && $overrides['smtp_password'])
- {
- $this->SMTPAuth = (!isset($smtp_options['pop3auth']));
- $this->Username = $overrides['smtp_username'];
- $this->Password = $overrides['smtp_password'];
- }
- break;
- case 'sendmail' :
- $this->Mailer = 'sendmail';
- $this->Sendmail = ($overrides['sendmail']) ? $overrides['sendmail'] : '/usr/sbin/sendmail -t -i -r '.varsettrue($pref['replyto_email'],$overrides['siteadminemail']);
- break;
- case 'php' :
- $this->Mailer = 'mail';
- break;
- }
- if (varsettrue($pref['mail_bounce_email'])) $this->Sender = $pref['mail_bounce_email'];
-
- $this->FromName = $e107->tp->toHTML(varsettrue($pref['replyto_name'],$overrides['siteadmin']),'','RAWTEXT');
- $this->From = $e107->tp->toHTML(varsettrue($pref['replyto_email'],$overrides['siteadminemail']),'','RAWTEXT');
- $this->WordWrap = 76; // Set a sensible default
-
- // Now look for any overrides - slightly cumbersome way of doing it, but does give control over what can be set from here
- // Options are those accepted by the arraySet() method.
- foreach (array('SMTPDebug', 'subject', 'from', 'fromname', 'replyto', 'send_html', 'add_html_header', 'attachments', 'cc', 'bcc',
- 'bouncepath', 'returnreceipt', 'priority', 'extra_header', 'wordwrap', 'split') as $opt)
- {
- if (isset($overrides[$opt]))
- {
- $this->arraySet(array($opt => $overrides[$opt]));
- }
- }
- }
-
- /**
- * Set log level
- * @param int $level 0|1|2
- * @param int $emailDetails 0|1
- * @return e107Email
- */
- public function logEnable($level, $emailDetails = null)
- {
- $this->logEnable = (int) $level;
- if(null !== $this->add_email)
- {
- $this->add_email = (int) $emailDetails;
- }
- return $this;
- }
-
- /**
- * Disable log completely
- * @return e107Email
- */
- public function logDisable()
- {
- $this->logEnable = 0;
- $this->add_email = 0;
- return $this;
- }
-
-
- /**
- * Format 'to' address and name
- *
- * @param string $email - email address of recipient
- * @param string $to - name of recipient
- * @return string in form: Fred Bloggs
- */
- public function makePrintableAddress($email,$to)
- {
- $to = trim($to);
- $email = trim($email);
- return $to.' <'.$email.'>';
- }
-
-
- /**
- * Log functions - write to a log file
- * Each entry logged to a separate line
- *
- * @return none
- */
- protected function openLog($logInfo = TRUE)
- {
- if ($this->logEnable && ($this->logHandle === FALSE))
- {
- $logFileName = MAIL_LOG_PATH.'mailoutlog.txt';
- $this->logHandle = fopen($logFileName, 'a'); // Always append to file
- }
- if ($this->logHandle !== FALSE)
- {
- fwrite($this->logHandle,"\n\n=====".date('H:i:s y.m.d')."----------------------------------------------------------------=====\r\n");
- if ($logInfo)
- {
- fwrite($this->logHandle,' Mailer opened by '.USERNAME." - ID: {$mail_id}. Subject: {$this->Subject} Log action: {$this->logEnable}\r\n");
- if ($this->add_email)
- {
- fwrite($this->logHandle, 'From: '.$this->From.' ('.$this->FromName.")\r\n");
- fwrite($this->logHandle, 'Sender: '.$this->Sender."\r\n");
- fwrite($this->logHandle, 'Subject: '.$this->Subject."\r\n");
- // Following are private variables ATM
-// fwrite($this->logHandle, 'CC: '.$email_info['copy_to']."\r\n");
-// fwrite($this->logHandle, 'BCC: '.$email_info['bcopy_to']."\r\n");
-// fwrite($this->logHandle, 'Attach: '.$attach."\r\n");
- fwrite($this->logHandle, 'Body: '.$this->Body."\r\n");
- fwrite($this->logHandle,"-----------------------------------------------------------\r\n");
- }
- }
- if (defined('LOG_CALLER'))
- {
- $temp = debug_backtrace();
- foreach ($temp as $t)
- {
- if (!isset($t['class']) || ($t['class'] != 'e107Email'))
- {
- fwrite($this->logHandle, print_a($t,TRUE)."\r\n"); // Found the caller
- break;
- }
- }
- }
- }
- }
-
- protected function logLine($text)
- {
- if ($this->logEnable && ($this->logHandle > 0))
- {
- fwrite($this->logHandle,date('H:i:s y.m.d').' - '.$text."\r\n");
- }
- }
-
- protected function closeLog()
- {
- if ($this->logEnable && ($this->logHandle > 0))
- {
- fclose($this->logHandle);
- }
- }
-
-
-
- /**
- * Add a list of addresses to one of the address lists.
- * @param string $list - 'to', 'replyto', 'cc', 'bcc'
- * @param string $addresses - comma separated
- * @param string $names - either a single name (used for all addresses) or a comma-separated list corresponding to the address list
- * If the name field for an entry is blank, or there are not enough entries, the address is substituted
- * @return TRUE if list accepted, FALSE if invalid list name
- */
- public function AddAddressList($list = 'to',$addresses,$names = '')
- {
- $list = trim(strtolower($list));
- $tmp = explode(',',$addresses);
-
- if (strpos($names,',') === FALSE)
- {
- $names = array_fill(0,count($tmp),$names); // Same value for all addresses
- }
- else
- {
- $names = explode(',',$names);
- }
- foreach($tmp as $k => $adr)
- {
- $to_name = ($names[$k]) ? $names[$k] : $adr;
- switch ($list)
- {
- case 'to' :
- $this->AddAddress($adr, $to_name);
- break;
- case 'replyto' :
- $this->AddReplyTo($adr, $to_name);
- break;
- case 'cc' :
- if($this->Mailer == 'mail')
- {
- $this->AddCustomHeader('Cc: '.$adr);
- }
- else
- {
- $this->AddCC($adr, $to_name);
- }
- break;
- case 'bcc' :
- if($this->Mailer == 'mail')
- {
- $this->AddCustomHeader('Bcc: '.$adr);
- }
- else
- {
- $this->AddBCC($adr, $to_name);
- }
- break;
- default :
- return FALSE;
- }
- }
- return TRUE;
- }
-
-
-
-
- // New method of making a body uses the inbuilt functionality of phpmailer
- // $want_HTML= 1 uses default setting for HTML part. Set TRUE to enable, FALSE to disable
- // $add_HTML_header - if TRUE, a standard HTML header is added to the front of the HTML part
- public function makeBody($message,$want_HTML = 1, $add_HTML_header = FALSE)
- {
- switch (varset($this->general_opts['textonly'],'off'))
- {
- case 'pref' : // Disable HTML as default
- if ($want_HTML == 1) $want_HTML = FALSE;
- break;
- case 'force' : // Always disable HTML
- $want_HTML = FALSE;
- break;
- }
-
- if ($want_HTML !== FALSE)
- {
- if (defined('MAIL_DEBUG')) echo "Generating multipart email
";
- if ($add_HTML_header)
- {
- $message = "\n
- \n".$message;
- }
- if ($this->legacyBody && !preg_match('/<(font|br|a|img|b)/i', $message)) // Assume html if it includes one of these tags
- { // Otherwise assume its a plain text message which needs some conversion to render in HTML
- $message = htmlspecialchars($message,ENT_QUOTES,$this->CharSet);
- $message = preg_replace('%(http|ftp|https)(://\S+)%', '\1\2', $message);
- $message = preg_replace('/([[:space:]()[{}])(www.[-a-zA-Z0-9@:%_\+.~#?&\/\/=]+)/i', '\\1\\2', $message);
- $message = preg_replace('/([_\.0-9a-z-]+@([0-9a-z][0-9a-z-]+\.)+[a-z]{2,3})/i', '\\1', $message);
- $message = str_replace("\r\n","\n",$message); // Handle alternative newline characters
- $message = str_replace("\n\r","\n",$message); // Handle alternative newline characters
- $message = str_replace("\r","\n",$message); // Handle alternative newline characters
- $message = str_replace("\n", "
\n", $message);
- }
- $this->MsgHTML($message); // Theoretically this should do everything, including handling of inline images.
- }
- else
- { // generate the plain text as the sole part of the email
- if (defined('MAIL_DEBUG')) echo "Generating plain text email
";
- if (strpos($message,'') !== FALSE)
- {
- $text = strstr($message,'');
- }
- else
- {
- $text = $message;
- }
-
- $text = str_replace('
', "\n", $text);
- $text = strip_tags(str_replace('
', "\n", $text));
-
- // TODO: strip bbcodes here
-
- $this->Body = $text;
- $this->AltBody = ''; // Single part email
- }
- }
-
-
-
- // Add attachments - either a single one as a string, or an array
- public function attach($attachments)
- {
- if (!$attachments) return;
- if (!is_array($attachments)) $attachments = array($attachments);
-
- foreach($attachments as $attach)
- {
- $tempName = basename($attach);
- if(is_readable($attach) && $tempName)
- { // First parameter is complete path + filename; second parameter is 'name' of file to send
- $ext = pathinfo($attach, PATHINFO_EXTENSION);
- $this->AddAttachment($attach, $tempName,'base64',$this->_mime_types($ext));
- }
- }
- }
-
-
- // Add inline images (should mostly be handled automatically)
- function addInlineImages($inline)
- {
- if(!$inline) return;
- $tmp = explode(",",$inline);
- foreach($tmp as $inline_img)
- {
- if(is_readable($inline_img) && !is_dir($inline_img))
- {
- $ext = pathinfo($inline_img, PATHINFO_EXTENSION);
- $this->AddEmbeddedImage($inline_img, md5($inline_img), basename($inline_img),'base64',$this->_mime_types($ext));
- }
- }
- }
-
-
- // Sets one or more parameters from an array. See send_array() for list of parameters
- // Where parameter not present, doesn't change it - so can repeatedly call this function for bulk mailing, or to build up the list
- // Return 0 on success.
- // (Note that there is no requirement to use this method for everything; parameters can be set by mixing this method with individual setting)
- public function arraySet($paramlist)
- {
- if (isset($paramlist['SMTPDebug'])) $this->SMTPDebug = $paramlist['SMTPDebug']; // 'FALSE' is a valid value!
- if (varsettrue($paramlist['mail_subject'])) $this->Subject = $paramlist['mail_subject'];
- if (varsettrue($paramlist['mail_sender_email'])) $this->From = $paramlist['mail_sender_email'];
- if (varsettrue($paramlist['mail_sender_name'])) $this->FromName = $paramlist['mail_sender_name'];
- if (varsettrue($paramlist['mail_replyto'])) $this->AddAddressList('replyto',$paramlist['mail_replyto'],varsettrue($paramlist['mail_replytonames'],''));
- if (isset($paramlist['send_html'])) $this->allow_html = $paramlist['send_html']; // 'FALSE' is a valid value!
- if (isset($paramlist['add_html_header'])) $this->add_HTML_header = $paramlist['add_html_header']; // 'FALSE' is a valid value!
- if (varsettrue($paramlist['mail_body'])) $this->makeBody($paramlist['mail_body'], $this->allow_html, $this->add_HTML_header);
- if (varsettrue($paramlist['mail_attach'])) $this->attach($paramlist['mail_attach']);
- if (varsettrue($paramlist['mail_copy_to'])) $this->AddAddressList('cc',$paramlist['mail_copy_to'],varsettrue($paramlist['mail_cc_names'],''));
- if (varsettrue($paramlist['mail_bcopy_to'])) $this->AddAddressList('bcc',$paramlist['mail_bcopy_to'],varsettrue($paramlist['mail_bcc_names'],''));
- if (varsettrue($paramlist['bouncepath']))
- {
- $this->Sender = $paramlist['bouncepath']; // Bounce path
- $this->save_bouncepath = $paramlist['bouncepath']; // Bounce path
- }
- if (varsettrue($paramlist['returnreceipt'])) $this->ConfirmReadingTo = $paramlist['returnreceipt'];
- if (varsettrue($paramlist['mail_inline_images'])) $this->addInlineImages($paramlist['mail_inline_images']);
- if (varsettrue($paramlist['mail_priority'])) $this->Priority = $paramlist['mail_priority'];
- if (varsettrue($paramlist['e107_header'])) $this->AddCustomHeader("X-e107-id: {$paramlist['e107_header']}");
- if (varsettrue($paramlist['extra_header']))
- {
- if (is_array($paramlist['extra_header']))
- {
- foreach($paramlist['extra_header'] as $eh)
- {
- $this->addCustomHeader($eh);
- }
- }
- else
- {
- $this->addCustomHeader($paramlist['extra_header']);
- }
- }
-
- if (varset($paramlist['wordwrap'])) $this->WordWrap = $paramlist['wordwrap'];
- if (varsettrue($paramlist['split'])) $this->SingleTo = ($paramlist['split'] != FALSE);
-
- return 0; // No error
- }
-
-
- /*
- Send an email where the bulk of the data is passed in an array. Returns 0 on success.
- (Even if the array is null, because everything previously set up, this is the preferred entry point)
- Where parameter not present in the array, doesn't get changed - useful for bulk mailing
- If doing bulk mailing with repetitive calls, set $bulkmail parameter true, and must call allSent() when completed
- Some of these parameters have been made compatible with the array calculated by render_email() in signup.php
- Possible array parameters:
- $eml['mail_subject']
- $eml['mail_sender_email'] - 'From' email address
- $eml['mail_sender_name'] - 'From' name
- $eml['mail_replyto'] - Optional 'reply to' field
- $eml['mail_replytonames'] - Name(s) corresponding to 'reply to' field - only used if 'replyto' used
- $eml['send_html'] - if TRUE, includes HTML part in messages (only those added after this flag)
- $eml['add_html_header'] - if TRUE, adds the 2-line DOCTYPE declaration to the front of the HTML part (but doesn't add ...)
- $eml['mail_body'] - message body. May be HTML or text. Added according to the current state of the HTML enable flag
- $eml['mail_attach'] - string if one file, array of filenames if one or more.
- $eml['mail_copy_to'] - comma-separated list of cc addresses.
- $eml['mail_cc_names''] - comma-separated list of cc names. Optional, used only if $eml['mail_copy_to'] specified
- $eml['mail_bcopy_to'] - comma-separated list
- $eml['mail_bcc_names''] - comma-separated list of bcc names. Optional, used only if $eml['mail_copy_to'] specified
- $eml['bouncepath'] - Sender field (used for bounces)
- $eml['returnreceipt'] - email address for notification of receipt (reading)
- $eml['mail_inline_images'] - array of files for inline images
- $eml['priority'] - Email priority (1 = High, 3 = Normal, 5 = low)
- $eml['e107_header'] - Adds specific 'X-e107-id:' header
- $eml['extra_header'] - additional headers (format is name: value
- $eml['wordwrap'] - Set wordwrap value
- $eml['split'] - If true, sends an individual email to each recipient
- */
- public function sendEmail($send_to, $to_name, $eml = '', $bulkmail = FALSE)
- {
-// $e107 = e107::getInstance();
- if (count($eml))
- { // Set parameters from list
- $ret = $this->arraySet($eml);
- if ($ret) return $ret;
- }
-
- if ($bulkmail && $this->localUseVerp && $this->save_bouncepath && (strpos($this->save_bouncepath,'@') !== FALSE))
- {
- // Format where sender is owner@origin, target is user@domain is: owner+user=domain@origin
- list($our_sender,$our_domain) = explode('@', $this->save_bouncepath,2);
- if ($our_sender && $our_domain)
- {
- $this->Sender = $our_sender.'+'.str_replace($send_to,'@','=').'@'.$our_domain;
- }
- }
-
- $this->AddAddressList('to',$send_to,$to_name);
-
- $this->openLog(); // Delay log open until now, so all parameters set up
-
- $result = TRUE; // Temporary 'success' flag
- $this->SendCount++;
-
- if (($this->logEnable == 0) || ($this->logEnable == 2))
- {
- $result = $this->Send(); // Actually send email
-
- if (!$bulkmail && !$this->SMTPKeepAlive && ($this->Mailer == 'smtp')) $this->SmtpClose();
- }
- else
- { // Debug
- $result = TRUE;
- if (($logenable == 3) && (($this->SendCount % 7) == 4)) $result = FALSE; // Fail one email in 7 for testing
- }
-
- $this->TotalSent++;
- if (($this->pause_amount > 0) && ($this->SendCount >= $this->pause_amount))
- {
- if ($this->SMTPKeepAlive && ($this->Mailer == 'smtp')) $this->SmtpClose();
- sleep($this->pause_time);
- $this->SendCount = 0;
- }
-
- $this->logLine("Send to {$to_name} at {$send_to} Mail-ID={$mail_custom} - ".($result ? 'Success' : 'Fail'));
-
- $this->ClearAddresses(); // In case we send another email
- $this->ClearCustomHeaders();
-
- if ($result)
- {
- $this->closeLog();
- return TRUE;
- }
-
- $this->logLine('Error info: '.$this->ErrorInfo);
- // Error sending email
- $e107 = e107::getInstance();
- $e107->admin_log->e_log_event(3,debug_backtrace(),"MAIL","Send Failed",$this->ErrorInfo,FALSE,LOG_TO_ROLLING);
- $this->TotalErrors++;
- $this->closeLog();
- return $this->ErrorInfo;
- }
-
-
- // Called after a bulk mailing completed, to tidy up nicely
- public function allSent()
- {
- if ($this->SMTPKeepAlive && ($this->Mailer == 'smtp') && ($this->SendCount > 0))
- {
- $this->SmtpClose();
- $this->SendCount = 0;
- }
- }
-
- /**
- * Evaluates the message and returns modifications for inline images and backgrounds
- * Also creates an alternative plain text part (unless $this->AltBody already non-empty)
- * Modification of standard PHPMailer function (which it overrides)
- * @access public
- * @return $message
- */
- public function MsgHTML($message, $basedir = '')
- {
-
- $tp = e107::getParser();
-
- $EMAIL_HEADER = $tp->parseTemplate($this->templateOption[$this->template]['header']);
- $EMAIL_FOOTER = $tp->parseTemplate($this->templateOption[$this->template]['footer']);
-
- $message = $EMAIL_HEADER.$message.$EMAIL_FOOTER;
-
-
-
- preg_match_all("/(src|background)=([\"\'])(.*)\\2/Ui", $message, $images); // Modified to accept single quotes as well
- if(isset($images[3]))
- {
- foreach($images[3] as $i => $url)
- {
- // do not change urls for absolute images (thanks to corvuscorax)
- if (!preg_match('#^[A-z]+://#',$url))
- {
- $delim = $images[2][$i]; // Will be single or double quote
- $filename = basename($url);
- $directory = dirname($url);
- if ($directory == '.') $directory='';
- if (strpos($directory, e_HTTP) === 0)
- {
- $directory = substr(SERVERBASE, 0, -1).$directory; // Convert to absolute server reference
- $basedir = '';
- }
- //echo "CID file {$filename} in {$directory}. Base = ".SERVERBASE."< BaseDir = {$basedir}
";
- $cid = 'cid:' . md5($filename);
- $ext = pathinfo($filename, PATHINFO_EXTENSION);
- $mimeType = self::_mime_types($ext);
- if ( (strlen($basedir) > 1) && (substr($basedir,-1) != '/') && (substr($basedir,-1) != '\\')) { $basedir .= '/'; }
- if ( strlen($directory) > 1 && substr($directory,-1) != '/' && substr($directory,-1) != '\\') { $directory .= '/'; }
- //echo "Add image: {$basedir}|{$directory}|{$filename}
";
- if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64',$mimeType) )
- {
- // $images[1][$i] contains 'src' or 'background'
- $message = preg_replace("/".$images[1][$i]."=".$delim.preg_quote($url, '/').$delim."/Ui", $images[1][$i]."=".$delim.$cid.$delim, $message);
- }
- else
- {
- if (defined('MAIL_DEBUG')) echo "Add embedded image {$url} failed
";
- }
- }
- }
- }
-
-
- $this->IsHTML(true);
- $this->Body = $message;
-
- // print_a($message);
- $textMsg = str_replace(array('
', '
'), "\n", $message); // Modified to make sure newlines carried through
- $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$textMsg)));
- if (!empty($textMsg) && empty($this->AltBody))
- {
- $this->AltBody = html_entity_decode($textMsg);
- }
- if (empty($this->AltBody))
- {
- $this->AltBody = 'To view this email message, enable HTML!' . "\n\n";
- }
- }
-
-
-} // End of e107Mailer class
-
-
-
-//-----------------------------
-// Exception handler
-//-----------------------------
-// Overrides the phpmailer handler
-// For now just work the same as the phpmailer handler - maybe add features to log to rolling log or something later
-// Could throw an e107Exception
-class e107MailerException extends phpmailerException
-{
- public function errorMessage()
- {
- return parent::errorMsg();
- }
-}
-
-
-//--------------------------------------
-// Generic e107 Exception handler
-//--------------------------------------
-// Overrides the default handler - start of a more general handler
-class e107Exception extends Exception
-{
- public function __construct($message = '', $code = 0)
- {
- parent::__construct($message, $code);
- $e107 = e107::getInstance();
- $e107->admin_log->e_log_event(10,
- $this->getFile().'|@'.$this->getLine(),
- 'EXCEPT',
- $this->getCode().':'.$this->getMessage(),
- $this->getTraceAsString(),
- FALSE,
- LOG_TO_ROLLING);
- }
-}
-
-
-//-----------------------------------------------------
-// Legacy interface for backward compatibility
-//-----------------------------------------------------
-// (Preferred interface is to instantiate an e107Mail object, then call sendEmail method with an array of parameters
-
-// If $send_from is blank, uses the 'replyto' name and email if set, otherwise site admins details
-// $inline is a comma-separated list of embedded images to be included
-function sendemail($send_to, $subject, $message, $to_name, $send_from='', $from_name='', $attachments='', $Cc='', $Bcc='', $returnpath='', $returnreceipt='',$inline ='')
-{
- global $mailheader_e107id;
-
-
- $overrides = array();
- // TODO: Find a way of doing this which doesn't use a global (or just ditch sendemail() )
- // ----- Mail pref. template override for parked domains, site mirrors or dynamic values
- global $EMAIL_OVERRIDES;
- if (isset($EMAIL_OVERRIDES) && is_array($EMAIL_OVERRIDES))
- {
- $overrides = &$EMAIL_OVERRIDES; // These can override many of the email-related prefs
- if (isset($EMAIL_OVERRIDES['bouncepath'])) $returnpath = $EMAIL_OVERRIDES['bouncepath'];
- if (isset($EMAIL_OVERRIDES['returnreceipt'])) $returnreceipt = $EMAIL_OVERRIDES['returnreceipt'];
- }
-
- // Create a mailer object of the correct type (which auto-fills in sending method, server details)
- $mail = new e107Email($overrides);
-
- if (varsettrue($mailheader_e107id)) $mail->AddCustomHeader("X-e107-id: {$mailheader_e107id}");
-
- $mail->legacyBody = TRUE; // Need to handle plain text email conversion to HTML
- $mail->makeBody($message); // Add body, with conversion if required
-
- if($Cc) $mail->AddAddressList('cc', $Cc);
-
- if ($Bcc) $mail->AddAddressList('bcc', $Bcc);
-
- if (trim($send_from))
- {
- $mail->SetFrom($send_from, $from_name); // These have already been defaulted to sitewide options, so no need to set again if blank
- }
-
- $mail->Subject = $subject;
-
- $mail->attach($attachments);
-
- // Add embedded images (should be auto-handled now)
- if ($inline) $mail->addInlineImages($inline);
-
- // Passed parameter overrides any system default for bounce - but should this be 'ReplyTo' address instead?
- // if (varsettrue($returnpath)) $mail->Sender = $AddReplyToAddresses($returnpath,'');
- if (varsettrue($returnpath)) $mail->Sender = $returnpath;
-
- if (varsettrue($returnreceipt)) $mail->ConfirmReadingTo($returnreceipt);
-
- if ($mail->sendEmail($send_to,$to_name) === TRUE)
- { // Success
- return TRUE;
- }
-
- // Error info already logged
- return FALSE;
-}
-
-
-
-?>
+) and footer (from )
+
+Maybe each template is an array with several parts - optional header and footer, use defaults if not defined
+header looks for the {STYLESHEET} variable
+If we do that, can have a single override file, plus a core file
+
+3. mail (PHP method) - note that it has parameters for additional headers and other parameters
+4. Check that language support works - PHPMailer defaults to English if other files not available
+ - PHPMailer expects a 2-letter code - $this->SetLanguage(CORE_LC) - e.g. 'en', 'br'
+5. Logging:
+ - Use rolling log for errors - error string(s) available - sort out entry
+ - Look at support of some other logging options
+9. Make sure SMTPDebug can be set (TRUE/FALSE)
+12. Check support for port number - ATM we just override for SSL. Looks as if phpmailer can take it from end of server link.
+18. Note object iteration - may be useful for dump of object state
+19. Consider overriding error handler
+20. Look at using new prefs structure
+21. Should we always send an ID?
+22. Force singleton so all mail sending flow controlled a bit (but not where parameters overridden in constructor)
+
+
+Tested so far (with PHP4 version)
+------------
+SMTP send (standard)
+Return receipts
+text or mixed
+replyto
+priority field
+TLS to googlemail (use TLS)
+
+
+Notes if problems
+-----------------
+1. Attachment adding call had dirname() added round path.
+2. There are legacy and new methods for generating a multi-part body (HTML + plain text). Only the new method handles inline images.
+ - Currently uses the new method (which is part of phpmailer)
+
+
+General notes
+-------------
+1. Can specify a comma-separated list of smtp servers - presumably all require the same login credentials
+2. qmail can be used (if available) by selecting sendmail, and setting the sendmail path to that for qmail instead
+3. phpmailer does trim() on passed parameters where needed - so we don't need to.
+4. phpmailer has its own list of MIME types.
+5. Attachments - note method available for passing string attachments
+ - AddStringAttachment($string,$filename,$encoding,$type)
+6. Several email address-related methods can accept two comma-separated strings, one for addresses and one for related names
+7. Its possible to send a text-only email by passing an array of parameters including 'send_html' = FALSE
+8. For bulk emailing, must call the 'allSent()' method when complete to ensure SMTP mailer is properly closed.
+9. For sending through googlemail (and presumably gmail), use TLS
+10. Note that the 'add_html_header' option adds only the DOCTYPE bits - not the .... section
+
+
+Possible Enhancements
+---------------------
+1. Support other fields:
+ ContentType
+ Encoding - ???. Defaults to 8-bit
+
+
+
+Preferences used:
+ $pref['mailer'] - connection type - SMTP, sendmail etc
+
+ $pref['mail_options'] - NEW - general mailing options
+ textonly - if true, defaults to plain text emails
+ hostname=text - used in Message ID and received headers, and default Helo string. (Otherwise server-related default used)
+
+ $pref['mail_log_options'] - NEW. Logging options (also used in mailout_process). Comma-separated list of values
+ 1 - logenable - numeric value 0..3 controlling logging to a text file
+ 2 - add_email - if '1', the detail of the email is logged as well
+
+ $pref['smtp_server'] |
+ $pref['smtp_username'] | Server details. USed for POP3 server if POP before SMTP authorisation
+ $pref['smtp_password'] |
+ $pref['smtp_keepalive'] - deprecated in favour of option - flag
+ $pref['smtp_pop3auth'] - deprecated in favour of option - POP before SMTP authorisation flag
+ $pref['smtp_options'] - NEW - comma separated list:
+ keepalive - If active, bulk email send keeps the SMTP connection open, closing it every $pref['mail_pause'] emails
+ useVERP - formats return path to facilitate bounce processing
+ secure=[TLS|SSL] - enable secure authorisation by TLS or SSL
+ pop3auth - enable POP before SMTP authorisation
+ helo=text - Alternative Helo string
+
+ $pref['sendmail'] - path to sendmail
+
+ $pref['mail_pause'] - number of emails to send before pause
+ $pref['mail_pausetime'] - time to pause
+
+ $pref['mail_bounce_email'] - 'reply to' address
+ $pref['mail_bounce_pop3']
+ $pref['mail_bounce_user']
+ $pref['mail_bounce_pass']
+
+Usage
+=====
+1. Create new object of the correct class
+2. Set up everything - to/from/email etc
+3. Call create_connection()
+4. Call send_mail()
++----------------------------------------------------------------------------+
+*/
+
+if (!defined('e107_INIT')) { exit; }
+
+
+//define('MAIL_DEBUG',TRUE);
+//define('LOG_CALLER', TRUE);
+
+require_once(e_HANDLER.'phpmailer/class.phpmailer.php');
+
+// Directory for log (if enabled)
+define('MAIL_LOG_PATH',e_LOG);
+
+class e107Email extends PHPMailer
+{
+ private $general_opts = array();
+ private $logEnable = 0; // 0 = log disabled, 1 = 'dry run' (debug and log, no send). 2 = 'log all' (send, and log result)
+ private $logHandle = FALSE; // Save handle of log file if opened
+
+ private $localUseVerp = FALSE; // Use our own variable - PHPMailer one doesn't work with all mailers
+ private $save_bouncepath = ''; // Used with VERP
+
+ private $add_email = 0; // 1 includes email detail in log (if logging enabled, of course)
+ private $allow_html = 1; // Flag for HTML conversion - '1' = default, FALSE = disable, TRUE = force.
+ private $add_HTML_header = FALSE; // If TRUE, inserts a standard HTML header at the front of the HTML part of the email (set FALSE for BC)
+ private $SendCount = 0; // Keep track of how many emails sent since last SMTP open/connect (used for SMTP KeepAlive)
+ private $TotalSent = 0; // Info might be of interest
+ private $TotalErrors = 0; // Count errors in sending emails
+ private $pause_amount = 10; // Number of emails to send before pausing/resetting (or closing if SMTPkeepAlive set)
+ private $pause_time = 1; // Time to pause after sending a block of emails
+
+ public $legacyBody = FALSE; // TRUE enables legacy conversion of plain text body to HTML in HTML emails
+
+ /**
+ * Constructor sets up all the global options, and sensible defaults - it should be the only place the prefs are accessed
+ *
+ * @var array $overrides - array of values which override mail-related prefs. Key is the same as the corresponding pref.
+ * - second batch of keys can preset values configurable through the arraySet() method
+ * @return none
+ */
+ public function __construct($overrides = FALSE)
+ {
+ parent::__construct(FALSE); // Parent constructor - no exceptions for now
+
+ $e107 = e107::getInstance();
+ $pref = e107::pref('core');
+
+ $this->CharSet = 'utf-8';
+ $this->SetLanguage(CORE_LC);
+
+ if (($overrides === FALSE) || !is_array($overrides))
+ {
+ $overrides = array();
+ }
+
+ foreach (array('mailer', 'smtp_server', 'smtp_username', 'smtp_password', 'sendmail', 'siteadminemail', 'siteadmin') as $k)
+ {
+ if (!isset($overrides[$k])) $overrides[$k] = $pref[$k];
+ }
+ $this->pause_amount = varset($pref['mail_pause'], 10);
+ $this->pause_time = varset($pref['mail_pausetime'], 1);
+
+ if (varsettrue($pref['mail_options'])) $this->general_opts = explode(',',$pref['mail_options'],'');
+ if (defined('MAIL_DEBUG')) echo 'Mail_options: '.$pref['mail_options'].' Count: '.count($this->general_opts).'
';
+ foreach ($this->general_opts as $k => $v)
+ {
+ $v = trim($v);
+ $this->general_opts[$k] = $v;
+ if (strpos($v,'hostname') === 0)
+ {
+ list(,$this->HostName) = explode('=',$v);
+ if (defined('MAIL_DEBUG')) echo "Host name set to: {$this->HostName}
";
+ }
+ }
+
+ list($this->logEnable,$this->add_email) = explode(',',varset($pref['mail_log_options'],'0,0'));
+
+ switch ($overrides['mailer'])
+ {
+ case 'smtp' :
+ $smtp_options = array();
+ $temp_opts = explode(',',varset($pref['smtp_options'],''));
+ if (varsettrue($overrides ['smtp_pop3auth'])) $temp_opts[] = 'pop3auth'; // Legacy option - remove later
+ if (varsettrue($pref['smtp_keepalive'])) $temp_opts[] = 'keepalive'; // Legacy option - remove later
+ foreach ($temp_opts as $k=>$v)
+ {
+ if (strpos($v,'=') !== FALSE)
+ {
+ list($v,$k) = explode('=',$v,2);
+ $smtp_options[trim($v)] = trim($k);
+ }
+ else
+ {
+ $smtp_options[trim($v)] = TRUE; // Simple on/off option
+ }
+ }
+ unset($temp_opts);
+
+ $this->IsSMTP(); // Enable SMTP functions
+ if (varsettrue($smtp_options['helo'])) $this->Helo = $smtp_options['helo'];
+
+ if (isset($smtp_options['pop3auth'])) // We've made sure this is set
+ { // Need POP-before-SMTP authorisation
+ require_once(e_HANDLER.'phpmailer/class.pop3.php');
+ $pop = new POP3();
+ $pop->Authorise($overrides['smtp_server'], 110, 30, $overrides['smtp_username'], $overrides['smtp_password'], 1);
+ }
+
+ $this->Mailer = 'smtp';
+ $this->localUseVerp = isset($smtp_options['useVERP']);
+ if (isset($smtp_options['secure']))
+ {
+ switch ($smtp_options['secure'])
+ {
+ case 'TLS' :
+ $this->SMTPSecure = 'tls';
+ $this->Port = 465; // Can also use port 587, and maybe even 25
+ break;
+ case 'SSL' :
+ $this->SMTPSecure = 'ssl';
+ $this->Port = 465;
+ break;
+ default :
+ if (defined('MAIL_DEBUG')) echo "Invalid option: {$smtp_options['secure']}
";
+ }
+ }
+ $this->SMTPKeepAlive = varset($smtp_options['keepalive'],FALSE); // ***** Control this
+ $this->Host = $overrides['smtp_server'];
+ if($overrides['smtp_username'] && $overrides['smtp_password'])
+ {
+ $this->SMTPAuth = (!isset($smtp_options['pop3auth']));
+ $this->Username = $overrides['smtp_username'];
+ $this->Password = $overrides['smtp_password'];
+ }
+ break;
+ case 'sendmail' :
+ $this->Mailer = 'sendmail';
+ $this->Sendmail = ($overrides['sendmail']) ? $overrides['sendmail'] : '/usr/sbin/sendmail -t -i -r '.varsettrue($pref['replyto_email'],$overrides['siteadminemail']);
+ break;
+ case 'php' :
+ $this->Mailer = 'mail';
+ break;
+ }
+ if (varsettrue($pref['mail_bounce_email'])) $this->Sender = $pref['mail_bounce_email'];
+
+ $this->FromName = $e107->tp->toHTML(varsettrue($pref['replyto_name'],$overrides['siteadmin']),'','RAWTEXT');
+ $this->From = $e107->tp->toHTML(varsettrue($pref['replyto_email'],$overrides['siteadminemail']),'','RAWTEXT');
+ $this->WordWrap = 76; // Set a sensible default
+
+ // Now look for any overrides - slightly cumbersome way of doing it, but does give control over what can be set from here
+ // Options are those accepted by the arraySet() method.
+ foreach (array('SMTPDebug', 'email_subject', 'email_sender_email', 'email_sender_name', 'email_replyto', 'send_html',
+ 'add_html_header', 'email_attach', 'email_copy_to', 'email_bcopy_to',
+ 'bouncepath', 'returnreceipt', 'email_inline_images', 'email_priority', 'extra_header', 'wordwrap', 'split') as $opt)
+ {
+ if (isset($overrides[$opt]))
+ {
+ $this->arraySet(array($opt => $overrides[$opt]));
+ }
+ }
+ }
+
+ /**
+ * Set log level
+ * @param int $level 0|1|2
+ * @param int $emailDetails 0|1
+ * @return e107Email
+ */
+ public function logEnable($level, $emailDetails = null)
+ {
+ $this->logEnable = (int) $level;
+ if(null !== $this->add_email)
+ {
+ $this->add_email = (int) $emailDetails;
+ }
+ return $this;
+ }
+
+ /**
+ * Disable log completely
+ * @return e107Email
+ */
+ public function logDisable()
+ {
+ $this->logEnable = 0;
+ $this->add_email = 0;
+ return $this;
+ }
+
+
+ /**
+ * Format 'to' address and name
+ *
+ * @param string $email - email address of recipient
+ * @param string $to - name of recipient
+ * @return string in form: Fred Bloggs
+ */
+ public function makePrintableAddress($email,$to)
+ {
+ $to = trim($to);
+ $email = trim($email);
+ return $to.' <'.$email.'>';
+ }
+
+
+ /**
+ * Log functions - write to a log file
+ * Each entry logged to a separate line
+ *
+ * @return none
+ */
+ protected function openLog($logInfo = TRUE)
+ {
+ if ($this->logEnable && ($this->logHandle === FALSE))
+ {
+ $logFileName = MAIL_LOG_PATH.'mailoutlog.txt';
+ $this->logHandle = fopen($logFileName, 'a'); // Always append to file
+ }
+ if ($this->logHandle !== FALSE)
+ {
+ fwrite($this->logHandle,"\n\n=====".date('H:i:s y.m.d')."----------------------------------------------------------------=====\r\n");
+ if ($logInfo)
+ {
+ fwrite($this->logHandle,' Mailer opened by '.USERNAME." - ID: {$this->MessageID}. Subject: {$this->Subject} Log action: {$this->logEnable}\r\n");
+ if ($this->add_email)
+ {
+ fwrite($this->logHandle, 'From: '.$this->From.' ('.$this->FromName.")\r\n");
+ fwrite($this->logHandle, 'Sender: '.$this->Sender."\r\n");
+ fwrite($this->logHandle, 'Subject: '.$this->Subject."\r\n");
+ // Following are private variables ATM
+// fwrite($this->logHandle, 'CC: '.$email_info['copy_to']."\r\n");
+// fwrite($this->logHandle, 'BCC: '.$email_info['bcopy_to']."\r\n");
+// fwrite($this->logHandle, 'Attach: '.$attach."\r\n");
+ fwrite($this->logHandle, 'Body: '.$this->Body."\r\n");
+ fwrite($this->logHandle,"-----------------------------------------------------------\r\n");
+ }
+ }
+ if (defined('LOG_CALLER'))
+ {
+ $temp = debug_backtrace();
+ foreach ($temp as $t)
+ {
+ if (!isset($t['class']) || ($t['class'] != 'e107Email'))
+ {
+ fwrite($this->logHandle, print_a($t,TRUE)."\r\n"); // Found the caller
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Add a line to log file - time/date is prepended, and CRLF is appended
+ *
+ * @param string $text - line to add
+ * @return none
+ */
+ protected function logLine($text)
+ {
+ if ($this->logEnable && ($this->logHandle > 0))
+ {
+ fwrite($this->logHandle,date('H:i:s y.m.d').' - '.$text."\r\n");
+ }
+ }
+
+ /**
+ * Close log
+ */
+ protected function closeLog()
+ {
+ if ($this->logEnable && ($this->logHandle > 0))
+ {
+ fclose($this->logHandle);
+ }
+ }
+
+
+
+ /**
+ * Add a list of addresses to one of the address lists.
+ * @param string $list - 'to', 'replyto', 'cc', 'bcc'
+ * @param string $addresses - comma separated
+ * @param string $names - either a single name (used for all addresses) or a comma-separated list corresponding to the address list
+ * If the name field for an entry is blank, or there are not enough entries, the address is substituted
+ * @return TRUE if list accepted, FALSE if invalid list name
+ */
+ public function AddAddressList($list = 'to',$addresses,$names = '')
+ {
+ $list = trim(strtolower($list));
+ $tmp = explode(',',$addresses);
+
+ if (strpos($names,',') === FALSE)
+ {
+ $names = array_fill(0,count($tmp),$names); // Same value for all addresses
+ }
+ else
+ {
+ $names = explode(',',$names);
+ }
+ foreach($tmp as $k => $adr)
+ {
+ $to_name = ($names[$k]) ? $names[$k] : $adr;
+ switch ($list)
+ {
+ case 'to' :
+ $this->AddAddress($adr, $to_name);
+ break;
+ case 'replyto' :
+ $this->AddReplyTo($adr, $to_name);
+ break;
+ case 'cc' :
+ if($this->Mailer == 'mail')
+ {
+ $this->AddCustomHeader('Cc: '.$adr);
+ }
+ else
+ {
+ $this->AddCC($adr, $to_name);
+ }
+ break;
+ case 'bcc' :
+ if($this->Mailer == 'mail')
+ {
+ $this->AddCustomHeader('Bcc: '.$adr);
+ }
+ else
+ {
+ $this->AddBCC($adr, $to_name);
+ }
+ break;
+ default :
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+
+
+
+ /**
+ * Create email body, primarily using the inbuilt functionality of phpmailer
+ *
+ * @param boolean|int $want_HTML determines whether an HTML part of the email is created. 1 uses default setting for HTML part. Set TRUE to enable, FALSE to disable
+ * @param boolean $add_HTML_header - if TRUE, a standard HTML header is added to the front of the HTML part
+ *
+ * @return none
+ */
+ public function makeBody($message,$want_HTML = 1, $add_HTML_header = FALSE)
+ {
+ switch (varset($this->general_opts['textonly'],'off'))
+ {
+ case 'pref' : // Disable HTML as default
+ if ($want_HTML == 1) $want_HTML = FALSE;
+ break;
+ case 'force' : // Always disable HTML
+ $want_HTML = FALSE;
+ break;
+ }
+
+ if ($want_HTML !== FALSE)
+ {
+ if (defined('MAIL_DEBUG')) echo "Generating multipart email
";
+ if ($add_HTML_header)
+ {
+ $message = "\n
+ \n".$message;
+ }
+ if ($this->legacyBody && !preg_match('/<(font|br|a|img|b)/i', $message)) // Assume html if it includes one of these tags
+ { // Otherwise assume its a plain text message which needs some conversion to render in HTML
+ $message = htmlspecialchars($message,ENT_QUOTES,$this->CharSet);
+ $message = preg_replace('%(http|ftp|https)(://\S+)%', '\1\2', $message);
+ $message = preg_replace('/([[:space:]()[{}])(www.[-a-zA-Z0-9@:%_\+.~#?&\/\/=]+)/i', '\\1\\2', $message);
+ $message = preg_replace('/([_\.0-9a-z-]+@([0-9a-z][0-9a-z-]+\.)+[a-z]{2,3})/i', '\\1', $message);
+ $message = str_replace("\r\n","\n",$message); // Handle alternative newline characters
+ $message = str_replace("\n\r","\n",$message); // Handle alternative newline characters
+ $message = str_replace("\r","\n",$message); // Handle alternative newline characters
+ $message = str_replace("\n", "
\n", $message);
+ }
+ $this->MsgHTML($message); // Theoretically this should do everything, including handling of inline images.
+ }
+ else
+ { // generate the plain text as the sole part of the email
+ if (defined('MAIL_DEBUG')) echo "Generating plain text email
";
+ if (strpos($message,'') !== FALSE)
+ {
+ $text = strstr($message,'');
+ }
+ else
+ {
+ $text = $message;
+ }
+
+ $text = str_replace('
', "\n", $text);
+ $text = strip_tags(str_replace('
', "\n", $text));
+
+ // TODO: strip bbcodes here
+
+ $this->Body = $text;
+ $this->AltBody = ''; // Single part email
+ }
+ }
+
+
+ /**
+ * Add attachments to the current email - either a single one as a string, or an array
+ * Always sent in base64 encoding
+ *
+ * @param string|array $attachments - single attachment name as a string, or any number as an array
+ *
+ * @return none
+ */
+ public function attach($attachments)
+ {
+ if (!$attachments) return;
+ if (!is_array($attachments)) $attachments = array($attachments);
+
+ foreach($attachments as $attach)
+ {
+ $tempName = basename($attach);
+ if(is_readable($attach) && $tempName)
+ { // First parameter is complete path + filename; second parameter is 'name' of file to send
+ $ext = pathinfo($attach, PATHINFO_EXTENSION);
+ $this->AddAttachment($attach, $tempName,'base64',$this->_mime_types($ext));
+ }
+ }
+ }
+
+
+ /**
+ * Add inline images (should usually be handled automatically by PHPMailer)
+ *
+ * @param string $inline - comma separated list of file names
+ */
+ function addInlineImages($inline)
+ {
+ if(!$inline) return;
+ $tmp = explode(',',$inline);
+ foreach($tmp as $inline_img)
+ {
+ if(is_readable($inline_img) && !is_dir($inline_img))
+ {
+ $ext = pathinfo($inline_img, PATHINFO_EXTENSION);
+ $this->AddEmbeddedImage($inline_img, md5($inline_img), basename($inline_img),'base64',$this->_mime_types($ext));
+ }
+ }
+ }
+
+
+ /**
+ * Sets one or more parameters from an array. See @see{sendEmail()} for list of parameters
+ * Where parameter not present, doesn't change it - so can repeatedly call this function for bulk mailing, or to build up the list
+ * (Note that there is no requirement to use this method for everything; parameters can be set by mixing this method with individual setting)
+ *
+ * @param array $paramlist - list of parameters to set/change. Key is parameter name. @see{sendEmail()} for list of parameters
+ *
+ * @return int zero if no errors detected
+ */
+ public function arraySet($paramlist)
+ {
+ if (isset($paramlist['SMTPDebug'])) $this->SMTPDebug = $paramlist['SMTPDebug']; // 'FALSE' is a valid value!
+ if (varsettrue($paramlist['email_subject'])) $this->Subject = $paramlist['email_subject'];
+ if (varsettrue($paramlist['email_sender_email'])) $this->From = $paramlist['email_sender_email'];
+ if (varsettrue($paramlist['email_sender_name'])) $this->FromName = $paramlist['email_sender_name'];
+ if (varsettrue($paramlist['email_replyto'])) $this->AddAddressList('replyto',$paramlist['email_replyto'],varsettrue($paramlist['email_replytonames'],''));
+ if (isset($paramlist['send_html'])) $this->allow_html = $paramlist['send_html']; // 'FALSE' is a valid value!
+ if (isset($paramlist['add_html_header'])) $this->add_HTML_header = $paramlist['add_html_header']; // 'FALSE' is a valid value!
+ if (varsettrue($paramlist['email_body'])) $this->makeBody($paramlist['email_body'], $this->allow_html, $this->add_HTML_header);
+ if (varsettrue($paramlist['email_attach'])) $this->attach($paramlist['email_attach']);
+ if (varsettrue($paramlist['email_copy_to'])) $this->AddAddressList('cc',$paramlist['email_copy_to'],varsettrue($paramlist['email_cc_names'],''));
+ if (varsettrue($paramlist['email_bcopy_to'])) $this->AddAddressList('bcc',$paramlist['email_bcopy_to'],varsettrue($paramlist['email_bcc_names'],''));
+ if (varsettrue($paramlist['bouncepath']))
+ {
+ $this->Sender = $paramlist['bouncepath']; // Bounce path
+ $this->save_bouncepath = $paramlist['bouncepath']; // Bounce path
+ }
+ if (varsettrue($paramlist['returnreceipt'])) $this->ConfirmReadingTo = $paramlist['returnreceipt'];
+ if (varsettrue($paramlist['email_inline_images'])) $this->addInlineImages($paramlist['email_inline_images']);
+ if (varsettrue($paramlist['email_priority'])) $this->Priority = $paramlist['email_priority'];
+ if (varsettrue($paramlist['e107_header'])) $this->AddCustomHeader("X-e107-id: {$paramlist['e107_header']}");
+ if (varsettrue($paramlist['extra_header']))
+ {
+ if (is_array($paramlist['extra_header']))
+ {
+ foreach($paramlist['extra_header'] as $eh)
+ {
+ $this->addCustomHeader($eh);
+ }
+ }
+ else
+ {
+ $this->addCustomHeader($paramlist['extra_header']);
+ }
+ }
+
+ if (varset($paramlist['wordwrap'])) $this->WordWrap = $paramlist['wordwrap'];
+ if (varsettrue($paramlist['split'])) $this->SingleTo = ($paramlist['split'] != FALSE);
+
+ return 0; // No error
+ }
+
+
+
+ /**
+ Send an email where the bulk of the data is passed in an array. Returns 0 on success.
+ (Even if the array is null, because everything previously set up, this is the preferred entry point)
+ Where parameter not present in the array, doesn't get changed - useful for bulk mailing
+ If doing bulk mailing with repetitive calls, set $bulkmail parameter true, and must call allSent() when completed
+ Some of these parameters have been made compatible with the array calculated by render_email() in signup.php
+ Possible array parameters:
+ $eml['email_subject']
+ $eml['email_sender_email'] - 'From' email address
+ $eml['email_sender_name'] - 'From' name
+ $eml['email_replyto'] - Optional 'reply to' field
+ $eml['email_replytonames'] - Name(s) corresponding to 'reply to' field - only used if 'replyto' used
+ $eml['send_html'] - if TRUE, includes HTML part in messages (only those added after this flag)
+ $eml['add_html_header'] - if TRUE, adds the 2-line DOCTYPE declaration to the front of the HTML part (but doesn't add ...)
+ $eml['email_body'] - message body. May be HTML or text. Added according to the current state of the HTML enable flag
+ $eml['email_attach'] - string if one file, array of filenames if one or more.
+ $eml['email_copy_to'] - comma-separated list of cc addresses.
+ $eml['email_cc_names'] - comma-separated list of cc names. Optional, used only if $eml['email_copy_to'] specified
+ $eml['email_bcopy_to'] - comma-separated list
+ $eml['email_bcc_names'] - comma-separated list of bcc names. Optional, used only if $eml['email_copy_to'] specified
+ $eml['bouncepath'] - Sender field (used for bounces)
+ $eml['returnreceipt'] - email address for notification of receipt (reading)
+ $eml['email_inline_images'] - array of files for inline images
+ $eml['priority'] - Email priority (1 = High, 3 = Normal, 5 = low)
+ $eml['e107_header'] - Adds specific 'X-e107-id:' header
+ $eml['extra_header'] - additional headers (format is name: value
+ $eml['wordwrap'] - Set wordwrap value
+ $eml['split'] - If true, sends an individual email to each recipient
+
+ * @param string $send_to - recipient email address
+ * @param string $to_name - recipient name
+ * @param array $eml - optional array of additional parameters (see above)
+ * @param boolean $bulkmail - set TRUE if this email is one of a bulk send; FALSE if an isolated email
+ *
+ * @return boolean|string - TRUE if success, error message if failure
+ */
+ public function sendEmail($send_to, $to_name, $eml = '', $bulkmail = FALSE)
+ {
+ if (count($eml))
+ { // Set parameters from list
+ $ret = $this->arraySet($eml);
+ if ($ret) return $ret;
+ }
+
+ if ($bulkmail && $this->localUseVerp && $this->save_bouncepath && (strpos($this->save_bouncepath,'@') !== FALSE))
+ {
+ // Format where sender is owner@origin, target is user@domain is: owner+user=domain@origin
+ list($our_sender,$our_domain) = explode('@', $this->save_bouncepath,2);
+ if ($our_sender && $our_domain)
+ {
+ $this->Sender = $our_sender.'+'.str_replace($send_to,'@','=').'@'.$our_domain;
+ }
+ }
+
+ $this->AddAddressList('to',$send_to,$to_name);
+
+ $this->openLog(); // Delay log open until now, so all parameters set up
+
+ $result = TRUE; // Temporary 'success' flag
+ $this->SendCount++;
+
+ if (($this->logEnable == 0) || ($this->logEnable == 2))
+ {
+ $result = $this->Send(); // Actually send email
+
+ if (!$bulkmail && !$this->SMTPKeepAlive && ($this->Mailer == 'smtp')) $this->SmtpClose();
+ }
+ else
+ { // Debug
+ $result = TRUE;
+ //print_a($this);
+ if (($this->logEnable == 3) && (($this->SendCount % 7) == 4)) $result = FALSE; // Fail one email in 7 for testing
+ }
+
+ $this->TotalSent++;
+ if (($this->pause_amount > 0) && ($this->SendCount >= $this->pause_amount))
+ {
+ if ($this->SMTPKeepAlive && ($this->Mailer == 'smtp')) $this->SmtpClose();
+ sleep($this->pause_time);
+ $this->SendCount = 0;
+ }
+
+ $this->logLine("Send to {$to_name} at {$send_to} Mail-ID={$this->MessageID} - ".($result ? 'Success' : 'Fail'));
+
+ $this->ClearAddresses(); // In case we send another email
+ $this->ClearCustomHeaders();
+
+ if ($result)
+ {
+ $this->closeLog();
+ return TRUE;
+ }
+
+ $this->logLine('Error info: '.$this->ErrorInfo);
+ // Error sending email
+ $e107 = e107::getInstance();
+ $e107->admin_log->e_log_event(3,debug_backtrace(),"MAIL","Send Failed",$this->ErrorInfo,FALSE,LOG_TO_ROLLING);
+ $this->TotalErrors++;
+ $this->closeLog();
+ return $this->ErrorInfo;
+ }
+
+
+
+ /**
+ * Called after a bulk mailing completed, to tidy up nicely
+ *
+ * @return none
+ */
+ public function allSent()
+ {
+ if ($this->SMTPKeepAlive && ($this->Mailer == 'smtp') && ($this->SendCount > 0))
+ {
+ $this->SmtpClose();
+ $this->SendCount = 0;
+ }
+ }
+
+
+
+ /**
+ * Evaluates the message and returns modifications for inline images and backgrounds
+ * Also creates an alternative plain text part (unless $this->AltBody already non-empty)
+ * Modification of standard PHPMailer function (which it overrides)
+ * @access public
+ *
+ * @param string $message - the mail body to send
+ * @basedir string - optional 'root part' of paths specified in email - prepended as necessary
+ *
+ * @return string none (message saved ready to send)
+ */
+ public function MsgHTML($message, $basedir = '')
+ {
+ preg_match_all("/(src|background)=([\"\'])(.*)\\2/Ui", $message, $images); // Modified to accept single quotes as well
+ if(isset($images[3]))
+ {
+ foreach($images[3] as $i => $url)
+ {
+ // do not change urls for absolute images (thanks to corvuscorax)
+ if (!preg_match('#^[A-z]+://#',$url))
+ {
+ $delim = $images[2][$i]; // Will be single or double quote
+ $filename = basename($url);
+ $directory = dirname($url);
+ if ($directory == '.') $directory='';
+ if (strpos($directory, e_HTTP) === 0)
+ {
+ $directory = substr(SERVERBASE, 0, -1).$directory; // Convert to absolute server reference
+ $basedir = '';
+ }
+ //echo "CID file {$filename} in {$directory}. Base = ".SERVERBASE."< BaseDir = {$basedir}
";
+ $cid = 'cid:' . md5($filename);
+ $ext = pathinfo($filename, PATHINFO_EXTENSION);
+ $mimeType = self::_mime_types($ext);
+ if ( (strlen($basedir) > 1) && (substr($basedir,-1) != '/') && (substr($basedir,-1) != '\\')) { $basedir .= '/'; }
+ if ( strlen($directory) > 1 && substr($directory,-1) != '/' && substr($directory,-1) != '\\') { $directory .= '/'; }
+ //echo "Add image: {$basedir}|{$directory}|{$filename}
";
+ if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64',$mimeType) )
+ {
+ // $images[1][$i] contains 'src' or 'background'
+ $message = preg_replace("/".$images[1][$i]."=".$delim.preg_quote($url, '/').$delim."/Ui", $images[1][$i]."=".$delim.$cid.$delim, $message);
+ }
+ else
+ {
+ if (defined('MAIL_DEBUG')) echo "Add embedded image {$url} failed
";
+ }
+ }
+ }
+ }
+ $this->IsHTML(true);
+ $this->Body = $message;
+ //print_a($message);
+ $textMsg = str_replace(array('
', '
'), "\n", $message); // Modified to make sure newlines carried through
+ $textMsg = preg_replace('#^.*?#', '', $textMsg); // Knock off everything up to and including the body statement (if present)
+ $textMsg = preg_replace('#.*$#', '', $textMsg); // Knock off everything after and including the (if present)
+ $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$textMsg)));
+ if (!empty($textMsg) && empty($this->AltBody))
+ {
+ $this->AltBody = html_entity_decode($textMsg);
+ }
+ if (empty($this->AltBody))
+ {
+ $this->AltBody = 'To view this email message, enable HTML!' . "\n\n";
+ }
+ }
+
+} // End of e107Mailer class
+
+
+
+//-----------------------------
+// Exception handler
+//-----------------------------
+// Overrides the phpmailer handler
+// For now just work the same as the phpmailer handler - maybe add features to log to rolling log or something later
+// Could throw an e107Exception
+class e107MailerException extends phpmailerException
+{
+ public function errorMessage()
+ {
+ return parent::errorMsg();
+ }
+}
+
+
+//--------------------------------------
+// Generic e107 Exception handler
+//--------------------------------------
+// Overrides the default handler - start of a more general handler
+class e107Exception extends Exception
+{
+ public function __construct($message = '', $code = 0)
+ {
+ parent::__construct($message, $code);
+ $e107 = e107::getInstance();
+ $e107->admin_log->e_log_event(10,
+ $this->getFile().'|@'.$this->getLine(),
+ 'EXCEPT',
+ $this->getCode().':'.$this->getMessage(),
+ $this->getTraceAsString(),
+ FALSE,
+ LOG_TO_ROLLING);
+ }
+}
+
+
+//-----------------------------------------------------
+// Function call to send an email
+//-----------------------------------------------------
+/**
+ * Function call to send an email
+ *
+ * Deprecated function
+ *
+ * Preferred method is to instantiate an e107MailManager object, and use the sendEmails() method, which also allows templates.
+ *
+ * see also sendTemplated() where non-default formating is required
+ *
+ * Note that plain text emails are converted to HTML, and also sent with a text part
+ *
+ * @param string $send_to - email address of recipient
+ * @param string $subject
+ * @param string $message
+ * @param string $to_name
+ * @param string $send_from - sender email address. (Defaults to the sitewide 'replyto' name and email if set, otherwise site admins details)
+ * @param string $from_name - sender name. If $send_from is empty, defaults to the sitewide 'replyto' name and email if set, otherwise site admins details
+ * @param string $attachments - comma-separated list of attachments
+ * @param string $Cc - comma-separated list of 'copy to' email addresses
+ * @param string $Bcc - comma-separated list of 'blind copy to' email addresses
+ * @param string $returnpath - Sets 'reply to' email address
+ * @param boolean $returnreceipt - TRUE to request receipt
+ * @param string $inline - comma separated list of images to send inline
+ *
+ * @return boolean TRUE if send successfully (NOT an indication of receipt!), FALSE if error
+ */
+function sendemail($send_to, $subject, $message, $to_name='', $send_from='', $from_name='', $attachments='', $Cc='', $Bcc='', $returnpath='', $returnreceipt='',$inline ='')
+{
+ global $mailheader_e107id;
+
+
+ $overrides = array();
+ // TODO: Find a way of doing this which doesn't use a global (or just ditch sendemail() )
+ // Use defaults from email template?
+ // ----- Mail pref. template override for parked domains, site mirrors or dynamic values
+ global $EMAIL_OVERRIDES;
+ if (isset($EMAIL_OVERRIDES) && is_array($EMAIL_OVERRIDES))
+ {
+ $overrides = &$EMAIL_OVERRIDES; // These can override many of the email-related prefs
+ if (isset($EMAIL_OVERRIDES['bouncepath'])) $returnpath = $EMAIL_OVERRIDES['bouncepath'];
+ if (isset($EMAIL_OVERRIDES['returnreceipt'])) $returnreceipt = $EMAIL_OVERRIDES['returnreceipt'];
+ }
+
+ // Create a mailer object of the correct type (which auto-fills in sending method, server details)
+ $mail = new e107Email($overrides);
+
+ if (varsettrue($mailheader_e107id)) $mail->AddCustomHeader("X-e107-id: {$mailheader_e107id}");
+
+ $mail->legacyBody = TRUE; // Need to handle plain text email conversion to HTML
+ $mail->makeBody($message); // Add body, with conversion if required
+
+ if($Cc) $mail->AddAddressList('cc', $Cc);
+
+ if ($Bcc) $mail->AddAddressList('bcc', $Bcc);
+
+ if (trim($send_from))
+ {
+ $mail->SetFrom($send_from, $from_name); // These have already been defaulted to sitewide options, so no need to set again if blank
+ }
+
+ $mail->Subject = $subject;
+
+ $mail->attach($attachments);
+
+ // Add embedded images (should be auto-handled now)
+ if ($inline) $mail->addInlineImages($inline);
+
+ // Passed parameter overrides any system default for bounce - but should this be 'ReplyTo' address instead?
+ // if (varsettrue($returnpath)) $mail->Sender = $AddReplyToAddresses($returnpath,'');
+ if (varsettrue($returnpath)) $mail->Sender = $returnpath;
+
+ if (varsettrue($returnreceipt)) $mail->ConfirmReadingTo($returnreceipt);
+
+ if ($mail->sendEmail($send_to,$to_name) === TRUE)
+ { // Success
+ return TRUE;
+ }
+
+ // Error info already logged
+ return FALSE;
+}
+
+
+
+?>
diff --git a/e107_handlers/mail_manager_class.php b/e107_handlers/mail_manager_class.php
index 9cecd79bc..843762c33 100644
--- a/e107_handlers/mail_manager_class.php
+++ b/e107_handlers/mail_manager_class.php
@@ -2,25 +2,25 @@
/*
* e107 website system
*
- * Copyright (C) 2008-2010 e107 Inc (e107.org)
+ * Copyright (C) 2008-2013 e107 Inc (e107.org)
* Released under the terms and conditions of the
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
*
* e107 Mailout - mail database API and utility routines
*
- * $Source: /cvs_backup/e107_0.8/e107_handlers/mail_manager_class.php,v $
- * $Revision$
- * $Date$
- * $Author$
+ * $URL: https://e107.svn.sourceforge.net/svnroot/e107/trunk/e107_0.8/e107_handlers/redirection_class.php $
+ * $Id: redirection_class.php 11922 2010-10-27 11:31:18Z secretr $
+ * $Revision: 12125 $
*/
/**
*
* @package e107
* @subpackage e107_handlers
- * @version $Id$;
+ * @version $Id: mail_manager_class.php 12125 2011-04-08 05:11:38Z e107coders $;
*
* @todo - consider whether to extract links in text-only emails
+ * @todo - support separate template for the text part of emails
This class isolates the caller from the underlying database used to buffer and send emails.
Also includes a number of useful routines
@@ -72,16 +72,33 @@ mail_content - Details of the email to be sent to a number of people
mail_last_date Don't send after this date/time
mail_title A description of the mailout - not sent
mail_subject Subject line
- mail_body Body text
+ mail_body Body text - the 'raw' text as entered/specified by the user
+ mail_body_templated Complete body text after applying the template, but before any variable substitutions
mail_other Evaluates to an array of misc info - cc, bcc, attachments etc
+mail_other constituents:
+ mail_sender_email Sender's email address
+ mail_sender_name Sender's name
+ mail_copy_to Any recipients to copy
+ mail_bcopy_to Any recipients to BCC
+ mail_attach Comma-separated list of attachments
+ mail_send_style Send style - HTML, text, template name etc
+ mail_selectors Details of the selection criteria used for recipients (Only used internally)
+ mail_include_images TRUE if to embed images, FALSE to add link to them
+ mail_body_alt If non-empty, use for alternate email text (generally the 'plain text' alternative)
+ mail_overrides If non-empty, any overrides for the mailer, set by the template
-Within internal arrays, a flat structure is adopted. Variables relating to DB values all begin 'mail_' - others are internal (volatile) control variables
+
+
+Within internal arrays, a flat structure is adopted, with 'mail_other' merged with the rest of the 'mail_content' values.
+Variables relating to DB values all begin 'mail_' - others are internal (volatile) control variables
*/
if (!defined('e107_INIT')) { exit; }
+include_lan(e_LANGUAGEDIR.e_LANGUAGE.'/admin/lan_mailout.php'); // May be needed by anything loading this class
+
define('MAIL_STATUS_SENT', 0); // Mail sent. Email handler happy, but may have bounced (or may be yet to bounce)
define('MAIL_STATUS_BOUNCED', 1);
define('MAIL_STATUS_CANCELLED', 2);
@@ -115,8 +132,11 @@ class e107MailManager
protected $queryCount = array(); // Stores total number of records if SQL_CALC_ROWS is used (index = db object #)
protected $currentBatchInfo = array(); // Used during batch send to hold info about current mailout
protected $currentMailBody = ''; // Buffers current mail body
+ protected $currentTextBody = ''; // Alternative text body (if required)
protected $mailer = NULL; // Mailer class when required
+ protected $mailOverrides = FALSE; // Any overrides to be passed to the mailer
+
// Array defines DB types to be used
protected $dbTypes = array(
@@ -149,6 +169,7 @@ class e107MailManager
'mail_title' => 'todb',
'mail_subject' => 'todb',
'mail_body' => 'todb',
+ 'mail_body_templated' => 'todb',
'mail_other' => 'string' // Don't want entities here!
)
);
@@ -160,6 +181,7 @@ class e107MailManager
),
'mail_content' => array(
'mail_body' => '',
+ 'mail_body_templated' => '',
'mail_other' => ''
)
);
@@ -171,9 +193,11 @@ class e107MailManager
'mail_copy_to' => 1,
'mail_bcopy_to' => 1,
'mail_attach' => 1,
- 'mail_send_style' => 1,
+ 'mail_send_style' => 1, // HTML, text, template name etc
'mail_selectors' => 1, // Only used internally
- 'mail_include_images' => 1 // Used to determine whether to embed images, or link to them
+ 'mail_include_images' => 1, // Used to determine whether to embed images, or link to them
+ 'mail_body_alt' => 1, // If non-empty, use for alternate email text (generally the 'plain text' alternative)
+ 'mail_overrides' => 1
);
// List of fields which are the status counts of an email, and their titles
@@ -190,9 +214,10 @@ class e107MailManager
*
* @return void
*/
- public function __construct()
+ public function __construct($overrides = FALSE)
{
$this->e107 = e107::getInstance();
+ $this->mailOverrides = $overrides;
}
@@ -269,18 +294,21 @@ class e107MailManager
$res[$f] = '';
}
}
-
if (isset($data['mail_other']))
{
$array = new ArrayData;
- $tmp = $array->ReadArray($data['mail_other']);
+ $tmp = $array->ReadArray(str_replace('\\\'', '\'',$data['mail_other'])); // May have escaped data
if (is_array($tmp))
{
$res = array_merge($res,$tmp);
}
+ else
+ {
+ $res['Array_ERROR'] = 'No array found';
+ }
unset($res['mail_other']);
}
- elseif ($addMissing)
+ if ($addMissing)
{
foreach ($this->dbOther as $f => $v)
{
@@ -398,7 +426,7 @@ class e107MailManager
$array = new ArrayData;
if (isset($data['mail_other']))
{
- $tmp = $array->ReadArray($data['mail_other']);
+ $tmp = $array->ReadArray(str_replace('\\\'', '\'',$data['mail_other'])); // May have escaped data
if (is_array($tmp))
{
$res = array_merge($res,$tmp);
@@ -414,7 +442,7 @@ class e107MailManager
}
if (isset($data['mail_target_info']))
{
- $tmp = $array->ReadArray($data['mail_target_info']);
+ $tmp = $array->ReadArray(str_replace('\\\'', '\'',$data['mail_target_info'])); // May have escaped data
$res['mail_target_info'] = $tmp;
}
return $res;
@@ -434,7 +462,10 @@ class e107MailManager
}
- // Internal function to create a db object for our use if none exists
+
+ /**
+ * Internal function to create a db object for our use if none exists
+ */
protected function checkDB($which = 1)
{
if (($which == 1) && ($this->db == NULL))
@@ -448,7 +479,9 @@ class e107MailManager
}
- // Internal function to create a mailer object for our use if none exists
+ /**
+ * Internal function to create a mailer object for our use if none exists
+ */
protected function checkMailer()
{
if ($this->mailer != NULL) return;
@@ -456,13 +489,29 @@ class e107MailManager
{
require_once(e_HANDLER.'mail.php');
}
- $this->mailer = new e107Email; // Could add in overrides here
+ $this->mailer = new e107Email($this->mailOverrides);
}
+ /**
+ * Set the override values for the mailer object.
+ *
+ * @param array $overrides - see mail.php for details of accepted values
+ *
+ * @return boolean TRUE if accepted, FALSE if rejected
+ */
+ public function setMailOverrides($overrides)
+ {
+ if ($this->mailer != NULL) return FALSE; // Mailer already created - it's too late!
+ $this->mailOverrides = $overrides;
+ }
+
+
+
+
/**
- * Convert numeric represntation of mail status to a text string
+ * Convert numeric representation of mail status to a text string
*
* @param integer $status - numeric value of status
* @return string text value
@@ -532,7 +581,7 @@ class e107MailManager
/**
- * Get next email from selection
+ * Get next email from selection (usually from selectEmails() )
* @return Returns array of email data if available - FALSE if no further data, no active query, or other error
*/
public function getNextEmail()
@@ -545,7 +594,6 @@ class e107MailManager
{
$this->queryActive--;
return $this->dbToBoth($result);
-// return array_merge($this->dbToMail($result), $this->dbToTarget($result));
}
else
{
@@ -569,6 +617,8 @@ class e107MailManager
* Call to send next email from selection
*
* @return Returns TRUE if successful, FALSE on fail (or no more to go)
+ *
+ * @todo Could maybe save parsed page in cache if more than one email to go
*/
public function sendNextEmail()
{
@@ -579,6 +629,10 @@ class e107MailManager
return FALSE;
}
+ /**
+ * The $email variable has all the email data in 'flat' form, including that of the current recipient.
+ * field $email['mail_target_info'] has variable substitution information relating to the current recipient
+ */
if (count($this->currentBatchInfo))
{
//print_a($this->currentBatchInfo);
@@ -587,6 +641,7 @@ class e107MailManager
//echo "New email body: {$this->currentBatchInfo['mail_source_id']} != {$email['mail_source_id']}
";
$this->currentBatchInfo = array(); // New source email - clear stored info
$this->currentMailBody = ''; // ...and clear cache for message body
+ $this->currentTextBody = '';
}
}
if (count($this->currentBatchInfo) == 0)
@@ -615,45 +670,23 @@ class e107MailManager
if (!$this->currentMailBody)
{
- $this->currentMailBody = $this->makeEmailBody($email['mail_body'], $email['mail_send_style'], varset($email['mail_include_images'], FALSE));
- }
- // Do any substitutions
- $search = array();
- $replace = array();
- foreach ($email['mail_target_info'] as $k => $v)
- {
- $search[] = '|'.$k.'|';
- $replace[] = $v;
- }
- $email['mail_body'] = str_replace($search, $replace, $this->currentMailBody);
- $email['send_html'] = ($email['mail_send_style'] != 'textonly');
-
- // Set up any extra mailer parameters that need it
- if (!vartrue($email['e107_header']))
- {
- $temp = intval($email['mail_recipient_id']).'/'.intval($email['mail_source_id']).'/'.intval($email['mail_target_id']).'/';
- $email['e107_header'] = $temp.md5($temp); // Set up an ID
- }
- if (isset($email['mail_attach']) && (trim($email['mail_attach']) || is_array($email['mail_attach'])))
- {
- $downDir = realpath(e_ROOT.$this->e107->getFolder('downloads'));
- if (is_array($email['mail_attach']))
+ if (isset($email['mail_body_templated']))
{
- foreach ($email['mail_attach'] as $k => $v)
- {
- $email['mail_attach'][$k] = $downDir.$v;
- }
+ $this->currentMailBody = $email['mail_body_templated'];
}
else
{
- $email['mail_attach'] = $downDir.$email['mail_attach'];
+ $this->currentMailBody = $email['mail_body'];
}
+ $this->currentTextBody = $email['mail_body_alt']; // May be null
}
+
+ $mailToSend = $this->makeEmailBlock($email); // Substitute mail-specific variables, attachments etc
// print_a($email);
// Try and send
- $result = $this->mailer->sendEmail($email['mail_recipient_email'], $email['mail_recipient_name'], $email, TRUE);
+ $result = $this->mailer->sendEmail($email['mail_recipient_email'], $email['mail_recipient_name'], $mailToSend, TRUE);
// return; // ************************************************** Temporarily stop DB being updated when line active *****************************
@@ -744,6 +777,84 @@ class e107MailManager
+ /**
+ * Given an email block, creates an array of data compatible with PHPMailer, including any necessary substitutions
+ */
+ protected function makeEmailBlock($email)
+ {
+ $mailSubsInfo = array(
+ 'email_subject' => 'mail_subject',
+ 'email_sender_email' => 'mail_sender_email',
+ 'email_sender_name' => 'mail_sender_name',
+ // 'email_replyto' - Optional 'reply to' field
+ // 'email_replytonames' - Name(s) corresponding to 'reply to' field - only used if 'replyto' used
+ 'email_copy_to' => 'mail_copy_to', // - comma-separated list of cc addresses.
+ //'email_cc_names' - comma-separated list of cc names. Optional, used only if $eml['email_copy_to'] specified
+ 'email_bcopy_to' => 'mail_bcopy_to',
+ // 'email_bcc_names' - comma-separated list of bcc names. Optional, used only if $eml['email_copy_to'] specified
+ //'bouncepath' - Sender field (used for bounces)
+ //'returnreceipt' - email address for notification of receipt (reading)
+ //'email_inline_images' - array of files for inline images
+ //'priority' - Email priority (1 = High, 3 = Normal, 5 = low)
+ //'extra_header' - additional headers (format is name: value
+ //'wordwrap' - Set wordwrap value
+ //'split' - If true, sends an individual email to each recipient
+ );
+ $result = array();
+ if (!isset($email['mail_source_id'])) $email['mail_source_id'] = 0;
+ if (!isset($email['mail_target_id'])) $email['mail_target_id'] = 0;
+ if (!isset($email['mail_recipient_id'])) $email['mail_recipient_id'] = 0;
+ foreach ($mailSubsInfo as $k => $v)
+ {
+ if (isset($email[$v]))
+ {
+ $result[$k] = $email[$v];
+ }
+ }
+
+ // Do any substitutions
+ $search = array();
+ $replace = array();
+ foreach ($email['mail_target_info'] as $k => $v)
+ {
+ $search[] = '|'.$k.'|';
+ $replace[] = $v;
+ }
+ $result['email_body'] = str_replace($search, $replace, $this->currentMailBody);
+ if ($this->currentTextBody)
+ {
+ $result['mail_body_alt'] = str_replace($search, $replace, $this->currentTextBody);
+ }
+ $result['send_html'] = ($email['mail_send_style'] != 'textonly');
+ $result['add_html_header'] = FALSE; // We look after our own headers
+
+ // Set up any extra mailer parameters that need it
+ if (!vartrue($email['e107_header']))
+ {
+ $temp = intval($email['mail_recipient_id']).'/'.intval($email['mail_source_id']).'/'.intval($email['mail_target_id']).'/';
+ $result['e107_header'] = $temp.md5($temp); // Set up an ID
+ }
+ if (isset($email['mail_attach']) && (trim($email['mail_attach']) || is_array($email['mail_attach'])))
+ {
+ $downDir = realpath(e_ROOT.$this->e107->getFolder('downloads'));
+ if (is_array($email['mail_attach']))
+ {
+ foreach ($email['mail_attach'] as $k => $v)
+ {
+ $result['email_attach'][$k] = $downDir.$v;
+ }
+ }
+ else
+ {
+ $result['email_attach'] = $downDir.trim($email['mail_attach']);
+ }
+ }
+ if (isset($email['mail_overrides']) && is_array($email['mail_overrides'])) $result = array_merge($result, $email['mail_overrides']);
+ return $result;
+ }
+
+
+
/**
* Call to do a number of 'units' of email processing - from a cron job, for example
* Each 'unit' sends one email from the queue - potentially it could do some other task.
@@ -1314,71 +1425,171 @@ class e107MailManager
- /**
- * Creates email body text according to options
- * @param $text string - text to process
- * @param $format string - options:
- * textonly - generate plain text email
- * texthtml - HTML format email, no theme info
- * texttheme - HTML format email, including current theme stylesheet etc
- * @param boolean $incImages - valid only with HTML output;
- * if true any 'absolute' format images are embedded in the source of the email.
- * if FALSE, absolute links are converted to URLs on the local server
- * @return string - updated body
- */
- protected function makeEmailBody($text, $format = 'textonly', $incImages = TRUE)
+//-----------------------------------------------------
+// Function call to send a templated email
+//-----------------------------------------------------
+
+/**
+ * Send an email to any number of recipients, using a template
+ *
+ * The template may contain normal shortcodes, which must already have been loaded. @see e107_themes/email_template.php
+ *
+ * The template (or other body text) may also contain field names in the form |USER_NAME| (as used in the bulk mailer edit page). These are
+ * filled in from $templateData - field name corresponds to the array index name (case-sensitive)
+ *
+ * The template definition may contain an array $template['email_overrides'] of values which override normal mailer settings.
+ *
+ * The template definition MUST contain a template variable $template['email_body']
+ *
+ * In general, any template definition which isn't overridden uses the default which should be specified in e_THEME.'templates/email_templates.php'
+ *
+ * There is a presumption that the email is being templated because it contains HTML, although this isn't mandatory.
+ *
+ * Any language string constants required in the template must be defined either by loading the requisite language file prior to calling this
+ * routine, or by loading them in the template file.
+ *
+ * @param array|string $templateName - if a string, the name of the template - information is loaded from theme and default templates.
+ * - if an array, template data as returned by gettemplateInfo() (and defined in the template files)
+ * - if empty, sends a simple email using the default template (much as the original sendemail() function in mail.php)
+ * @param array $emailData - defines the email information (generally as the 'mail_content' and 'mail_other' info above):
+ * $emailData = array(
+ 'mail_create_app' => 'notify',
+ 'mail_title' => 'NOTIFY',
+ 'mail_subject' => $subject,
+ 'mail_sender_email' => $pref['siteadminemail'],
+ 'mail_sender_name' => $pref['siteadmin'],
+ 'mail_send_style' => 'textonly',
+ 'mail_notify_complete' => 0, // NEVER notify when this email sent!!!!!
+ 'mail_body' => $message
+ );
+ * @param array|string $recipientData - if a string, its the email address of a single recipient.
+ * - if an array, each entry is the data for a single recipient, as the 'mail_recipients' definition above
+ * $recipientData = array('mail_recipient_id' => $row['user_id'],
+ 'mail_recipient_name' => $row['user_name'],
+ 'mail_recipient_email' => $row['user_email']
+ );
+ * ....and other data as appropriate
+ * @param boolean|array $extra - any additional parameters to be passed to the mailer - as accepted by arraySet method.
+ * These parameters will override any defaults, and any set in the template
+ * if ($extra['mail_force_queue'] is TRUE, the mail will be added to the queue regardless of the number of recipients
+ *
+ * @return boolean TRUE if either added to queue, or sent, successfully (does NOT indicate receipt). FALSE on any error
+ * (Note that with a small number of recipients FALSE indicates that one or more emails weren't sent - some may have been sent successfully)
+ */
+
+ public function sendEmails($templateName, $emailData, $recipientData, $extra = FALSE)
{
- global $pref;
- if ($format == 'textonly')
- { // Plain text email - strip bbcodes etc
- $temp = $this->e107->tp->toHTML($text, TRUE, 'E_BODY_PLAIN'); // Decode bbcodes into HTML, plain text as far as possible etc
- return stripslashes(strip_tags($temp)); // Have to do strip_tags() again in case bbcode added some
+ if (!is_array($emailData)) return FALSE;
+ if (!is_array($recipientData))
+ {
+ $recipientData = array('mail_recipient_email' => $recipientData, 'mail_recipient_name' => $recipientData);
+ }
+ $emailData['mail_content_status'] = MAIL_STATUS_TEMP;
+
+ if ($templateName == '')
+ {
+ $templateName = varset($email['mail_send_style'], 'textonly'); // Safest default if nothing specified
+ }
+ $templateName = trim($templateName);
+ if ($templateName == '') return FALSE;
+
+
+ // Get template data, override email settings as appropriate
+ require_once(e_HANDLER.'mail_template_class.php');
+ $ourTemplate = new e107MailTemplate();
+ if (!$ourTemplate->setNewTemplate($templateName)) return FALSE; // Probably template not found if error
+ if (!$ourTemplate->makeEmailBody($emailData['mail_body'], varset($emailData['mail_include_images'], TRUE))) return FALSE; // Create body text
+ $emailData['mail_body_templated'] = $ourTemplate->mainBodyText;
+ $this->currentMailBody = $emailData['mail_body_templated']; // In case we send immediately
+ $emailData['mail_body_alt'] = $ourTemplate->altBodyText;
+ $this->currentTextBody = $emailData['mail_body_alt'];
+ if (!isset($emailData['mail_overrides']))
+ {
+ $emailData['mail_overrides'] = $ourTemplate->lastTemplateData['email_overrides'];
+ }
+
+
+ $forceQueue = FALSE;
+ if (is_array($extra) && isset($extra['mail_force_queue']))
+ {
+ $forceQueue = $extra['mail_force_queue'];
+ unset($extra['mail_force_queue']);
}
- $consts = $incImages ? ',consts_abs' : 'consts_full'; // If inline images, absolute constants so we can change them
-
- // HTML format email here
- $mail_head = "\n";
- $mail_head .= "\n";
- $mail_head .= "\n";
- if ($format == 'texttheme')
+ if ((count($recipientData) <= 5) && !$forceQueue) // Arbitrary upper limit for sending multiple emails immediately
{
- $styleFile = THEME.'emailstyle.css';
- if (!is_readable($styleFile)) { $styleFile = e_THEME.$pref['sitetheme']."/style.css"; }
- $style_css = file_get_contents($styleFile);
- $mail_head .= "";
+ if ($this->mailer == NULL)
+ {
+ e107_require_once(e_HANDLER.'mail.php');
+ $this->mailer = new e107Email($extra);
+ }
+ $tempResult = TRUE;
+ $eCount = 0;
+
+ // @TODO: Generate alt text etc
+
+
+ foreach ($recipientData as $recip)
+ {
+ // Fill in other bits of email
+ $emailData['mail_target_info'] = $recip;
+ $mailToSend = $this->makeEmailBlock($emailData); // Substitute mail-specific variables, attachments etc
+ if (FALSE == $this->mailer->sendEmail($recip['mail_recipient_email'], $recip['mail_recipient_name'], $mailToSend, TRUE))
+ {
+ $tempResult = FALSE;
+ }
+ else
+ { // Success here
+ if ($eCount == 0)
+ { // Only send these on first email - otherwise someone could get inundated!
+ unset($emailData['mail_copy_to']);
+ unset($emailData['mail_bcopy_to']);
+ }
+ $eCount++; // Count number of successful emails sent
+ }
+ }
+ return $tempResult;
}
- $mail_head .= "\n";
-
- $message_body = $mail_head."\n";
- if ($format == 'texttheme')
+ // To many recipients to send at once - add to the emailing queue
+ // @TODO - handle any other relevant $extra fields
+ $result = $this->saveEmail($emailData, TRUE);
+ if ($result === FALSE)
{
- $message_body .= "";
+ // TODO: Handle error
+ return FALSE; // Probably nothing else we can do
+ }
+ elseif (is_numeric($result))
+ {
+ $mailMainID = $emailData['mail_source_id'] = $result;
}
else
{
- $message_body .= $this->e107->tp->toHTML($text, TRUE, 'E_BODY'.$consts)."