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 .= "
\n"; - $message_body .= $this->e107->tp->toHTML($text, TRUE, 'E_BODY'.$consts)."
"; + // 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).""; - $message_body = str_replace(""", '"', $message_body); + // TODO: Handle strange error + return FALSE; // Probably nothing else we can do } + $this->mailInitCounters($mailMainID); // Initialise counters for emails added - $message_body = stripslashes($message_body); - - - if (!$incImages) + // Now add email addresses to the list + foreach ($recipientData as $email) { - // Handle internally generated 'absolute' links - they need the full URL - $message_body = str_replace("src='".e_HTTP, "src='".SITEURL, $message_body); - $message_body = str_replace('src="'.e_HTTP, 'src="'.SITEURL, $message_body); - $message_body = str_replace("href='".e_HTTP, "src='".SITEURL, $message_body); - $message_body = str_replace('href="'.e_HTTP, 'src="'.SITEURL, $message_body); + $result = $this->mailAddNoDup($mailMainID, $email, MAIL_STATUS_TEMP); } - -// print_a($message_body); - return $message_body; + $this->mailUpdateCounters($mailMainID); // Update the counters + $counters = $this->mailRetrieveCounters($mailMainID); // Retrieve the counters + if ($counters['add'] == 0) + { + $this->deleteEmail($mailMainID); // Probably a fault, but precautionary - delete email + // Don't treat as an error if no recipients + } + else + { + $this->activateEmail($mailMainID, FALSE); // Actually mark the email for sending + } + return TRUE; } - + } + ?> \ No newline at end of file diff --git a/e107_handlers/mailout_admin_class.php b/e107_handlers/mailout_admin_class.php index 8ec3d29e8..5eb5ea462 100644 --- a/e107_handlers/mailout_admin_class.php +++ b/e107_handlers/mailout_admin_class.php @@ -1,1674 +1,1834 @@ - array - ( 'mail_target_id' => array('title' => LAN_MAILOUT_143, 'thclass' => 'center', 'forced' => TRUE), - 'mail_recipient_id' => array('title' => LAN_MAILOUT_142, 'thclass' => 'center'), - 'mail_recipient_name' => array('title' => LAN_MAILOUT_141, 'forced' => TRUE), - 'mail_recipient_email' => array('title' => LAN_MAILOUT_140, 'thclass' => 'left', 'forced' => TRUE), - 'mail_status' => array('title' => LAN_MAILOUT_138, 'thclass' => 'center', 'proc' => 'contentstatus'), - 'mail_detail_id' => array('title' => LAN_MAILOUT_137), - 'mail_send_date' => array('title' => LAN_MAILOUT_139, 'proc' => 'sdatetime'), - 'mail_target_info' => array('title' => LAN_MAILOUT_148, 'proc' => 'array'), - 'options' => array('title' => LAN_OPTIONS, 'forced' => TRUE) - ), - 'mail_content' => array( - 'mail_source_id' => array('title' => LAN_MAILOUT_137, 'thclass' => 'center', 'forced' => TRUE), - 'mail_title' => array('title' => LAN_MAILOUT_135, 'forced' => TRUE), - 'mail_subject' => array('title' => LAN_MAILOUT_06, 'forced' => TRUE), - 'mail_content_status' => array('title' => LAN_MAILOUT_136, 'thclass' => 'center', 'proc' => 'contentstatus'), - 'mail_togo_count' => array('title' => LAN_MAILOUT_83), - 'mail_sent_count' => array('title' => LAN_MAILOUT_82), - 'mail_fail_count' => array('title' => LAN_MAILOUT_128), - 'mail_bounce_count' => array('title' => LAN_MAILOUT_144), - 'mail_start_send' => array('title' => LAN_MAILOUT_131, 'proc' => 'sdatetime'), - 'mail_end_send' => array('title' => LAN_MAILOUT_132, 'proc' => 'sdatetime'), - 'mail_create_date' => array('title' => LAN_MAILOUT_130, 'proc' => 'sdatetime'), - 'mail_creator' => array('title' => LAN_MAILOUT_85, 'proc' => 'username'), - 'mail_create_app' => array('title' => LAN_MAILOUT_133), - 'mail_e107_priority' => array('title' => LAN_MAILOUT_134), - 'mail_notify_complete' => array('title' => LAN_MAILOUT_243, 'nolist' => 'TRUE'), - 'mail_last_date' => array('title' => LAN_MAILOUT_129, 'proc' => 'sdatetime'), - 'mail_body' => array('title' => LAN_MAILOUT_100, 'proc' => 'trunc200'), - // 'mail_other' = array('title' => LAN_MAILOUT_84), - 'mail_sender_email' => array('title' => LAN_MAILOUT_149), - 'mail_sender_name' => array('title' => LAN_MAILOUT_150), - 'mail_copy_to' => array('title' => LAN_MAILOUT_151), - 'mail_bcopy_to' => array('title' => LAN_MAILOUT_152), - 'mail_attach' => array('title' => LAN_MAILOUT_153), - 'mail_send_style' => array('title' => LAN_MAILOUT_154), - 'mail_selectors' => array('title' => LAN_MAILOUT_155, 'proc' => 'selectors', 'nolist' => 'TRUE'), - 'mail_include_images' => array('title' => LAN_MAILOUT_224, 'proc' => 'yesno'), - 'options' => array('title' => LAN_OPTIONS, 'forced' => TRUE) - ) - ); - - // List of fields to be hidden for each action ('nolist' attribute true) - protected $hideFields = array( - 'orphans' => array(), - 'saved' => 'mail_content_status,mail_togo_count,mail_sent_count,mail_fail_count,mail_bounce_count,mail_start_send,mail_end_send,mail_e107_priority,mail_notify_complete,mail_last_date,mail_selectors', - 'sent' => 'mail_togo_count,mail_last_date,mail_selectors,mail_notify_complete', -// 'pending' => 'mail_togo_count,mail_sent_count,mail_fail_count,mail_bounce_count,mail_start_send,mail_end_send,mail_e107_priority,mail_last_date,mail_selectors', - 'pending' => 'mail_start_send,mail_end_send,mail_e107_priority,mail_notify_complete,mail_last_date,mail_selectors', - 'held' => 'mail_sent_count,mail_fail_count,mail_bounce_count,mail_start_send,mail_end_send,mail_e107_priority,mail_notify_complete,mail_last_date,mail_selectors', - 'resend' => 'mail_Selectors,mail_notify_complete', - 'recipients' => 'mail_detail_id' - ); - - // Array of info associated with each task we might do - protected $tasks = array( - 'makemail' => array('title' => LAN_MAILOUT_190, 'defaultSort' => '', 'defaultTable' => ''), - 'saved' => array('title' => LAN_MAILOUT_191, 'defaultSort' => 'mail_source_id', 'defaultTable' => 'mail_content'), - 'marksend' => array('title' => 'Internal: marksend', 'defaultSort' => 'mail_source_id', 'defaultTable' => 'mail_content'), - 'sent' => array('title' => LAN_MAILOUT_192, 'defaultSort' => 'mail_source_id', 'defaultTable' => 'mail_content'), - 'pending' => array('title' => LAN_MAILOUT_193, 'defaultSort' => 'mail_source_id', 'defaultTable' => 'mail_content'), - 'held' => array('title' => LAN_MAILOUT_194, 'defaultSort' => 'mail_source_id', 'defaultTable' => 'mail_content'), - 'recipients' => array('title' => LAN_MAILOUT_173, 'defaultSort' => 'mail_recipient_email', 'defaultTable' => 'mail_recipients'), - 'mailtargets' => array('title' => LAN_MAILOUT_173, 'defaultSort' => 'mail_recipient_email', 'defaultTable' => 'mail_recipients'), - 'prefs' => array('title' => ADLAN_40, 'defaultSort' => '', 'defaultTable' => ''), - 'maint' => array('title' => ADLAN_40, 'defaultSort' => '', 'defaultTable' => '') - ); - - - // Options for mail listing dropdown - protected $modeOptions = array( - 'saved' => array( - 'mailedit' => LAN_MAILOUT_163, - 'maildelete' => LAN_DELETE - ), - 'pending' => array( - 'mailsendimmediately' => "Send Immediately", - 'mailhold' => LAN_MAILOUT_159, - 'mailcancel' => LAN_MAILOUT_160, - 'mailtargets' => LAN_MAILOUT_181 - ), - 'held' => array( - 'mailsendnow' => LAN_MAILOUT_158, - 'mailcancel' => LAN_MAILOUT_160, - 'mailtargets' => LAN_MAILOUT_181 - ), - 'sent' => array( - 'mailcopy' => LAN_MAILOUT_251, - 'maildelete' => LAN_DELETE, - 'mailtargets' => LAN_MAILOUT_181 - ), - 'recipients' => array( - 'mailonedelete' => LAN_DELETE - ) - ); - - - // List of fields to be included in email display for various options - protected $mailDetailDisplay = array( - 'basic' => array('mail_source_id' => 1, 'mail_title' => 1, 'mail_subject' => 1, 'mail_body' => 200), - 'send' => array('mail_source_id' => 1, 'mail_title' => 1, 'mail_subject' => 1, 'mail_body' => 500) - ); - - - /** - * Constructor - * - * - * @return void - */ - public function __construct($mode = '') - { - parent::__construct(); - // require_once(e_HANDLER.'calendar/calendar_class.ph_'); - // $this->_cal = new DHTML_Calendar(true); - - $dbTable = ''; - if (isset($this->tasks[$mode])) - { - $dbTable = $this->tasks[$mode]['defaultTable']; - } - if(isset($_GET['frm'])) - { - $temp = intval($_GET['frm']); - if ($temp < 0) $temp = 0; - $this->showFrom = $temp; - } - if(isset($_GET['count'])) - { - $temp = min(intval($_GET['count']), 50); // Limit to 50 per page - $temp = max($temp, 5); // ...and minimum 5 per page - $this->showCount = $temp; - } - if (isset($_GET['fld'])) - { - $temp = $this->e107->tp->toDB($_GET['fld']); - if (is_array($this->fields[$dbTable][$temp])) - { - $this->sortField = $temp; - } - } - if (isset($_GET['asc'])) - { - $temp = strtolower($this->e107->tp->toDB($_GET['asc'])); - if (($temp == 'asc') || ($temp == 'desc')) - { - $this->sortOrder = $temp; - } - } - $this->newMode($mode); - } - - - - /** - * Set up new mode - * - * @param $mode - display mode - * @return none - */ - public function newMode($mode = '') - { - global $user_pref; - $this->mode = $mode; - $curTable = $this->tasks[$this->mode]['defaultTable']; - if ($curTable) - { - if (isset($user_pref['admin_mailout_columns'][$mode]) && is_array($user_pref['admin_mailout_columns'][$mode])) - { // Use saved list of fields to view if it exists - $this->fieldPref = $user_pref['admin_mailout_columns'][$mode]; - } - else - { // Default list is minimal fields only - $this->fieldPref = array(); - foreach ($this->fields[$curTable] as $f => $v) - { - if (vartrue($v['forced'])) - { - $this->fieldPref[] = $f; - } - } - } - } - - // Possibly the sort field needs changing - if (!isset($this->fields[$curTable][$this->sortField])) - { - $this->sortField = $this->tasks[$mode]['defaultSort']; - } - - // Now hide any fields that need to be for this mode - if (isset($this->hideFields[$mode])) - { - $hideList = array_flip(explode(',',$this->hideFields[$mode])); - foreach ($this->fields[$curTable] as $f => $v) - { - $this->fields[$curTable][$f]['nolist'] = isset($hideList[$f]); - } - foreach ($this->fieldPref as $k => $v) // Remove from list of active fields (shouldn't often do anything) - { - if (isset($hideList[$v])) - { - unset($this->fieldPref[$k]); - } - } - } - } - - - - /** - * Calculate the list of fields (columns) to be displayed for a given mode - * - * @param string $mode - display mode - * @param boolean $noOptions - set TRUE to suppress inclusion of any 'options' column. FALSE to include 'options' (default) - * @return array of field definitions - */ - protected function calcFieldSpec($mode, $noOptions = FALSE) - { - if (!isset($this->tasks[$mode])) - { - echo "CalcfieldSpec({$mode}) - programming bungle
"; - return FALSE; - } - $ret = array(); - $curTable = $this->tasks[$this->mode]['defaultTable']; - foreach ($this->fields[$curTable] as $f => $v) - { - if ((vartrue($v['forced']) && !vartrue($v['nolist'])) || in_array($f, $this->fieldPref)) - { - if (($f != 'options') || ($noOptions === FALSE)) - { - $ret[] = $f; - } - } - } - return $ret; - } - - - /** - * Save the column visibility prefs for this mode - * - * @param $target - display mode - * @return none - */ - public function mailbodySaveColumnPref($target) - { - global $user_pref; - if (!$target) return; - if (!isset($this->tasks[$target])) - { - echo "Invalid prefs target: {$target}
"; - return; - } - if (isset ($_POST['etrigger_ecolumns'])) - { - $user_pref['admin_mailout_columns'][$target] = $_POST['e-columns']; - save_prefs('user'); - $this->fieldPref = $user_pref['admin_mailout_columns'][$target]; - } - } - - - - /** - * Get the user name associated with a user ID. - * The result is cached in case required again - * - * @param int $uid - User ID - * - * @return string with user name and user login name (UID if user not found) - */ - protected function getUserName($uid) - { - if (!isset($this->userCache[$uid])) - { - // Look up user - $this->checkDB(2); // Make sure DB object created - if ($this->db2->db_Select('user','user_name, user_loginname', 'user_id='.intval($uid))) - { - $row = $this->db2->db_Fetch(MYSQL_ASSOC); - $this->userCache[$uid] = $row['user_name'].' ('.$row['user_loginname'].')'; - } - else - { - $this->userCache[$uid] = 'UID: '.$uid; - } - } - return $this->userCache[$uid]; - } - - - - /** - * Generate the HTML for displaying actions box for emails - * - * Options given depend on $mode, and also values in the email data. - * - * @param array $mailData - array of email-related info - * @return string HTML for display - */ - public function makeMailOptions($mode,$mailData) - { - if (!is_numeric($mailData['mail_source_id']) || ($mailData['mail_source_id'] == 0)) - { - echo "makeMailOptions ({$mode}): Programming bungle!"; - print_a($mailData); - return 'Error'; - } - $text = "\n"; - return $text; - } - - - /** - * Generate the HTML for displaying actions box for emails - * - * Options given depend on $mode, and also values in the email data. - * - * @param $mailData - array of email-related info - * @return HTML for display - */ - public function makeTargetOptions($mode,$targetData) - { - if (!is_numeric($targetData['mail_target_id']) || ($targetData['mail_target_id'] == 0)) - { - echo "makeTargetOptions ({$mode}): Programming bungle!"; - print_a($targetData); - return 'Error'; - } - $text = "\n"; - return $text; - } - - - /** - * Generate the HTML for displaying email selection fields - * - * @param $options - comma-separate string of handlers to load - * 'core' - core handler - * plugin name - obvious! - * 'all' - obvious! - * @return Number of handlers loaded - */ - public function loadMailHandlers($options = 'all') - { - global $pref; - - $ret = 0; - $toLoad = explode(',', $options); - if (in_array('core', $toLoad) || ($options == 'all')) - { - require_once(e_HANDLER.'mailout_class.php'); - $this->mailHandlers['core'] = new core_mailout; // Start by loading the core mailout class - $ret++; - } - - $active_mailers = explode(',',varset($pref['mailout_enabled'],'')); - - // Load additional configured handlers - foreach ($pref['e_mailout_list'] as $mailer => $v) - { - if (isset($pref['plug_installed'][$mailer]) && in_array($mailer,$active_mailers) && (($options == 'all') || in_array($mailer, $toLoad))) - { // Could potentially use this handler - its installed and enabled - if (!is_readable(e_PLUGIN.$mailer.'/e_mailout.php')) - { - echo 'Invalid mailer selected: '.$mailer.'
'; - exit; - } - require_once(e_PLUGIN.$mailer.'/e_mailout.php'); - if (varset($mailerIncludeWithDefault,TRUE)) - { // Definitely need this plugin - $mailClass = $mailer.'_mailout'; - $temp = new $mailClass; - if ($temp->mailerEnabled) - { - $this->mailHandlers[$mailer] = $temp; - $ret++; - if (varset($mailerExcludeDefault,FALSE) && isset($this->mailHandlers['core'])) - { - $this->mailHandlers['core']->mailerEnabled = FALSE; // Don't need default (core) handler - $ret--; - } - } - else - { - unset($temp); - } - } - } - } - - return $ret; - } - - - /** - * Generate the HTML for displaying email selection fields - * - * @param $options - comma-separated string of areas to display: - * plugins - selectors from any available plugins - * cc - field for 'cc' options - * bcc - field for 'bcc' options - * src=plugname - selector from the specified plugin - * 'all' - all available fields - * @return text for display - */ - public function emailSelector($options = 'all', $selectorInfo = FALSE) - { - $ret = ''; - $tab = ''; - $tabc = ''; - - $ret .= "
\n"; - - foreach ($this->mailHandlers as $key => $m) - { - if ($m->mailerEnabled) - { - $tab .= "
  • ".$m->mailerName."
  • "; + array + ( 'mail_target_id' => array('title' => LAN_MAILOUT_143, 'thclass' => 'center', 'forced' => TRUE), + 'mail_recipient_id' => array('title' => LAN_MAILOUT_142, 'thclass' => 'center'), + 'mail_recipient_name' => array('title' => LAN_MAILOUT_141, 'forced' => TRUE), + 'mail_recipient_email' => array('title' => LAN_MAILOUT_140, 'thclass' => 'left', 'forced' => TRUE), + 'mail_status' => array('title' => LAN_MAILOUT_138, 'thclass' => 'center', 'proc' => 'contentstatus'), + 'mail_detail_id' => array('title' => LAN_MAILOUT_137), + 'mail_send_date' => array('title' => LAN_MAILOUT_139, 'proc' => 'sdatetime'), + 'mail_target_info' => array('title' => LAN_MAILOUT_148, 'proc' => 'array'), + 'options' => array('title' => LAN_OPTIONS, 'forced' => TRUE) + ), + 'mail_content' => array( + 'mail_source_id' => array('title' => LAN_MAILOUT_137, 'thclass' => 'center', 'forced' => TRUE), + 'mail_title' => array('title' => LAN_MAILOUT_135, 'forced' => TRUE), + 'mail_subject' => array('title' => LAN_MAILOUT_06, 'forced' => TRUE), + 'mail_content_status' => array('title' => LAN_MAILOUT_136, 'thclass' => 'center', 'proc' => 'contentstatus'), + 'mail_togo_count' => array('title' => LAN_MAILOUT_83), + 'mail_sent_count' => array('title' => LAN_MAILOUT_82), + 'mail_fail_count' => array('title' => LAN_MAILOUT_128), + 'mail_bounce_count' => array('title' => LAN_MAILOUT_144), + 'mail_start_send' => array('title' => LAN_MAILOUT_131, 'proc' => 'sdatetime'), + 'mail_end_send' => array('title' => LAN_MAILOUT_132, 'proc' => 'sdatetime'), + 'mail_create_date' => array('title' => LAN_MAILOUT_130, 'proc' => 'sdatetime'), + 'mail_creator' => array('title' => LAN_MAILOUT_85, 'proc' => 'username'), + 'mail_create_app' => array('title' => LAN_MAILOUT_133), + 'mail_e107_priority' => array('title' => LAN_MAILOUT_134), + 'mail_notify_complete' => array('title' => LAN_MAILOUT_243, 'nolist' => 'TRUE'), + 'mail_last_date' => array('title' => LAN_MAILOUT_129, 'proc' => 'sdatetime'), + 'mail_body' => array('title' => LAN_MAILOUT_100, 'proc' => 'trunc200'), + 'mail_body_templated' => array('title' => LAN_MAILOUT_257, 'proc' => 'chars'), + // 'mail_other' = array('title' => LAN_MAILOUT_84), + 'mail_sender_email' => array('title' => LAN_MAILOUT_149), + 'mail_sender_name' => array('title' => LAN_MAILOUT_150), + 'mail_copy_to' => array('title' => LAN_MAILOUT_151), + 'mail_bcopy_to' => array('title' => LAN_MAILOUT_152), + 'mail_attach' => array('title' => LAN_MAILOUT_153), + 'mail_send_style' => array('title' => LAN_MAILOUT_154), + 'mail_selectors' => array('title' => LAN_MAILOUT_155, 'proc' => 'selectors', 'nolist' => 'TRUE'), + 'mail_include_images' => array('title' => LAN_MAILOUT_224, 'proc' => 'yesno'), + 'options' => array('title' => LAN_OPTIONS, 'forced' => TRUE) + ) + ); + + // List of fields to be hidden for each action ('nolist' attribute true) + protected $hideFields = array( + 'orphans' => array(), + 'saved' => 'mail_content_status,mail_togo_count,mail_sent_count,mail_fail_count,mail_bounce_count,mail_start_send,mail_end_send,mail_e107_priority,mail_notify_complete,mail_last_date,mail_selectors', + 'sent' => 'mail_togo_count,mail_last_date,mail_selectors,mail_notify_complete', +// 'pending' => 'mail_togo_count,mail_sent_count,mail_fail_count,mail_bounce_count,mail_start_send,mail_end_send,mail_e107_priority,mail_last_date,mail_selectors', + 'pending' => 'mail_start_send,mail_end_send,mail_e107_priority,mail_notify_complete,mail_last_date,mail_selectors', + 'held' => 'mail_sent_count,mail_fail_count,mail_bounce_count,mail_start_send,mail_end_send,mail_e107_priority,mail_notify_complete,mail_last_date,mail_selectors', + 'resend' => 'mail_Selectors,mail_notify_complete', + 'recipients' => 'mail_detail_id' + ); + + // Array of info associated with each task we might do + protected $tasks = array( + 'makemail' => array('title' => LAN_MAILOUT_190, 'defaultSort' => '', 'defaultTable' => ''), + 'saved' => array('title' => LAN_MAILOUT_191, 'defaultSort' => 'mail_source_id', 'defaultTable' => 'mail_content'), + 'marksend' => array('title' => 'Internal: marksend', 'defaultSort' => 'mail_source_id', 'defaultTable' => 'mail_content'), + 'sent' => array('title' => LAN_MAILOUT_192, 'defaultSort' => 'mail_source_id', 'defaultTable' => 'mail_content'), + 'pending' => array('title' => LAN_MAILOUT_193, 'defaultSort' => 'mail_source_id', 'defaultTable' => 'mail_content'), + 'held' => array('title' => LAN_MAILOUT_194, 'defaultSort' => 'mail_source_id', 'defaultTable' => 'mail_content'), + 'recipients' => array('title' => LAN_MAILOUT_173, 'defaultSort' => 'mail_recipient_email', 'defaultTable' => 'mail_recipients'), + 'mailtargets' => array('title' => LAN_MAILOUT_173, 'defaultSort' => 'mail_recipient_email', 'defaultTable' => 'mail_recipients'), + 'prefs' => array('title' => ADLAN_40, 'defaultSort' => '', 'defaultTable' => ''), + 'maint' => array('title' => ADLAN_40, 'defaultSort' => '', 'defaultTable' => '') + ); + + + // Options for mail listing dropdown - actions apertaining to a stored email + protected $modeOptions = array( + 'saved' => array( + 'mailedit' => LAN_MAILOUT_163, + 'maildelete' => LAN_DELETE, + 'mailshowtemplate' => LAN_MAILOUT_254 + ), + 'pending' => array( + 'mailsendimmediately' => "Send Immediately", + 'mailhold' => LAN_MAILOUT_159, + 'mailcancel' => LAN_MAILOUT_160, + 'mailtargets' => LAN_MAILOUT_181 + ), + 'held' => array( + 'mailsendnow' => LAN_MAILOUT_158, + 'mailcancel' => LAN_MAILOUT_160, + 'mailtargets' => LAN_MAILOUT_181 + ), + 'sent' => array( + 'mailcopy' => LAN_MAILOUT_251, + 'maildelete' => LAN_DELETE, + 'mailtargets' => LAN_MAILOUT_181 + ), + 'recipients' => array( + 'mailonedelete' => LAN_DELETE + ) + ); + + + // List of fields to be included in email display for various options + protected $mailDetailDisplay = array( + 'basic' => array('mail_source_id' => 1, 'mail_title' => 1, 'mail_subject' => 1, 'mail_body' => 200), + 'send' => array('mail_source_id' => 1, 'mail_title' => 1, 'mail_subject' => 1, 'mail_body' => 500, 'mail_send_style' => 1), + 'template' => array('mail_source_id' => 1, 'mail_title' => 1, 'mail_subject' => 1, 'mail_body' => 200, 'mail_body_templated' => 'chars'), + ); + + + /** + * Constructor + * + * + * @return void + */ + public function __construct($mode = '') + { + parent::__construct(); + + $dbTable = ''; + if (isset($this->tasks[$mode])) + { + $dbTable = $this->tasks[$mode]['defaultTable']; + } + if(isset($_GET['frm'])) + { + $temp = intval($_GET['frm']); + if ($temp < 0) $temp = 0; + $this->showFrom = $temp; + } + if(isset($_GET['count'])) + { + $temp = min(intval($_GET['count']), 50); // Limit to 50 per page + $temp = max($temp, 5); // ...and minimum 5 per page + $this->showCount = $temp; + } + if (isset($_GET['fld'])) + { + $temp = $this->e107->tp->toDB($_GET['fld']); + if (is_array($this->fields[$dbTable][$temp])) + { + $this->sortField = $temp; + } + } + if (isset($_GET['asc'])) + { + $temp = strtolower($this->e107->tp->toDB($_GET['asc'])); + if (($temp == 'asc') || ($temp == 'desc')) + { + $this->sortOrder = $temp; + } + } + $this->newMode($mode); + } + + + + /** + * Set up new mode + * + * @param $mode - display mode + * @return none + */ + public function newMode($mode = '') + { + global $user_pref; + $this->mode = $mode; + $curTable = $this->tasks[$this->mode]['defaultTable']; + if ($curTable) + { + if (isset($user_pref['admin_mailout_columns'][$mode]) && is_array($user_pref['admin_mailout_columns'][$mode])) + { // Use saved list of fields to view if it exists + $this->fieldPref = $user_pref['admin_mailout_columns'][$mode]; + } + else + { // Default list is minimal fields only + $this->fieldPref = array(); + foreach ($this->fields[$curTable] as $f => $v) + { + if (vartrue($v['forced'])) + { + $this->fieldPref[] = $f; + } + } + } + } + + // Possibly the sort field needs changing + if (!isset($this->fields[$curTable][$this->sortField])) + { + $this->sortField = $this->tasks[$mode]['defaultSort']; + } + + // Now hide any fields that need to be for this mode + if (isset($this->hideFields[$mode])) + { + $hideList = array_flip(explode(',',$this->hideFields[$mode])); + foreach ($this->fields[$curTable] as $f => $v) + { + $this->fields[$curTable][$f]['nolist'] = isset($hideList[$f]); + } + foreach ($this->fieldPref as $k => $v) // Remove from list of active fields (shouldn't often do anything) + { + if (isset($hideList[$v])) + { + unset($this->fieldPref[$k]); + } + } + } + } + + + + /** + * Calculate the list of fields (columns) to be displayed for a given mode + * + * @param string $mode - display mode + * @param boolean $noOptions - set TRUE to suppress inclusion of any 'options' column. FALSE to include 'options' (default) + * @return array of field definitions + */ + protected function calcFieldSpec($mode, $noOptions = FALSE) + { + if (!isset($this->tasks[$mode])) + { + echo "CalcfieldSpec({$mode}) - programming bungle
    "; + return FALSE; + } + $ret = array(); + $curTable = $this->tasks[$this->mode]['defaultTable']; + foreach ($this->fields[$curTable] as $f => $v) + { + if ((vartrue($v['forced']) && !vartrue($v['nolist'])) || in_array($f, $this->fieldPref)) + { + if (($f != 'options') || ($noOptions === FALSE)) + { + $ret[] = $f; + } + } + } + return $ret; + } + + + /** + * Save the column visibility prefs for this mode + * + * @param $target - display mode + * @return none + */ + public function mailbodySaveColumnPref($target) + { + global $user_pref; + if (!$target) return; + if (!isset($this->tasks[$target])) + { + echo "Invalid prefs target: {$target}
    "; + return; + } + if (isset ($_POST['etrigger_ecolumns'])) + { + $user_pref['admin_mailout_columns'][$target] = $_POST['e-columns']; + save_prefs('user'); + $this->fieldPref = $user_pref['admin_mailout_columns'][$target]; + } + } + + + + /** + * Get the user name associated with a user ID. + * The result is cached in case required again + * + * @param int $uid - User ID + * + * @return string with user name and user login name (UID if user not found) + */ + protected function getUserName($uid) + { + if (!isset($this->userCache[$uid])) + { + // Look up user + $this->checkDB(2); // Make sure DB object created + if ($this->db2->db_Select('user','user_name, user_loginname', 'user_id='.intval($uid))) + { + $row = $this->db2->db_Fetch(MYSQL_ASSOC); + $this->userCache[$uid] = $row['user_name'].' ('.$row['user_loginname'].')'; + } + else + { + $this->userCache[$uid] = 'UID: '.$uid; + } + } + return $this->userCache[$uid]; + } + + + + + + /** + * Generate the HTML for displaying actions box for emails + * + * Options given depend on $mode (saved|sent|pending|held), and also values in the email data. + * + * @param array $mailData - array of email-related info + * @return string HTML for display + */ + public function makeMailOptions($mode,$mailData) + { + if (!is_numeric($mailData['mail_source_id']) || ($mailData['mail_source_id'] == 0)) + { + echo "makeMailOptions ({$mode}): Programming bungle!"; + print_a($mailData); + return 'Error'; + } + $text = "\n"; + return $text; + } + + + /** + * Generate the HTML for displaying actions box for emails + * + * Options given depend on $mode, and also values in the email data. + * + * @param $mailData - array of email-related info + * @return HTML for display + */ + public function makeTargetOptions($mode,$targetData) + { + if (!is_numeric($targetData['mail_target_id']) || ($targetData['mail_target_id'] == 0)) + { + echo "makeTargetOptions ({$mode}): Programming bungle!"; + print_a($targetData); + return 'Error'; + } + $text = "\n"; + return $text; + } + + + /** + * Generate the HTML for displaying email selection fields + * + * @param $options - comma-separate string of handlers to load + * 'core' - core handler + * plugin name - obvious! + * 'all' - obvious! + * @return Number of handlers loaded + */ + public function loadMailHandlers($options = 'all') + { + global $pref; + + $ret = 0; + $toLoad = explode(',', $options); + if (in_array('core', $toLoad) || ($options == 'all')) + { + require_once(e_HANDLER.'mailout_class.php'); + $this->mailHandlers['core'] = new core_mailout; // Start by loading the core mailout class + $ret++; + } + + $active_mailers = explode(',',varset($pref['mailout_enabled'],'')); + + // Load additional configured handlers + foreach ($pref['e_mailout_list'] as $mailer => $v) + { + if (isset($pref['plug_installed'][$mailer]) && in_array($mailer,$active_mailers) && (($options == 'all') || in_array($mailer, $toLoad))) + { // Could potentially use this handler - its installed and enabled + if (!is_readable(e_PLUGIN.$mailer.'/e_mailout.php')) + { + echo 'Invalid mailer selected: '.$mailer.'
    '; + exit; + } + require_once(e_PLUGIN.$mailer.'/e_mailout.php'); + if (varset($mailerIncludeWithDefault,TRUE)) + { // Definitely need this plugin + $mailClass = $mailer.'_mailout'; + $temp = new $mailClass; + if ($temp->mailerEnabled) + { + $this->mailHandlers[$mailer] = $temp; + $ret++; + if (varset($mailerExcludeDefault,FALSE) && isset($this->mailHandlers['core'])) + { + $this->mailHandlers['core']->mailerEnabled = FALSE; // Don't need default (core) handler + $ret--; + } + } + else + { + unset($temp); + } + } + } + } + + return $ret; + } + + + /** + * Generate the HTML for displaying email selection fields + * + * @param $options - comma-separated string of areas to display: + * plugins - selectors from any available plugins + * cc - field for 'cc' options + * bcc - field for 'bcc' options + * src=plugname - selector from the specified plugin + * 'all' - all available fields + * @return text for display + */ + public function emailSelector($options = 'all', $selectorInfo = FALSE) + { + $ret = ''; + $tab = ''; + $tabc = ''; + + $ret .= "
    \n"; + + foreach ($this->mailHandlers as $key => $m) + { + if ($m->mailerEnabled) + { + $tab .= "
  • ".$m->mailerName."
  • "; $tabc .= "
    "; - - $content = $m->showSelect(TRUE, varset($selectorInfo[$key], FALSE)); - - if(is_array($content)) - { + + $content = $m->showSelect(TRUE, varset($selectorInfo[$key], FALSE)); + + if(is_array($content)) + { $tabc .= " - - - - - "; - - foreach($content as $var) - { - $tabc .= ""; - } - $tabc .= "
    ".$var['caption']."".$var['html']."
    "; - } - else - { - $tabc .= $content; //BC (0.8 only) but should be deprecated - } - - $tabc .= "
    "; - } - } + + + + + "; + + foreach($content as $var) + { + $tabc .= "".$var['caption']."".$var['html'].""; + } + $tabc .= ""; + } + else + { + $tabc .= $content; //BC (0.8 only) but should be deprecated + } + + $tabc .= "
    "; + } + } // $ret .= "
      ".$tab."
    "; // This hides tabs! $ret .= "
      ".$tab."
    "; - $ret .= $tabc; - $ret .= "
    "; - - return $ret; - - } - - - /** - * Get the selector details from each mail plugin (to add to mail data) - * - * @return array of selectors - key is the plugin name, value is the selector data (often itself an array) - */ - public function getAllSelectors() - { - $ret = array(); - foreach ($this->mailHandlers as $key => $m) - { - if ($m->mailerEnabled) - { - $ret[$key] = $m->returnSelectors(); - } - } - return $ret; - } - - - /** - * Creates a 'select' dropdown of userclasses, including the number of members in each class. - * - * @param string $name - name for - \n"; - - foreach ($fixedClasses as $k => $v) - { - $sel = ($k == $curSel) ? " selected='selected'" : ''; - $ret .= "\n"; - } - $query = "SELECT uc.*, count(u.user_id) AS members - FROM #userclass_classes AS uc - LEFT JOIN #user AS u ON u.user_class REGEXP concat('(^|,)',uc.userclass_id,'(,|$)') - GROUP BY uc.userclass_id - "; - - $this->db2->db_Select_gen($query); - while ($row = $this->db2->db_Fetch()) - { - $public = ($row['userclass_editclass'] == e_UC_PUBLIC)? "(".LAN_MAILOUT_10.")" : ""; - $selected = ($row['userclass_id'] == $curSel) ? " selected='selected'" : ''; - $ret .= "\n"; - } - $ret .= " \n"; - - return $ret; - } - - - - /** - * Creates a 'select' dropdown of non-system user fields - * - * @param string $list_name - name for \n"; - if ($add_blank) $ret .= "\n"; - - foreach ($ue->fieldDefinitions as $fd) - { - if ($fd['user_extended_struct_text'] != '_system_') - { - $value = 'ue.user_'.$fd['user_extended_struct_name']; - $selected = ($value == $curval) ? " selected='selected'" : ''; - $ret .= "\n"; - } - } - $ret .= "\n"; - return $ret; - } - - - - /** - * Creates an array of data from standard $_POST fields - * - * @param $newMail - set TRUE for initial creation, FALSE when updating - * @return array of data - */ - public function parseEmailPost($newMail = TRUE) - { - $tp = e107::getParser(); - - $ret = array( 'mail_title' => $_POST['email_title'], - 'mail_subject' => $_POST['email_subject'], - 'mail_body' => $_POST['email_body'], - 'mail_sender_email' => $_POST['email_from_email'], - 'mail_sender_name' => $_POST['email_from_name'], - 'mail_copy_to' => $_POST['email_cc'], - 'mail_bcopy_to' => $_POST['email_bcc'], - 'mail_attach' => trim($_POST['email_attachment']), - 'mail_send_style' => varset($_POST['send_style'],'textonly'), - 'mail_include_images' => (isset($_POST['mail_include_images']) ? 1 : 0) - ); - - $ret = $tp->toDB($ret); // recursive - - if (isset($_POST['mail_source_id'])) - { - $ret['mail_source_id'] = intval($_POST['mail_source_id']); - } - if ($newMail) - { - $ret['mail_creator'] = USERID; - $ret['mail_create_date'] = time(); - } - return $ret; - } - - - - /** - * Does some basic checking on email data. - * - * @param $email - array of data in parseEmailPost() format - * @param $fullCheck - TRUE to check all fields that are required (immediately prior to sending); FALSE to just check a few basics (prior to save) - * @return TRUE if OK. Array of error messages if any errors found - */ - public function checkEmailPost($email, $fullCheck = FALSE) - { - $errList = array(); - if (count($email) < 3) - { - $errList[] = LAN_MAILOUT_201; - return $errList; - } - if (!trim($email['mail_subject'])) $errList[] = LAN_MAILOUT_200; - if (!trim($email['mail_body'])) $errList[] = LAN_MAILOUT_202; - if (!trim($email['mail_sender_name'])) $errList[] = LAN_MAILOUT_203; - if (!trim($email['mail_sender_email'])) $errList[] = LAN_MAILOUT_204; - switch ($email['mail_send_style']) - { - case 'textonly' : - case 'texthtml' : - case 'texttheme' : - break; - default : - $errList[] = LAN_MAILOUT_205; - } - if (count($errList) == 0) - { - return TRUE; - } - return $errList; - } - - - - /** - * Generate a table which shows some information about an email. - * Intended to be part of a 2-column table - includes the row detail, but not the surrounding table definitions - * - * @param $mailSource - array of mail information - * @param $options - controls how much information is displayed - * @return text for display - */ - public function showMailDetail(&$mailSource, $options='basic') - { - $tp = e107::getParser(); - - - if (!isset($this->mailDetailDisplay[$options])) - { - return "Programming bungle - invalid option value: {$options}"; - } - - $res = ''; - foreach ($this->mailDetailDisplay[$options] as $k => $v) - { - $res .= ''.$this->fields['mail_content'][$k]['title'].''; - $res .= ($v > 1) ? $tp->text_truncate($mailSource[$k], $v, '...') : $mailSource[$k]; - $res .= ''."\n"; - } - return $res; - } - - - - /** - * Generate the HTML for dropdown to select mail sending style (text/HTML/styled - * - * @param $curval - current value - * @param $name name of item - * @return text for display - */ + $ret .= $tabc; + $ret .= ""; + + return $ret; + + } + + + /** + * Get the selector details from each mail plugin (to add to mail data) + * + * @return array of selectors - key is the plugin name, value is the selector data (often itself an array) + */ + public function getAllSelectors() + { + $ret = array(); + foreach ($this->mailHandlers as $key => $m) + { + if ($m->mailerEnabled) + { + $ret[$key] = $m->returnSelectors(); + } + } + return $ret; + } + + + /** + * Creates a 'select' dropdown of userclasses, including the number of members in each class. + * + * @param string $name - name for + \n"; + + foreach ($fixedClasses as $k => $v) + { + $sel = ($k == $curSel) ? " selected='selected'" : ''; + $ret .= "\n"; + } + $query = "SELECT uc.*, count(u.user_id) AS members + FROM #userclass_classes AS uc + LEFT JOIN #user AS u ON u.user_class REGEXP concat('(^|,)',uc.userclass_id,'(,|$)') + WHERE NOT uc.userclass_id IN (".e_UC_PUBLIC.','.e_UC_NOBODY.','.e_UC_READONLY.','.e_UC_BOTS.") + GROUP BY uc.userclass_id + "; + + $this->db2->db_Select_gen($query); + while ($row = $this->db2->db_Fetch()) + { + $public = ($row['userclass_editclass'] == e_UC_PUBLIC)? "(".LAN_MAILOUT_10.")" : ""; + $selected = ($row['userclass_id'] == $curSel) ? " selected='selected'" : ''; + $ret .= "\n"; + } + $ret .= " \n"; + + return $ret; + } + + + + /** + * Creates a 'select' dropdown of non-system user fields + * + * @param string $list_name - name for \n"; + if ($add_blank) $ret .= "\n"; + + foreach ($ue->fieldDefinitions as $fd) + { + if ($fd['user_extended_struct_text'] != '_system_') + { + $value = 'ue.user_'.$fd['user_extended_struct_name']; + $selected = ($value == $curval) ? " selected='selected'" : ''; + $ret .= "\n"; + } + } + $ret .= "\n"; + return $ret; + } + + + + /** + * Creates an array of data from standard $_POST fields + * + * @param $newMail - set TRUE for initial creation, FALSE when updating + * @return array of data + */ + public function parseEmailPost($newMail = TRUE) + { + $tp = e107::getParser(); + + $ret = array( 'mail_title' => $_POST['email_title'], + 'mail_subject' => $_POST['email_subject'], + 'mail_body' => $_POST['email_body'], + 'mail_sender_email' => $_POST['email_from_email'], + 'mail_sender_name' => $_POST['email_from_name'], + 'mail_copy_to' => $_POST['email_cc'], + 'mail_bcopy_to' => $_POST['email_bcc'], + 'mail_attach' => trim($_POST['email_attachment']), + 'mail_send_style' => varset($_POST['email_send_style'],'textonly'), + 'mail_include_images' => (isset($_POST['email_include_images']) ? 1 : 0) + ); + + $ret = $tp->toDB($ret); // recursive + + if (isset($_POST['mail_source_id'])) + { + $ret['mail_source_id'] = intval($_POST['mail_source_id']); + } + if ($newMail) + { + $ret['mail_creator'] = USERID; + $ret['mail_create_date'] = time(); + } + return $ret; + } + + + + /** + * Does some basic checking on email data. + * + * @param $email - array of data in parseEmailPost() format + * @param $fullCheck - TRUE to check all fields that are required (immediately prior to sending); FALSE to just check a few basics (prior to save) + * @return TRUE if OK. Array of error messages if any errors found + */ + public function checkEmailPost(&$email, $fullCheck = FALSE) + { + $errList = array(); + if (count($email) < 3) + { + $errList[] = LAN_MAILOUT_201; + return $errList; + } + if (!trim($email['mail_subject'])) $errList[] = LAN_MAILOUT_200; + if (!trim($email['mail_body'])) $errList[] = LAN_MAILOUT_202; + if (!trim($email['mail_sender_name'])) $errList[] = LAN_MAILOUT_203; + if (!trim($email['mail_sender_email'])) $errList[] = LAN_MAILOUT_204; + if (strlen($email['mail_send_style']) == 0) + { // Can be a template name now + $errList[] = LAN_MAILOUT_205; + break; + } + else + { + // Get template data, override email settings as appropriate + require_once(e_HANDLER.'mail_template_class.php'); + $ourTemplate = new e107MailTemplate(); + $templateName = $email['mail_send_style']; + if (!$ourTemplate->setNewTemplate($templateName)) + { + $errList[] = LAN_MAILOUT_207.':'.$templateName; + print_a($ourTemplate); // Probably template not found if error + } + if (!$ourTemplate->makeEmailBody($email['mail_body'], $email['mail_include_images'])) + { + $errList[] = LAN_MAILOUT_205.':'.$templateName; + print_a($ourTemplate); + } + else + { + $email['mail_body_templated'] = $ourTemplate->mainBodyText; + $email['mail_body_alt'] = $ourTemplate->altBodyText; + if (count($ourTemplate->lastTemplateData['email_overrides'])) + { + $email['mail_overrides'] = $ourTemplate->lastTemplateData['email_overrides']; + } + } + } + + if (count($errList) == 0) + { + return TRUE; + } + return $errList; + } + + + + /** + * Generate a table which shows some information about an email. + * Intended to be part of a 2-column table - includes the row detail, but not the surrounding table definitions + * + * @param $mailSource - array of mail information + * @param $options - controls how much information is displayed + * @return text for display + */ + public function showMailDetail(&$mailSource, $options='basic') + { + $tp = e107::getParser(); + + + if (!isset($this->mailDetailDisplay[$options])) + { + return "Programming bungle - invalid option value: {$options}"; + } + + $text = ''; + foreach ($this->mailDetailDisplay[$options] as $k => $v) + { + $text .= ''.$this->fields['mail_content'][$k]['title'].''; + $val = $mailSource[$k]; + if (is_numeric($v)) + { + $text .= ($v > 1) ? $tp->text_truncate($val, $v, '...') : $val; + } + else + { + switch ($v) + { + case 'username' : + $text .= $this->getUserName($val); + break; + case 'sdatetime' : + $text .= $gen->convert_date($val, 'short'); + break; + case 'trunc200' : + $text .= $this->e107->tp->text_truncate($val, 200, '...'); + break; + case 'chars' : // Show generated html as is + $text .= htmlspecialchars($val, ENT_COMPAT, 'UTF-8'); + break; + case 'contentstatus' : + $text .= $this->statusToText($val); + break; + case 'selectors' : + $text .= 'cannot display'; + break; + case 'yesno' : + $text .= $val ? LAN_YES : LAN_NO; + break; + case 'default' : + default : + $text .= $val; + } + } + $text .= ''."\n"; + } + return $text; + } + + + + /** + * Generate the HTML for dropdown to select mail sending style (text/HTML/styled + * + * @param $curval - current value + * @param $name name of item + * @return text for display + */ //FIXME use $frm->selectbox() instead. - public function sendStyleSelect($curval = '', $name = 'send_style') - { - - $emFormat = array( - 'textonly' => LAN_MAILOUT_125, - 'texthtml' => LAN_MAILOUT_126, - 'texttheme' => LAN_MAILOUT_127 - ); - - $text = "\n"; - return $text; - } - - - /** - * Generate the HTML to show the mailout form. Used for both sending and editing - * - * @param $mailSource - array of mail information - * @return text for display - */ - function show_mailform(&$mailSource) - { - global $pref,$HANDLERS_DIRECTORY; - global $mailAdmin; - - $sql = e107::getDb(); - $ns = e107::getRender(); - $tp = e107::getParser(); - $frm = e107::getForm(); - $mes = e107::getMessage(); - - if (!is_array($mailSource)) - { - $mes = e107::getMessage(); - $mes->add('Coding error - mail not array (521)', E_MESSAGE_ERROR); - //$ns->tablerender('ERROR!!', ); - //exit; - } - - $email_subject = varset($mailSource['mail_subject'], ''); - $email_body = $tp->toForm(varset($mailSource['mail_body'],'')); - $email_id = varset($mailSource['mail_source_id'],''); - - $text = ''; - - if(strpos($_SERVER['SERVER_SOFTWARE'],'mod_gzip') && !is_readable(e_HANDLER.'phpmailer/.htaccess')) - { - $warning = LAN_MAILOUT_40.' '.$HANDLERS_DIRECTORY.'phpmailer/ '.LAN_MAILOUT_41; - $ns->tablerender(LAN_MAILOUT_42, $warning); - } - - $debug = (e_MENU == "debug") ? "?[debug]" : ""; - - - - $text .= "
    -
    - ".$this->emailSelector('all', varset($mailSource['mail_selectors'], FALSE))." + public function sendStyleSelect($curval = '', $name = 'email_send_style', $incTemplates = TRUE) + { + + $emFormat = array( + 'textonly' => LAN_MAILOUT_125, + 'texthtml' => LAN_MAILOUT_126, + 'texttheme' => LAN_MAILOUT_127 + ); + + $text = "\n"; + return $text; + } + + + /** + * Generate the HTML to show the mailout form. Used for both sending and editing + * + * @param $mailSource - array of mail information + * @return text for display + */ + function show_mailform(&$mailSource) + { + global $pref,$HANDLERS_DIRECTORY; + global $mailAdmin; + + $sql = e107::getDb(); + $ns = e107::getRender(); + $tp = e107::getParser(); + $frm = e107::getForm(); + $mes = e107::getMessage(); + + if (!is_array($mailSource)) + { + $mes = e107::getMessage(); + $mes->add('Coding error - mail not array (521)', E_MESSAGE_ERROR); + //$ns->tablerender('ERROR!!', ); + //exit; + } + + $email_subject = varset($mailSource['mail_subject'], ''); + $email_body = $tp->toForm(varset($mailSource['mail_body'],'')); + $email_id = varset($mailSource['mail_source_id'],''); + + $text = ''; + + if(strpos($_SERVER['SERVER_SOFTWARE'],'mod_gzip') && !is_readable(e_HANDLER.'phpmailer/.htaccess')) + { + $warning = LAN_MAILOUT_40.' '.$HANDLERS_DIRECTORY.'phpmailer/ '.LAN_MAILOUT_41; + $ns->tablerender(LAN_MAILOUT_42, $warning); + } + + $debug = (e_MENU == "debug") ? "?[debug]" : ""; + + + + $text .= "
    + + ".$this->emailSelector('all', varset($mailSource['mail_selectors'], FALSE))." - - - - - - - - - - - - - - - - - - "; - - - // Add in the core and any plugin selectors here - - /*$text .= " - - - - - ";*/ - - $text .= " - - - - - - - - - - - - - - "; - - - // Attachment. - if (e107::isInstalled('download')) - { - // TODO - use download plugin API - - if($sql->db_Select("download", "download_url,download_name", "download_id !='' ORDER BY download_name")) - { - $text .= " - - - "; - } - - - } + + + + + + + + + + + + + + + + + + "; + + + // Add in the core and any plugin selectors here + + /*$text .= " + + + + + ";*/ + + $text .= " + + + + + + + + + + + + + + "; + + + // Attachment. + if (e107::isInstalled('download')) + { + // TODO - use download plugin API + + if($sql->db_Select("download", "download_url,download_name", "download_id !='' ORDER BY download_name")) + { + $text .= " + + + "; + } + + + } // TODO File-Picker from Media-Manager. - - $text .= " - - - \n - + + $text .= " + + + \n + - "; - - $text .=" - - "; + + $text .=" + + - -
    ".LAN_MAILOUT_111.": ".$frm->text('email_title',varset($mailSource['mail_title'],''))."
    ".LAN_MAILOUT_01.": ".$frm->text('email_from_name',varset($mailSource['mail_from_name'],USERNAME))."
    ".LAN_MAILOUT_02.": ".$frm->text('email_from_email',varset($mailSource['mail_from_email'],USEREMAIL))."
    ".LAN_MAILOUT_03.": ".$this->emailSelector('all', varset($mailSource['mail_selectors'], FALSE))."
    ".LAN_MAILOUT_04.": ".$frm->text('email_cc',varset($mailSource['mail_cc'],''))."
    ".LAN_MAILOUT_05.": ".$frm->text('email_bcc',varset($mailSource['mail_bcc'],''))."
    ".LAN_MAILOUT_51.": ".$frm->text('email_subject',varset($email_subject,''))."
    ".LAN_MAILOUT_07.": "; - $text .= ""; - - $text .= "
    ".LAN_MAILOUT_111.": ".$frm->text('email_title',varset($mailSource['mail_title'],''))."
    ".LAN_MAILOUT_01.": ".$frm->text('email_from_name',varset($mailSource['mail_from_name'],USERNAME))."
    ".LAN_MAILOUT_02.": ".$frm->text('email_from_email',varset($mailSource['mail_from_email'],USEREMAIL))."
    ".LAN_MAILOUT_03.": ".$this->emailSelector('all', varset($mailSource['mail_selectors'], FALSE))."
    ".LAN_MAILOUT_04.": ".$frm->text('email_cc',varset($mailSource['mail_cc'],''))."
    ".LAN_MAILOUT_05.": ".$frm->text('email_bcc',varset($mailSource['mail_bcc'],''))."
    ".LAN_MAILOUT_51.": ".$frm->text('email_subject',varset($email_subject,''))."
    ".LAN_MAILOUT_07.": "; + $text .= ""; + + $text .= "
    ".LAN_MAILOUT_09.": \n"; - - global $eplug_bb; - - $eplug_bb[] = array( - 'name' => 'shortcode', - 'onclick' => 'expandit', - 'onclick_var' => 'sc_selector', - 'icon' => e_IMAGE.'generic/bbcode/shortcode.png', - 'helptext' => LAN_MAILOUT_11, - 'function' => array($this,'sc_Select'), - 'function_var' => 'sc_selector' - ); - - - $text .= $this->sendStyleSelect(varset($mailSource['mail_send_style'], '')); - $checked = (isset($mailSource['mail_include_images']) && $mailSource['mail_include_images']) ? " checked='checked'" : ''; - $text .= "  ".LAN_MAILOUT_225; - $text .=" -
    ".LAN_MAILOUT_09.": \n"; + + global $eplug_bb; + + $eplug_bb[] = array( + 'name' => 'shortcode', + 'onclick' => 'expandit', + 'onclick_var' => 'sc_selector', + 'icon' => e_IMAGE.'generic/bbcode/shortcode.png', + 'helptext' => LAN_MAILOUT_11, + 'function' => array($this,'sc_Select'), + 'function_var' => 'sc_selector' + ); + + + $text .= $this->sendStyleSelect(varset($mailSource['mail_send_style'], '')); + $checked = (isset($mailSource['mail_include_images']) && $mailSource['mail_include_images']) ? " checked='checked'" : ''; + $text .= "  ".LAN_MAILOUT_225; + $text .=" +
    ".$frm->bbarea('email_body',$email_body,'mailout','helpb')."
    -
    "; - - // $text .= display_help('helpb','mailout'); - - if(e_WYSIWYG) - { +
    +
    "; + + // $text .= display_help('helpb','mailout'); + + if(e_WYSIWYG) + { $text .=" "; - } - - $text .=" -
    "; - - - $text .= "
    "; - - if($email_id) - { - $text .= $frm->hidden('mail_source_id',$email_id); - $text .= $frm->admin_button('update_email',LAN_UPDATE); - //$text .= ""; - //$text .= ""; - } - else - { + } + + $text .=" +
    + + "; + + + $text .= "
    "; + + if($email_id) + { + $text .= $frm->hidden('mail_source_id',$email_id); + $text .= $frm->admin_button('update_email',LAN_UPDATE); + //$text .= ""; + //$text .= ""; + } + else + { $text .= $frm->admin_button('save_email',LAN_SAVE,'other'); - } + } - - $text .= $frm->admin_button('send_email',LAN_MAILOUT_08); // - - $text .= "
    - - -
    "; - - $ns->tablerender(LAN_MAILOUT_15,$mes->render(). $text); // Render the complete form - } - - - // Helper function manages the shortcodes which can be inserted - function sc_Select($container='sc_selector') - { - $text =" - \n - - \n - "; - - return $text; - } - - - - - /** - * Return dropdown for arithmetic comparisons - * - * @param $name string name of select structure - * @param $curval string current value - * @return text for display - */ - public function comparisonSelect($name, $curval = '') - { - $compVals = array(' ' => ' ', '<' => LAN_MAILOUT_175, '=' => LAN_MAILOUT_176, '>' => LAN_MAILOUT_177); - $ret = "\n"; - return $ret; - } - - - /** - * Show a screen to confirm deletion of an email - * - * @param $mailid - number of email - * @param $nextPage - 'mode' specification for page to return to following delete - * @return text for display - */ - public function showDeleteConfirm($mailID, $nextPage = 'saved') - { - $mailData = $this->retrieveEmail($mailID); - - $text = "
    "; - - if ($mailData === FALSE) - { - $text = "
    ".LAN_MAILOUT_79."
    "; - $this->e107->ns-> tablerender("
    ".LAN_MAILOUT_171."
    ", $text); - exit; - } - - $text .= " -
    -
    + + $text .= $frm->admin_button('send_email',LAN_MAILOUT_08); // + + $text .= "
    + + +
    "; + + $ns->tablerender(LAN_MAILOUT_15,$mes->render(). $text); // Render the complete form + } + + + + + /** + * Helper function manages the shortcodes which can be inserted + */ + function sc_Select($container='sc_selector') + { + $text =" + \n + + \n + "; + + return $text; + } + + + + + /** + * Return dropdown for arithmetic comparisons + * + * @param $name string name of select structure + * @param $curval string current value + * @return text for display + */ + public function comparisonSelect($name, $curval = '') + { + $compVals = array(' ' => ' ', '<' => LAN_MAILOUT_175, '=' => LAN_MAILOUT_176, '>' => LAN_MAILOUT_177); + $ret = "\n"; + return $ret; + } + + + + /** + * Show the generated template of a saved email + */ + public function showEmailTemplate($mailId) + { + $mailData = $this->retrieveEmail($mailId); + + $text = "
    "; + + if ($mailData === FALSE) + { + $text = "
    ".LAN_MAILOUT_79.'
    '; + $this->e107->ns-> tablerender("
    ".LAN_MAILOUT_171."
    ", $text); + exit; + } + + $text .= " +
    +
    + + + + + + "; + + $text .= $this->showMailDetail($mailData, 'template'); + $text .= '"; + + $text .= "
    '.LAN_MAILOUT_172.''.$this->statusToText($mailData['mail_content_status'])."
    \n
    "; + + $text .= "
    + +
    "; + + $text .= "
    "; + $this->e107->ns->tablerender("
    ".ADLAN_136." :: ".LAN_MAILOUT_255.$mailId.'
    ', $text); + } + + + + + /** + * Show a screen to confirm deletion of an email + * + * @param $mailid - number of email + * @param $nextPage - 'mode' specification for page to return to following delete + * @return text for display + */ + public function showDeleteConfirm($mailID, $nextPage = 'saved') + { + $mailData = $this->retrieveEmail($mailID); + + $text = "
    "; + + if ($mailData === FALSE) + { + $text = "
    ".LAN_MAILOUT_79."
    "; + $this->e107->ns-> tablerender("
    ".LAN_MAILOUT_171."
    ", $text); + exit; + } + + $text .= " +
    +
    - - - - - - "; - - $text .= $this->showMailDetail($mailData, 'basic'); - $text .= '"; - if ($mailData['mail_content_status'] != MAIL_STATUS_SAVED) - { - $text .= ''; - } - - $text .= "
    '.LAN_MAILOUT_172.''.$this->statusToText($mailData['mail_content_status'])."
    '.LAN_MAILOUT_173.''.($mailData['mail_togo_count'] + $mailData['mail_sent_count'] + $mailData['mail_fail_count']).'
    \n
    "; - - $text .= "
    + + + + + + "; + + $text .= $this->showMailDetail($mailData, 'basic'); + $text .= ''.LAN_MAILOUT_172.''.$this->statusToText($mailData['mail_content_status']).""; + if ($mailData['mail_content_status'] != MAIL_STATUS_SAVED) + { + $text .= ''.LAN_MAILOUT_173.''.($mailData['mail_togo_count'] + $mailData['mail_sent_count'] + $mailData['mail_fail_count']).''; + } + + $text .= "\n"; + + $text .= "
      -
    "; - - $text .= "
    "; - $this->e107->ns->tablerender("
    ".ADLAN_136." :: ".LAN_MAILOUT_171."
    ", $text); - } - - - - /** - * Generate the HTML to show a list of emails of a particular type, in tabular form - * - * @param $type - type of email to display - * @param $from - offset into table of candidates - * @param $amount - number to return - * @return text for display - */ - public function showEmailList($type, $from = 0, $amount = 10) - { - // Need to select main email entries; count number of addresses attached to each - $gen = new convert; - $frm = e107::getForm(); - switch ($type) - { - case 'sent' : - $searchType = 'allcomplete'; - break; - default : - $searchType = $type; - } - - if ($from < 0) { $from = $this->showFrom; } - if ($amount < 0) { $amount = $this->showCount; } - // in $_GET, so = sort order, sf = sort field - $count = $this->selectEmailStatus($from, $amount, '*', $searchType, $this->sortField, $this->sortOrder); - $totalCount = $this->getEmailCount(); - - $emails_found = array(); // Log ID and count for later - - $text = "
    "; - - if (!$count) - { - $text = "
    ".LAN_MAILOUT_79."
    "; - $this->e107->ns-> tablerender("
    ".$this->tasks[$type]['title']."
    ", $text); +
    "; + + $text .= "
    "; + $this->e107->ns->tablerender("
    ".ADLAN_136." :: ".LAN_MAILOUT_171."
    ", $text); + } + + + + /** + * Generate the HTML to show a list of emails of a particular type, in tabular form + * + * @param $type - type of email to display (saved|sent|pending|held) + * @param $from - offset into table of candidates + * @param $amount - number to return + * @return text for display + */ + public function showEmailList($type, $from = 0, $amount = 10) + { + // Need to select main email entries; count number of addresses attached to each + $gen = new convert; + $frm = e107::getForm(); + switch ($type) + { + case 'sent' : + $searchType = 'allcomplete'; + break; + default : + $searchType = $type; + } + + if ($from < 0) { $from = $this->showFrom; } + if ($amount < 0) { $amount = $this->showCount; } + // in $_GET, so = sort order, sf = sort field + $count = $this->selectEmailStatus($from, $amount, '*', $searchType, $this->sortField, $this->sortOrder); + $totalCount = $this->getEmailCount(); + + $emails_found = array(); // Log ID and count for later + + $text = "
    "; + + if (!$count) + { + $text = "
    ".LAN_MAILOUT_79."
    "; + $this->e107->ns-> tablerender("
    ".$this->tasks[$type]['title']."
    ", $text); return; - } - - $text .= " -
    -
    + } + + $text .= " + +
    "; - - $fieldPrefs = $this->calcFieldSpec($type, TRUE); // Get columns to display - - // Must use '&' rather than '&' in query pattern - $text .= $frm->colGroup($this->fields['mail_content'],$this->fieldPref).$frm->thead($this->fields['mail_content'],$this->fieldPref,'mode='.$type."&fld=[FIELD]&asc=[ASC]&frm=[FROM]").""; - - while ($row = $this->getNextEmailStatus(FALSE)) - { - //print_a($row); - $text .= ''; - foreach ($fieldPrefs as $fieldName) - { // Output column data value - $text .= ''; - } - // Add in options here - $text .= ''; - $text .= ''; - } - $text .= "
    '; - if (isset($row[$fieldName])) - { - $proctype = varset($this->fields['mail_content'][$fieldName]['proc'], 'default'); - switch ($proctype) - { - case 'username' : - $text .= $this->getUserName($row[$fieldName]); - break; - case 'sdatetime' : - $text .= $gen->convert_date($row[$fieldName], 'short'); - break; - case 'trunc200' : - $text .= $this->e107->tp->text_truncate($row[$fieldName], 200, '...'); - break; - case 'contentstatus' : - $text .= $this->statusToText($row[$fieldName]); - break; - case 'selectors' : - $text .= 'cannot display'; - break; - case 'yesno' : - $text .= $row[$fieldName] ? LAN_YES : LAN_NO; - break; - case 'default' : - default : - $text .= $row[$fieldName]; - } - } - else - { // Special stuff - } - $text .= ''.$this->makeMailOptions($type,$row).'


    \n"; - - if ($totalCount > $count) - { - $parms = "{$totalCount},{$amount},{$from},".e_SELF."?mode={$type}&count={$amount}&frm=[FROM]&fld={$this->sortField}&asc={$this->sortOrder}"; - $text .= $this->e107->tp->parseTemplate("{NEXTPREV={$parms}}"); - } - - $text .= '

    '; - $this->e107->ns->tablerender("
    ".ADLAN_136." :: ".$this->tasks[$type]['title']."
    ", $text); - } - - - - - /** - * Generate a list of emails to send - * Returns various information to display in a confirmation screen - * - * The email and its recipients are stored in the DB with a tag of 'MAIL_STATUS_TEMP' of its a new email (no change if already on hold) - * - * @param array $mailData - Details of the email, selection criteria etc - * @param boolean $fromHold - FALSE if this is a 'new' email to send, TRUE if its already been put on hold (selects processing path) - * @return text for display - */ - public function sendEmailCircular($mailData, $fromHold = FALSE) - { - $sql = e107::getDb(); - $mes = e107::getMessage(); - $frm = e107::getForm(); - - - if ($fromHold) - { // Email data already generated - $mailMainID = $mailData['mail_source_id']; - if ($mailMainID == 0) return FALSE; - if (FALSE === ($mailData = $this->retrieveEmail($mailMainID))) // Get the new data - { - return FALSE; - } - $counters['add'] = $mailData['mail_togo_count']; // Set up the counters - $counters['dups'] = 0; - } - else - { - // Start by saving the email - $mailData['mail_content_status'] = MAIL_STATUS_TEMP; - $mailData['mail_create_app'] = 'core'; - $result = $this->saveEmail($mailData, TRUE); - if (is_numeric($result)) - { - $mailMainID = $mailData['mail_source_id'] = $result; - } - else - { - // TODO: Handle error - } - - - $this->mailInitCounters($mailMainID); // Initialise counters for emails added - - foreach ($this->mailHandlers as $key => $m) - { // Get email addresses from each handler in turn. Do them one at a time, so that all can use the $sql data object - if ($m->mailerEnabled && isset($mailData['mail_selectors'][$key])) - { - // Initialise - $mailerCount = $m->selectInit($mailData['mail_selectors'][$key]); - if ($mailerCount > 0) - { - // Get email addresses - add to list, strip duplicates - while ($row = $m->selectAdd()) - { // Add email addresses to the database ready for sending (the body is never saved in the DB - it gets passed as a $_POST value) - $result = $this->mailAddNoDup($mailMainID, $row, MAIL_STATUS_TEMP); - if ($result === FALSE) - { - // Error - } - } - } - $m->select_close(); // Close - // Update the stats after each handler - $this->mailUpdateCounters($mailMainID); - } - } - - $counters = $this->mailRetrieveCounters($mailMainID); - // $this->e107->admin_log->log_event('MAIL_02','ID: '.$mailMainID.' '.$counters['add'].'[!br!]'.$_POST['email_from_name']." <".$_POST['email_from_email'],E_LOG_INFORMATIVE,''); - } - - - // We've got all the email addresses here - display a confirmation form - // Include start/end dates for send - $text = "
    "; - - $text .= " -
    -
    + + $fieldPrefs = $this->calcFieldSpec($type, TRUE); // Get columns to display + + // Must use '&' rather than '&' in query pattern + $text .= $frm->colGroup($this->fields['mail_content'],$this->fieldPref).$frm->thead($this->fields['mail_content'],$this->fieldPref,'mode='.$type."&fld=[FIELD]&asc=[ASC]&frm=[FROM]").""; + + while ($row = $this->getNextEmailStatus(FALSE)) + { + //print_a($row); + $text .= ''; + foreach ($fieldPrefs as $fieldName) + { // Output column data value + $text .= ''; + if (isset($row[$fieldName])) + { + $proctype = varset($this->fields['mail_content'][$fieldName]['proc'], 'default'); + switch ($proctype) + { + case 'username' : + $text .= $this->getUserName($row[$fieldName]); + break; + case 'sdatetime' : + $text .= $gen->convert_date($row[$fieldName], 'short'); + break; + case 'trunc200' : + $text .= $this->e107->tp->text_truncate($row[$fieldName], 200, '...'); + break; + case 'chars' : // Show generated html as is + $text .= htmlspecialchars($row[$fieldName], ENT_COMPAT, 'UTF-8'); + break; + case 'contentstatus' : + $text .= $this->statusToText($row[$fieldName]); + break; + case 'selectors' : + $text .= 'cannot display'; + break; + case 'yesno' : + $text .= $row[$fieldName] ? LAN_YES : LAN_NO; + break; + case 'default' : + default : + $text .= $row[$fieldName]; + } + } + else + { // Special stuff + } + $text .= ''; + } + // Add in options here + $text .= ''.$this->makeMailOptions($type,$row).''; + $text .= ''; + } + $text .= "

    \n"; + + if ($totalCount > $count) + { + $parms = "{$totalCount},{$amount},{$from},".e_SELF."?mode={$type}&count={$amount}&frm=[FROM]&fld={$this->sortField}&asc={$this->sortOrder}"; + $text .= $this->e107->tp->parseTemplate("{NEXTPREV={$parms}}"); + } + + $text .= '

    '; + $this->e107->ns->tablerender("
    ".ADLAN_136." :: ".$this->tasks[$type]['title']."
    ", $text); + } + + + + + /** + * Generate a list of emails to send + * Returns various information to display in a confirmation screen + * + * The email and its recipients are stored in the DB with a tag of 'MAIL_STATUS_TEMP' if its a new email (no change if already on hold) + * + * @param array $mailData - Details of the email, selection criteria etc + * @param boolean $fromHold - FALSE if this is a 'new' email to send, TRUE if its already been put on hold (selects processing path) + * @return text for display + */ + public function sendEmailCircular($mailData, $fromHold = FALSE) + { + $sql = e107::getDb(); + $mes = e107::getMessage(); + $frm = e107::getForm(); + + + if ($fromHold) + { // Email data already generated + $mailMainID = $mailData['mail_source_id']; + if ($mailMainID == 0) return FALSE; + if (FALSE === ($mailData = $this->retrieveEmail($mailMainID))) // Get the new data + { + return FALSE; + } + $counters['add'] = $mailData['mail_togo_count']; // Set up the counters + $counters['dups'] = 0; + } + else + { + // Start by saving the email + $mailData['mail_content_status'] = MAIL_STATUS_TEMP; + $mailData['mail_create_app'] = 'core'; + $result = $this->saveEmail($mailData, TRUE); + if (is_numeric($result)) + { + $mailMainID = $mailData['mail_source_id'] = $result; + } + else + { + // TODO: Handle error + } + + + $this->mailInitCounters($mailMainID); // Initialise counters for emails added + + foreach ($this->mailHandlers as $key => $m) + { // Get email addresses from each handler in turn. Do them one at a time, so that all can use the $sql data object + if ($m->mailerEnabled && isset($mailData['mail_selectors'][$key])) + { + // Initialise + $mailerCount = $m->selectInit($mailData['mail_selectors'][$key]); + if ($mailerCount > 0) + { + // Get email addresses - add to list, strip duplicates + while ($row = $m->selectAdd()) + { // Add email addresses to the database ready for sending (the body is never saved in the DB - it gets passed as a $_POST value) + $result = $this->mailAddNoDup($mailMainID, $row, MAIL_STATUS_TEMP); + if ($result === FALSE) + { + // Error + } + } + } + $m->select_close(); // Close + // Update the stats after each handler + $this->mailUpdateCounters($mailMainID); + } + } + + $counters = $this->mailRetrieveCounters($mailMainID); + // $this->e107->admin_log->log_event('MAIL_02','ID: '.$mailMainID.' '.$counters['add'].'[!br!]'.$_POST['email_from_name']." <".$_POST['email_from_email'],E_LOG_INFORMATIVE,''); + } + + + // We've got all the email addresses here - display a confirmation form + // Include start/end dates for send + $text = "
    "; + + $text .= " +
    +
    - - - - - "; - - $text .= $this->showMailDetail($mailData, 'send'); - - - // Add in core and any plugin selectors here - foreach ($this->mailHandlers as $key => $m) - { - - if ($m->mailerEnabled && ($contentArray = $m->showSelect(FALSE,$mailData['mail_selectors'][$key]))) - { - $text .= ''; - $text .= ''; - } - } - - // Figures - number of emails to send, number of duplicates stripped - $text .= '"; - $text .= ''; - $text .= "
    '.LAN_MAILOUT_180.'
    '.$m->mailerName.'
      '; - foreach($contentArray as $val) - { - $text .= "
    • ".$val['caption']." : ".$val['html']."
    • "; - } - $text .= '
    '.LAN_MAILOUT_173.''.($mailData['mail_togo_count'])."
    '.LAN_MAILOUT_71.' '.$counters['add'].' '.LAN_MAILOUT_69.$counters['dups'].LAN_MAILOUT_70.'
    \n
    "; - - $text .= $this->makeAdvancedOptions(TRUE); // Show the table of advanced options - - $text .= "
    "; - - $text .= $frm->admin_button('email_sendnow',"Send Now"); - $text .= $frm->admin_button('email_send',"Send Later"); - - // $text .= ""; - - if (!$fromHold) - { - $text .= $frm->admin_button('email_hold',LAN_HOLD); - $text .= $frm->admin_button('email_cancel',LAN_CANCEL); - // $text .= " "; - // $text .= " "; - } - $text .= "
    -
    -
    "; - - e107::getRender()->tablerender("
    ".ADLAN_136." :: ".LAN_MAILOUT_179."
    ",$mes->render(). $text); - } // End of previewed email - - - - protected function makeAdvancedOptions($initHide = FALSE) - { - // Separate table for advanced mailout options - // mail_notify_complete field - $text = " - ".LAN_MAILOUT_242." -
    + + + + + "; + + $text .= $this->showMailDetail($mailData, 'send'); + + + // Add in core and any plugin selectors here + foreach ($this->mailHandlers as $key => $m) + { + + if ($m->mailerEnabled && ($contentArray = $m->showSelect(FALSE,$mailData['mail_selectors'][$key]))) + { + $text .= ''.LAN_MAILOUT_180.'
    '.$m->mailerName.''; + $text .= '
      '; + foreach($contentArray as $val) + { + $text .= "
    • ".$val['caption']." : ".$val['html']."
    • "; + } + $text .= '
    '; + } + } + + // Figures - number of emails to send, number of duplicates stripped + $text .= ''.LAN_MAILOUT_173.''.($mailData['mail_togo_count']).""; + $text .= ''.LAN_MAILOUT_71.' '.$counters['add'].' '.LAN_MAILOUT_69.$counters['dups'].LAN_MAILOUT_70.''; + $text .= "\n
    "; + + $text .= $this->makeAdvancedOptions(TRUE); // Show the table of advanced options + + $text .= "
    "; + + $text .= $frm->admin_button('email_sendnow',"Send Now"); + $text .= $frm->admin_button('email_send',"Send Later"); + + // $text .= ""; + + if (!$fromHold) + { + $text .= $frm->admin_button('email_hold',LAN_HOLD); + $text .= $frm->admin_button('email_cancel',LAN_CANCEL); + // $text .= " "; + // $text .= " "; + } + $text .= "
    + + "; + + e107::getRender()->tablerender("
    ".ADLAN_136." :: ".LAN_MAILOUT_179."
    ",$mes->render(). $text); + } // End of previewed email + + + + + /** + * + */ + protected function makeAdvancedOptions($initHide = FALSE) + { + // Separate table for advanced mailout options + // mail_notify_complete field + $text = " + ".LAN_MAILOUT_242." +
    - - - - - "; - - $text .= ""; - $text .= ""; - // Can comment the two lines above, uncomment two lines below, and default time/date is shown. May or may not be preferable -// $text .= ""; -// $text .= ""; - $text .= ""; - $text .= "
    ".LAN_MAILOUT_238."".$this->makeCalendar('mail_earliest_time', '', CORE_DATE_ORDER)."
    ".LAN_MAILOUT_239."".$this->makeCalendar('mail_latest_time', '', CORE_DATE_ORDER)."
    ".LAN_MAILOUT_238."".$this->makeCalendar('mail_earliest_time', time(), CORE_DATE_ORDER)."
    ".LAN_MAILOUT_239."".$this->makeCalendar('mail_latest_time', time()+86400, CORE_DATE_ORDER)."
    ".LAN_MAILOUT_240."".LAN_MAILOUT_241."
    \n
    "; - return $text; - } - - - - public function makeCalendar($calName, $calVal = '', $dateOrder = 'dmy') - { - // Determine formatting strings this way, to give sensible default - switch ($dateOrder) - { - case 'mdy' : - $dateString = '%m/%d/%Y %H:%I'; - $dispString = 'm/d/Y H:I'; - break; - case 'ymd' : - $dateString = '%Y/%m/%d %H:%I'; - $dispString = 'Y/m/d H:I'; - break; - case 'dmy' : - default : - $dateString = '%d/%m/%Y %H:%I'; - $dispString = 'd/m/Y H:I'; - } - $calOptions = array( - 'showsTime' => TRUE, - 'showOthers' => false, - 'weekNumbers' => false, - 'ifFormat' => $dateString - ); - $calAttrib = array( - 'class' => 'tbox', - 'size' => 15, // Number of characters - 'name' => $calName, - 'value' => (($calVal == '') ? '' : date($dispString,$calVal)) - ); - - - list($dformat,$tformat) = explode(" ",$dateString); - $options['type'] = 'datetime'; - $options['dateFormat'] = $dformat; - $options['timeFormat'] = $tformat; - - return e107::getForm()->datepicker($calName,$calVal,$options); - - // return $this->_cal->make_input_field($calOptions, $calAttrib); - } - - - /** - * Show recipients of an email - * - * @param $mailid - number of email - * @param $nextPage - 'mode' specification for page to return to following delete - * @return text for display - */ - public function showmailRecipients($mailID, $nextPage = 'saved') - { - $gen = new convert; - $frm = e107::getForm(); - - $mailData = $this->retrieveEmail($mailID); - - $text = "
    "; - - if ($mailData === FALSE) - { - $text = "
    ".LAN_MAILOUT_79."
    "; - $this->e107->ns-> tablerender("
    ".LAN_MAILOUT_171."
    ", $text); - exit; - } - - $text .= " -
    -
    + + + + + "; + + $text .= "".LAN_MAILOUT_238."".$this->makeCalendar('mail_earliest_time', '', CORE_DATE_ORDER).""; + $text .= "".LAN_MAILOUT_239."".$this->makeCalendar('mail_latest_time', '', CORE_DATE_ORDER).""; + // Can comment the two lines above, uncomment two lines below, and default time/date is shown. May or may not be preferable +// $text .= "".LAN_MAILOUT_238."".$this->makeCalendar('mail_earliest_time', time(), CORE_DATE_ORDER).""; +// $text .= "".LAN_MAILOUT_239."".$this->makeCalendar('mail_latest_time', time()+86400, CORE_DATE_ORDER).""; + $text .= "".LAN_MAILOUT_240."".LAN_MAILOUT_241.""; + $text .= "\n
    "; + return $text; + } + + + + /** + * + */ + public function makeCalendar($calName, $calVal = '', $dateOrder = 'dmy') + { + // Determine formatting strings this way, to give sensible default + switch ($dateOrder) + { + case 'mdy' : + $dFormat = '%m/%d/%y'; + $tFormat = '%H:%M'; + break; + case 'ymd' : + $dFormat = '%Y/%m/%d'; + $tFormat = ' %H:%M'; + break; + case 'dmy' : + default : + $dFormat = '%d/%m/%Y'; + $tFormat = ' %H:%M'; + } + + $options = array( + 'type' => 'datetime', + 'dateformat' => $dFormat, + 'timeformat' => $tFormat, + 'firstDay' => 1, // 0 = Sunday. + 'size' => 12 + ); +// $options['dateFormat'] = $dformat; +// $options['timeFormat'] = $tformat; + + return e107::getForm()->datepicker($calName,$calVal,$options); + } + + + /** + * Show recipients of an email + * + * @param $mailid - number of email + * @param $nextPage - 'mode' specification for page to return to following delete + * @return text for display + */ + public function showmailRecipients($mailID, $nextPage = 'saved') + { + $gen = new convert; + $frm = e107::getForm(); + + $mailData = $this->retrieveEmail($mailID); + + $text = "
    "; + + if ($mailData === FALSE) + { + $text = "
    ".LAN_MAILOUT_79."
    "; + $this->e107->ns-> tablerender("
    ".LAN_MAILOUT_171."
    ", $text); + exit; + } + + $text .= " + +
    - - - - - - "; - - $text .= $this->showMailDetail($mailData, 'basic'); - $text .= '"; - if ($mailData['mail_content_status'] != MAIL_STATUS_SAVED) - { - $text .= ''; - } - - $text .= "
    '.LAN_MAILOUT_172.''.$this->statusToText($mailData['mail_content_status'])."
    '.LAN_MAILOUT_173.''.($mailData['mail_togo_count'] + $mailData['mail_sent_count'] + $mailData['mail_fail_count']).'
    \n
    "; - - - // List of recipients - // in $_GET, asc = sort order, fld = sort field - $count = $this->selectTargetStatus($mailID, $this->showFrom, $this->showCount, '*', FALSE, $this->sortField, $this->sortOrder); - $totalCount = $this->getTargetCount(); - - if ($count == 0) - { - $text .= "".LAN_MAILOUT_253.''; - } - else - { - $text .= " -
    -
    + + + + + + "; + + $text .= $this->showMailDetail($mailData, 'basic'); + $text .= ''.LAN_MAILOUT_172.''.$this->statusToText($mailData['mail_content_status']).""; + if ($mailData['mail_content_status'] != MAIL_STATUS_SAVED) + { + $text .= ''.LAN_MAILOUT_173.''.($mailData['mail_togo_count'] + $mailData['mail_sent_count'] + $mailData['mail_fail_count']).''; + } + + $text .= "\n
    "; + + + // List of recipients + // in $_GET, asc = sort order, fld = sort field + $count = $this->selectTargetStatus($mailID, $this->showFrom, $this->showCount, '*', FALSE, $this->sortField, $this->sortOrder); + $totalCount = $this->getTargetCount(); + + if ($count == 0) + { + $text .= "".LAN_MAILOUT_253.''; + } + else + { + $text .= " +
    +
    "; - - - $fieldPrefs = $this->calcFieldSpec('recipients', TRUE); // Get columns to display - - // Must use '&' rather than '&' in query pattern - $text .= $frm->colGroup($this->fields['mail_recipients'],$this->fieldPref).$frm->thead($this->fields['mail_recipients'],$this->fieldPref,'mode='.'recipients&m='.$mailID."&fld=[FIELD]&asc=[ASC]&frm=[FROM]").""; - - while ($row = $this->getNextTargetStatus(FALSE)) - { - // print_a($row); - $text .= ''; - foreach ($fieldPrefs as $fieldName) - { // Output column data value - $text .= ''; - } - // Add in options here - $text .= ''; - $text .= ''; - } - - $text .= "
    '; - if (isset($row[$fieldName])) - { - $proctype = varset($this->fields['mail_recipients'][$fieldName]['proc'], 'default'); - switch ($proctype) - { - case 'username' : - $text .= $this->getUserName($row[$fieldName]); - break; - case 'sdatetime' : - $text .= $gen->convert_date($row[$fieldName], 'short'); - break; - case 'trunc200' : - $text .= $this->e107->tp->text_truncate($row[$fieldName], 200, '...'); - break; - case 'contentstatus' : - $text .= $this->statusToText($row[$fieldName]); - break; - case 'selectors' : - $text .= 'cannot display'; - break; - case 'array' : - if (is_array($row[$fieldName])) - { - $nl = ''; - foreach ($row[$fieldName] as $k => $v) - { - if ($v) - { - $text .= $nl.$k.' => '.$v; - $nl = '
    '; - } - } - } - else - { - $text .= 'bad data: '; - } - break; - case 'default' : - default : - $text .= $row[$fieldName]; - } - } - else - { // Special stuff - $text .= 'special'; - } - $text .= '
    '.$this->makeTargetOptions('recipients',$row).'
    \n


    "; - - if ($totalCount > $count) - { - $parms = "{$totalCount},{$this->showCount},{$this->showFrom},".e_SELF."?mode=recipients&m={$mailID}&count={$this->showCount}&frm=[FROM]&fld={$this->sortField}&asc={$this->sortOrder}&savepage={$nextPage}"; - $text .= $this->e107->tp->parseTemplate("{NEXTPREV={$parms}}"); - } - } - - $text .= "
    "; - - $this->e107->ns->tablerender("
    ".ADLAN_136." :: ".LAN_MAILOUT_181."
    ", $text); - } - - - /** - * Clean up mailout DB - * Dump array of results to admin log - * - * @return boolean TRUE if no errors, FALSE if errors - */ - public function dbTidy() - { - $noError = TRUE; - $results = array(); - $this->checkDB(2); // Make sure DB object created - - // First thing, delete temporary records from both tables - if (($res = $this->db2->db_Delete('mail_content', '`mail_content_status` = '.MAIL_STATUS_TEMP)) === FALSE) - { - $results[] = 'Error '.$this->db2->mySQLlastErrNum.':'.$this->db2->mySQLlastErrText.' deleting temporary records from mail_content'; - $noError = FALSE; - } - else - { - if ($res) $results[] = str_replace(array('--COUNT--', '--TABLE--'), array($res, 'mail_content'), LAN_MAILOUT_227); - } - if (($res = $this->db2->db_Delete('mail_recipients', '`mail_status` = '.MAIL_STATUS_TEMP)) === FALSE) - { - $results[] = 'Error '.$this->db2->mySQLlastErrNum.':'.$this->db2->mySQLlastErrText.' deleting temporary records from mail_recipients'; - $noError = FALSE; - } - else - { - if ($res) $results[] = str_replace(array('--COUNT--', '--TABLE--'), array($res, 'mail_recipients'), LAN_MAILOUT_227); - } - - // Now look for 'orphaned' recipient records - if (($res = $this->db2->db_Select_gen("DELETE `#mail_recipients` FROM `#mail_recipients` - LEFT JOIN `#mail_content` ON `#mail_recipients`.`mail_detail_id` = `#mail_content`.`mail_source_id` - WHERE `#mail_content`.`mail_source_id` IS NULL")) === FALSE) - { - $results[] = 'Error '.$this->db2->mySQLlastErrNum.':'.$this->db2->mySQLlastErrText.' deleting orphaned records from mail_recipients'; - $noError = FALSE; - } - elseif ($res) - { - if ($res) $results[] = str_replace('--COUNT--', $res, LAN_MAILOUT_226); - } - - // Scan content table for anomalies, out of time records - if (($res = $this->db2->db_Select_gen("SELECT * FROM `#mail_content` - WHERE (`mail_content_status` >".MAIL_STATUS_FAILED.") AND (`mail_content_status` <=".MAIL_STATUS_MAX_ACTIVE.") - AND ((`mail_togo_count`=0) OR ( (`mail_last_date` != 0) AND (`mail_last_date` < ".time().")))")) === FALSE) - { - $results[] = 'Error '.$this->db2->mySQLlastErrNum.':'.$this->db2->mySQLlastErrText.' checking bad status in mail_content'; - $noError = FALSE; - } - else - { - $items = array(); // Store record number of any content record that needs to be changed - while ($row = $this->db2->db_Fetch(MYSQL_ASSOC)) - { - $items[] = $row['mail_source_id']; - if ($row['mail_source_id']) - { - if (FALSE == $this->cancelEmail($row['mail_source_id'])) - { - $results[] = 'Error cancelling email ref: '.$row['mail_source_id']; - } - else - { - $results[] = 'Email cancelled: '.$row['mail_source_id']; - } - } - } - if (count($items)) $results[] = str_replace(array('--COUNT--', '--RECORDS--'), array(count($items), implode(', ', $items)), LAN_MAILOUT_228); - } - - - //Finally - check for inconsistent recipient and content status records - basically verify counts - if (($res = $this->db2->db_Select_gen("SELECT COUNT(mr.`mail_status`) AS mr_count, mr.`mail_status`, - mc.`mail_source_id`, mc.`mail_togo_count`, mc.`mail_sent_count`, mc.`mail_fail_count`, mc.`mail_bounce_count`, mc.`mail_source_id` FROM `#mail_recipients` AS mr - LEFT JOIN `#mail_content` AS mc ON mr.`mail_detail_id` = mc.`mail_source_id` - WHERE mc.`mail_content_status` <= ".MAIL_STATUS_MAX_ACTIVE." - GROUP BY mr.`mail_status`, mc.`mail_source_id` ORDER BY mc.`mail_source_id` - ")) === FALSE) - { - $results[] = 'Error '.$this->db2->mySQLlastErrNum.':'.$this->db2->mySQLlastErrText.' assembling email counts'; - $noError = FALSE; - } - else - { - $lastMail = 0; // May get several rows per mail - $notLast = TRUE; // This forces one more loop, so we can clean up for last record read - $changeCount = 0; - $saveRow = array(); - while (($row = $this->db2->db_Fetch(MYSQL_ASSOC)) || $notLast) - { - if (($lastMail > 0 && $row === FALSE) || ($lastMail != $row['mail_source_id'])) - { // Change of mail ID here - handle any accumulated info - if ($lastMail > 0) - { // Need to verify counts for mail just read - $changes = array(); - foreach ($counters as $k => $v) - { - if ($saveRow[$k] != $v) - { - $changes[$k] = $v; // Assume the counters have got it right - } - } - if (count($changes)) - { - // *************** Update mail record here ********************* - $this->checkDB(1); - $this->db->db_Update('mail_content', array('data' => $changes, 'WHERE' => '`mail_source_id` = '.$lastMail, '_FIELDS' => $this->dbTypes['mail_content'])); - $line = "Count update for {$saveRow['mail_source_id']} - {$saveRow['mail_togo_count']}, {$saveRow['mail_sent_count']}, {$saveRow['mail_fail_count']}, {$saveRow['mail_bounce_count']} => "; - $line .= implode (', ', $counters); - $results[] = $line; - $changeCount++; - //echo $line.'
    '; - } - } - - // Now reset for current mail - $lastMail = $row['mail_source_id']; - $counters = array('mail_togo_count' => 0, 'mail_sent_count' => 0, 'mail_fail_count' => 0, 'mail_bounce_count' => 0); - $saveRow = $row; - } - if ($row === FALSE) $notLast = FALSE; - // We get one record for each mail_status value for a given email - use them to update counts - if ($notLast) - { - switch ($row['mail_status']) - { - case MAIL_STATUS_SENT : // Mail sent. Email handler happy, but may have bounced (or may be yet to bounce) - $counters['mail_sent_count'] += $row['mr_count']; - break; - case MAIL_STATUS_BOUNCED : - $counters['mail_sent_count'] += $row['mr_count']; // It was sent, so increment that counter - $counters['mail_bounce_count'] += $row['mr_count']; //...but bounced, so extra status - break; - case MAIL_STATUS_CANCELLED : // Cancelled email - treat as a failure - case MAIL_STATUS_FAILED : - $counters['mail_fail_count'] += $row['mr_count']; // Never sent at all - break; - case MAIL_STATUS_PARTIAL : // Shouldn't get this on individual emails - ignore if we do - break; - default : - if (($row['mail_status'] >= MAIL_STATUS_PENDING) && ($row['mail_status'] <= MAIL_STATUS_MAX_ACTIVE)) - { - $counters['mail_togo_count'] += $row['mr_count']; // Still in the queue - } - } - } - } - if ($changeCount) $results[] = str_replace('--COUNT--', $changeCount, LAN_MAILOUT_237); - } - - $this->e107->admin_log->log_event('MAIL_05', implode('[!br!]', $results), E_LOG_INFORMATIVE, ''); - return $noError; - } -} - - -?> + + + $fieldPrefs = $this->calcFieldSpec('recipients', TRUE); // Get columns to display + + // Must use '&' rather than '&' in query pattern + $text .= $frm->colGroup($this->fields['mail_recipients'],$this->fieldPref).$frm->thead($this->fields['mail_recipients'],$this->fieldPref,'mode='.'recipients&m='.$mailID."&fld=[FIELD]&asc=[ASC]&frm=[FROM]").""; + + while ($row = $this->getNextTargetStatus(FALSE)) + { + // print_a($row); + $text .= ''; + foreach ($fieldPrefs as $fieldName) + { // Output column data value + $text .= ''; + if (isset($row[$fieldName])) + { + $proctype = varset($this->fields['mail_recipients'][$fieldName]['proc'], 'default'); + switch ($proctype) + { + case 'username' : + $text .= $this->getUserName($row[$fieldName]); + break; + case 'sdatetime' : + $text .= $gen->convert_date($row[$fieldName], 'short'); + break; + case 'trunc200' : + $text .= $this->e107->tp->text_truncate($row[$fieldName], 200, '...'); + break; + case 'chars' : // Show generated html as is + $text .= htmlspecialchars($row[$fieldName], ENT_COMPAT, 'UTF-8'); + break; + case 'contentstatus' : + $text .= $this->statusToText($row[$fieldName]); + break; + case 'selectors' : + $text .= 'cannot display'; + break; + case 'array' : + if (is_array($row[$fieldName])) + { + $nl = ''; + foreach ($row[$fieldName] as $k => $v) + { + if ($v) + { + $text .= $nl.$k.' => '.$v; + $nl = '
    '; + } + } + } + else + { + $text .= 'bad data: '; + } + break; + case 'default' : + default : + $text .= $row[$fieldName]; + } + } + else + { // Special stuff + $text .= 'special'; + } + $text .= ''; + } + // Add in options here + $text .= ''.$this->makeTargetOptions('recipients',$row).''; + $text .= ''; + } + + $text .= "\n

    "; + + if ($totalCount > $count) + { + $parms = "{$totalCount},{$this->showCount},{$this->showFrom},".e_SELF."?mode=recipients&m={$mailID}&count={$this->showCount}&frm=[FROM]&fld={$this->sortField}&asc={$this->sortOrder}&savepage={$nextPage}"; + $text .= $this->e107->tp->parseTemplate("{NEXTPREV={$parms}}"); + } + } + + $text .= "
    "; + + $this->e107->ns->tablerender("
    ".ADLAN_136." :: ".LAN_MAILOUT_181."
    ", $text); + } + + + /** + * Clean up mailout DB + * Dump array of results to admin log + * + * @return boolean TRUE if no errors, FALSE if errors + */ + public function dbTidy() + { + $noError = TRUE; + $results = array(); + $this->checkDB(2); // Make sure DB object created + + // First thing, delete temporary records from both tables + if (($res = $this->db2->db_Delete('mail_content', '`mail_content_status` = '.MAIL_STATUS_TEMP)) === FALSE) + { + $results[] = 'Error '.$this->db2->mySQLlastErrNum.':'.$this->db2->mySQLlastErrText.' deleting temporary records from mail_content'; + $noError = FALSE; + } + else + { + if ($res) $results[] = str_replace(array('--COUNT--', '--TABLE--'), array($res, 'mail_content'), LAN_MAILOUT_227); + } + if (($res = $this->db2->db_Delete('mail_recipients', '`mail_status` = '.MAIL_STATUS_TEMP)) === FALSE) + { + $results[] = 'Error '.$this->db2->mySQLlastErrNum.':'.$this->db2->mySQLlastErrText.' deleting temporary records from mail_recipients'; + $noError = FALSE; + } + else + { + if ($res) $results[] = str_replace(array('--COUNT--', '--TABLE--'), array($res, 'mail_recipients'), LAN_MAILOUT_227); + } + + // Now look for 'orphaned' recipient records + if (($res = $this->db2->db_Select_gen("DELETE `#mail_recipients` FROM `#mail_recipients` + LEFT JOIN `#mail_content` ON `#mail_recipients`.`mail_detail_id` = `#mail_content`.`mail_source_id` + WHERE `#mail_content`.`mail_source_id` IS NULL")) === FALSE) + { + $results[] = 'Error '.$this->db2->mySQLlastErrNum.':'.$this->db2->mySQLlastErrText.' deleting orphaned records from mail_recipients'; + $noError = FALSE; + } + elseif ($res) + { + if ($res) $results[] = str_replace('--COUNT--', $res, LAN_MAILOUT_226); + } + + // Scan content table for anomalies, out of time records + if (($res = $this->db2->db_Select_gen("SELECT * FROM `#mail_content` + WHERE (`mail_content_status` >".MAIL_STATUS_FAILED.") AND (`mail_content_status` <=".MAIL_STATUS_MAX_ACTIVE.") + AND ((`mail_togo_count`=0) OR ( (`mail_last_date` != 0) AND (`mail_last_date` < ".time().")))")) === FALSE) + { + $results[] = 'Error '.$this->db2->mySQLlastErrNum.':'.$this->db2->mySQLlastErrText.' checking bad status in mail_content'; + $noError = FALSE; + } + else + { + $items = array(); // Store record number of any content record that needs to be changed + while ($row = $this->db2->db_Fetch(MYSQL_ASSOC)) + { + $items[] = $row['mail_source_id']; + if ($row['mail_source_id']) + { + if (FALSE == $this->cancelEmail($row['mail_source_id'])) + { + $results[] = 'Error cancelling email ref: '.$row['mail_source_id']; + } + else + { + $results[] = 'Email cancelled: '.$row['mail_source_id']; + } + } + } + if (count($items)) $results[] = str_replace(array('--COUNT--', '--RECORDS--'), array(count($items), implode(', ', $items)), LAN_MAILOUT_228); + } + + + //Finally - check for inconsistent recipient and content status records - basically verify counts + if (($res = $this->db2->db_Select_gen("SELECT COUNT(mr.`mail_status`) AS mr_count, mr.`mail_status`, + mc.`mail_source_id`, mc.`mail_togo_count`, mc.`mail_sent_count`, mc.`mail_fail_count`, mc.`mail_bounce_count`, mc.`mail_source_id` FROM `#mail_recipients` AS mr + LEFT JOIN `#mail_content` AS mc ON mr.`mail_detail_id` = mc.`mail_source_id` + WHERE mc.`mail_content_status` <= ".MAIL_STATUS_MAX_ACTIVE." + GROUP BY mr.`mail_status`, mc.`mail_source_id` ORDER BY mc.`mail_source_id` + ")) === FALSE) + { + $results[] = 'Error '.$this->db2->mySQLlastErrNum.':'.$this->db2->mySQLlastErrText.' assembling email counts'; + $noError = FALSE; + } + else + { + $lastMail = 0; // May get several rows per mail + $notLast = TRUE; // This forces one more loop, so we can clean up for last record read + $changeCount = 0; + $saveRow = array(); + while (($row = $this->db2->db_Fetch(MYSQL_ASSOC)) || $notLast) + { + if (($lastMail > 0 && $row === FALSE) || ($lastMail != $row['mail_source_id'])) + { // Change of mail ID here - handle any accumulated info + if ($lastMail > 0) + { // Need to verify counts for mail just read + $changes = array(); + foreach ($counters as $k => $v) + { + if ($saveRow[$k] != $v) + { + $changes[$k] = $v; // Assume the counters have got it right + } + } + if (count($changes)) + { + // *************** Update mail record here ********************* + $this->checkDB(1); + $this->db->db_Update('mail_content', array('data' => $changes, 'WHERE' => '`mail_source_id` = '.$lastMail, '_FIELDS' => $this->dbTypes['mail_content'])); + $line = "Count update for {$saveRow['mail_source_id']} - {$saveRow['mail_togo_count']}, {$saveRow['mail_sent_count']}, {$saveRow['mail_fail_count']}, {$saveRow['mail_bounce_count']} => "; + $line .= implode (', ', $counters); + $results[] = $line; + $changeCount++; + //echo $line.'
    '; + } + } + + // Now reset for current mail + $lastMail = $row['mail_source_id']; + $counters = array('mail_togo_count' => 0, 'mail_sent_count' => 0, 'mail_fail_count' => 0, 'mail_bounce_count' => 0); + $saveRow = $row; + } + if ($row === FALSE) $notLast = FALSE; + // We get one record for each mail_status value for a given email - use them to update counts + if ($notLast) + { + switch ($row['mail_status']) + { + case MAIL_STATUS_SENT : // Mail sent. Email handler happy, but may have bounced (or may be yet to bounce) + $counters['mail_sent_count'] += $row['mr_count']; + break; + case MAIL_STATUS_BOUNCED : + $counters['mail_sent_count'] += $row['mr_count']; // It was sent, so increment that counter + $counters['mail_bounce_count'] += $row['mr_count']; //...but bounced, so extra status + break; + case MAIL_STATUS_CANCELLED : // Cancelled email - treat as a failure + case MAIL_STATUS_FAILED : + $counters['mail_fail_count'] += $row['mr_count']; // Never sent at all + break; + case MAIL_STATUS_PARTIAL : // Shouldn't get this on individual emails - ignore if we do + break; + default : + if (($row['mail_status'] >= MAIL_STATUS_PENDING) && ($row['mail_status'] <= MAIL_STATUS_MAX_ACTIVE)) + { + $counters['mail_togo_count'] += $row['mr_count']; // Still in the queue + } + } + } + } + if ($changeCount) $results[] = str_replace('--COUNT--', $changeCount, LAN_MAILOUT_237); + } + + $this->e107->admin_log->log_event('MAIL_05', implode('[!br!]', $results), E_LOG_INFORMATIVE, ''); + return $noError; + } + + + + /** + * Get a list of all the available email templates, by name and variable name + * + * @param string $sel - currently (all|system|user) - selects template type + * + * @return array - key is the variable name of the template, value is the stored template name + */ + public function getEmailTemplateNames($sel = 'all') + { + $ret = array(); + foreach (array(e_THEME.'templates/email_template.php', THEME.'templates/email_template.php') as $templateFileName ) // Override file then defaults + if (is_readable($templateFileName)) + { + require($templateFileName); + $tVars = get_defined_vars(); + if (isset($tVars['GLOBALS'])) unset($tVars['GLOBALS']); + foreach ($tVars as $tKey => $tData) + { + if (is_array($tData) && isset($tData['template_name'])) + { + if (!isset($tData['template_type']) || ($tData['template_type'] == 'all') || ($tData['template_type'] == $sel)) + { + $ret[$tKey] = $tData['template_name']; + } + } + if ($tKey != 'ret') + { + unset($tVars[$tKey]); + } + } + } + return $ret; + } +} + + +?> diff --git a/e107_handlers/mailout_class.php b/e107_handlers/mailout_class.php index d3c1f5f9d..744885629 100644 --- a/e107_handlers/mailout_class.php +++ b/e107_handlers/mailout_class.php @@ -2,16 +2,16 @@ /* * 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) * * Mailout handling - selector for 'core' users * * $Source: /cvs_backup/e107_0.8/e107_handlers/mailout_class.php,v $ - * $Revision$ - * $Date$ - * $Author$ + * $Revision: 11315 $ + * $Date: 2010-02-10 18:18:01 +0000 (Wed, 10 Feb 2010) $ + * $Author: secretr $ * */ @@ -20,7 +20,7 @@ * * @package e107 * @subpackage e107_handlers - * @version $Id$; + * @version $Id: mailout_class.php 11315 2010-02-10 18:18:01Z secretr $; * * @todo last visit date needs XHTML calendar on display, and needs to accept varying input formats */ @@ -278,14 +278,20 @@ class core_mailout $var[0]['html'] = $admin->userClassesTotals('email_to', varset($selectVals['email_to'], '')); $var[1]['html'] = $frm->selectbox('user_search_name', $u_array, varset($selectVals['user_search_name'], ''),'',TRUE)." ".LAN_MAILOUT_47." ".$frm->text('user_search_value', varset($selectVals['user_search_value'], '')); - $var[2]['html'] = $admin->comparisonSelect('last_visit_match', varset($selectVals['last_visit_match'], ''))." ".$frm->text('last_visit_date', varset($selectVals['last_visit_date'], 0)); // FIXME: Should include date selector - $var[3]['html'] = $admin->ret_extended_field_list('extended_1_name', varset($selectVals['extended_1_name'], ''), TRUE).LAN_MAILOUT_48." ".$frm->text('extended_1_value',varset($selectVals['extended_1_value'], '')); - $var[4]['html'] = $admin->ret_extended_field_list('extended_2_name', varset($selectVals['extended_2_name'], ''), TRUE).LAN_MAILOUT_48." ".$frm->text('extended_2_value',varset($selectVals['extended_2_value'],'')); - + //$var[2]['html'] = $admin->comparisonSelect('last_visit_match', varset($selectVals['last_visit_match'], ''))." ".$frm->text('last_visit_date', varset($selectVals['last_visit_date'], 0)); + $var[2]['html'] = $admin->comparisonSelect('last_visit_match', varset($selectVals['last_visit_match'], ''))." ".$admin->makeCalendar('last_visit_date', varset($selectVals['last_visit_date'], 0)); $var[1]['caption'] = LAN_MAILOUT_46; // User Search Field. $var[2]['caption'] = LAN_MAILOUT_56; // User last visit - $var[3]['caption'] = LAN_MAILOUT_46; // Extended user field - $var[4]['caption'] = LAN_MAILOUT_46; // Extended user field + + $extFields = $admin->ret_extended_field_list('extended_1_name', varset($selectVals['extended_1_name'], ''), TRUE); + if ($extFields !== FALSE) // Only display next bit if UEFs defined + { + $var[3]['html'] = $extFields.LAN_MAILOUT_48." ".$frm->text('extended_1_value',varset($selectVals['extended_1_value'], '')); + $var[4]['html'] = $admin->ret_extended_field_list('extended_2_name', varset($selectVals['extended_2_name'], ''), TRUE).LAN_MAILOUT_48." ".$frm->text('extended_2_value',varset($selectVals['extended_2_value'],'')); + + $var[3]['caption'] = LAN_MAILOUT_46; // Extended user field + $var[4]['caption'] = LAN_MAILOUT_46; // Extended user field + } } else // Display existing values { @@ -320,21 +326,24 @@ class core_mailout $var[2]['html'] = $selectVals['last_visit_match'].' '.gmstrftime("%D-%M-%Y",$selectVals['last_visit_date']); //FIXME use e107 date function. $var[2]['caption'] = LAN_MAILOUT_56; // User last visit } - if (vartrue($selectVals['extended_1_name']) && vartrue($selectVals['extended_1_value'])) + $extFields = $admin->ret_extended_field_list('extended_1_name', varset($selectVals['extended_1_name'], ''), TRUE); + if ($extFields !== FALSE) { - $var[3]['html'] = $selectVals['extended_1_name'].' '.$selectVals['extended_1_value']; - $var[3]['caption'] = LAN_MAILOUT_46; // Extended user field - } - if (vartrue($selectVals['extended_2_name']) && vartrue($selectVals['extended_2_value'])) - { - $var[4]['html'] = $selectVals['extended_2_name'].' '.$selectVals['extended_2_value']; - $var[4]['caption'] = LAN_MAILOUT_46; // Extended user field + if (vartrue($selectVals['extended_1_name']) && vartrue($selectVals['extended_1_value'])) + { + $var[3]['html'] = $selectVals['extended_1_name'].' '.$selectVals['extended_1_value']; + $var[3]['caption'] = LAN_MAILOUT_46; // Extended user field + } + if (vartrue($selectVals['extended_2_name']) && vartrue($selectVals['extended_2_value'])) + { + $var[4]['html'] = $selectVals['extended_2_name'].' '.$selectVals['extended_2_value']; + $var[4]['caption'] = LAN_MAILOUT_46; // Extended user field + } } } return $var; - } } diff --git a/e107_handlers/notify_class.php b/e107_handlers/notify_class.php index 460eb05e9..b1330633c 100644 --- a/e107_handlers/notify_class.php +++ b/e107_handlers/notify_class.php @@ -1,304 +1,273 @@ -notify_prefs = e107::getConfig("notify")->getPref(); - - if(varset($this->notify_prefs['event'])) - { - foreach ($this->notify_prefs['event'] as $id => $status) - { - if ($status['class'] != 255) - { - $e_event->register($id, 'notify_'.$id); - } - } - } - - include_lan(e_LANGUAGEDIR.e_LANGUAGE.'/lan_notify.php'); - } - - - - /** - * Send an email notification following an event. - * - * For up to a (hard-coded) number of recipients, the mail is sent immediately. - * Otherwise its added to the queue - * - * @param string $id - identifies event actions - * @param string $subject - subject for email - * @param string $message - email message body - * @return none - * - * @todo handle 'everyone except' clauses (email address filter done) - * @todo set up pref to not notify originator of event which caused notify (see $blockOriginator) - */ - function send($id, $subject, $message) - { - $e107 = e107::getInstance(); - - $subject = $e107->tp->toEmail(SITENAME.': '.$subject); - $message = $e107->tp->toEmail($message); - $emailFilter = ''; - $notifyTarget = $this->notify_prefs['event'][$id]['class']; - if ($notifyTarget == '-email') - { - $emailFilter = $this->notify_prefs['event'][$id]['email']; - } - $blockOriginator = FALSE; // TODO: set this using a pref - if (is_numeric($this -> notify_prefs['event'][$id]['class'])) - { - switch ($notifyTarget) - { - case e_UC_MAINADMIN : - $qry = "`user_admin` = 1 AND `user_perms` = '0' AND `user_ban` = 0"; - break; - case e_UC_ADMIN : - $qry = "`user_admin` = 1 AND `user_ban` = 0"; - break; - case e_UC_MEMBER : - $qry = "`user_ban` = 0"; - break; - default : - $qry = "user_ban = 0 AND user_class REGEXP '(^|,)(".$this->notify_prefs['event'][$id]['class'].")(,|$)'"; - break; - } - $qry = 'SELECT user_id,user_name,user_email FROM `#user` WHERE '.$qry; - if ($blockOriginator) - { - $qry .= ' AND `user_id` != '.USERID; - } - if (FALSE !== ($count = $e107->sql->db_Select_gen($qry))) - { - if ($count <= 5) - { // Arbitrary number below which we send emails immediately - e107_require_once(e_HANDLER.'mail.php'); - while ($email = $e107->sql->db_Fetch()) - { - if ($email['user_email'] != $emailFilter) - { - sendemail($email['user_email'], $subject, $message, $email['user_name']); - } - } - } - else - { // Otherwise add to mailout queue - require_once(e_HANDLER.'mail_manager_class.php'); - $mailer = new e107MailManager; - - - // Start by creating the mail body - $mailData = array( - 'mail_content_status' => MAIL_STATUS_TEMP, - '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 - ); - $result = $mailer->saveEmail($mailData, TRUE); - if (is_numeric($result)) - { - $mailMainID = $mailData['mail_source_id'] = $result; - } - else - { - // TODO: Handle error - return; // Probably nothing else we can do - } - $mailer->mailInitCounters($mailMainID); // Initialise counters for emails added - - // Now add email addresses to the list - while ($row = $e107->sql->db_Fetch(MYSQL_ASSOC)) - { - if ($row['user_email'] != $emailFilter) - { - $uTarget = array('mail_recipient_id' => $row['user_id'], - 'mail_recipient_name' => $row['user_name'], // Should this use realname? - 'mail_recipient_email' => $row['user_email'] - ); - $result = $mailer->mailAddNoDup($mailMainID, $uTarget, MAIL_STATUS_TEMP); - } - } - $mailer->mailUpdateCounters($mailMainID); // Update the counters - $counters = $mailer->mailRetrieveCounters($mailMainID); // Retrieve the counters - if ($counters['add'] == 0) - { - $mailer->deleteEmail($mailMainID); // Probably a fault, but precautionary - delete email - } - else - { - $mailer->activateEmail($mailMainID, FALSE); // Actually mark the email for sending - } - } - $e107->admin_log->e_log_event(10,-1,'NOTIFY',$subject,$message,FALSE,LOG_TO_ROLLING); - } - } - elseif ($notifyTarget == 'email') - { // Single email address - that can always go immediately - if (!$blockOriginator || ($this->notify_prefs['event'][$id]['email'] != USEREMAIL)) - { - e107_require_once(e_HANDLER.'mail.php'); - sendemail($this->notify_prefs['event'][$id]['email'], $subject, $message); - } - } - } -} - - - - -//DEPRECATED, BC, call the method only when needed, $e107->notify caught by __get() -global $nt; -$nt = e107::getNotify(); //TODO - find & replace $nt, $e107->notify - - -function notify_usersup($data) -{ - global $nt; - foreach ($data as $key => $value) - { - if($key != "password1" && $key != "password2" && $key != "email_confirm" && $key != "register") - { - if(is_array($value)) // show user-extended values. - { - foreach($value as $k => $v) - { - $message .= str_replace("user_","",$k).': '.$v.'
    '; - } - } - else - { - $message .= $key.': '.$value.'
    '; - } - } - } - $nt->send('usersup', NT_LAN_US_1, $message); -} - -function notify_userveri($data) -{ - global $nt, $e107; - $msgtext = NT_LAN_UV_2.$data['user_id']."\n"; - $msgtext .= NT_LAN_UV_3.$data['user_loginname']."\n"; - $msgtext .= NT_LAN_UV_4.e107::getIPHandler()->getIP(FALSE); - $nt->send('userveri', NT_LAN_UV_1, $msgtext); -} - -function notify_login($data) -{ - global $nt; - foreach ($data as $key => $value) { - $message .= $key.': '.$value.'
    '; - } - $nt->send('login', NT_LAN_LI_1, $message); -} - -function notify_logout() -{ - global $nt; - $nt->send('logout', NT_LAN_LO_1, USERID.'. '.USERNAME.' '.NT_LAN_LO_2); -} - -function notify_flood($data) -{ - global $nt; - $nt->send('flood', NT_LAN_FL_1, NT_LAN_FL_2.': '.$data); -} - -function notify_subnews($data) -{ - global $nt,$tp; - foreach ($data as $key => $value) { - $message .= $key.': '.$value.'
    '; - } - $nt->send('subnews', NT_LAN_SN_1, $message); -} - -function notify_newspost($data) -{ - $message = ''.$data['news_title'].''; - if (vartrue($data['news_summary'])) $message .= '

    '.$data['news_summary']; - if (vartrue($data['news_body'])) $message .= '

    '.$data['news_body']; - if (vartrue($data['news_extended'])) $message.= '

    '.$data['news_extended']; - e107::getNotify()->send('newspost', $data['news_title'], e107::getParser()->text_truncate(e107::getParser()->toDB($message), 400, '...')); -} - -function notify_newsupd($data) -{ - $message = ''.$data['news_title'].''; - if (vartrue($data['news_summary'])) $message .= '

    '.$data['news_summary']; - if (vartrue($data['news_body'])) $message .= '

    '.$data['news_body']; - if (vartrue($data['news_extended'])) $message.= '

    '.$data['news_extended']; - e107::getNotify()->send('newsupd', NT_LAN_NU_1.': '.$data['news_title'], e107::getParser()->text_truncate(e107::getParser()->toDB($message), 400, '...')); -} - -function notify_newsdel($data) -{ - global $nt; - $nt->send('newsdel', NT_LAN_ND_1, NT_LAN_ND_2.': '.$data); -} - - -function notify_maildone($data) -{ - $message = ''.$data['mail_subject'].'

    '.$data['mail_body']; - e107::getNotify()->send('maildone', NT_LAN_ML_1.': '.$data['mail_subject'], $message); -} - - -function notify_fileupload($data) -{ - global $nt; - $message = ''.$data['upload_name'].'

    '.$data['upload_description'].'

    '.$data['upload_size'].'

    '.$data['upload_user']; - $nt->send('fileupload', $data['upload_name'], $message); -} - - -if (isset($nt->notify_prefs['plugins'])) -{ - foreach ($nt->notify_prefs['plugins'] as $plugin_id => $plugin_settings) - { - if(is_readable(e_PLUGIN.$plugin_id.'/e_notify.php')) - { - require_once(e_PLUGIN.$plugin_id.'/e_notify.php'); - } - } -} - +notify_prefs = e107::getConfig('notify')->getPref(); + + if(varset($this->notify_prefs['event'])) + { + foreach ($this->notify_prefs['event'] as $id => $status) + { + if ($status['class'] != 255) + { + $e_event->register($id, 'notify_'.$id); + } + } + } + + include_lan(e_LANGUAGEDIR.e_LANGUAGE.'/lan_notify.php'); + } + + + + /** + * Send an email notification following an event. + * + * The email is sent via a common interface, which will send immediately for small numbers of recipients, and queue for larger. + * + * @param string $id - identifies event actions + * @param string $subject - subject for email + * @param string $message - email message body + * @return none + * + * @todo handle 'everyone except' clauses (email address filter done) + * @todo set up pref to not notify originator of event which caused notify (see $blockOriginator) + */ + function send($id, $subject, $message) + { + $e107 = e107::getInstance(); + + $subject = $e107->tp->toEmail(SITENAME.': '.$subject); + $message = $e107->tp->toEmail($message); + $emailFilter = ''; + $notifyTarget = $this->notify_prefs['event'][$id]['class']; + if ($notifyTarget == '-email') + { + $emailFilter = $this->notify_prefs['event'][$id]['email']; + } + $blockOriginator = FALSE; // TODO: set this using a pref + $recipients = array(); + + if ($notifyTarget == 'email') + { // Single email address - that can always go immediately + if (!$blockOriginator || ($this->notify_prefs['event'][$id]['email'] != USEREMAIL)) + { + $recipients[] = array( + 'mail_recipient_email' => $this->notify_prefs['event'][$id]['email'] + ); + } + } + elseif (is_numeric($this->notify_prefs['event'][$id]['class'])) + { + switch ($notifyTarget) + { + case e_UC_MAINADMIN : + $qry = "`user_admin` = 1 AND `user_perms` = '0' AND `user_ban` = 0"; + break; + case e_UC_ADMIN : + $qry = "`user_admin` = 1 AND `user_ban` = 0"; + break; + case e_UC_MEMBER : + $qry = "`user_ban` = 0"; + break; + default : + $qry = "user_ban = 0 AND user_class REGEXP '(^|,)(".$this->notify_prefs['event'][$id]['class'].")(,|$)'"; + break; + } + $qry = 'SELECT user_id,user_name,user_email FROM `#user` WHERE '.$qry; + if ($blockOriginator) + { + $qry .= ' AND `user_id` != '.USERID; + } + if (FALSE !== ($count = $e107->sql->db_Select_gen($qry))) + { + // Now add email addresses to the list + while ($row = $e107->sql->db_Fetch(MYSQL_ASSOC)) + { + if ($row['user_email'] != $emailFilter) + { + $recipients[] = array('mail_recipient_id' => $row['user_id'], + 'mail_recipient_name' => $row['user_name'], // Should this use realname? + 'mail_recipient_email' => $row['user_email'] + ); + } + } + } + } + + if (count($recipients)) + { + require_once(e_HANDLER.'mail_manager_class.php'); + $mailer = new e107MailManager; + + // Create the mail body + $mailData = array( + 'mail_content_status' => MAIL_STATUS_TEMP, + 'mail_create_app' => 'notify', + 'mail_title' => 'NOTIFY', + 'mail_subject' => $subject, + 'mail_sender_email' => e107::getPref('siteadminemail'), + 'mail_sender_name' => e107::getPref('siteadmin'), + 'mail_send_style' => 'textonly', + 'mail_notify_complete' => 0, // NEVER notify when this email sent!!!!! + 'mail_body' => $message + ); + $result = $mailer->sendEmails('NOTIFY_TEMPLATE', $mailData, $recipients); + $e107->admin_log->e_log_event(10,-1,'NOTIFY',$subject,$message,FALSE,LOG_TO_ROLLING); + } + } +} + + + + +//DEPRECATED, BC, call the method only when needed, $e107->notify caught by __get() +global $nt; +$nt = e107::getNotify(); //TODO - find & replace $nt, $e107->notify + + +function notify_usersup($data) +{ + global $nt; + foreach ($data as $key => $value) + { + if($key != "password1" && $key != "password2" && $key != "email_confirm" && $key != "register") + { + if(is_array($value)) // show user-extended values. + { + foreach($value as $k => $v) + { + $message .= str_replace("user_","",$k).': '.$v.'
    '; + } + } + else + { + $message .= $key.': '.$value.'
    '; + } + } + } + $nt->send('usersup', NT_LAN_US_1, $message); +} + +function notify_userveri($data) +{ + global $nt, $e107; + $msgtext = NT_LAN_UV_2.$data['user_id']."\n"; + $msgtext .= NT_LAN_UV_3.$data['user_loginname']."\n"; + $msgtext .= NT_LAN_UV_4.e107::getIPHandler()->getIP(FALSE); + $nt->send('userveri', NT_LAN_UV_1, $msgtext); +} + +function notify_login($data) +{ + global $nt; + foreach ($data as $key => $value) { + $message .= $key.': '.$value.'
    '; + } + $nt->send('login', NT_LAN_LI_1, $message); +} + +function notify_logout() +{ + global $nt; + $nt->send('logout', NT_LAN_LO_1, USERID.'. '.USERNAME.' '.NT_LAN_LO_2); +} + +function notify_flood($data) +{ + global $nt; + $nt->send('flood', NT_LAN_FL_1, NT_LAN_FL_2.': '.$data); +} + +function notify_subnews($data) +{ + global $nt,$tp; + foreach ($data as $key => $value) { + $message .= $key.': '.$value.'
    '; + } + $nt->send('subnews', NT_LAN_SN_1, $message); +} + +function notify_newspost($data) +{ + $message = ''.$data['news_title'].''; + if (vartrue($data['news_summary'])) $message .= '

    '.$data['news_summary']; + if (vartrue($data['news_body'])) $message .= '

    '.$data['news_body']; + if (vartrue($data['news_extended'])) $message.= '

    '.$data['news_extended']; + e107::getNotify()->send('newspost', $data['news_title'], e107::getParser()->text_truncate(e107::getParser()->toDB($message), 400, '...')); +} + +function notify_newsupd($data) +{ + $message = ''.$data['news_title'].''; + if (vartrue($data['news_summary'])) $message .= '

    '.$data['news_summary']; + if (vartrue($data['news_body'])) $message .= '

    '.$data['news_body']; + if (vartrue($data['news_extended'])) $message.= '

    '.$data['news_extended']; + e107::getNotify()->send('newsupd', NT_LAN_NU_1.': '.$data['news_title'], e107::getParser()->text_truncate(e107::getParser()->toDB($message), 400, '...')); +} + +function notify_newsdel($data) +{ + global $nt; + $nt->send('newsdel', NT_LAN_ND_1, NT_LAN_ND_2.': '.$data); +} + + +function notify_maildone($data) +{ + $message = ''.$data['mail_subject'].'

    '.$data['mail_body']; + e107::getNotify()->send('maildone', NT_LAN_ML_1.': '.$data['mail_subject'], $message); +} + + +function notify_fileupload($data) +{ + global $nt; + $message = ''.$data['upload_name'].'

    '.$data['upload_description'].'

    '.$data['upload_size'].'

    '.$data['upload_user']; + $nt->send('fileupload', $data['upload_name'], $message); +} + + +if (isset($nt->notify_prefs['plugins'])) +{ + foreach ($nt->notify_prefs['plugins'] as $plugin_id => $plugin_settings) + { + if(is_readable(e_PLUGIN.$plugin_id.'/e_notify.php')) + { + require_once(e_PLUGIN.$plugin_id.'/e_notify.php'); + } + } +} + ?> \ No newline at end of file diff --git a/e107_handlers/phpmailer/class.phpmailer.php b/e107_handlers/phpmailer/class.phpmailer.php index e22f7f3b7..7f1071dee 100644 --- a/e107_handlers/phpmailer/class.phpmailer.php +++ b/e107_handlers/phpmailer/class.phpmailer.php @@ -2,15 +2,15 @@ /*~ class.phpmailer.php .---------------------------------------------------------------------------. | Software: PHPMailer - PHP email class | -| Version: 5.0.2 | -| Contact: via sourceforge.net support pages (also www.codeworxtech.com) | -| Info: http://phpmailer.sourceforge.net | -| Support: http://sourceforge.net/projects/phpmailer/ | +| Version: 5.2.2 | +| Site: https://code.google.com/a/apache-extras.org/p/phpmailer/ | | ------------------------------------------------------------------------- | -| Admin: Andy Prevost (project admininistrator) | +| Admin: Jim Jagielski (project admininistrator) | | Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net | | : Marcus Bointon (coolbru) coolbru@users.sourceforge.net | +| : Jim Jagielski (jimjag) jimjag@gmail.com | | Founder: Brent R. Matzelle (original founder) | +| Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved. | | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. | | Copyright (c) 2001-2003, Brent R. Matzelle | | ------------------------------------------------------------------------- | @@ -19,27 +19,27 @@ | This program is distributed in the hope that it will be useful - WITHOUT | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | | FITNESS FOR A PARTICULAR PURPOSE. | -| ------------------------------------------------------------------------- | -| We offer a number of paid services (www.codeworxtech.com): | -| - Web Hosting on highly optimized fast and secure servers | -| - Technology Consulting | -| - Oursourcing (highly qualified programmers and graphic designers) | '---------------------------------------------------------------------------' */ /** - * PHPMailer - PHP email transport class + * PHPMailer - PHP email creation and transport class * NOTE: Requires PHP version 5 or later * @package PHPMailer * @author Andy Prevost * @author Marcus Bointon + * @author Jim Jagielski + * @copyright 2010 - 2012 Jim Jagielski * @copyright 2004 - 2009 Andy Prevost - * @version $Id$ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License */ if (version_compare(PHP_VERSION, '5.0.0', '<') ) exit("Sorry, this version of PHPMailer will only run on PHP version 5 or greater!\n"); +/** + * PHP email creation and transport class + * @package PHPMailer + */ class PHPMailer { ///////////////////////////////////////////////// @@ -96,6 +96,13 @@ class PHPMailer { */ public $Sender = ''; + /** + * Sets the Return-Path of the message. If empty, it will + * be set to either From or Sender. + * @var string + */ + public $ReturnPath = ''; + /** * Sets the Subject of the message. * @var string @@ -118,6 +125,27 @@ class PHPMailer { */ public $AltBody = ''; + /** + * Stores the complete compiled MIME message body. + * @var string + * @access protected + */ + protected $MIMEBody = ''; + + /** + * Stores the complete compiled MIME message headers. + * @var string + * @access protected + */ + protected $MIMEHeader = ''; + + /** + * Stores the extra header list which CreateHeader() doesn't fold in + * @var string + * @access protected + */ + protected $mailHeader = ''; + /** * Sets word wrapping on the body of the message to a given number of * characters. @@ -137,6 +165,13 @@ class PHPMailer { */ public $Sendmail = '/usr/sbin/sendmail'; + /** + * Determine if mail() uses a fully sendmail compatible MTA that + * supports sendmail's "-oi -f" options + * @var boolean + */ + public $UseSendmailOptions = true; + /** * Path to PHPMailer plugins. Useful if the SMTP class * is in a different directory than the PHP include path. @@ -165,12 +200,21 @@ class PHPMailer { */ public $MessageID = ''; + /** + * Sets the message Date to be used in the Date header. + * If empty, the current date will be added. + * @var string + */ + public $MessageDate = ''; + ///////////////////////////////////////////////// // PROPERTIES FOR SMTP ///////////////////////////////////////////////// /** - * Sets the SMTP hosts. All hosts must be separated by a + * Sets the SMTP hosts. + * + * All hosts must be separated by a * semicolon. You can also specify a different port * for each host by using this format: [hostname:port] * (e.g. "smtp1.example.com:25;smtp2.example.com"). @@ -192,8 +236,7 @@ class PHPMailer { public $Helo = ''; /** - * Sets connection prefix. - * Options are "", "ssl" or "tls" + * Sets connection prefix. Options are "", "ssl" or "tls" * @var string */ public $SMTPSecure = ''; @@ -216,6 +259,24 @@ class PHPMailer { */ public $Password = ''; + /** + * Sets SMTP auth type. Options are LOGIN | PLAIN | NTLM (default LOGIN) + * @var string + */ + public $AuthType = ''; + + /** + * Sets SMTP realm. + * @var string + */ + public $Realm = ''; + + /** + * Sets SMTP workstation. + * @var string + */ + public $Workstation = ''; + /** * Sets the SMTP server timeout in seconds. * This function will not work with the win32 version. @@ -229,6 +290,13 @@ class PHPMailer { */ public $SMTPDebug = false; + /** + * Sets the function/method to use for debugging output. + * Right now we only honor "echo" or "error_log" + * @var string + */ + public $Debugoutput = "echo"; + /** * Prevents the SMTP connection from being closed after each mail * sending. If this is set to true then to close the connection @@ -244,51 +312,224 @@ class PHPMailer { */ public $SingleTo = false; - /** - * Provides the ability to change the line ending + /** + * If SingleTo is true, this provides the array to hold the email addresses + * @var bool + */ + public $SingleToArray = array(); + + /** + * Provides the ability to change the generic line ending + * NOTE: The default remains '\n'. We force CRLF where we KNOW + * it must be used via self::CRLF * @var string */ public $LE = "\n"; + /** + * Used with DKIM Signing + * required parameter if DKIM is enabled + * + * domain selector example domainkey + * @var string + */ + public $DKIM_selector = ''; + + /** + * Used with DKIM Signing + * required if DKIM is enabled, in format of email address 'you@yourdomain.com' typically used as the source of the email + * @var string + */ + public $DKIM_identity = ''; + + /** + * Used with DKIM Signing + * optional parameter if your private key requires a passphras + * @var string + */ + public $DKIM_passphrase = ''; + + /** + * Used with DKIM Singing + * required if DKIM is enabled, in format of email address 'domain.com' + * @var string + */ + public $DKIM_domain = ''; + + /** + * Used with DKIM Signing + * required if DKIM is enabled, path to private key file + * @var string + */ + public $DKIM_private = ''; + + /** + * Callback Action function name. + * The function that handles the result of the send email action. + * It is called out by Send() for each email sent. + * + * Value can be: + * - 'function_name' for function names + * - 'Class::Method' for static method calls + * - array($object, 'Method') for calling methods on $object + * See http://php.net/is_callable manual page for more details. + * + * Parameters: + * bool $result result of the send action + * string $to email address of the recipient + * string $cc cc email addresses + * string $bcc bcc email addresses + * string $subject the subject + * string $body the email body + * string $from email address of sender + * @var string + */ + public $action_function = ''; //'callbackAction'; + /** * Sets the PHPMailer Version number * @var string */ - public $Version = '5.0.2'; + public $Version = '5.2.2'; + + /** + * What to use in the X-Mailer header + * @var string NULL for default, whitespace for None, or actual string to use + */ + public $XMailer = ''; ///////////////////////////////////////////////// // PROPERTIES, PRIVATE AND PROTECTED ///////////////////////////////////////////////// - private $smtp = NULL; - private $to = array(); - private $cc = array(); - private $bcc = array(); - private $ReplyTo = array(); - private $all_recipients = array(); - private $attachment = array(); - private $CustomHeader = array(); - private $message_type = ''; - private $boundary = array(); - protected $language = array(); - private $error_count = 0; - private $sign_cert_file = ""; - private $sign_key_file = ""; - private $sign_key_pass = ""; - private $exceptions = false; + /** + * @var SMTP An instance of the SMTP sender class + * @access protected + */ + protected $smtp = null; + /** + * @var array An array of 'to' addresses + * @access protected + */ + protected $to = array(); + /** + * @var array An array of 'cc' addresses + * @access protected + */ + protected $cc = array(); + /** + * @var array An array of 'bcc' addresses + * @access protected + */ + protected $bcc = array(); + /** + * @var array An array of reply-to name and address + * @access protected + */ + protected $ReplyTo = array(); + /** + * @var array An array of all kinds of addresses: to, cc, bcc, replyto + * @access protected + */ + protected $all_recipients = array(); + /** + * @var array An array of attachments + * @access protected + */ + protected $attachment = array(); + /** + * @var array An array of custom headers + * @access protected + */ + protected $CustomHeader = array(); + /** + * @var string The message's MIME type + * @access protected + */ + protected $message_type = ''; + /** + * @var array An array of MIME boundary strings + * @access protected + */ + protected $boundary = array(); + /** + * @var array An array of available languages + * @access protected + */ + protected $language = array(); + /** + * @var integer The number of errors encountered + * @access protected + */ + protected $error_count = 0; + /** + * @var string The filename of a DKIM certificate file + * @access protected + */ + protected $sign_cert_file = ''; + /** + * @var string The filename of a DKIM key file + * @access protected + */ + protected $sign_key_file = ''; + /** + * @var string The password of a DKIM key + * @access protected + */ + protected $sign_key_pass = ''; + /** + * @var boolean Whether to throw exceptions for errors + * @access protected + */ + protected $exceptions = false; ///////////////////////////////////////////////// // CONSTANTS ///////////////////////////////////////////////// - const STOP_MESSAGE = 0; // message only, continue processing + const STOP_MESSAGE = 0; // message only, continue processing const STOP_CONTINUE = 1; // message?, likely ok to continue processing const STOP_CRITICAL = 2; // message, plus full stop, critical error reached - + const CRLF = "\r\n"; // SMTP RFC specified EOL + ///////////////////////////////////////////////// // METHODS, VARIABLES ///////////////////////////////////////////////// + /** + * Calls actual mail() function, but in a safe_mode aware fashion + * Also, unless sendmail_path points to sendmail (or something that + * claims to be sendmail), don't pass params (not a perfect fix, + * but it will do) + * @param string $to To + * @param string $subject Subject + * @param string $body Message Body + * @param string $header Additional Header(s) + * @param string $params Params + * @access private + * @return bool + */ + private function mail_passthru($to, $subject, $body, $header, $params) { + if ( ini_get('safe_mode') || !($this->UseSendmailOptions) ) { + $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($subject)), $body, $header); + } else { + $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($subject)), $body, $header, $params); + } + return $rt; + } + + /** + * Outputs debugging info via user-defined method + * @param string $str + */ + private function edebug($str) { + if ($this->Debugoutput == "error_log") { + error_log($str); + } else { + echo $str; + } + } + /** * Constructor * @param boolean $exceptions Should we throw external exceptions? @@ -313,6 +554,7 @@ class PHPMailer { /** * Sets Mailer to send message using SMTP. * @return void + * @deprecated */ public function IsSMTP() { $this->Mailer = 'smtp'; @@ -321,6 +563,7 @@ class PHPMailer { /** * Sets Mailer to send message using PHP mail() function. * @return void + * @deprecated */ public function IsMail() { $this->Mailer = 'mail'; @@ -329,6 +572,7 @@ class PHPMailer { /** * Sets Mailer to send message using the $Sendmail program. * @return void + * @deprecated */ public function IsSendmail() { if (!stristr(ini_get('sendmail_path'), 'sendmail')) { @@ -340,6 +584,7 @@ class PHPMailer { /** * Sets Mailer to send message using the qmail MTA. * @return void + * @deprecated */ public function IsQmail() { if (stristr(ini_get('sendmail_path'), 'qmail')) { @@ -391,7 +636,7 @@ class PHPMailer { * @return boolean */ public function AddReplyTo($address, $name = '') { - return $this->AddAnAddress('ReplyTo', $address, $name); + return $this->AddAnAddress('Reply-To', $address, $name); } /** @@ -400,82 +645,109 @@ class PHPMailer { * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo' * @param string $address The email address to send to * @param string $name + * @throws phpmailerException * @return boolean true on success, false if address already used or invalid in some way - * @access private + * @access protected */ - private function AddAnAddress($kind, $address, $name = '') { - if (!preg_match('/^(to|cc|bcc|ReplyTo)$/', $kind)) { - if (defined('MAIL_DEBUG')) echo 'Invalid recipient array: ' . kind; + protected function AddAnAddress($kind, $address, $name = '') { + if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) { + $this->SetError($this->Lang('Invalid recipient array').': '.$kind); + if ($this->exceptions) { + throw new phpmailerException('Invalid recipient array: ' . $kind); + } + if ($this->SMTPDebug) { + $this->edebug($this->Lang('Invalid recipient array').': '.$kind); + } return false; } $address = trim($address); $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim - if (!self::ValidateAddress($address)) { + if (!$this->ValidateAddress($address)) { $this->SetError($this->Lang('invalid_address').': '. $address); if ($this->exceptions) { throw new phpmailerException($this->Lang('invalid_address').': '.$address); } - if (defined('MAIL_DEBUG')) echo $this->Lang('invalid_address').': '.$address; + if ($this->SMTPDebug) { + $this->edebug($this->Lang('invalid_address').': '.$address); + } return false; } - if ($kind != 'ReplyTo') { - if (!isset($this->all_recipients[strtolower($address)])) { + if ($kind != 'Reply-To') { + if (!isset($this->all_recipients[strtolower($address)])) { array_push($this->$kind, array($address, $name)); $this->all_recipients[strtolower($address)] = true; - return true; + return true; } - } else { - if (!array_key_exists(strtolower($address), $this->ReplyTo)) { + } else { + if (!array_key_exists(strtolower($address), $this->ReplyTo)) { $this->ReplyTo[strtolower($address)] = array($address, $name); - return true; + return true; } } - return false; - } + return false; +} /** * Set the From and FromName properties * @param string $address * @param string $name + * @param int $auto Also set Reply-To and Sender + * @throws phpmailerException * @return boolean */ - public function SetFrom($address, $name = '') { + public function SetFrom($address, $name = '', $auto = 1) { $address = trim($address); $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim - if (!self::ValidateAddress($address)) { + if (!$this->ValidateAddress($address)) { $this->SetError($this->Lang('invalid_address').': '. $address); if ($this->exceptions) { throw new phpmailerException($this->Lang('invalid_address').': '.$address); } - if (defined('MAIL_DEBUG')) echo $this->Lang('invalid_address').': '.$address; + if ($this->SMTPDebug) { + $this->edebug($this->Lang('invalid_address').': '.$address); + } return false; } - $this->From = $address; - $this->FromName = $name; - return true; + $this->From = $address; + $this->FromName = $name; + if ($auto) { + if (empty($this->ReplyTo)) { + $this->AddAnAddress('Reply-To', $address, $name); + } + if (empty($this->Sender)) { + $this->Sender = $address; + } + } + return true; } /** * Check that a string looks roughly like an email address should - * Static so it can be used without instantiation - * Tries to use PHP built-in validator in the filter extension (from PHP 5.2), falls back to a reasonably competent regex validator - * Conforms approximately to RFC2822 - * @link http://www.hexillion.com/samples/#Regex Original pattern found here + * Static so it can be used without instantiation, public so people can overload + * Conforms to RFC5322: Uses *correct* regex on which FILTER_VALIDATE_EMAIL is + * based; So why not use FILTER_VALIDATE_EMAIL? Because it was broken to + * not allow a@b type valid addresses :( + * Some Versions of PHP break on the regex though, likely due to PCRE, so use + * the older validation method for those users. (http://php.net/manual/en/pcre.installation.php) + * @link http://squiloople.com/2009/12/20/email-address-validation/ + * @copyright regex Copyright Michael Rushton 2009-10 | http://squiloople.com/ | Feel free to use and redistribute this code. But please keep this copyright notice. * @param string $address The email address to check * @return boolean * @static * @access public */ public static function ValidateAddress($address) { - if (function_exists('filter_var')) { //Introduced in PHP 5.2 - if(filter_var($address, FILTER_VALIDATE_EMAIL) === FALSE) { - return false; - } else { - return true; - } + if ((defined('PCRE_VERSION')) && (version_compare(PCRE_VERSION, '8.0') >= 0)) { + return preg_match('/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)((?>(?>(?>((?>(?>(?>\x0D\x0A)?[ ])+|(?>[ ]*\x0D\x0A)?[ ]+)?)(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}|(?!(?:.*[a-f0-9][:\]]){7,})((?6)(?>:(?6)){0,5})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:|(?!(?:.*[a-f0-9]:){5,})(?8)?::(?>((?6)(?>:(?6)){0,3}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', $address); + } elseif (function_exists('filter_var')) { //Introduced in PHP 5.2 + if(filter_var($address, FILTER_VALIDATE_EMAIL) === FALSE) { + return false; + } else { + return true; + } } else { - return preg_match('/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!\.)){0,61}[a-zA-Z0-9_-]?\.)+[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!$)){0,61}[a-zA-Z0-9_]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/', $address); - } + return preg_match('/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!\.)){0,61}[a-zA-Z0-9_-]?\.)+[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!$)){0,61}[a-zA-Z0-9_]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/', $address); + } } ///////////////////////////////////////////////// @@ -486,10 +758,31 @@ class PHPMailer { * Creates message and assigns Mailer. If the message is * not sent successfully then it returns false. Use the ErrorInfo * variable to view description of the error. + * @throws phpmailerException * @return bool */ public function Send() { try { + if(!$this->PreSend()) return false; + return $this->PostSend(); + } catch (phpmailerException $e) { + $this->mailHeader = ''; + $this->SetError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + return false; + } + } + + /** + * Prep mail by constructing all message entities + * @throws phpmailerException + * @return bool + */ + public function PreSend() { + try { + $this->mailHeader = ""; if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL); } @@ -501,55 +794,119 @@ class PHPMailer { $this->error_count = 0; // reset errors $this->SetMessageType(); - $header = $this->CreateHeader(); - $body = $this->CreateBody(); - + //Refuse to send an empty message if (empty($this->Body)) { throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL); } - // Choose the mailer and send through it - switch($this->Mailer) { - case 'sendmail': - return $this->SendmailSend($header, $body); - case 'smtp': - return $this->SmtpSend($header, $body); - case 'mail': - default: - return $this->MailSend($header, $body); + $this->MIMEHeader = $this->CreateHeader(); + $this->MIMEBody = $this->CreateBody(); + + // To capture the complete message when using mail(), create + // an extra header list which CreateHeader() doesn't fold in + if ($this->Mailer == 'mail') { + if (count($this->to) > 0) { + $this->mailHeader .= $this->AddrAppend("To", $this->to); + } else { + $this->mailHeader .= $this->HeaderLine("To", "undisclosed-recipients:;"); + } + $this->mailHeader .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader(trim($this->Subject)))); + // if(count($this->cc) > 0) { + // $this->mailHeader .= $this->AddrAppend("Cc", $this->cc); + // } } + // digitally sign with DKIM if enabled + if (!empty($this->DKIM_domain) && !empty($this->DKIM_private) && !empty($this->DKIM_selector) && !empty($this->DKIM_domain) && file_exists($this->DKIM_private)) { + $header_dkim = $this->DKIM_Add($this->MIMEHeader, $this->EncodeHeader($this->SecureHeader($this->Subject)), $this->MIMEBody); + $this->MIMEHeader = str_replace("\r\n", "\n", $header_dkim) . $this->MIMEHeader; + } + + return true; + } catch (phpmailerException $e) { $this->SetError($e->getMessage()); if ($this->exceptions) { throw $e; } - if (defined('MAIL_DEBUG')) echo $e->getMessage()."\n"; return false; } } + /** + * Actual Email transport function + * Send the email via the selected mechanism + * @throws phpmailerException + * @return bool + */ + public function PostSend() { + try { + // Choose the mailer and send through it + switch($this->Mailer) { + case 'sendmail': + return $this->SendmailSend($this->MIMEHeader, $this->MIMEBody); + case 'smtp': + return $this->SmtpSend($this->MIMEHeader, $this->MIMEBody); + case 'mail': + return $this->MailSend($this->MIMEHeader, $this->MIMEBody); + default: + return $this->MailSend($this->MIMEHeader, $this->MIMEBody); + } + } catch (phpmailerException $e) { + $this->SetError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + if ($this->SMTPDebug) { + $this->edebug($e->getMessage()."\n"); + } + } + return false; + } + /** * Sends mail using the $Sendmail program. * @param string $header The message headers * @param string $body The message body + * @throws phpmailerException * @access protected * @return bool */ protected function SendmailSend($header, $body) { if ($this->Sender != '') { - $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); + $sendmail = sprintf("%s -oi -f%s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); } else { $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); } - if(!@$mail = popen($sendmail, 'w')) { - throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); - } - fputs($mail, $header); - fputs($mail, $body); - $result = pclose($mail); - if($result != 0) { - throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + if ($this->SingleTo === true) { + foreach ($this->SingleToArray as $val) { + if(!@$mail = popen($sendmail, 'w')) { + throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + fputs($mail, "To: " . $val . "\n"); + fputs($mail, $header); + fputs($mail, $body); + $result = pclose($mail); + // implement call back function if it exists + $isSent = ($result == 0) ? 1 : 0; + $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); + if($result != 0) { + throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + } else { + if(!@$mail = popen($sendmail, 'w')) { + throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + fputs($mail, $header); + fputs($mail, $body); + $result = pclose($mail); + // implement call back function if it exists + $isSent = ($result == 0) ? 1 : 0; + $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body); + if($result != 0) { + throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } } return true; } @@ -558,6 +915,7 @@ class PHPMailer { * Sends mail using the PHP mail() function. * @param string $header The message headers * @param string $body The message body + * @throws phpmailerException * @access protected * @return bool */ @@ -568,25 +926,28 @@ class PHPMailer { } $to = implode(', ', $toArr); - $params = sprintf("-oi -f %s", $this->Sender); - if ($this->Sender != '' && strlen(ini_get('safe_mode'))< 1) { + if (empty($this->Sender)) { + $params = "-oi "; + } else { + $params = sprintf("-oi -f%s", $this->Sender); + } + if ($this->Sender != '' and !ini_get('safe_mode')) { $old_from = ini_get('sendmail_from'); ini_set('sendmail_from', $this->Sender); - if ($this->SingleTo === true && count($toArr) > 1) { - foreach ($toArr as $key => $val) { - $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); - } - } else { - $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); + } + $rt = false; + if ($this->SingleTo === true && count($toArr) > 1) { + foreach ($toArr as $val) { + $rt = $this->mail_passthru($val, $this->Subject, $body, $header, $params); + // implement call back function if it exists + $isSent = ($rt == 1) ? 1 : 0; + $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); } } else { - if ($this->SingleTo === true && count($toArr) > 1) { - foreach ($toArr as $key => $val) { - $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); - } - } else { - $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header); - } + $rt = $this->mail_passthru($to, $this->Subject, $body, $header, $params); + // implement call back function if it exists + $isSent = ($rt == 1) ? 1 : 0; + $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body); } if (isset($old_from)) { ini_set('sendmail_from', $old_from); @@ -602,6 +963,7 @@ class PHPMailer { * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. * @param string $header The message headers * @param string $body The message body + * @throws phpmailerException * @uses SMTP * @access protected * @return bool @@ -615,25 +977,49 @@ class PHPMailer { } $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; if(!$this->smtp->Mail($smtp_from)) { - throw new phpmailerException($this->Lang('from_failed') . $smtp_from, self::STOP_CRITICAL); + $this->SetError($this->Lang('from_failed') . $smtp_from . " : " . implode(",",$this->smtp->getError())) ; + throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL); } // Attempt to send attach all recipients foreach($this->to as $to) { if (!$this->smtp->Recipient($to[0])) { $bad_rcpt[] = $to[0]; + // implement call back function if it exists + $isSent = 0; + $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body); + } else { + // implement call back function if it exists + $isSent = 1; + $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body); } } foreach($this->cc as $cc) { if (!$this->smtp->Recipient($cc[0])) { $bad_rcpt[] = $cc[0]; + // implement call back function if it exists + $isSent = 0; + $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body); + } else { + // implement call back function if it exists + $isSent = 1; + $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body); } } foreach($this->bcc as $bcc) { if (!$this->smtp->Recipient($bcc[0])) { $bad_rcpt[] = $bcc[0]; + // implement call back function if it exists + $isSent = 0; + $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body); + } else { + // implement call back function if it exists + $isSent = 1; + $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body); } } + + if (count($bad_rcpt) > 0 ) { //Create error message for any bad addresses $badaddresses = implode(', ', $bad_rcpt); throw new phpmailerException($this->Lang('recipients_failed') . $badaddresses); @@ -643,6 +1029,9 @@ class PHPMailer { } if($this->SMTPKeepAlive == true) { $this->smtp->Reset(); + } else { + $this->smtp->Quit(); + $this->smtp->Close(); } return true; } @@ -652,13 +1041,15 @@ class PHPMailer { * Returns false if the operation failed. * @uses SMTP * @access public + * @throws phpmailerException * @return bool */ public function SmtpConnect() { if(is_null($this->smtp)) { - $this->smtp = new SMTP(); + $this->smtp = new SMTP; } + $this->smtp->Timeout = $this->Timeout; $this->smtp->do_debug = $this->SMTPDebug; $hosts = explode(';', $this->Host); $index = 0; @@ -686,7 +1077,7 @@ class PHPMailer { if ($tls) { if (!$this->smtp->StartTLS()) { - throw new phpmailerException($this->Lang('tls')); + throw new phpmailerException($this->Lang('connect_host')); } //We must resend HELO after tls negotiation @@ -695,19 +1086,22 @@ class PHPMailer { $connection = true; if ($this->SMTPAuth) { - if (!$this->smtp->Authenticate($this->Username, $this->Password)) { + if (!$this->smtp->Authenticate($this->Username, $this->Password, $this->AuthType, + $this->Realm, $this->Workstation)) { throw new phpmailerException($this->Lang('authenticate')); } } } $index++; - if (!$connection) { - throw new phpmailerException($this->Lang('connect_host')); - } + if (!$connection) { + throw new phpmailerException($this->Lang('connect_host')); + } } } catch (phpmailerException $e) { $this->smtp->Reset(); - throw $e; + if ($this->exceptions) { + throw $e; + } } return true; } @@ -717,7 +1111,7 @@ class PHPMailer { * @return void */ public function SmtpClose() { - if(!is_null($this->smtp)) { + if ($this->smtp !== null) { if($this->smtp->Connected()) { $this->smtp->Quit(); $this->smtp->Close(); @@ -730,28 +1124,30 @@ class PHPMailer { * Returns false if it cannot load the language file. The default language is English. * @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br") * @param string $lang_path Path to the language file directory + * @return bool * @access public */ function SetLanguage($langcode = 'en', $lang_path = 'language/') { //Define full set of translatable strings $PHPMAILER_LANG = array( - 'provide_address' => 'You must provide at least one recipient email address.', + 'authenticate' => 'SMTP Error: Could not authenticate.', + 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', + 'data_not_accepted' => 'SMTP Error: Data not accepted.', + 'empty_message' => 'Message body empty', + 'encoding' => 'Unknown encoding: ', + 'execute' => 'Could not execute: ', + 'file_access' => 'Could not access file: ', + 'file_open' => 'File Error: Could not open file: ', + 'from_failed' => 'The following From address failed: ', + 'instantiate' => 'Could not instantiate mail function.', + 'invalid_address' => 'Invalid address', 'mailer_not_supported' => ' mailer is not supported.', - 'execute' => 'Could not execute: ', - 'instantiate' => 'Could not instantiate mail function.', - 'authenticate' => 'SMTP Error: Could not authenticate.', - 'from_failed' => 'The following From address failed: ', - 'recipients_failed' => 'SMTP Error: The following recipients failed: ', - 'data_not_accepted' => 'SMTP Error: Data not accepted.', - 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', - 'file_access' => 'Could not access file: ', - 'file_open' => 'File Error: Could not open file: ', - 'encoding' => 'Unknown encoding: ', - 'signing' => 'Signing Error: ', - 'smtp_error' => 'SMTP server error: ', - 'empty_message' => 'Message body empty', - 'invalid_address' => 'Invalid address', - 'variable_set' => 'Cannot set or reset variable: ' + 'provide_address' => 'You must provide at least one recipient email address.', + 'recipients_failed' => 'SMTP Error: The following recipients failed: ', + 'signing' => 'Signing Error: ', + 'smtp_connect_failed' => 'SMTP Connect() failed.', + 'smtp_error' => 'SMTP server error: ', + 'variable_set' => 'Cannot set or reset variable: ' ); //Overwrite language-specific strings. This way we'll never have missing translations - no more "language string failed to load"! $l = true; @@ -777,6 +1173,8 @@ class PHPMailer { /** * Creates recipient headers. * @access public + * @param string $type + * @param array $addr * @return string */ public function AddrAppend($type, $addr) { @@ -794,6 +1192,7 @@ class PHPMailer { /** * Formats an address correctly. * @access public + * @param string $addr * @return string */ public function AddrFormat($addr) { @@ -819,21 +1218,23 @@ class PHPMailer { // If utf-8 encoding is used, we will need to make sure we don't // split multibyte characters when we wrap $is_utf8 = (strtolower($this->CharSet) == "utf-8"); + $lelen = strlen($this->LE); + $crlflen = strlen(self::CRLF); $message = $this->FixEOL($message); - if (substr($message, -1) == $this->LE) { - $message = substr($message, 0, -1); + if (substr($message, -$lelen) == $this->LE) { + $message = substr($message, 0, -$lelen); } - $line = explode($this->LE, $message); + $line = explode($this->LE, $message); // Magic. We know FixEOL uses $LE $message = ''; - for ($i=0 ;$i < count($line); $i++) { + for ($i = 0 ;$i < count($line); $i++) { $line_part = explode(' ', $line[$i]); $buf = ''; for ($e = 0; $e $length)) { - $space_left = $length - strlen($buf) - 1; + $space_left = $length - strlen($buf) - $crlflen; if ($e != 0) { if ($space_left > 20) { $len = $space_left; @@ -847,7 +1248,7 @@ class PHPMailer { $part = substr($word, 0, $len); $word = substr($word, $len); $buf .= ' ' . $part; - $message .= $buf . sprintf("=%s", $this->LE); + $message .= $buf . sprintf("=%s", self::CRLF); } else { $message .= $buf . $soft_break; } @@ -866,7 +1267,7 @@ class PHPMailer { $word = substr($word, $len); if (strlen($word) > 0) { - $message .= $part . sprintf("=%s", $this->LE); + $message .= $part . sprintf("=%s", self::CRLF); } else { $buf = $part; } @@ -881,7 +1282,7 @@ class PHPMailer { } } } - $message .= $buf . $this->LE; + $message .= $buf . self::CRLF; } return $message; @@ -941,7 +1342,9 @@ class PHPMailer { switch($this->message_type) { case 'alt': - case 'alt_attachments': + case 'alt_inline': + case 'alt_attach': + case 'alt_inline_attach': $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap); break; default: @@ -962,9 +1365,17 @@ class PHPMailer { $uniq_id = md5(uniqid(time())); $this->boundary[1] = 'b1_' . $uniq_id; $this->boundary[2] = 'b2_' . $uniq_id; + $this->boundary[3] = 'b3_' . $uniq_id; - $result .= $this->HeaderLine('Date', self::RFCDate()); - if($this->Sender == '') { + if ($this->MessageDate == '') { + $result .= $this->HeaderLine('Date', self::RFCDate()); + } else { + $result .= $this->HeaderLine('Date', $this->MessageDate); + } + + if ($this->ReturnPath) { + $result .= $this->HeaderLine('Return-Path', trim($this->ReturnPath)); + } elseif ($this->Sender == '') { $result .= $this->HeaderLine('Return-Path', trim($this->From)); } else { $result .= $this->HeaderLine('Return-Path', trim($this->Sender)); @@ -972,10 +1383,16 @@ class PHPMailer { // To be created automatically by mail() if($this->Mailer != 'mail') { - if(count($this->to) > 0) { - $result .= $this->AddrAppend('To', $this->to); - } elseif (count($this->cc) == 0) { - $result .= $this->HeaderLine('To', 'undisclosed-recipients:;'); + if ($this->SingleTo === true) { + foreach($this->to as $t) { + $this->SingleToArray[] = $this->AddrFormat($t); + } + } else { + if(count($this->to) > 0) { + $result .= $this->AddrAppend('To', $this->to); + } elseif (count($this->cc) == 0) { + $result .= $this->HeaderLine('To', 'undisclosed-recipients:;'); + } } } @@ -995,7 +1412,7 @@ class PHPMailer { } if(count($this->ReplyTo) > 0) { - $result .= $this->AddrAppend('Reply-to', $this->ReplyTo); + $result .= $this->AddrAppend('Reply-To', $this->ReplyTo); } // mail() sets the subject itself @@ -1004,12 +1421,19 @@ class PHPMailer { } if($this->MessageID != '') { - $result .= $this->HeaderLine('Message-ID',$this->MessageID); + $result .= $this->HeaderLine('Message-ID', $this->MessageID); } else { $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE); } $result .= $this->HeaderLine('X-Priority', $this->Priority); - $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (phpmailer.codeworxtech.com)'); + if ($this->XMailer == '') { + $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (http://code.google.com/a/apache-extras.org/p/phpmailer/)'); + } else { + $myXmailer = trim($this->XMailer); + if ($myXmailer) { + $result .= $this->HeaderLine('X-Mailer', $myXmailer); + } + } if($this->ConfirmReadingTo != '') { $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>'); @@ -1035,47 +1459,86 @@ class PHPMailer { public function GetMailMIME() { $result = ''; switch($this->message_type) { - case 'plain': - $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding); - $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet); + case 'inline': + $result .= $this->HeaderLine('Content-Type', 'multipart/related;'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); break; - case 'attachments': - case 'alt_attachments': - if($this->InlineImageExists()){ - $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE); - } else { - $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;'); - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); - } + case 'attach': + case 'inline_attach': + case 'alt_attach': + case 'alt_inline_attach': + $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); break; case 'alt': + case 'alt_inline': $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); break; + default: + // Catches case 'plain': and case '': + $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding); + $result .= $this->TextLine('Content-Type: '.$this->ContentType.'; charset='.$this->CharSet); + break; } if($this->Mailer != 'mail') { - $result .= $this->LE.$this->LE; + $result .= $this->LE; } return $result; } + /** + * Returns the MIME message (headers and body). Only really valid post PreSend(). + * @access public + * @return string + */ + public function GetSentMIMEMessage() { + return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody; + } + + /** * Assembles the message body. Returns an empty string on failure. * @access public + * @throws phpmailerException * @return string The assembled message body */ public function CreateBody() { $body = ''; if ($this->sign_key_file) { - $body .= $this->GetMailMIME(); + $body .= $this->GetMailMIME().$this->LE; } $this->SetWordWrap(); switch($this->message_type) { + case 'inline': + $body .= $this->GetBoundary($this->boundary[1], '', '', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->AttachAll("inline", $this->boundary[1]); + break; + case 'attach': + $body .= $this->GetBoundary($this->boundary[1], '', '', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->AttachAll("attachment", $this->boundary[1]); + break; + case 'inline_attach': + $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', '', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->AttachAll("inline", $this->boundary[2]); + $body .= $this->LE; + $body .= $this->AttachAll("attachment", $this->boundary[1]); + break; case 'alt': $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); $body .= $this->EncodeString($this->AltBody, $this->Encoding); @@ -1085,26 +1548,60 @@ class PHPMailer { $body .= $this->LE.$this->LE; $body .= $this->EndBoundary($this->boundary[1]); break; - case 'plain': - $body .= $this->EncodeString($this->Body, $this->Encoding); - break; - case 'attachments': - $body .= $this->GetBoundary($this->boundary[1], '', '', ''); - $body .= $this->EncodeString($this->Body, $this->Encoding); - $body .= $this->LE; - $body .= $this->AttachAll(); - break; - case 'alt_attachments': - $body .= sprintf("--%s%s", $this->boundary[1], $this->LE); - $body .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE); - $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body + case 'alt_inline': + $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); $body .= $this->EncodeString($this->AltBody, $this->Encoding); $body .= $this->LE.$this->LE; - $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body + $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->AttachAll("inline", $this->boundary[2]); + $body .= $this->LE; + $body .= $this->EndBoundary($this->boundary[1]); + break; + case 'alt_attach': + $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', ''); + $body .= $this->EncodeString($this->AltBody, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', ''); $body .= $this->EncodeString($this->Body, $this->Encoding); $body .= $this->LE.$this->LE; $body .= $this->EndBoundary($this->boundary[2]); - $body .= $this->AttachAll(); + $body .= $this->LE; + $body .= $this->AttachAll("attachment", $this->boundary[1]); + break; + case 'alt_inline_attach': + $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', ''); + $body .= $this->EncodeString($this->AltBody, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->TextLine("--" . $this->boundary[2]); + $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[3] . '"'); + $body .= $this->LE; + $body .= $this->GetBoundary($this->boundary[3], '', 'text/html', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->AttachAll("inline", $this->boundary[3]); + $body .= $this->LE; + $body .= $this->EndBoundary($this->boundary[2]); + $body .= $this->LE; + $body .= $this->AttachAll("attachment", $this->boundary[1]); + break; + default: + // catch case 'plain' and case '' + $body .= $this->EncodeString($this->Body, $this->Encoding); break; } @@ -1117,8 +1614,8 @@ class PHPMailer { $signed = tempnam("", "signed"); if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) { @unlink($file); - @unlink($signed); $body = file_get_contents($signed); + @unlink($signed); } else { @unlink($file); @unlink($signed); @@ -1137,9 +1634,14 @@ class PHPMailer { /** * Returns the start of a message boundary. - * @access private + * @access protected + * @param string $boundary + * @param string $charSet + * @param string $contentType + * @param string $encoding + * @return string */ - private function GetBoundary($boundary, $charSet, $contentType, $encoding) { + protected function GetBoundary($boundary, $charSet, $contentType, $encoding) { $result = ''; if($charSet == '') { $charSet = $this->CharSet; @@ -1151,7 +1653,7 @@ class PHPMailer { $encoding = $this->Encoding; } $result .= $this->TextLine('--' . $boundary); - $result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet); + $result .= sprintf("Content-Type: %s; charset=%s", $contentType, $charSet); $result .= $this->LE; $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding); $result .= $this->LE; @@ -1161,36 +1663,33 @@ class PHPMailer { /** * Returns the end of a message boundary. - * @access private + * @access protected + * @param string $boundary + * @return string */ - private function EndBoundary($boundary) { + protected function EndBoundary($boundary) { return $this->LE . '--' . $boundary . '--' . $this->LE; } /** * Sets the message type. - * @access private + * @access protected * @return void */ - private function SetMessageType() { - if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) { - $this->message_type = 'plain'; - } else { - if(count($this->attachment) > 0) { - $this->message_type = 'attachments'; - } - if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) { - $this->message_type = 'alt'; - } - if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) { - $this->message_type = 'alt_attachments'; - } - } + protected function SetMessageType() { + $this->message_type = array(); + if($this->AlternativeExists()) $this->message_type[] = "alt"; + if($this->InlineImageExists()) $this->message_type[] = "inline"; + if($this->AttachmentExists()) $this->message_type[] = "attach"; + $this->message_type = implode("_", $this->message_type); + if($this->message_type == "") $this->message_type = "plain"; } /** * Returns a formatted header line. * @access public + * @param string $name + * @param string $value * @return string */ public function HeaderLine($name, $value) { @@ -1200,6 +1699,7 @@ class PHPMailer { /** * Returns a formatted mail line. * @access public + * @param string $value * @return string */ public function TextLine($value) { @@ -1218,6 +1718,7 @@ class PHPMailer { * @param string $name Overrides the attachment name. * @param string $encoding File encoding (see $Encoding). * @param string $type File extension (MIME) type. + * @throws phpmailerException * @return bool */ public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { @@ -1246,7 +1747,9 @@ class PHPMailer { if ($this->exceptions) { throw $e; } - if (defined('MAIL_DEBUG')) echo $e->getMessage()."\n"; + if ($this->SMTPDebug) { + $this->edebug($e->getMessage()."\n"); + } if ( $e->getCode() == self::STOP_CRITICAL ) { return false; } @@ -1265,10 +1768,12 @@ class PHPMailer { /** * Attaches all fs, string, and binary attachments to the message. * Returns an empty string on failure. - * @access private + * @access protected + * @param string $disposition_type + * @param string $boundary * @return string */ - private function AttachAll() { + protected function AttachAll($disposition_type, $boundary) { // Return text of body $mime = array(); $cidUniq = array(); @@ -1276,54 +1781,60 @@ class PHPMailer { // Add all attachments foreach ($this->attachment as $attachment) { - // Check for string attachment - $bString = $attachment[5]; - if ($bString) { - $string = $attachment[0]; - } else { - $path = $attachment[0]; - } - - if (in_array($attachment[0], $incl)) { continue; } - $filename = $attachment[1]; - $name = $attachment[2]; - $encoding = $attachment[3]; - $type = $attachment[4]; - $disposition = $attachment[6]; - $cid = $attachment[7]; - $incl[] = $attachment[0]; - if ( $disposition == 'inline' && isset($cidUniq[$cid]) ) { continue; } - $cidUniq[$cid] = true; - - $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE); - $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE); - $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); - - if($disposition == 'inline') { - $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); - } - - $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); - - // Encode as string attachment - if($bString) { - $mime[] = $this->EncodeString($string, $encoding); - if($this->IsError()) { - return ''; + // CHECK IF IT IS A VALID DISPOSITION_FILTER + if($attachment[6] == $disposition_type) { + // Check for string attachment + $string = ''; + $path = ''; + $bString = $attachment[5]; + if ($bString) { + $string = $attachment[0]; + } else { + $path = $attachment[0]; } - $mime[] = $this->LE.$this->LE; - } else { - $mime[] = $this->EncodeFile($path, $encoding); - if($this->IsError()) { - return ''; + + $inclhash = md5(serialize($attachment)); + if (in_array($inclhash, $incl)) { continue; } + $incl[] = $inclhash; + $filename = $attachment[1]; + $name = $attachment[2]; + $encoding = $attachment[3]; + $type = $attachment[4]; + $disposition = $attachment[6]; + $cid = $attachment[7]; + if ( $disposition == 'inline' && isset($cidUniq[$cid]) ) { continue; } + $cidUniq[$cid] = true; + + $mime[] = sprintf("--%s%s", $boundary, $this->LE); + $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE); + $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); + + if($disposition == 'inline') { + $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); + } + + $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); + + // Encode as string attachment + if($bString) { + $mime[] = $this->EncodeString($string, $encoding); + if($this->IsError()) { + return ''; + } + $mime[] = $this->LE.$this->LE; + } else { + $mime[] = $this->EncodeFile($path, $encoding); + if($this->IsError()) { + return ''; + } + $mime[] = $this->LE.$this->LE; } - $mime[] = $this->LE.$this->LE; } } - $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE); + $mime[] = sprintf("--%s--%s", $boundary, $this->LE); - return join('', $mime); + return implode("", $mime); } /** @@ -1331,27 +1842,38 @@ class PHPMailer { * Returns an empty string on failure. * @param string $path The full path to the file * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' + * @throws phpmailerException * @see EncodeFile() - * @access private + * @access protected * @return string */ - private function EncodeFile($path, $encoding = 'base64') { + protected function EncodeFile($path, $encoding = 'base64') { try { if (!is_readable($path)) { throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE); } - if (function_exists('get_magic_quotes')) { - function get_magic_quotes() { - return false; + // if (!function_exists('get_magic_quotes')) { + // function get_magic_quotes() { + // return false; + // } + // } + $magic_quotes = get_magic_quotes_runtime(); + if ($magic_quotes) { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + set_magic_quotes_runtime(0); + } else { + ini_set('magic_quotes_runtime', 0); } } - if (PHP_VERSION < 6) { - $magic_quotes = get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - } $file_buffer = file_get_contents($path); $file_buffer = $this->EncodeString($file_buffer, $encoding); - if (PHP_VERSION < 6) { set_magic_quotes_runtime($magic_quotes); } + if ($magic_quotes) { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + set_magic_quotes_runtime($magic_quotes); + } else { + ini_set('magic_quotes_runtime', $magic_quotes); + } + } return $file_buffer; } catch (Exception $e) { $this->SetError($e->getMessage()); @@ -1367,7 +1889,7 @@ class PHPMailer { * @access public * @return string */ - public function EncodeString ($str, $encoding = 'base64') { + public function EncodeString($str, $encoding = 'base64') { $encoded = ''; switch(strtolower($encoding)) { case 'base64': @@ -1396,6 +1918,8 @@ class PHPMailer { /** * Encode a header string to best (shortest) of Q, B, quoted or none. * @access public + * @param string $str + * @param string $position * @return string */ public function EncodeHeader($str, $position = 'text') { @@ -1434,7 +1958,7 @@ class PHPMailer { if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) { // Use a custom function which correctly encodes and wraps long // multibyte strings without breaking lines within a character - $encoded = $this->Base64EncodeWrapMB($str); + $encoded = $this->Base64EncodeWrapMB($str, "\n"); } else { $encoded = base64_encode($str); $maxlen -= $maxlen % 4; @@ -1444,7 +1968,7 @@ class PHPMailer { $encoding = 'Q'; $encoded = $this->EncodeQ($str, $position); $encoded = $this->WrapText($encoded, $maxlen, true); - $encoded = str_replace('='.$this->LE, "\n", trim($encoded)); + $encoded = str_replace('='.self::CRLF, "\n", trim($encoded)); } $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded); @@ -1473,12 +1997,16 @@ class PHPMailer { * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php * @access public * @param string $str multi-byte text to wrap encode + * @param string $lf string to use as linefeed/end-of-line * @return string */ - public function Base64EncodeWrapMB($str) { + public function Base64EncodeWrapMB($str, $lf=null) { $start = "=?".$this->CharSet."?B?"; $end = "?="; $encoded = ""; + if ($lf === null) { + $lf = $this->LE; + } $mb_length = mb_strlen($str, $this->CharSet); // Each line must have length <= 75, including $start and $end @@ -1499,11 +2027,11 @@ class PHPMailer { } while (strlen($chunk) > $length); - $encoded .= $chunk . $this->LE; + $encoded .= $chunk . $lf; } // Chomp the last linefeed - $encoded = substr($encoded, 0, -strlen($this->LE)); + $encoded = substr($encoded, 0, -strlen($lf)); return $encoded; } @@ -1511,12 +2039,14 @@ class PHPMailer { * Encode string to quoted-printable. * Only uses standard PHP, slow, but will always work * @access public - * @param string $string the text to encode + * @param string $input * @param integer $line_max Number of chars allowed on a line before wrapping + * @param bool $space_conv + * @internal param string $string the text to encode * @return string */ public function EncodeQPphp( $input = '', $line_max = 76, $space_conv = false) { - $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); + $hex = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); $lines = preg_split('/(?:\r\n|\r|\n)/', $input); $eol = "\r\n"; $escape = '='; @@ -1537,8 +2067,8 @@ class PHPMailer { $c = '=20'; } } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required - $h2 = floor($dec/16); - $h1 = floor($dec%16); + $h2 = (integer)floor($dec/16); + $h1 = (integer)floor($dec%16); $c = $escape.$hex[$h2].$hex[$h1]; } if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted @@ -1597,30 +2127,38 @@ class PHPMailer { * @access public * @return string */ - public function EncodeQ ($str, $position = 'text') { - // There should not be any EOL in the string - $encoded = preg_replace('/[\r\n]*/', '', $str); - + public function EncodeQ($str, $position = 'text') { + //There should not be any EOL in the string + $pattern=""; + $encoded = str_replace(array("\r", "\n"), '', $str); switch (strtolower($position)) { case 'phrase': - $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); + $pattern = '^A-Za-z0-9!*+\/ -'; break; + case 'comment': - $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); + $pattern = '\(\)"'; + //note that we dont break here! + //for this reason we build the $pattern withoud including delimiters and [] + case 'text': default: - // Replace every high ascii, control =, ? and _ characters - //TODO using /e (equivalent to eval()) is probably not a good idea - $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e', - "'='.sprintf('%02X', ord('\\1'))", $encoded); + //Replace every high ascii, control =, ? and _ characters + //We put \075 (=) as first value to make sure it's the first one in being converted, preventing double encode + $pattern = '\075\000-\011\013\014\016-\037\077\137\177-\377' . $pattern; break; } + + if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) { + foreach (array_unique($matches[0]) as $char) { + $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded); + } + } + + //Replace every spaces to _ (more readable than =20) + return str_replace(' ', '_', $encoded); +} - // Replace every spaces to _ (more readable than =20) - $encoded = str_replace(' ', '_', $encoded); - - return $encoded; - } /** * Adds a string or binary attachment (non-filesystem) to the list. @@ -1637,7 +2175,7 @@ class PHPMailer { $this->attachment[] = array( 0 => $string, 1 => $filename, - 2 => $filename, + 2 => basename($filename), 3 => $encoding, 4 => $type, 5 => true, // isStringAttachment @@ -1686,6 +2224,33 @@ class PHPMailer { return true; } + /** + * Adds an embedded stringified attachment. This can include images, sounds, and + * just about any other document. Make sure to set the $type to an + * image type. For JPEG images use "image/jpeg" and for GIF images + * use "image/gif". + * @param string $string The attachment. + * @param string $cid Content ID of the attachment. Use this to identify + * the Id for accessing the image in an HTML form. + * @param string $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @return bool + */ + public function AddStringEmbeddedImage($string, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { + // Append to $attachment array + $this->attachment[] = array( + 0 => $string, + 1 => $name, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => 'inline', + 7 => $cid + ); + } + /** * Returns true if an inline attachment is present. * @access public @@ -1700,6 +2265,27 @@ class PHPMailer { return false; } + /** + * Returns true if an attachment (non-inline) is present. + * @return bool + */ + public function AttachmentExists() { + foreach($this->attachment as $attachment) { + if ($attachment[6] == 'attachment') { + return true; + } + } + return false; + } + + /** + * Does this message have an alternative body set? + * @return bool + */ + public function AlternativeExists() { + return !empty($this->AltBody); + } + ///////////////////////////////////////////////// // CLASS METHODS, MESSAGE RESET ///////////////////////////////////////////////// @@ -1781,6 +2367,7 @@ class PHPMailer { /** * Adds the error message to the error container. * @access protected + * @param string $msg * @return void */ protected function SetError($msg) { @@ -1812,10 +2399,10 @@ class PHPMailer { /** * Returns the server hostname or 'localhost.localdomain' if unknown. - * @access private + * @access protected * @return string */ - private function ServerHostname() { + protected function ServerHostname() { if (!empty($this->Hostname)) { $result = $this->Hostname; } elseif (isset($_SERVER['SERVER_NAME'])) { @@ -1829,10 +2416,11 @@ class PHPMailer { /** * Returns a message in the appropriate language. - * @access private + * @access protected + * @param string $key * @return string */ - private function Lang($key) { + protected function Lang($key) { if(count($this->language) < 1) { $this->SetLanguage('en'); // set the default language } @@ -1854,84 +2442,105 @@ class PHPMailer { } /** - * Changes every end of line from CR or LF to CRLF. - * @access private + * Changes every end of line from CRLF, CR or LF to $this->LE. + * @access public + * @param string $str String to FixEOL * @return string */ - private function FixEOL($str) { - $str = str_replace("\r\n", "\n", $str); - $str = str_replace("\r", "\n", $str); - $str = str_replace("\n", $this->LE, $str); - return $str; + public function FixEOL($str) { + // condense down to \n + $nstr = str_replace(array("\r\n", "\r"), "\n", $str); + // Now convert LE as needed + if ($this->LE !== "\n") { + $nstr = str_replace("\n", $this->LE, $nstr); + } + return $nstr; } /** - * Adds a custom header. + * Adds a custom header. $name value can be overloaded to contain + * both header name and value (name:value) * @access public + * @param string $name custom header name + * @param string $value header value * @return void */ - public function AddCustomHeader($custom_header) { - $this->CustomHeader[] = explode(':', $custom_header, 2); + public function AddCustomHeader($name, $value=null) { + if ($value === null) { + // Value passed in as name:value + $this->CustomHeader[] = explode(':', $name, 2); + } else { + $this->CustomHeader[] = array($name, $value); + } } /** * Evaluates the message and returns modifications for inline images and backgrounds * @access public - * @return $message + * @param string $message Text to be HTML modified + * @param string $basedir baseline directory for path + * @return string $message */ public function MsgHTML($message, $basedir = '') { - preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images); + preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images); if(isset($images[2])) { foreach($images[2] as $i => $url) { // do not change urls for absolute images (thanks to corvuscorax) - if (!preg_match('#^[A-z]+://#',$url)) { + if (!preg_match('#^[A-z]+://#', $url)) { $filename = basename($url); $directory = dirname($url); - ($directory == '.')?$directory='':''; - $cid = 'cid:' . md5($filename); + if ($directory == '.') { + $directory = ''; + } + $cid = 'cid:' . md5($url); $ext = pathinfo($filename, PATHINFO_EXTENSION); $mimeType = self::_mime_types($ext); - if ( strlen($basedir) > 1 && substr($basedir,-1) != '/') { $basedir .= '/'; } - if ( strlen($directory) > 1 && substr($directory,-1) != '/') { $directory .= '/'; } - if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64',$mimeType) ) { - $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message); + if ( strlen($basedir) > 1 && substr($basedir, -1) != '/') { $basedir .= '/'; } + if ( strlen($directory) > 1 && substr($directory, -1) != '/') { $directory .= '/'; } + if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($url), $filename, 'base64', $mimeType) ) { + $message = preg_replace("/".$images[1][$i]."=[\"']".preg_quote($url, '/')."[\"']/Ui", $images[1][$i]."=\"".$cid."\"", $message); } } } } $this->IsHTML(true); $this->Body = $message; - $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$message))); - if (!empty($textMsg) && empty($this->AltBody)) { - $this->AltBody = html_entity_decode($textMsg); + if (empty($this->AltBody)) { + $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s', '', $message))); + if (!empty($textMsg)) { + $this->AltBody = html_entity_decode($textMsg, ENT_QUOTES, $this->CharSet); + } } if (empty($this->AltBody)) { $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; } + return $message; } /** * Gets the MIME type of the embedded or inline image - * @param string File extension + * @param string $ext File extension * @access public * @return string MIME type of ext * @static */ public static function _mime_types($ext = '') { $mimes = array( + 'xl' => 'application/excel', 'hqx' => 'application/mac-binhex40', 'cpt' => 'application/mac-compactpro', - 'doc' => 'application/msword', 'bin' => 'application/macbinary', + 'doc' => 'application/msword', + 'word' => 'application/msword', + 'class' => 'application/octet-stream', + 'dll' => 'application/octet-stream', 'dms' => 'application/octet-stream', + 'exe' => 'application/octet-stream', 'lha' => 'application/octet-stream', 'lzh' => 'application/octet-stream', - 'exe' => 'application/octet-stream', - 'class' => 'application/octet-stream', 'psd' => 'application/octet-stream', - 'so' => 'application/octet-stream', 'sea' => 'application/octet-stream', - 'dll' => 'application/octet-stream', + 'so' => 'application/octet-stream', 'oda' => 'application/oda', 'pdf' => 'application/pdf', 'ai' => 'application/postscript', @@ -1949,9 +2558,9 @@ class PHPMailer { 'dxr' => 'application/x-director', 'dvi' => 'application/x-dvi', 'gtar' => 'application/x-gtar', - 'php' => 'application/x-httpd-php', - 'php4' => 'application/x-httpd-php', 'php3' => 'application/x-httpd-php', + 'php4' => 'application/x-httpd-php', + 'php' => 'application/x-httpd-php', 'phtml' => 'application/x-httpd-php', 'phps' => 'application/x-httpd-php-source', 'js' => 'application/x-javascript', @@ -1959,53 +2568,50 @@ class PHPMailer { 'sit' => 'application/x-stuffit', 'tar' => 'application/x-tar', 'tgz' => 'application/x-tar', - 'xhtml' => 'application/xhtml+xml', 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', 'zip' => 'application/zip', 'mid' => 'audio/midi', 'midi' => 'audio/midi', - 'mpga' => 'audio/mpeg', 'mp2' => 'audio/mpeg', 'mp3' => 'audio/mpeg', + 'mpga' => 'audio/mpeg', 'aif' => 'audio/x-aiff', - 'aiff' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', 'ram' => 'audio/x-pn-realaudio', 'rm' => 'audio/x-pn-realaudio', 'rpm' => 'audio/x-pn-realaudio-plugin', 'ra' => 'audio/x-realaudio', - 'rv' => 'video/vnd.rn-realvideo', 'wav' => 'audio/x-wav', 'bmp' => 'image/bmp', 'gif' => 'image/gif', 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', 'jpe' => 'image/jpeg', + 'jpg' => 'image/jpeg', 'png' => 'image/png', 'tiff' => 'image/tiff', 'tif' => 'image/tiff', + 'eml' => 'message/rfc822', 'css' => 'text/css', 'html' => 'text/html', 'htm' => 'text/html', 'shtml' => 'text/html', - 'txt' => 'text/plain', - 'text' => 'text/plain', 'log' => 'text/plain', + 'text' => 'text/plain', + 'txt' => 'text/plain', 'rtx' => 'text/richtext', 'rtf' => 'text/rtf', 'xml' => 'text/xml', 'xsl' => 'text/xml', 'mpeg' => 'video/mpeg', - 'mpg' => 'video/mpeg', 'mpe' => 'video/mpeg', - 'qt' => 'video/quicktime', + 'mpg' => 'video/mpeg', 'mov' => 'video/quicktime', + 'qt' => 'video/quicktime', + 'rv' => 'video/vnd.rn-realvideo', 'avi' => 'video/x-msvideo', - 'movie' => 'video/x-sgi-movie', - 'doc' => 'application/msword', - 'word' => 'application/msword', - 'xl' => 'application/excel', - 'eml' => 'message/rfc822' + 'movie' => 'video/x-sgi-movie' ); return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)]; } @@ -2020,6 +2626,8 @@ class PHPMailer { * @param string $name Parameter Name * @param mixed $value Parameter Value * NOTE: will not work with arrays, there are no arrays to set/reset + * @throws phpmailerException + * @return bool * @todo Should this not be using __set() magic function? */ public function set($name, $value = '') { @@ -2045,15 +2653,14 @@ class PHPMailer { * @return string */ public function SecureHeader($str) { - $str = str_replace("\r", '', $str); - $str = str_replace("\n", '', $str); - return trim($str); + return trim(str_replace(array("\r", "\n"), '', $str)); } /** * Set the private key file and password to sign the message. * * @access public + * @param $cert_filename * @param string $key_filename Parameter File Name * @param string $key_pass Password for private key */ @@ -2062,14 +2669,162 @@ class PHPMailer { $this->sign_key_file = $key_filename; $this->sign_key_pass = $key_pass; } -} -class phpmailerException extends Exception { - public function errorMessage() { - $errorMsg = '' . $this->getMessage() . "
    \n"; - - if (defined('MAIL_DEBUG')) return $errorMsg; - return ''; + /** + * Set the private key file and password to sign the message. + * + * @access public + * @param string $txt + * @return string + */ + public function DKIM_QP($txt) { + $line = ''; + for ($i = 0; $i < strlen($txt); $i++) { + $ord = ord($txt[$i]); + if ( ((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E)) ) { + $line .= $txt[$i]; + } else { + $line .= "=".sprintf("%02X", $ord); + } + } + return $line; + } + + /** + * Generate DKIM signature + * + * @access public + * @param string $s Header + * @return string + */ + public function DKIM_Sign($s) { + $privKeyStr = file_get_contents($this->DKIM_private); + if ($this->DKIM_passphrase != '') { + $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); + } else { + $privKey = $privKeyStr; + } + if (openssl_sign($s, $signature, $privKey)) { + return base64_encode($signature); + } + return ''; + } + + /** + * Generate DKIM Canonicalization Header + * + * @access public + * @param string $s Header + * @return string + */ + public function DKIM_HeaderC($s) { + $s = preg_replace("/\r\n\s+/", " ", $s); + $lines = explode("\r\n", $s); + foreach ($lines as $key => $line) { + list($heading, $value) = explode(":", $line, 2); + $heading = strtolower($heading); + $value = preg_replace("/\s+/", " ", $value) ; // Compress useless spaces + $lines[$key] = $heading.":".trim($value) ; // Don't forget to remove WSP around the value + } + $s = implode("\r\n", $lines); + return $s; + } + + /** + * Generate DKIM Canonicalization Body + * + * @access public + * @param string $body Message Body + * @return string + */ + public function DKIM_BodyC($body) { + if ($body == '') return "\r\n"; + // stabilize line endings + $body = str_replace("\r\n", "\n", $body); + $body = str_replace("\n", "\r\n", $body); + // END stabilize line endings + while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { + $body = substr($body, 0, strlen($body) - 2); + } + return $body; + } + + /** + * Create the DKIM header, body, as new header + * + * @access public + * @param string $headers_line Header lines + * @param string $subject Subject + * @param string $body Body + * @return string + */ + public function DKIM_Add($headers_line, $subject, $body) { + $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms + $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body + $DKIMquery = 'dns/txt'; // Query method + $DKIMtime = time() ; // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) + $subject_header = "Subject: $subject"; + $headers = explode($this->LE, $headers_line); + $from_header = ""; + $to_header = ""; + foreach($headers as $header) { + if (strpos($header, 'From:') === 0) { + $from_header = $header; + } elseif (strpos($header, 'To:') === 0) { + $to_header = $header; + } + } + $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); + $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); + $subject = str_replace('|', '=7C', $this->DKIM_QP($subject_header)) ; // Copied header fields (dkim-quoted-printable + $body = $this->DKIM_BodyC($body); + $DKIMlen = strlen($body) ; // Length of body + $DKIMb64 = base64_encode(pack("H*", sha1($body))) ; // Base64 of packed binary SHA-1 hash of body + $ident = ($this->DKIM_identity == '')? '' : " i=" . $this->DKIM_identity . ";"; + $dkimhdrs = "DKIM-Signature: v=1; a=" . $DKIMsignatureType . "; q=" . $DKIMquery . "; l=" . $DKIMlen . "; s=" . $this->DKIM_selector . ";\r\n". + "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n". + "\th=From:To:Subject;\r\n". + "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n". + "\tz=$from\r\n". + "\t|$to\r\n". + "\t|$subject;\r\n". + "\tbh=" . $DKIMb64 . ";\r\n". + "\tb="; + $toSign = $this->DKIM_HeaderC($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs); + $signed = $this->DKIM_Sign($toSign); + return "X-PHPMAILER-DKIM: code.google.com/a/apache-extras.org/p/phpmailer/\r\n".$dkimhdrs.$signed."\r\n"; + } + + /** + * Perform callback + * @param boolean $isSent + * @param string $to + * @param string $cc + * @param string $bcc + * @param string $subject + * @param string $body + * @param string $from + */ + protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from=null) { + if (!empty($this->action_function) && is_callable($this->action_function)) { + $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); + call_user_func_array($this->action_function, $params); + } } } -?> \ No newline at end of file + +/** + * Exception handler for PHPMailer + * @package PHPMailer + */ +class phpmailerException extends Exception { + /** + * Prettify error message output + * @return string + */ + public function errorMessage() { + $errorMsg = '' . $this->getMessage() . "
    \n"; + return $errorMsg; + } +} +?> diff --git a/e107_handlers/phpmailer/class.pop3.php b/e107_handlers/phpmailer/class.pop3.php index 021a068f2..f4172a5b5 100644 --- a/e107_handlers/phpmailer/class.pop3.php +++ b/e107_handlers/phpmailer/class.pop3.php @@ -2,15 +2,15 @@ /*~ class.pop3.php .---------------------------------------------------------------------------. | Software: PHPMailer - PHP email class | -| Version: 5.0.0 | -| Contact: via sourceforge.net support pages (also www.codeworxtech.com) | -| Info: http://phpmailer.sourceforge.net | -| Support: http://sourceforge.net/projects/phpmailer/ | +| Version: 5.2.2 | +| Site: https://code.google.com/a/apache-extras.org/p/phpmailer/ | | ------------------------------------------------------------------------- | -| Admin: Andy Prevost (project admininistrator) | +| Admin: Jim Jagielski (project admininistrator) | | Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net | | : Marcus Bointon (coolbru) coolbru@users.sourceforge.net | +| : Jim Jagielski (jimjag) jimjag@gmail.com | | Founder: Brent R. Matzelle (original founder) | +| Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved. | | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. | | Copyright (c) 2001-2003, Brent R. Matzelle | | ------------------------------------------------------------------------- | @@ -19,11 +19,6 @@ | This program is distributed in the hope that it will be useful - WITHOUT | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | | FITNESS FOR A PARTICULAR PURPOSE. | -| ------------------------------------------------------------------------- | -| We offer a number of paid services (www.codeworxtech.com): | -| - Web Hosting on highly optimized fast and secure servers | -| - Technology Consulting | -| - Oursourcing (highly qualified programmers and graphic designers) | '---------------------------------------------------------------------------' */ @@ -33,21 +28,21 @@ * @package PHPMailer * @author Andy Prevost * @author Marcus Bointon + * @author Jim Jagielski + * @copyright 2010 - 2012 Jim Jagielski * @copyright 2004 - 2009 Andy Prevost * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) - * @version $Id$ */ /** - * POP Before SMTP Authentication Class - * Version 5.0.0 + * PHP POP-Before-SMTP Authentication Class * - * Author: Richard Davey (rich@corephp.co.uk) - * Modifications: Andy Prevost - * License: LGPL, see PHPMailer License + * Version 5.2.2 + * + * @license: LGPL, see PHPMailer License * * Specifically for PHPMailer to allow POP before SMTP authentication. - * Does not yet work with APOP - if you have an APOP account, contact Richard Davey + * Does not yet work with APOP - if you have an APOP account, contact Jim Jagielski * and we can test changes to this script. * * This class is based on the structure of the SMTP class originally authored by Chris Ryan @@ -56,7 +51,9 @@ * required for POP3 connection, authentication and disconnection. * * @package PHPMailer - * @author Richard Davey + * @author Richard Davey (orig) + * @author Andy Prevost + * @author Jim Jagielski */ class POP3 { @@ -114,12 +111,27 @@ class POP3 { */ public $password; + /** + * Sets the POP3 PHPMailer Version number + * @var string + */ + public $Version = '5.2.2'; + ///////////////////////////////////////////////// // PROPERTIES, PRIVATE AND PROTECTED ///////////////////////////////////////////////// + /** + * @var resource Resource handle for the POP connection socket + */ private $pop_conn; + /** + * @var boolean Are we connected? + */ private $connected; + /** + * @var array Error container + */ private $error; // Error log array /** @@ -137,10 +149,12 @@ class POP3 { * Combination of public events - connect, login, disconnect * @access public * @param string $host - * @param integer $port - * @param integer $tval + * @param bool|int $port + * @param bool|int $tval * @param string $username * @param string $password + * @param int $debug_level + * @return bool */ public function Authorise ($host, $port = false, $tval = false, $username, $password, $debug_level = 0) { $this->host = $host; @@ -190,7 +204,7 @@ class POP3 { * Connect to the POP3 server * @access public * @param string $host - * @param integer $port + * @param bool|int $port * @param integer $tval * @return boolean */ @@ -259,7 +273,7 @@ class POP3 { $this->connected = true; return true; } - + return false; } /** @@ -300,12 +314,9 @@ class POP3 { if ($this->checkResponse($pop3_response)) { return true; - } else { - return false; } - } else { - return false; } + return false; } /** @@ -404,4 +415,4 @@ class POP3 { // End of class } -?> \ No newline at end of file +?> diff --git a/e107_handlers/phpmailer/class.smtp.php b/e107_handlers/phpmailer/class.smtp.php index f2bb4bdd2..8d9cd7d9e 100644 --- a/e107_handlers/phpmailer/class.smtp.php +++ b/e107_handlers/phpmailer/class.smtp.php @@ -2,15 +2,15 @@ /*~ class.smtp.php .---------------------------------------------------------------------------. | Software: PHPMailer - PHP email class | -| Version: 5.0.0 | -| Contact: via sourceforge.net support pages (also www.codeworxtech.com) | -| Info: http://phpmailer.sourceforge.net | -| Support: http://sourceforge.net/projects/phpmailer/ | +| Version: 5.2.2 | +| Site: https://code.google.com/a/apache-extras.org/p/phpmailer/ | | ------------------------------------------------------------------------- | -| Admin: Andy Prevost (project admininistrator) | +| Admin: Jim Jagielski (project admininistrator) | | Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net | | : Marcus Bointon (coolbru) coolbru@users.sourceforge.net | +| : Jim Jagielski (jimjag) jimjag@gmail.com | | Founder: Brent R. Matzelle (original founder) | +| Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved. | | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. | | Copyright (c) 2001-2003, Brent R. Matzelle | | ------------------------------------------------------------------------- | @@ -19,11 +19,6 @@ | This program is distributed in the hope that it will be useful - WITHOUT | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | | FITNESS FOR A PARTICULAR PURPOSE. | -| ------------------------------------------------------------------------- | -| We offer a number of paid services (www.codeworxtech.com): | -| - Web Hosting on highly optimized fast and secure servers | -| - Technology Consulting | -| - Oursourcing (highly qualified programmers and graphic designers) | '---------------------------------------------------------------------------' */ @@ -34,16 +29,18 @@ * @author Andy Prevost * @author Marcus Bointon * @copyright 2004 - 2008 Andy Prevost + * @author Jim Jagielski + * @copyright 2010 - 2012 Jim Jagielski * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) - * @version $Id$ */ /** - * SMTP is rfc 821 compliant and implements all the rfc 821 SMTP - * commands except TURN which will always return a not implemented - * error. SMTP also provides some utility methods for sending mail - * to an SMTP server. - * original author: Chris Ryan + * PHP RFC821 SMTP client + * + * Implements all the RFC 821 SMTP commands except TURN which will always return a not implemented error. + * SMTP also provides some utility methods for sending mail to an SMTP server. + * @author Chris Ryan + * @package PHPMailer */ class SMTP { @@ -54,7 +51,7 @@ class SMTP { public $SMTP_PORT = 25; /** - * SMTP reply line ending + * SMTP reply line ending (don't change) * @var string */ public $CRLF = "\r\n"; @@ -65,24 +62,70 @@ class SMTP { */ public $do_debug; // the level of debug to perform + /** + * Sets the function/method to use for debugging output. + * Right now we only honor "echo" or "error_log" + * @var string + */ + public $Debugoutput = "echo"; + /** * Sets VERP use on/off (default is off) * @var bool */ public $do_verp = false; + /** + * Sets the SMTP timeout value for reads, in seconds + * @var int + */ + public $Timeout = 15; + + /** + * Sets the SMTP timelimit value for reads, in seconds + * @var int + */ + public $Timelimit = 30; + + /** + * Sets the SMTP PHPMailer Version number + * @var string + */ + public $Version = '5.2.2'; + ///////////////////////////////////////////////// // PROPERTIES, PRIVATE AND PROTECTED ///////////////////////////////////////////////// - private $smtp_conn; // the socket to the server - private $error; // error if any on the last call - private $helo_rply; // the reply the server sent to us for HELO + /** + * @var resource The socket to the server + */ + private $smtp_conn; + /** + * @var string Error message, if any, for the last call + */ + private $error; + /** + * @var string The reply the server sent to us for HELO + */ + private $helo_rply; + + /** + * Outputs debugging info via user-defined method + * @param string $str + */ + private function edebug($str) { + if ($this->Debugoutput == "error_log") { + error_log($str); + } else { + echo $str; + } + } /** * Initialize the class so that the data is in a known state. * @access public - * @return void + * @return SMTP */ public function __construct() { $this->smtp_conn = 0; @@ -107,6 +150,9 @@ class SMTP { * SMTP CODE SUCCESS: 220 * SMTP CODE FAILURE: 421 * @access public + * @param string $host + * @param int $port + * @param int $tval * @return bool */ public function Connect($host, $port = 0, $tval = 30) { @@ -136,21 +182,26 @@ class SMTP { "errno" => $errno, "errstr" => $errstr); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": $errstr ($errno)" . $this->CRLF . '
    '; + $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": $errstr ($errno)" . $this->CRLF . '
    '); } return false; } // SMTP server can take longer to respond, give longer timeout for first read // Windows does not have support for this timeout function - if(substr(PHP_OS, 0, 3) != "WIN") - socket_set_timeout($this->smtp_conn, $tval, 0); + if(substr(PHP_OS, 0, 3) != "WIN") { + $max = ini_get('max_execution_time'); + if ($max != 0 && $tval > $max) { // don't bother if unlimited + @set_time_limit($tval); + } + stream_set_timeout($this->smtp_conn, $tval, 0); + } // get any announcement $announce = $this->get_lines(); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $announce . $this->CRLF . '
    '; + $this->edebug("SMTP -> FROM SERVER:" . $announce . $this->CRLF . '
    '); } return true; @@ -179,7 +230,7 @@ class SMTP { $code = substr($rply,0,3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
    '; + $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
    '); } if($code != 220) { @@ -188,7 +239,7 @@ class SMTP { "smtp_code" => $code, "smtp_msg" => substr($rply,4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '; + $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '); } return false; } @@ -205,60 +256,164 @@ class SMTP { * Performs SMTP authentication. Must be run after running the * Hello() method. Returns true if successfully authenticated. * @access public + * @param string $username + * @param string $password + * @param string $authtype + * @param string $realm + * @param string $workstation * @return bool */ - public function Authenticate($username, $password) { - // Start authentication - fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 334) { - $this->error = - array("error" => "AUTH not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '; - } - return false; + public function Authenticate($username, $password, $authtype='LOGIN', $realm='', $workstation='') { + if (empty($authtype)) { + $authtype = 'LOGIN'; } - // Send encoded username - fputs($this->smtp_conn, base64_encode($username) . $this->CRLF); + switch ($authtype) { + case 'PLAIN': + // Start authentication + fputs($this->smtp_conn,"AUTH PLAIN" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 334) { + $this->error = + array("error" => "AUTH not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '); + } + return false; + } + // Send encoded username and password + fputs($this->smtp_conn, base64_encode("\0".$username."\0".$password) . $this->CRLF); - $rply = $this->get_lines(); - $code = substr($rply,0,3); + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 235) { + $this->error = + array("error" => "Authentication not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '); + } + return false; + } + break; + case 'LOGIN': + // Start authentication + fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 334) { + $this->error = + array("error" => "AUTH not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '); + } + return false; + } + + // Send encoded username + fputs($this->smtp_conn, base64_encode($username) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 334) { + $this->error = + array("error" => "Username not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '); + } + return false; + } + + // Send encoded password + fputs($this->smtp_conn, base64_encode($password) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 235) { + $this->error = + array("error" => "Password not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '); + } + return false; + } + break; + case 'NTLM': + /* + * ntlm_sasl_client.php + ** Bundled with Permission + ** + ** How to telnet in windows: http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx + ** PROTOCOL Documentation http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication + */ + require_once('ntlm_sasl_client.php'); + $temp = new stdClass(); + $ntlm_client = new ntlm_sasl_client_class; + if(! $ntlm_client->Initialize($temp)){//let's test if every function its available + $this->error = array("error" => $temp->error); + if($this->do_debug >= 1) { + $this->edebug("You need to enable some modules in your php.ini file: " . $this->error["error"] . $this->CRLF); + } + return false; + } + $msg1 = $ntlm_client->TypeMsg1($realm, $workstation);//msg1 + + fputs($this->smtp_conn,"AUTH NTLM " . base64_encode($msg1) . $this->CRLF); - if($code != 334) { - $this->error = - array("error" => "Username not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '; - } - return false; + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + + if($code != 334) { + $this->error = + array("error" => "AUTH not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF); + } + return false; + } + + $challange = substr($rply,3);//though 0 based, there is a white space after the 3 digit number....//msg2 + $challange = base64_decode($challange); + $ntlm_res = $ntlm_client->NTLMResponse(substr($challange,24,8),$password); + $msg3 = $ntlm_client->TypeMsg3($ntlm_res,$username,$realm,$workstation);//msg3 + // Send encoded username + fputs($this->smtp_conn, base64_encode($msg3) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 235) { + $this->error = + array("error" => "Could not authenticate", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF); + } + return false; + } + break; } - - // Send encoded password - fputs($this->smtp_conn, base64_encode($password) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 235) { - $this->error = - array("error" => "Password not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '; - } - return false; - } - return true; } @@ -273,7 +428,7 @@ class SMTP { if($sock_status["eof"]) { // the socket is valid but we are not connected if($this->do_debug >= 1) { - echo "SMTP -> NOTICE:" . $this->CRLF . "EOF caught while checking if connected"; + $this->edebug("SMTP -> NOTICE:" . $this->CRLF . "EOF caught while checking if connected"); } $this->Close(); return false; @@ -321,6 +476,7 @@ class SMTP { * SMTP CODE FAILURE: 451,554 * SMTP CODE ERROR : 500,501,503,421 * @access public + * @param string $msg_data * @return bool */ public function Data($msg_data) { @@ -338,7 +494,7 @@ class SMTP { $code = substr($rply,0,3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
    '; + $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
    '); } if($code != 354) { @@ -347,7 +503,7 @@ class SMTP { "smtp_code" => $code, "smtp_msg" => substr($rply,4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '; + $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '); } return false; } @@ -432,7 +588,7 @@ class SMTP { $code = substr($rply,0,3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
    '; + $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
    '); } if($code != 250) { @@ -441,7 +597,7 @@ class SMTP { "smtp_code" => $code, "smtp_msg" => substr($rply,4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '; + $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '); } return false; } @@ -458,6 +614,7 @@ class SMTP { * SMTP CODE SUCCESS: 250 * SMTP CODE ERROR : 500, 501, 504, 421 * @access public + * @param string $host * @return bool */ public function Hello($host = '') { @@ -488,6 +645,8 @@ class SMTP { /** * Sends a HELO/EHLO command. * @access private + * @param string $hello + * @param string $host * @return bool */ private function SendHello($hello, $host) { @@ -497,7 +656,7 @@ class SMTP { $code = substr($rply,0,3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER: " . $rply . $this->CRLF . '
    '; + $this->edebug("SMTP -> FROM SERVER: " . $rply . $this->CRLF . '
    '); } if($code != 250) { @@ -506,7 +665,7 @@ class SMTP { "smtp_code" => $code, "smtp_msg" => substr($rply,4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '; + $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '); } return false; } @@ -528,6 +687,7 @@ class SMTP { * SMTP CODE SUCCESS: 552,451,452 * SMTP CODE SUCCESS: 500,501,421 * @access public + * @param string $from * @return bool */ public function Mail($from) { @@ -539,14 +699,14 @@ class SMTP { return false; } - $useVerp = ($this->do_verp ? "XVERP" : ""); + $useVerp = ($this->do_verp ? " XVERP" : ""); fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF); $rply = $this->get_lines(); $code = substr($rply,0,3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
    '; + $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
    '); } if($code != 250) { @@ -555,7 +715,7 @@ class SMTP { "smtp_code" => $code, "smtp_msg" => substr($rply,4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '; + $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '); } return false; } @@ -571,6 +731,7 @@ class SMTP { * SMTP CODE SUCCESS: 221 * SMTP CODE ERROR : 500 * @access public + * @param bool $close_on_error * @return bool */ public function Quit($close_on_error = true) { @@ -589,7 +750,7 @@ class SMTP { $byemsg = $this->get_lines(); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $byemsg . $this->CRLF . '
    '; + $this->edebug("SMTP -> FROM SERVER:" . $byemsg . $this->CRLF . '
    '); } $rval = true; @@ -603,7 +764,7 @@ class SMTP { "smtp_rply" => substr($byemsg,4)); $rval = false; if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $e["error"] . ": " . $byemsg . $this->CRLF . '
    '; + $this->edebug("SMTP -> ERROR: " . $e["error"] . ": " . $byemsg . $this->CRLF . '
    '); } } @@ -624,6 +785,7 @@ class SMTP { * SMTP CODE FAILURE: 550,551,552,553,450,451,452 * SMTP CODE ERROR : 500,501,503,421 * @access public + * @param string $to * @return bool */ public function Recipient($to) { @@ -641,7 +803,7 @@ class SMTP { $code = substr($rply,0,3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
    '; + $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
    '); } if($code != 250 && $code != 251) { @@ -650,7 +812,7 @@ class SMTP { "smtp_code" => $code, "smtp_msg" => substr($rply,4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '; + $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '); } return false; } @@ -684,7 +846,7 @@ class SMTP { $code = substr($rply,0,3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
    '; + $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
    '); } if($code != 250) { @@ -693,7 +855,7 @@ class SMTP { "smtp_code" => $code, "smtp_msg" => substr($rply,4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '; + $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '); } return false; } @@ -715,6 +877,7 @@ class SMTP { * SMTP CODE SUCCESS: 552,451,452 * SMTP CODE SUCCESS: 500,501,502,421 * @access public + * @param string $from * @return bool */ public function SendAndMail($from) { @@ -732,7 +895,7 @@ class SMTP { $code = substr($rply,0,3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
    '; + $this->edebug("SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
    '); } if($code != 250) { @@ -741,7 +904,7 @@ class SMTP { "smtp_code" => $code, "smtp_msg" => substr($rply,4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '; + $this->edebug("SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
    '); } return false; } @@ -765,7 +928,7 @@ class SMTP { $this->error = array("error" => "This method, TURN, of the SMTP ". "is not implemented"); if($this->do_debug >= 1) { - echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF . '
    '; + $this->edebug("SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF . '
    '); } return false; } @@ -794,21 +957,47 @@ class SMTP { */ private function get_lines() { $data = ""; - while($str = @fgets($this->smtp_conn,515)) { + $endtime = 0; + /* If for some reason the fp is bad, don't inf loop */ + if (!is_resource($this->smtp_conn)) { + return $data; + } + stream_set_timeout($this->smtp_conn, $this->Timeout); + if ($this->Timelimit > 0) { + $endtime = time() + $this->Timelimit; + } + while(is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { + $str = @fgets($this->smtp_conn,515); if($this->do_debug >= 4) { - echo "SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '
    '; - echo "SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '
    '; + $this->edebug("SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '
    '); + $this->edebug("SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '
    '); } $data .= $str; if($this->do_debug >= 4) { - echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF . '
    '; + $this->edebug("SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF . '
    '); } // if 4th character is a space, we are done reading, break the loop if(substr($str,3,1) == " ") { break; } + // Timed-out? Log and break + $info = stream_get_meta_data($this->smtp_conn); + if ($info['timed_out']) { + if($this->do_debug >= 4) { + $this->edebug("SMTP -> get_lines(): timed-out (" . $this->Timeout . " seconds)
    "); + } + break; + } + // Now check if reads took too long + if ($endtime) { + if (time() > $endtime) { + if($this->do_debug >= 4) { + $this->edebug("SMTP -> get_lines(): timelimit reached (" . $this->Timelimit . " seconds)
    "); + } + break; + } + } } return $data; } } - -?> \ No newline at end of file +?> diff --git a/e107_languages/English/admin/help/mailout.php b/e107_languages/English/admin/help/mailout.php index 98a5d547a..7c741b52c 100644 --- a/e107_languages/English/admin/help/mailout.php +++ b/e107_languages/English/admin/help/mailout.php @@ -1,85 +1,85 @@ -tp->toDB(varset($_GET['mode'],'makemail')); - - switch ($action) - { - case 'justone' : - $text = 'Send mail with constraints specified by an optional plugin'; - break; - case 'debug' : - $text = 'For devs only. Not used at present'; - break; - case 'saved' : - $text = 'Select and use a saved email template to send a mailshot. Delete any template no longer required'; - break; - case 'pending' : - $text = 'List of mailshots released for sending, together with current status. The mail scheduler task will process these emails as it is able, taking account of - the earliest and latest sending dates you set'; - break; - case 'held' : - $text = 'List of emails which have been prepared for sending, but not yet released'; - break; - case 'sent' : - $text = 'List of completed mailshots. Allows you to see the sending results.
    '; - break; - case 'savedmail' : - case 'makemail' : - $text = 'Create an email, give it a meaningful title, and select the list of recipients. You can save everything as a template for later, or send immediately.
    '; - $text .= 'Email addresses may be contributed by plugins (such as newsletter), and duplicates are removed when the mail is sent
    '; - $text .= 'Any attachment is selected from the list of valid downloads.
    '; - $text .= 'Mail may be sent as plain text (most universal, and least at risk of being classed as spam), or as HTML (in which case a plain text alternative is automatically generated). The theme style - may optionally be added to the email'; - break; - case 'recipients' : - $text = 'Shows all recipients or potential recipients of an email, together with current status'; - break; - case 'prefs' : - $text = 'Configure mailshot options.
    - A test email is sent using the current method and settings.

    '; - $text .= 'Emailing Method
    - Use SMTP to send mail if possible. The settings will depend on your host\'s mail server.

    '; - $text .= 'Default email format
    - Emails may be sent either in plain text only, or in HTML format. The latter generally gives a better appearance, but is more prone to being filtered by various - security measures. If you select HTML, a separate plain text part is added.

    '; - $text .= 'Bulk mail controls
    - The values you set here will depend on your host, and on the number of emails you send; it may be possible to set all values to zero so that the - mail queue is emptied virtually instantly.

    '; - $text .= 'Bounced Emails
    - You can specify an email address to receive the return response when an email is undeliverable. If you have control over your server, you can specify the - separate scheduler-driven auto-processing script; this receives bounce messages as they arrive, and updates status instantly. Otherwise you can specify a separate email account, - which can be checked either periodically (using the scheduler), or manually via the user options menu. Normally this will be a standard - POP3 account; use the TLS-related options only if specifically required by your host

    '; - $text .= 'Email Address Sources
    - If you have additional mail-related plugins, you can select which of them may contribute email addresses to the list.

    '; - $text .= 'Logging
    - The logging option creates a text file in the system log directory. This must be deleted periodically. The \'logging - only\' options allow you to see exactly who would receive emails if actually sent. The \'with errors\' option fails every - 7th email, primarily for testing'; - break; - case 'maint' : - $text = 'Maintenance functions for the mail database'; - break; - default : - $text = 'Undocumented option'; - } - -$ns -> tablerender('Mail Help', $text); +tp->toDB(varset($_GET['mode'],'makemail')); + + switch ($action) + { + case 'justone' : + $text = 'Send mail with constraints specified by an optional plugin'; + break; + case 'debug' : + $text = 'For devs only. Not used at present'; + break; + case 'saved' : + $text = 'Select and use a saved email template to send a mailshot. Delete any template no longer required'; + break; + case 'pending' : + $text = 'List of mailshots released for sending, together with current status. The mail scheduler task will process these emails as it is able, taking account of + the earliest and latest sending dates you set'; + break; + case 'held' : + $text = 'List of emails which have been prepared for sending, but not yet released'; + break; + case 'sent' : + $text = 'List of completed mailshots. Allows you to see the sending results.
    '; + break; + case 'savedmail' : + case 'makemail' : + $text = 'Create an email, give it a meaningful title, and select the list of recipients. You can save everything as a template for later, or send immediately.
    '; + $text .= 'Email addresses may be contributed by plugins (such as newsletter), and duplicates are removed when the mail is sent
    '; + $text .= 'Any attachment is selected from the list of valid downloads.
    '; + $text .= 'Mail may be sent as plain text (most universal, and least at risk of being classed as spam), or as HTML (in which case a plain text alternative is automatically generated). The theme style + may optionally be added to the email. Alternatively a predefined template can be selected.'; + break; + case 'recipients' : + $text = 'Shows all recipients or potential recipients of an email, together with current status'; + break; + case 'prefs' : + $text = 'Configure mailshot options.
    + A test email is sent using the current method and settings.

    '; + $text .= 'Emailing Method
    + Use SMTP to send mail if possible. The settings will depend on your host\'s mail server.

    '; + $text .= 'Default email format
    + Emails may be sent either in plain text only, or in HTML format. The latter generally gives a better appearance, but is more prone to being filtered by various + security measures. If you select HTML, a separate plain text part is added.

    '; + $text .= 'Bulk mail controls
    + The values you set here will depend on your host, and on the number of emails you send; it may be possible to set all values to zero so that the + mail queue is emptied virtually instantly.

    '; + $text .= 'Bounced Emails
    + You can specify an email address to receive the return response when an email is undeliverable. If you have control over your server, you can specify the + separate scheduler-driven auto-processing script; this receives bounce messages as they arrive, and updates status instantly. Otherwise you can specify a separate email account, + which can be checked either periodically (using the scheduler), or manually via the user options menu. Normally this will be a standard + POP3 account; use the TLS-related options only if specifically required by your host

    '; + $text .= 'Email Address Sources
    + If you have additional mail-related plugins, you can select which of them may contribute email addresses to the list.

    '; + $text .= 'Logging
    + The logging option creates a text file in the system log directory. This must be deleted periodically. The \'logging + only\' options allow you to see exactly who would receive emails if actually sent. The \'with errors\' option fails every + 7th email, primarily for testing'; + break; + case 'maint' : + $text = 'Maintenance functions for the mail database'; + break; + default : + $text = 'Undocumented option'; + } + +$ns -> tablerender('Mail Help', $text); diff --git a/e107_languages/English/admin/lan_mailout.php b/e107_languages/English/admin/lan_mailout.php index 6a7caed75..f03318572 100644 --- a/e107_languages/English/admin/lan_mailout.php +++ b/e107_languages/English/admin/lan_mailout.php @@ -1,286 +1,291 @@ -e107.htaccess to .htaccess in'); -define('LAN_MAILOUT_41', 'before sending mail from this page.'); -define('LAN_MAILOUT_42', 'Warning'); -define('LAN_MAILOUT_43', 'Username'); -define('LAN_MAILOUT_44', 'User Login'); -define('LAN_MAILOUT_45', 'User Email'); -define('LAN_MAILOUT_46', 'User-Match'); -define('LAN_MAILOUT_47', 'contains'); -define('LAN_MAILOUT_48', 'equals'); -define('LAN_MAILOUT_49', 'Id'); -define('LAN_MAILOUT_50', 'Author'); -define('LAN_MAILOUT_51', 'Subject'); -define('LAN_MAILOUT_52', 'Last mod'); -define('LAN_MAILOUT_53', 'Admins'); -define('LAN_MAILOUT_54', 'Self'); -define('LAN_MAILOUT_55', 'Userclass'); -define('LAN_MAILOUT_56', 'Last Visit (dd-mm-yy)'); -define('LAN_MAILOUT_57', 'Send bulk SMTP emails in blocks'); // SMTP KeepAlive option -//define('LAN_MAILOUT_58', 'There is a problem with the attachment:'); -//define('LAN_MAILOUT_59', 'Mailing Progress'); -//define('LAN_MAILOUT_60', 'Sending...'); -//define('LAN_MAILOUT_61', 'There are no remaining emails to be sent.'); -//define('LAN_MAILOUT_62', 'Emails sent:'); -//define('LAN_MAILOUT_63', 'Emails failed:'); -//define('LAN_MAILOUT_64', 'Total time elapsed:'); -//define('LAN_MAILOUT_65', 'seconds'); -//define('LAN_MAILOUT_66', 'Cancelled Successfully'); -define('LAN_MAILOUT_67', 'The email could not be sent. Please review your SMTP settings, or select another mailing method and try again.'); -define('LAN_MAILOUT_68','Registered Users'); -define('LAN_MAILOUT_69','matches, after '); -define('LAN_MAILOUT_70',' duplicates stripped.'); -define('LAN_MAILOUT_71','Total emails to send'); -define('LAN_MAILOUT_72','Mailshot logging'); -define('LAN_MAILOUT_73','No logging'); -define('LAN_MAILOUT_74','Logging only (no send)'); -define('LAN_MAILOUT_75','Log and send'); -define('LAN_MAILOUT_76','Include email info in log'); -define('LAN_MAILOUT_77','Supplementary email address sources'); -define('LAN_MAILOUT_78','Mailshot Status'); -define('LAN_MAILOUT_79','No mailshots to display'); -define('LAN_MAILOUT_80','Date'); -define('LAN_MAILOUT_81','The email has been successfully sent, please check your inbox.'); -define('LAN_MAILOUT_82','Mails sent'); -define('LAN_MAILOUT_83','Mails to go'); -define('LAN_MAILOUT_84','Mail ID'); -define('LAN_MAILOUT_85','Originator'); -define('LAN_MAILOUT_86','Re-send'); -define('LAN_MAILOUT_87','SMTP Server'); -define('LAN_MAILOUT_88','SMTP Username'); -define('LAN_MAILOUT_89','SMTP Password'); -define('LAN_MAILOUT_90','SMTP Features'); -define('LAN_MAILOUT_91','POP before SMTP'); -define('LAN_MAILOUT_92','SSL'); -define('LAN_MAILOUT_93','TLS'); -define('LAN_MAILOUT_94','(Use SSL for gmail/googlemail)'); -define('LAN_MAILOUT_95','Use VERP for bulk mailing'); -define('LAN_MAILOUT_96','none'); -define('LAN_MAILOUT_97','Mailer Results'); -define('LAN_MAILOUT_98','Orphaned entries'); -define('LAN_MAILOUT_99','Confirm retry mailshot'); -define('LAN_MAILOUT_100','Message'); -define('LAN_MAILOUT_101','Email Detail'); -define('LAN_MAILOUT_102','Detail of mailshot'); -define('LAN_MAILOUT_103','Results of attempts to send'); -define('LAN_MAILOUT_104','No attempt to send, or error saving result'); -define('LAN_MAILOUT_105','Details of up to 10 failures'); -define('LAN_MAILOUT_106','The email could not be sent. It appears that your server is not correctly configured to send emails, please try again using SMTP, or contact your hosts and ask them to check their sendmail / email server settings.'); -define('LAN_MAILOUT_107','at'); -define('LAN_MAILOUT_108','Result'); -define('LAN_MAILOUT_109','Show detail'); -define('LAN_MAILOUT_110','Send test email'); -define('LAN_MAILOUT_111','Email Title (not sent)'); -define('LAN_MAILOUT_112','Click to send email to'); -define('LAN_MAILOUT_113','Test email from'); -define('LAN_MAILOUT_114',"This is a test email, it appears that your email settings are working ok!\n\nRegards\nfrom the e107 website system."); -define('LAN_MAILOUT_115','Emailing method'); -define('LAN_MAILOUT_116','If unsure, leave as php'); -define('LAN_MAILOUT_117','complete'); -define('LAN_MAILOUT_118','Click on \'proceed\' to start sending emails. Click on \'cancel\' to stop the run. Once complete, select another page. Unsent emails cal be viewed through the \'Mailshot status\' screen'); -define('LAN_MAILOUT_119','Logging only, with errors'); -define('LAN_MAILOUT_120','Account type'); -define('LAN_MAILOUT_121','Standard POP3'); -define('LAN_MAILOUT_122','POP3, TLS disabled'); -define('LAN_MAILOUT_123','POP3 with TLS'); -define('LAN_MAILOUT_124','IMAP'); -define('LAN_MAILOUT_125', 'Text only'); -define('LAN_MAILOUT_126', 'Text and HTML'); -define('LAN_MAILOUT_127', 'Include theme'); -define('LAN_MAILOUT_128', 'Send Error'); -define('LAN_MAILOUT_129', 'Expiry Date'); -define('LAN_MAILOUT_130', 'Creation Date'); -define('LAN_MAILOUT_131', 'Sending Started'); -define('LAN_MAILOUT_132', 'Sending Complete'); -define('LAN_MAILOUT_133', 'Source'); -define('LAN_MAILOUT_134', 'Priority'); -define('LAN_MAILOUT_135', 'Title'); -define('LAN_MAILOUT_136', 'Mailout Status'); -define('LAN_MAILOUT_137', 'Mail Ref'); -define('LAN_MAILOUT_138', 'Email status'); -define('LAN_MAILOUT_139', 'Date active'); -define('LAN_MAILOUT_140', 'Recipient Email'); -define('LAN_MAILOUT_141', 'Recipient Name'); -define('LAN_MAILOUT_142', 'Recipient User ID'); -define('LAN_MAILOUT_143', 'Email ref.'); -define('LAN_MAILOUT_144', 'Bounced'); -define('LAN_MAILOUT_145', 'New email saved'); -define('LAN_MAILOUT_146', 'Error saving email'); -define('LAN_MAILOUT_147', 'Email updated'); -define('LAN_MAILOUT_148', 'User values'); -define('LAN_MAILOUT_149', 'Sender Email'); -define('LAN_MAILOUT_150', 'Sender Name'); -define('LAN_MAILOUT_151', 'Copy to'); -define('LAN_MAILOUT_152', 'Blind copy to'); -define('LAN_MAILOUT_153', 'Attachments'); -define('LAN_MAILOUT_154', 'Send Format'); -define('LAN_MAILOUT_155', 'Selectors'); -define('LAN_MAILOUT_156', 'Maximum number of emails to send per scheduler tick'); -define('LAN_MAILOUT_157', 'Value will depend on a number of factors, including how often your mail queue scheduler job is triggered and the rate at which your ISP will accept outgoing mail. Zero to clear queue each time'); -define('LAN_MAILOUT_158', 'Send now'); -define('LAN_MAILOUT_159', 'Hold email'); -define('LAN_MAILOUT_160', 'Cancel send'); -define('LAN_MAILOUT_161', 'IMPORTANT! This file appears to not exist'); -define('LAN_MAILOUT_162', 'IMPORTANT! You need to make this file executable'); -define('LAN_MAILOUT_163', 'Edit/Send Mail'); -define('LAN_MAILOUT_164', 'Email information not found'); -define('LAN_MAILOUT_165', 'Confirm delete the following stored email, including any recipient records'); -define('LAN_MAILOUT_166', 'General error deleting mail ref: --ID--'); -define('LAN_MAILOUT_167', 'Error deleting mail content ref: --ID--'); -define('LAN_MAILOUT_168', 'Mail content deleted ref: --ID--'); -define('LAN_MAILOUT_169', 'Error deleting mail recipients ref: --ID--'); -define('LAN_MAILOUT_170', 'Deleted --NUM-- recipients for mail ref: --ID--'); -define('LAN_MAILOUT_171', 'Confirm email delete'); -define('LAN_MAILOUT_172', 'Mail Type/Status'); -define('LAN_MAILOUT_173', 'Recipients'); -define('LAN_MAILOUT_174', 'Security check fail: --ID-- --CHECK--'); -define('LAN_MAILOUT_175', 'Before'); -define('LAN_MAILOUT_176', 'Equal to'); -define('LAN_MAILOUT_177', 'After'); -define('LAN_MAILOUT_178', 'Last site visit'); -define('LAN_MAILOUT_179', 'Confirm email send'); -define('LAN_MAILOUT_180', 'Selection criteria:'); -define('LAN_MAILOUT_181', 'Show recipients'); -define('LAN_MAILOUT_182', 'Tidy database tables'); -define('LAN_MAILOUT_183', 'Error tidying database'); -define('LAN_MAILOUT_184', 'Database tidied'); -define('LAN_MAILOUT_185', 'Emails added to send queue'); -define('LAN_MAILOUT_186', 'General error putting mail ref: --ID-- on hold'); -define('LAN_MAILOUT_187', 'Email --ID-- put on hold'); -define('LAN_MAILOUT_188', 'General error sending mail ref: --ID--'); -define('LAN_MAILOUT_189', 'Test address'); - -// Admin menu text -define('LAN_MAILOUT_190', 'Create/Send Mail'); -define('LAN_MAILOUT_191', 'Saved emails'); -define('LAN_MAILOUT_192', 'Completed Mailshots'); -define('LAN_MAILOUT_193', 'Pending Mailshots'); -define('LAN_MAILOUT_194', 'Held Mailshots'); -define('LAN_MAILOUT_195', ''); -define('LAN_MAILOUT_196', ''); - -// Block of error messages kept together -define('LAN_MAILOUT_200', 'No subject specified'); -define('LAN_MAILOUT_201', 'No meaningful data for email'); -define('LAN_MAILOUT_202', 'No text in email body'); -define('LAN_MAILOUT_203', 'No sender name specified'); -define('LAN_MAILOUT_204', 'No sender email address specified'); -define('LAN_MAILOUT_205', 'Email send format error'); -define('LAN_MAILOUT_206', 'Invalid mail ID (--ID--) specified'); -define('LAN_MAILOUT_207', ''); -define('LAN_MAILOUT_208', ''); -define('LAN_MAILOUT_209', ''); -define('LAN_MAILOUT_210', ''); - -// Block of status messages kept together -define('LAN_MAILOUT_211', 'Sent'); -define('LAN_MAILOUT_212', 'Failed'); -define('LAN_MAILOUT_213', 'Bounced'); -define('LAN_MAILOUT_214', 'To send'); -define('LAN_MAILOUT_215', 'Saved'); -define('LAN_MAILOUT_216', 'Code error'); -define('LAN_MAILOUT_217', 'Held'); -define('LAN_MAILOUT_218', 'Cancelled'); -define('LAN_MAILOUT_219', 'Partial'); - -// General messages continued -define('LAN_MAILOUT_220', 'Email ID --ID-- cancelled'); -define('LAN_MAILOUT_221', 'Error cancelling email with ID --ID--'); -define('LAN_MAILOUT_222', 'Default email format'); -define('LAN_MAILOUT_223', '(Used for some non-bulk emails)'); -define('LAN_MAILOUT_224', 'Inc. Images'); -define('LAN_MAILOUT_225', 'Include images in email'); -define('LAN_MAILOUT_226', '--COUNT-- orphaned recipient record(s) removed'); -define('LAN_MAILOUT_227', 'Deleted --COUNT-- records from --TABLE--'); -define('LAN_MAILOUT_228', '--COUNT-- anomalies in mail_content corrected; records: --RECORDS--'); -define('LAN_MAILOUT_229', 'Email ID --ID-- put on hold'); -define('LAN_MAILOUT_230', 'Error holding email with ID --ID--'); -define('LAN_MAILOUT_231', 'Processing method'); -define('LAN_MAILOUT_232', 'None'); -define('LAN_MAILOUT_233', 'Auto-process script'); -define('LAN_MAILOUT_234', 'Mail account'); -define('LAN_MAILOUT_235', '(Your server has to be configured to use this script)'); -define('LAN_MAILOUT_236', 'Last Bounce Processed'); -define('LAN_MAILOUT_237', 'Summary counters updated on --COUNT-- emails'); -define('LAN_MAILOUT_238', 'Earliest time to send'); -define('LAN_MAILOUT_239', 'Latest time to send'); -define('LAN_MAILOUT_240', 'Notify me when run complete'); -define('LAN_MAILOUT_241', ' (This is in addition to the standard e107 notify options)'); -define('LAN_MAILOUT_242', 'Additional options (only when sending)'); -define('LAN_MAILOUT_243', 'Notify'); -define('LAN_MAILOUT_244', 'Email sent: '); -define('LAN_MAILOUT_245', 'Check for bounces automatically'); -define('LAN_MAILOUT_246', 'If checked, you will need to activate the task in the scheduler'); -define('LAN_MAILOUT_247', 'Email information:'); -define('LAN_MAILOUT_248', 'Completion status: '); -define('LAN_MAILOUT_249', 'Send results:'); -define('LAN_MAILOUT_250', '--- End of notification ---'); -define('LAN_MAILOUT_251', 'Copy and edit'); -define('LAN_MAILOUT_252', 'Does various consistency checks on the data, corrects counts, deletes temporary data'); -define('LAN_MAILOUT_253', 'No recipients found - check for database corruption'); -define('LAN_MAILOUT_254', ''); - - -define('LAN_SEND', 'Send'); -define('LAN_HOLD', 'Hold'); -define('LAN_SUBMIT', 'Do it!'); - +e107.htaccess to .htaccess in'); +define('LAN_MAILOUT_41', 'before sending mail from this page.'); +define('LAN_MAILOUT_42', 'Warning'); +define('LAN_MAILOUT_43', 'Username'); +define('LAN_MAILOUT_44', 'User Login'); +define('LAN_MAILOUT_45', 'User Email'); +define('LAN_MAILOUT_46', 'User-Match'); +define('LAN_MAILOUT_47', 'contains'); +define('LAN_MAILOUT_48', 'equals'); +define('LAN_MAILOUT_49', 'Id'); +define('LAN_MAILOUT_50', 'Author'); +define('LAN_MAILOUT_51', 'Subject'); +define('LAN_MAILOUT_52', 'Last mod'); +define('LAN_MAILOUT_53', 'Admins'); +define('LAN_MAILOUT_54', 'Self'); +define('LAN_MAILOUT_55', 'Userclass'); +define('LAN_MAILOUT_56', 'Last Visit (dd-mm-yy)'); +define('LAN_MAILOUT_57', 'Send bulk SMTP emails in blocks'); // SMTP KeepAlive option +//define('LAN_MAILOUT_58', 'There is a problem with the attachment:'); +//define('LAN_MAILOUT_59', 'Mailing Progress'); +//define('LAN_MAILOUT_60', 'Sending...'); +//define('LAN_MAILOUT_61', 'There are no remaining emails to be sent.'); +//define('LAN_MAILOUT_62', 'Emails sent:'); +//define('LAN_MAILOUT_63', 'Emails failed:'); +//define('LAN_MAILOUT_64', 'Total time elapsed:'); +//define('LAN_MAILOUT_65', 'seconds'); +//define('LAN_MAILOUT_66', 'Cancelled Successfully'); +define('LAN_MAILOUT_67', 'The email could not be sent. Please review your SMTP settings, or select another mailing method and try again.'); +define('LAN_MAILOUT_68','Registered Users'); +define('LAN_MAILOUT_69','matches, after '); +define('LAN_MAILOUT_70',' duplicates stripped.'); +define('LAN_MAILOUT_71','Total emails to send'); +define('LAN_MAILOUT_72','Mailshot logging'); +define('LAN_MAILOUT_73','No logging'); +define('LAN_MAILOUT_74','Logging only (no send)'); +define('LAN_MAILOUT_75','Log and send'); +define('LAN_MAILOUT_76','Include email info in log'); +define('LAN_MAILOUT_77','Supplementary email address sources'); +define('LAN_MAILOUT_78','Mailshot Status'); +define('LAN_MAILOUT_79','No mailshots to display'); +define('LAN_MAILOUT_80','Date'); +define('LAN_MAILOUT_81','The email has been successfully sent, please check your inbox.'); +define('LAN_MAILOUT_82','Mails sent'); +define('LAN_MAILOUT_83','Mails to go'); +define('LAN_MAILOUT_84','Mail ID'); +define('LAN_MAILOUT_85','Originator'); +define('LAN_MAILOUT_86','Re-send'); +define('LAN_MAILOUT_87','SMTP Server'); +define('LAN_MAILOUT_88','SMTP Username'); +define('LAN_MAILOUT_89','SMTP Password'); +define('LAN_MAILOUT_90','SMTP Features'); +define('LAN_MAILOUT_91','POP before SMTP'); +define('LAN_MAILOUT_92','SSL'); +define('LAN_MAILOUT_93','TLS'); +define('LAN_MAILOUT_94','(Use SSL for gmail/googlemail)'); +define('LAN_MAILOUT_95','Use VERP for bulk mailing'); +define('LAN_MAILOUT_96','none'); +define('LAN_MAILOUT_97','Mailer Results'); +define('LAN_MAILOUT_98','Orphaned entries'); +define('LAN_MAILOUT_99','Confirm retry mailshot'); +define('LAN_MAILOUT_100','Message'); +define('LAN_MAILOUT_101','Email Detail'); +define('LAN_MAILOUT_102','Detail of mailshot'); +define('LAN_MAILOUT_103','Results of attempts to send'); +define('LAN_MAILOUT_104','No attempt to send, or error saving result'); +define('LAN_MAILOUT_105','Details of up to 10 failures'); +define('LAN_MAILOUT_106','The email could not be sent. It appears that your server is not correctly configured to send emails, please try again using SMTP, or contact your hosts and ask them to check their sendmail / email server settings.'); +define('LAN_MAILOUT_107','at'); +define('LAN_MAILOUT_108','Result'); +define('LAN_MAILOUT_109','Show detail'); +define('LAN_MAILOUT_110','Send test email'); +define('LAN_MAILOUT_111','Email Title (not sent)'); +define('LAN_MAILOUT_112','Click to send email to'); +define('LAN_MAILOUT_113','Test email from'); +define('LAN_MAILOUT_114',"This is a test email, it appears that your email settings are working ok!\n\nRegards\nfrom the e107 website system."); +define('LAN_MAILOUT_115','Emailing method'); +define('LAN_MAILOUT_116','If unsure, leave as php'); +define('LAN_MAILOUT_117','complete'); +define('LAN_MAILOUT_118','Click on \'proceed\' to start sending emails. Click on \'cancel\' to stop the run. Once complete, select another page. Unsent emails cal be viewed through the \'Mailshot status\' screen'); +define('LAN_MAILOUT_119','Logging only, with errors'); +define('LAN_MAILOUT_120','Account type'); +define('LAN_MAILOUT_121','Standard POP3'); +define('LAN_MAILOUT_122','POP3, TLS disabled'); +define('LAN_MAILOUT_123','POP3 with TLS'); +define('LAN_MAILOUT_124','IMAP'); +define('LAN_MAILOUT_125', 'Text only'); +define('LAN_MAILOUT_126', 'Text and HTML'); +define('LAN_MAILOUT_127', 'Include theme'); +define('LAN_MAILOUT_128', 'Send Error'); +define('LAN_MAILOUT_129', 'Expiry Date'); +define('LAN_MAILOUT_130', 'Creation Date'); +define('LAN_MAILOUT_131', 'Sending Started'); +define('LAN_MAILOUT_132', 'Sending Complete'); +define('LAN_MAILOUT_133', 'Source'); +define('LAN_MAILOUT_134', 'Priority'); +define('LAN_MAILOUT_135', 'Title'); +define('LAN_MAILOUT_136', 'Mailout Status'); +define('LAN_MAILOUT_137', 'Mail Ref'); +define('LAN_MAILOUT_138', 'Email status'); +define('LAN_MAILOUT_139', 'Date active'); +define('LAN_MAILOUT_140', 'Recipient Email'); +define('LAN_MAILOUT_141', 'Recipient Name'); +define('LAN_MAILOUT_142', 'Recipient User ID'); +define('LAN_MAILOUT_143', 'Email ref.'); +define('LAN_MAILOUT_144', 'Bounced'); +define('LAN_MAILOUT_145', 'New email saved'); +define('LAN_MAILOUT_146', 'Error saving email'); +define('LAN_MAILOUT_147', 'Email updated'); +define('LAN_MAILOUT_148', 'User values'); +define('LAN_MAILOUT_149', 'Sender Email'); +define('LAN_MAILOUT_150', 'Sender Name'); +define('LAN_MAILOUT_151', 'Copy to'); +define('LAN_MAILOUT_152', 'Blind copy to'); +define('LAN_MAILOUT_153', 'Attachments'); +define('LAN_MAILOUT_154', 'Send Format'); +define('LAN_MAILOUT_155', 'Selectors'); +define('LAN_MAILOUT_156', 'Maximum number of emails to send per scheduler tick'); +define('LAN_MAILOUT_157', 'Value will depend on a number of factors, including how often your mail queue scheduler job is triggered and the rate at which your ISP will accept outgoing mail. Zero to clear queue each time'); +define('LAN_MAILOUT_158', 'Send now'); +define('LAN_MAILOUT_159', 'Hold email'); +define('LAN_MAILOUT_160', 'Cancel send'); +define('LAN_MAILOUT_161', 'IMPORTANT! This file appears to not exist'); +define('LAN_MAILOUT_162', 'IMPORTANT! You need to make this file executable'); +define('LAN_MAILOUT_163', 'Edit/Send Mail'); +define('LAN_MAILOUT_164', 'Email information not found'); +define('LAN_MAILOUT_165', 'Confirm delete the following stored email, including any recipient records'); +define('LAN_MAILOUT_166', 'General error deleting mail ref: --ID--'); +define('LAN_MAILOUT_167', 'Error deleting mail content ref: --ID--'); +define('LAN_MAILOUT_168', 'Mail content deleted ref: --ID--'); +define('LAN_MAILOUT_169', 'Error deleting mail recipients ref: --ID--'); +define('LAN_MAILOUT_170', 'Deleted --NUM-- recipients for mail ref: --ID--'); +define('LAN_MAILOUT_171', 'Confirm email delete'); +define('LAN_MAILOUT_172', 'Mail Type/Status'); +define('LAN_MAILOUT_173', 'Recipients'); +define('LAN_MAILOUT_174', 'Security check fail: --ID-- --CHECK--'); +define('LAN_MAILOUT_175', 'Before'); +define('LAN_MAILOUT_176', 'Equal to'); +define('LAN_MAILOUT_177', 'After'); +define('LAN_MAILOUT_178', 'Last site visit'); +define('LAN_MAILOUT_179', 'Confirm email send'); +define('LAN_MAILOUT_180', 'Selection criteria:'); +define('LAN_MAILOUT_181', 'Show recipients'); +define('LAN_MAILOUT_182', 'Tidy database tables'); +define('LAN_MAILOUT_183', 'Error tidying database'); +define('LAN_MAILOUT_184', 'Database tidied'); +define('LAN_MAILOUT_185', 'Emails added to send queue'); +define('LAN_MAILOUT_186', 'General error putting mail ref: --ID-- on hold'); +define('LAN_MAILOUT_187', 'Email --ID-- put on hold'); +define('LAN_MAILOUT_188', 'General error sending mail ref: --ID--'); +define('LAN_MAILOUT_189', 'Test address'); + +// Admin menu text +define('LAN_MAILOUT_190', 'Create/Send Mail'); +define('LAN_MAILOUT_191', 'Saved emails'); +define('LAN_MAILOUT_192', 'Completed Mailshots'); +define('LAN_MAILOUT_193', 'Pending Mailshots'); +define('LAN_MAILOUT_194', 'Held Mailshots'); +define('LAN_MAILOUT_195', ''); +define('LAN_MAILOUT_196', ''); + +// Block of error messages kept together +define('LAN_MAILOUT_200', 'No subject specified'); +define('LAN_MAILOUT_201', 'No meaningful data for email'); +define('LAN_MAILOUT_202', 'No text in email body'); +define('LAN_MAILOUT_203', 'No sender name specified'); +define('LAN_MAILOUT_204', 'No sender email address specified'); +define('LAN_MAILOUT_205', 'Email send format error'); +define('LAN_MAILOUT_206', 'Invalid mail ID (--ID--) specified'); +define('LAN_MAILOUT_207', 'Template load error'); +define('LAN_MAILOUT_208', 'Template conversion error'); +define('LAN_MAILOUT_209', ''); +define('LAN_MAILOUT_210', ''); + +// Block of status messages kept together +define('LAN_MAILOUT_211', 'Sent'); +define('LAN_MAILOUT_212', 'Failed'); +define('LAN_MAILOUT_213', 'Bounced'); +define('LAN_MAILOUT_214', 'To send'); +define('LAN_MAILOUT_215', 'Saved'); +define('LAN_MAILOUT_216', 'Code error'); +define('LAN_MAILOUT_217', 'Held'); +define('LAN_MAILOUT_218', 'Cancelled'); +define('LAN_MAILOUT_219', 'Partial'); + +// General messages continued +define('LAN_MAILOUT_220', 'Email ID --ID-- cancelled'); +define('LAN_MAILOUT_221', 'Error cancelling email with ID --ID--'); +define('LAN_MAILOUT_222', 'Default email format'); +define('LAN_MAILOUT_223', '(Used for some system-generated emails)'); +define('LAN_MAILOUT_224', 'Inc. Images'); +define('LAN_MAILOUT_225', 'Include images in email'); +define('LAN_MAILOUT_226', '--COUNT-- orphaned recipient record(s) removed'); +define('LAN_MAILOUT_227', 'Deleted --COUNT-- records from --TABLE--'); +define('LAN_MAILOUT_228', '--COUNT-- anomalies in mail_content corrected; records: --RECORDS--'); +define('LAN_MAILOUT_229', 'Email ID --ID-- put on hold'); +define('LAN_MAILOUT_230', 'Error holding email with ID --ID--'); +define('LAN_MAILOUT_231', 'Bounced emails - Processing method'); +define('LAN_MAILOUT_232', 'None'); +define('LAN_MAILOUT_233', 'Auto-process script'); +define('LAN_MAILOUT_234', 'Mail account'); +define('LAN_MAILOUT_235', '(Your server has to be configured to use this script)'); +define('LAN_MAILOUT_236', 'Last Bounce Processed'); +define('LAN_MAILOUT_237', 'Summary counters updated on --COUNT-- emails'); +define('LAN_MAILOUT_238', 'Earliest time to send'); +define('LAN_MAILOUT_239', 'Latest time to send'); +define('LAN_MAILOUT_240', 'Notify me when run complete'); +define('LAN_MAILOUT_241', ' (This is in addition to the standard e107 notify options)'); +define('LAN_MAILOUT_242', 'Additional options (only when sending)'); +define('LAN_MAILOUT_243', 'Notify'); +define('LAN_MAILOUT_244', 'Email sent: '); +define('LAN_MAILOUT_245', 'Check for bounces automatically'); +define('LAN_MAILOUT_246', 'If checked, you will need to activate the task in the scheduler'); +define('LAN_MAILOUT_247', 'Email information:'); +define('LAN_MAILOUT_248', 'Completion status: '); +define('LAN_MAILOUT_249', 'Send results:'); +define('LAN_MAILOUT_250', '--- End of notification ---'); +define('LAN_MAILOUT_251', 'Copy and edit'); +define('LAN_MAILOUT_252', 'Does various consistency checks on the data, corrects counts, deletes temporary data'); +define('LAN_MAILOUT_253', 'No recipients found - check for database corruption'); +define('LAN_MAILOUT_254', 'View templated email'); +define('LAN_MAILOUT_255', 'Templated Email, ID: '); +define('LAN_MAILOUT_256', 'Return'); +define('LAN_MAILOUT_257', 'Generated template'); +define('LAN_MAILOUT_258', 'Template: '); +define('LAN_MAILOUT_259', ''); + + +define('LAN_SEND', 'Send'); +define('LAN_HOLD', 'Hold'); +define('LAN_SUBMIT', 'Do it!'); + ?> \ No newline at end of file diff --git a/e107_languages/English/admin/lan_userclass2.php b/e107_languages/English/admin/lan_userclass2.php index 9c84cb981..1ee4b31bb 100644 --- a/e107_languages/English/admin/lan_userclass2.php +++ b/e107_languages/English/admin/lan_userclass2.php @@ -29,7 +29,7 @@ define("UCSLAN_15", "Create New Class"); define("UCSLAN_18", "Clear Class"); //define("UCSLAN_19", "Assign users to"); define("UCSLAN_20", "class"); -//define("UCSLAN_21", "User Class Settings"); +define("UCSLAN_21", "User Class Settings"); //define("UCSLAN_22", "Users - click to move ..."); //define("UCSLAN_23", "Users in this class ..."); define("UCSLAN_24", 'Class Manager'); diff --git a/e107_themes/templates/email_template.php b/e107_themes/templates/email_template.php index 5ecb942c2..29f954cbd 100644 --- a/e107_themes/templates/email_template.php +++ b/e107_themes/templates/email_template.php @@ -2,31 +2,118 @@ /* * 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) * - * Template for signup emails + * Templates for all emails * - * $Source: /cvs_backup/e107_0.8/e107_themes/templates/email_template.php,v $ - * $Revision$ - * $Date$ - * $Author$ + * $URL: $ + * $Revision: 11315 $ + * $Id: $ + */ + +/** + * + * @package e107 + * @subpackage e107_templates + * @version $Id: mail_manager_class.php 11315 2010-02-10 18:18:01Z secretr $; + * +*/ + + +/** + * This file defines the default templates for each type of email which may be sent. + * In general it is assumed that HTML emails are being sent (with a plain text alternate part), although simple plain text emails are also possible. + * + * Default values are defined for the key elements of an email: + * + * $EMAIL_HEADER - the first part of the email, usually defining the headers, and everything up to and including + * $EMAIL_FOOTER - the last part of the email - it may include a displayed footer, as well as and other 'closing' tags + * + * Taken as a pair, $EMAIL_HEADER.$EMAIL_FOOTER must generate standards-compliant XHTML + * + * $EMAIL_BODY - the body text of the email - essentially, the message. It gets sandwiched between $EMAIL_HEADER and $EMAIL_FOOTER + * This must generate standards-compliant XHTML in its own right, when taken with an appropriate header and footer section. + * Within the template definition, insert the shortcode '{BODY}' to indicate where the passed text of the email is to be stored. + * + * $EMAIL_OVERRIDES may optionally be defined, in which case it can override default mailout settings (see later). Only define this variable + * if you explicitly want overrides - a defined, but empty, variable may have unexpected consequences! + * + * $EMAIL_PLAINTEXT - an alternative template for the alternative text part of HTML emails. Set to empty string if hard-coded default to be used + * + * + * Templates may be defined for specific purposes + * Each template is given a name, which is the name of the variable. + * This variable may be a simple string, in which case it defines the email body, and is only available via code. + * Alternatively the variable may be an array, in which case each element of the array defines a different aspect of the email: + * + * $NAME['template_name'] is a user-friendly name shown in the mass mailer + * $NAME['template_type'] takes values (user|system|all) to define its purpose - only 'user' and 'all' templates are shown in the mass mailer + * $NAME['email_header'] defines the header - optional + * $NAME['email_footer'] defines the footer - optional + * $NAME['email_body'] defines the body text + * $NAME['email_overrides'] defines any mailout settings which are to be overridden (see later) - optional + * + * The format and functionality of these four main array elements correspond exactly to those of the defaults already described. + * + * The template need only define those variables which are to be overridden, in which case the default definitions will be used for the others. + * + * + * For templated HTML emails, a style sheet MUST be specified in the header field (if its required), in one of the following forms: + * + * {STYLESHEET} - embeds the stylesheet for the current site theme + * {STYLESHEET=filename,link} - embeds a link to the referenced stylesheet file + * {STYLESHEET=filename} - embeds the contents of the specified file + * {STYLESHEET=filename,embed} - embeds the contents of the specified file + * + * + * Where no style sheet is specified for an HTML-format email, the following applies: + * If 'emailstyle.css' exists in the current theme directory, it is used + * otherwise, the theme's 'style.css' is used + * + * The override variable is an array, which can override any of the following mailer parameters: +'mailer', 'smtp_server', 'smtp_username', 'smtp_password', 'sendmail', 'siteadminemail', 'siteadmin', 'smtp_pop3auth', +'SMTPDebug', 'subject', 'from', 'fromname', 'replyto', 'send_html', 'add_html_header', 'attachments', 'cc', 'bcc', +'bouncepath', 'returnreceipt', 'priority', 'extra_header', 'wordwrap', 'split' + +See e_HANDLER.mail.php for more information */ if (!defined('e107_INIT')) { exit; } -global $pref; + +// @TODO: Move signup email into templated form +$includeSiteButton = e107::getPref('sitebutton'); + + $SIGNUPEMAIL_SUBJECT = LAN_SIGNUP_96.' {SITENAME}'; $SIGNUPEMAIL_USETHEME = 1; // Use CSS STYLE from THEME: 0 = Off, 1 = external, 2 = embedded $SIGNUPEMAIL_LINKSTYLE = ''; // css to use on links eg. color:red; -//$SIGNUPEMAIL_IMAGES = e_IMAGE.$pref['sitebutton']; // comma separated paths to image to embed. referenced below with {IMAGE1} (IMAGE2} etc. Not required +//$SIGNUPEMAIL_IMAGES = e_IMAGE.$includeSiteButton; // comma separated paths to image to embed. referenced below with {IMAGE1} (IMAGE2} etc. Not required $SIGNUPEMAIL_CC = ""; // comma separated email addresses to put in CC of the signup email. $SIGNUPEMAIL_BCC = ""; // comma separated email addresses to put in BCC of the signup email. $SIGNUPEMAIL_ATTACHMENTS = ""; // files-path array of attachments. eg. array(e_FILE."myfile.zip",e_FILE."myotherfile.zip"); $SIGNUPEMAIL_BACKGROUNDIMAGE = ""; // absolute path to a background image eg. e_IMAGE."mybackground.jpg"; + +/*=========================================================================== + DEFAULT EMAIL TEMPLATE VALUES +=============================================================================*/ +/** +These defaults are used if not overridden by the requirements for a specific template. + +There are five defaults, which must exist, and must be named as follows: + $EMAIL_OVERRIDES - array of override settings; e.g. for mail server to use + $EMAIL_HEADER - string for the first part of an HTML email + $EMAIL_BODY - the 'body' text (usually a default here is meaningless!) + $EMAIL_FOOTER - a standard footer - could include a disclaimer, a link to the site + $EMAIL_PLAINTEXT - an alternative template for the alternative text part of HTML emails (if empty, alternate text is + derived from the HTLM body. + +In most cases only the body will be overridden; in this case it can be overridden using a variable rather than an array. +*/ /* Optional mailer admin preferences Override. The following mailer parameters can be overridden: 'mailer', 'smtp_server', 'smtp_username', 'smtp_password', 'sendmail', 'siteadminemail', 'siteadmin', 'smtp_pop3auth', @@ -34,32 +121,31 @@ Optional mailer admin preferences Override. The following mailer parameters can 'bouncepath', 'returnreceipt', 'priority', 'extra_header', 'wordwrap', 'split' See e_HANDLER.mail.php for more information + If required, uncomment the following block and add array elements for options to be overridden - array key is the option name DON'T put in empty fields unless you wish to set the value to an empty value! */ /* -global $EMAIL_OVERRIDES; $EMAIL_OVERRIDES = array( 'bouncepath' => 'some email address', 'returnreceipt' => 1 ); */ -/** - * Default HEADER for all emails - */ +// Not used in signup email $EMAIL_HEADER = " +{STYLESHEET} -
    +
    "; -/** - * Default FOOTER for all emails - */ +$EMAIL_BODY = 'Software malfunction - no email body text specified for template'; // Help debug + +// Not used in signup email $EMAIL_FOOTER = "

    {SITENAME=link} @@ -68,22 +154,43 @@ $EMAIL_FOOTER = " "; -/** - * Mass-Mailing HEADER (admin->mailout) - */ +$EMAIL_PLAINTEXT = ''; +/*=========================================================================== + TEMPLATES FOR SPECIFIC EMAIL TYPES +=============================================================================*/ + +/** +Each template is an array whose name must match that used in the code. +The array has two mandatory elements (name and type). +The array may have up to five optional elements, each of which overrides the corresponding default value if present +An empty element sets the field to empty. +An element that is not present results in the default being used. + +Elements are as follows: + 'template_name' - string - mandatory - a 'user-friendly' name for display + 'template_type' - string(user|system|all) - mandatory - 'all' and 'user' templates are available for selection in the bulk mailer + 'email_overrides' - an array + 'email_header' - string + 'email_body' - string + 'email_footer' - string + 'email_plainText' - string + +// If everything is standard apart from the body, the body can be defined as a simple variable + +*/ +//TODO - integrate into mailout routine +/* $MAILOUT_HEADER = " +{STYLESHEET} -
    +
    "; -/** - * Mass-Mailing FOOTER (admin->mailout) - */ $MAILOUT_FOOTER = "

    {SITENAME=link} @@ -92,35 +199,16 @@ $MAILOUT_FOOTER = " "; -/** - * Notification Email HEADER (admin->notify) - */ -//TODO - integrate into notification routine -$NOTIFY_HEADER = " - - - - - -
    -"; -/** - * Notification Email FOOTER (admin->notify) - */ -$NOTIFY_FOOTER = " -

    -{SITENAME=link} -
    - -"; - - -/* - * SIGNUP EMAIL TEMPLATE - BODY. */ + + +//------------------------------------------------------------- +// 'SIGNUP' TEMPLATE +//------------------------------------------------------------- + $SIGNUPEMAIL_TEMPLATE = " -
    +
    ".LAN_EMAIL_01." {USERNAME},

    ". @@ -143,21 +231,89 @@ LAN_SIGNUP_97." {SITENAME}

    {SITENAME}
    {SITEURL} -

    ".($pref['sitebutton'] ? "" : '')." +

    ".($includeSiteButton ? "" : '')."
    "; +//------------------------------------------------------------- +// 'NOTIFY' TEMPLATE +//------------------------------------------------------------- +$NOTIFY_TEMPLATE = array( + 'template_name' => 'Notify', + 'template_type' => 'system', + 'email_overrides' => '', + 'email_header' => " + + + + + +
    + ", + 'email_body' => '{BODY}', + 'email_footer' => "

    + {SITENAME=link} +
    + + ", + 'email_plainText' => '' + ); + +//------------------------------------------------------------- +// USER-DEFINED TEMPLATES (for mass mailouts) +//------------------------------------------------------------- /* - * QUICK ADD USER EMAIL TEMPLATE - BODY. - * This is the email that is sent when an admin creates a user account in admin. "Quick Add User" - USRLAN_185 = A user account has been created for you at {SITEURL} with the following login:
    Login Name: {LOGIN}
    Password: {PASSWORD}

    - USRLAN_186 = Please go to the site as soon as possible and log in, then change your password using the \'Settings\' option.

    - You can also change other settings at the same time.

    Note that your password cannot be recovered if you lose it. -*/ - -$QUICKADDUSER_TEMPLATE = "
    ".USRLAN_185.USRLAN_186."
    "; - +$TEST_TEMPLATE = array( + 'template_name' => 'TEst1', + 'template_type' => 'system', + 'email_overrides' => '', +// 'email_header' - any header information (usually loaded from the default) + 'email_body' => '{BODY}', + 'email_footer' => 'footer', + 'email_plainText' => '' + ); +$TEST2_TEMPLATE = array( + 'template_name' => 'TEst2', + 'template_type' => 'all', + 'email_overrides' => '', +// 'email_header' - any header information (usually loaded from the default) + 'email_body' => '{BODY}', + 'email_footer' => 'footer' + ); +$TEST3_TEMPLATE = array( + 'template_name' => 'TEst4', + 'template_type' => 'user', + 'email_overrides' => '', +// 'email_header' - any header information (usually loaded from the default) + 'email_body' => '{BODY}', + 'email_footer' => 'footer' + ); +$TEST4_TEMPLATE = array( + 'template_name' => 'TEst5', + 'email_overrides' => '', +// 'email_header' - any header information (usually loaded from the default) + 'email_body' => '{BODY}', + 'email_footer' => 'footer' + ); + */ +$WHATSNEW_TEMPLATE = array( + 'template_name' => 'WhatsNew', + 'template_type' => 'user', + 'email_overrides' => '', +// 'email_header' - any header information (usually loaded from the default) + 'email_body' => 'All the latest news and updates.
    {BODY}
    To find out more, simply click on the links!', +// 'email_footer' => 'footer' + ); +$MONTHLYUPDATE_TEMPLATE = array( + 'template_name' => 'MonthlyUpdate', + 'template_type' => 'user', + 'email_overrides' => '', +// 'email_header' - any header information (usually loaded from the default) + 'email_body' => 'Just to keep you up to date, here\'s a reminder of what\'s changed in the past month.
    + {BODY}
    To find out more, simply click on the links!', +// 'email_footer' => 'footer' + ); ?> \ No newline at end of file