diff --git a/e107_handlers/mail.php b/e107_handlers/mail.php index 42f1b7834..ae1105d8c 100644 --- a/e107_handlers/mail.php +++ b/e107_handlers/mail.php @@ -1,225 +1,827 @@ 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 + - Debug option to just log, not send emails (variables in place, and supported by current bulk mailer) +6. Capability to identify and log caller (for tracking down which bit of code sent emails) +7. Look at how cc, bcc addresses are handled in bulk mailing scenarios - may need to disable them on later sends. +8. Include theme info in header if enabled? +9. Make sure SMTPDebug can be set (TRUE/FALSE) +10. Get rid of mime_content_type() - deprecated (function $this->mime_types() does something similar, given a file extension) +11. Add public/protected/private to all functions and variables +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 +14. Note option for class constants +16. Add a routine or class which adds a list of recipients and an email to the mailout DB +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? + + +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; } - if(is_readable(THEME."email_template.php")) - { - require(THEME."email_template.php"); - } - else - { - require(e_THEME."templates/email_template.php"); - } - if(isset($EMAIL_HEADER) && isset($EMAIL_FOOTER) && is_object($tp)){ - $EMAIL_HEADER = $tp->parseTemplate($EMAIL_HEADER); - $EMAIL_FOOTER = $tp->parseTemplate($EMAIL_FOOTER); - } -/* -Please note that mailed attachments have been found to be corrupted using php 4.3.3 -php 4.3.6 does NOT have this problem. -*/ +//define('MAIL_DEBUG',TRUE); +define('LOG_CALLER', TRUE); -// If $send_from is blank, uses the 'replyto' name and email if set, otherwise site admins details -function sendemail($send_to, $subject, $message, $to_name, $send_from='', $from_name='', $attachments='', $Cc='', $Bcc='', $returnpath='', $returnreceipt='',$inline ="") +require_once(e_HANDLER.'phpmailer/class.phpmailer.php'); + +// Directory for log (if enabled) +define('MAIL_LOG_PATH',e_FILE.'logs/'); + +class e107Email extends PHPMailer { - global $pref,$mailheader_e107id,$tp; + 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 - require_once(e_HANDLER."phpmailer/class.phpmailer.php"); + private $localUseVerp = FALSE; // Use our own variable - PHPMailer one doesn't work with all mailers + private $save_bouncepath = ''; // Used with VERP - $mail = new PHPMailer(); - - // ----- Mail pref. template override for parked domains, site mirrors or dynamic values - global $EMAIL_METHOD, $EMAIL_SMTP_SERVER, $EMAIL_SMTP_USER, $EMAIL_SMTP_PASS, $EMAIL_SENDMAIL_PATH, $EMAIL_FROM, $EMAIL_FROM_NAME, $EMAIL_POP3AUTH,$EMAIL_DEBUG,$EMAIL_RETURN; - if($EMAIL_METHOD){ $pref['mailer'] = $EMAIL_METHOD; } - if($EMAIL_SMTP_SERVER){ $pref['smtp_server'] = $EMAIL_SMTP_SERVER; } - if($EMAIL_SMTP_USER){ $pref['smtp_username'] = $EMAIL_SMTP_USER; } - if($EMAIL_SMTP_PASS){ $pref['smtp_password'] = $EMAIL_SMTP_PASS; } - if($EMAIL_SENDMAIL_PATH){ $pref['sendmail'] = $EMAIL_SENDMAIL_PATH; } - if($EMAIL_FROM){ $pref['siteadminemail'] = $EMAIL_FROM; } - if($EMAIL_FROM_NAME){ $pref['siteadmin'] = $EMAIL_FROM_NAME; } - if($EMAIL_POP3AUTH){ $pref['smtp_pop3auth'] = TRUE; } - if($EMAIL_RETURN){ $returnpath = $EMAIL_RETURN; } - if($EMAIL_DEBUG){ - $mail->SMTPDebug = TRUE; - } - // ------------------------------------------------------------------------- + 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 - - if($mailheader_e107id){ - $mail->AddCustomHeader("X-e107-id: {$mailheader_e107id}"); - } - - if ($pref['mailer']== 'smtp') + /** + * 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) { - if(isset($pref['smtp_pop3auth']) && $pref['smtp_pop3auth']) + parent::__construct(FALSE); // Parent constructor - no exceptions for now + + $e107 = e107::getInstance(); + global $pref; + + $this->CharSet = 'utf-8'; + $this->SetLanguage(CORE_LC); + + if (($overrides === FALSE) || !is_array($overrides)) { - // http://www.corephp.co.uk/archives/18-POP-before-SMTP-Authentication-for-PHPMailer.html - require_once(e_HANDLER."phpmailer/class.pop3.php"); - $pop = new POP3(); - $pop->Authorise($pref['smtp_server'], 110, 30, $pref['smtp_username'], $pref['smtp_password'], 1); - } - - $mail->Mailer = "smtp"; - $mail->SMTPKeepAlive = FALSE; - $mail->Host = $pref['smtp_server']; - if($pref['smtp_username'] && $pref['smtp_password']){ - $mail->SMTPAuth = (isset($pref['smtp_pop3auth']) && $pref['smtp_pop3auth']) ? FALSE : TRUE; - $mail->Username = $pref['smtp_username']; - $mail->Password = $pref['smtp_password']; - $mail->PluginDir = e_HANDLER."phpmailer/"; + $overrides = array(); } - } - elseif ($pref['mailer']== 'sendmail') - { - $mail->Mailer = "sendmail"; - $mail->Sendmail = ($pref['sendmail']) ? $pref['sendmail'] : "/usr/sbin/sendmail -t -i -r ".$pref['siteadminemail']; - } - else - { - $mail->Mailer = "mail"; - } + + 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); - $to_name = ($to_name) ? $to_name: $send_to; - - if (!trim($send_from)) - { - $from_name = $tp->toEmail(varsettrue($pref['replyto_name'],$pref['siteadmin']),"","RAWTEXT"); - $send_from = $tp->toEmail(varsettrue($pref['replyto_email'],$pref['siteadminemail']),"","RAWTEXT"); - } - $mail->CharSet = 'utf-8'; - $mail->From = $send_from; - $mail->FromName = $from_name; - $mail->Subject = $subject; - $mail->SetLanguage("en",e_HANDLER."phpmailer/language/"); - - $lb = "\n"; - - - // Clean up the HTML. == - - if (preg_match('/<(font|br|a|img|b)/i', $message)) { - $Html = $message; // Assume html if it begins with one of these tags - } else { - $Html = htmlspecialchars($message); - $Html = preg_replace('%(http|ftp|https)(://\S+)%', '\1\2', $Html); - $Html = preg_replace('/([[:space:]()[{}])(www.[-a-zA-Z0-9@:%_\+.~#?&\/\/=]+)/i', '\\1\\2', $Html); - $Html = preg_replace('/([_\.0-9a-z-]+@([0-9a-z][0-9a-z-]+\.)+[a-z]{2,3})/i', '\\1', $Html); - $Html = str_replace("\r","\n",$Html); // Handle alternative newline characters - $Html = str_replace("\n", "
\n", $Html); - } - if (strpos($message,"") !== FALSE){ - $text = strstr($message,""); - }else{ - $text = $message; - } - $text = str_replace("
", "\n", $text); - $text = strip_tags(str_replace("
", "\n", $text)); - - $mail->Body = $Html; //Main message is HTML - $mail->IsHTML(TRUE); - $mail->AltBody = $text; //Include regular plaintext as well - - - $tmp = explode(",",$send_to); - foreach($tmp as $adr){ - $mail->AddAddress($adr, $to_name); - } - - - if ($attachments){ - if (is_array($attachments)) { - foreach($attachments as $attach){ - if(is_readable($attach)){ - $mail->AddAttachment($attach, basename($attach),"base64",mime_content_type($attach)); - } - } - }else{ - if(is_readable($attachments)){ - $mail->AddAttachment($attachments, basename($attachments),"base64",mime_content_type($attachments)); - } + 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}
"; } } - if($inline){ - $tmp = explode(",",$inline); - foreach($tmp as $inline_img){ - if(is_readable($inline_img) && !is_dir($inline_img)){ - $mail->AddEmbeddedImage($inline_img, md5($inline_img), basename($inline_img),"base64",mime_content_type($inline_img)); + 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 + break; + case 'SSL' : + $this->SMTPSecure = 'ssl'; + $this->Port = 465; + break; + default : + 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])); + } + } + } + + + + /** + * 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,"=====".date('H:i:s y.m.d')."----------------------------------------------------------------=====\r\n"); + if ($logInfo) + { + fwrite($this->logHandle,' Start of mail run by '.USERNAME." - {$count} emails to go. ID: {$mail_id}. Subject: {$mail_subject}\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; + } } } } + } - - if($Cc){ - if($mail->Mailer == "mail"){ - $mail->AddCustomHeader("Cc: {$Cc}"); - }else{ - $tmp = explode(",",$Cc); - foreach($tmp as $addc){ - $mail->AddCC($addc); - } + protected function logLine($text) + { + if ($this->logEnable && ($this->logHandle > 0)) + { + fwrite($this->logHandle,date('H:i:s y.m.d').' - '.$text."\r\n"); } } - if($Bcc){ - if($mail->Mailer == "mail"){ - $mail->AddCustomHeader("Bcc: {$Bcc}"); - }else{ - $tmp = explode(",",$Bcc); - foreach($tmp as $addbc){ - $mail->AddBCC($addbc); - } + protected function closeLog() + { + if ($this->logEnable && ($this->logHandle > 0)) + { + fclose($this->logHandle); } } - if (isset($returnpath) && ($returnpath != "")) - { // Passed parameter overrides any system default - $mail->Sender = $returnpath; - } - elseif($pref['mail_bounce_email'] !=''){ - $mail->Sender = $pref['mail_bounce_email']; - } - if (!$mail->Send()) { - // echo "There has been a mail error sending to " . $row["email"] . "
"; - return FALSE; - // Clear all addresses and attachments for next loop - $mail->ClearAddresses(); - $mail->ClearAttachments(); - } else { - // Clear all addresses and attachments for next loop - $mail->ClearAddresses(); - $mail->ClearAttachments(); + + /** + * 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; + } + $this->MsgHTML($message); // Theoretically this should do everything, including handling of inline images. + } + else + { + // generate the plain text part + 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; + } + } + + + // Legacy way of creating an HTML and a text part - as used in 0.7 + // $want_HTML= 1 uses default setting for HTML part. Set TRUE to enable, FALSE to disable + function makeBodyLegacy($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) + { + if (preg_match('/<(font|br|a|img|b)/i', $message)) + { + $Html = $message; // Assume html if it includes one of these tags + } + else + { + $Html = htmlspecialchars($message); + $Html = preg_replace('%(http|ftp|https)(://\S+)%', '\1\2', $Html); + $Html = preg_replace('/([[:space:]()[{}])(www.[-a-zA-Z0-9@:%_\+.~#?&\/\/=]+)/i', '\\1\\2', $Html); + $Html = preg_replace('/([_\.0-9a-z-]+@([0-9a-z][0-9a-z-]+\.)+[a-z]{2,3})/i', '\\1', $Html); + $Html = str_replace("\r\n","\n",$Html); // Handle alternative newline characters + $Html = str_replace("\n\r","\n",$Html); // Handle alternative newline characters + $Html = str_replace("\r","\n",$Html); // Handle alternative newline characters + $Html = str_replace("\n", "
\n", $Html); + } + if ($add_HTML_header) + { + $this->Body = "\n + \n".$Html; + } + else + { + $this->Body = $Html; + } + } + + // Now generate the plain text part - always have one + if (strpos($message,"") !== FALSE) + { + $text = strstr($message,""); + } + else + { + $text = $message; + } + + $text = str_replace("
", "\n", $text); + $text = strip_tags(str_replace("
", "\n", $text)); + + if ($want_HTML) + { + $this->AltBody = $text; + $this->IsHTML = TRUE; + } + else + { + $this->Body = $text; + } + } + + + // 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) + { + if(is_readable($attach)) + { + $mail->AddAttachment(dirname($attach), basename($attach),"base64",$this->_mime_types($attach)); + } + } + } + + + // 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)) + { + $mail->AddEmbeddedImage($inline_img, md5($inline_img), basename($inline_img),"base64",mime_content_type($inline_img)); + } + } + } + + + // 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['subject'])) $this->Subject = $paramlist['subject']; + if (varsettrue($paramlist['from'])) $this->From = $paramlist['from']; + if (varsettrue($paramlist['fromname'])) $this->FromName = $paramlist['fromname']; + if (varsettrue($paramlist['replyto'])) $this->AddAddressList('replyto',$paramlist['replyto'],varsettrue($paramlist['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['message'])) $this->makeBody($paramlist['message'], $this->allow_html, $this->add_HTML_header); + if (varsettrue($paramlist['attachments'])) $this->attach($paramlist['attachments']); + if (varsettrue($paramlist['cc'])) $this->AddAddressList('cc',$paramlist['cc'],varsettrue($paramlist['ccnames'],'')); + if (varsettrue($paramlist['bcc'])) $this->AddAddressList('bcc',$paramlist['bcc'],varsettrue($paramlist['bccnames'],'')); + 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['inline-images'])) $this->addInlineImages($paramlist['inline-images']); + if (varsettrue($paramlist['priority'])) $this->Priority = $paramlist['priority']; + 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['subject'] + // $eml['from'] + // $eml['fromname'] + // $eml['replyto'] - Optional 'reply to' field + // $eml['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['message'] - message body. May be HTML or text. Added according to the current state of the HTML enable flag + // $eml['attachments'] - string if one file, array of filenames if one or more. + // $eml['cc'] - comma-separated list + // $eml['bcc'] - comma-separated list + // $eml['bouncepath'] - Sender field (used for bounces) + // $eml['returnreceipt'] - email address for notification of receipt (reading) + // $eml['inline-images'] - array of files for inline images + // $eml['priority'] - Email priority (1 = High, 3 = Normal, 5 = low) + // $eml['extra_header'] - additional headers + // $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}"); + + $this->ClearAddresses(); // In case we send another email + $this->ClearCustomHeaders(); + + if ($result) return TRUE; + + // 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++; + 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; + } + } } -/* Deprecated. - Use mail_validation_class.php instead. -function validatemail($Email) { + +//----------------------------- +// 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 e107_mail 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->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)) + { // Success + return TRUE; + } + + // TODO: Possibly Log this somewhere (sendEmail method logs to rolling log) + echo "Mail sending error: ".$mail->ErrorInfo."
"; + return FALSE; } -*/ diff --git a/e107_handlers/phpmailer/class.phpmailer.php b/e107_handlers/phpmailer/class.phpmailer.php index afb71b25f..e8cf56efa 100644 --- a/e107_handlers/phpmailer/class.phpmailer.php +++ b/e107_handlers/phpmailer/class.phpmailer.php @@ -2,14 +2,16 @@ /*~ class.phpmailer.php .---------------------------------------------------------------------------. | Software: PHPMailer - PHP email class | -| Version: 2.0.4 | +| 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/ | | ------------------------------------------------------------------------- | -| Author: Andy Prevost (project admininistrator) | -| Author: Brent R. Matzelle (original founder) | -| Copyright (c) 2004-2007, Andy Prevost. All Rights Reserved. | +| Admin: Andy Prevost (project admininistrator) | +| Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net | +| : Marcus Bointon (coolbru) coolbru@users.sourceforge.net | +| Founder: Brent R. Matzelle (original founder) | +| Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. | | Copyright (c) 2001-2003, Brent R. Matzelle | | ------------------------------------------------------------------------- | | License: Distributed under the Lesser General Public License (LGPL) | @@ -23,14 +25,21 @@ | - Technology Consulting | | - Oursourcing (highly qualified programmers and graphic designers) | '---------------------------------------------------------------------------' +*/ /** * PHPMailer - PHP email transport class + * NOTE: Requires PHP version 5 or later * @package PHPMailer * @author Andy Prevost + * @author Marcus Bointon * @copyright 2004 - 2009 Andy Prevost + * @version $Id: class.phpmailer.php,v 1.6 2009-09-01 19:53:08 e107steved Exp $ + * @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"); + class PHPMailer { ///////////////////////////////////////////////// @@ -41,64 +50,64 @@ class PHPMailer { * Email priority (1 = High, 3 = Normal, 5 = low). * @var int */ - var $Priority = 3; + public $Priority = 3; /** * Sets the CharSet of the message. * @var string */ - var $CharSet = 'iso-8859-1'; + public $CharSet = 'iso-8859-1'; /** * Sets the Content-type of the message. * @var string */ - var $ContentType = 'text/plain'; + public $ContentType = 'text/plain'; /** - * Sets the Encoding of the message. Options for this are "8bit", - * "7bit", "binary", "base64", and "quoted-printable". + * Sets the Encoding of the message. Options for this are + * "8bit", "7bit", "binary", "base64", and "quoted-printable". * @var string */ - var $Encoding = '8bit'; + public $Encoding = '8bit'; /** * Holds the most recent mailer error message. * @var string */ - var $ErrorInfo = ''; + public $ErrorInfo = ''; /** * Sets the From email address for the message. * @var string */ - var $From = 'root@localhost'; + public $From = 'root@localhost'; /** * Sets the From name of the message. * @var string */ - var $FromName = 'Root User'; + public $FromName = 'Root User'; /** * Sets the Sender email (Return-Path) of the message. If not empty, * will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode. * @var string */ - var $Sender = ''; + public $Sender = ''; /** * Sets the Subject of the message. * @var string */ - var $Subject = ''; + public $Subject = ''; /** * Sets the Body of the message. This can be either an HTML or text body. * If HTML then run IsHTML(true). * @var string */ - var $Body = ''; + public $Body = ''; /** * Sets the text-only body of the message. This automatically sets the @@ -107,45 +116,39 @@ class PHPMailer { * that can read HTML will view the normal Body. * @var string */ - var $AltBody = ''; + public $AltBody = ''; /** * Sets word wrapping on the body of the message to a given number of * characters. * @var int */ - var $WordWrap = 0; + public $WordWrap = 0; /** * Method to send mail: ("mail", "sendmail", or "smtp"). * @var string */ - var $Mailer = 'mail'; + public $Mailer = 'mail'; /** * Sets the path of the sendmail program. * @var string */ - var $Sendmail = '/usr/sbin/sendmail'; + public $Sendmail = '/usr/sbin/sendmail'; /** - * Path to PHPMailer plugins. This is now only useful if the SMTP class + * Path to PHPMailer plugins. Useful if the SMTP class * is in a different directory than the PHP include path. * @var string */ - var $PluginDir = ''; - - /** - * Holds PHPMailer version. - * @var string - */ - var $Version = "2.0.4"; + public $PluginDir = ''; /** * Sets the email address that a reading confirmation will be sent. * @var string */ - var $ConfirmReadingTo = ''; + public $ConfirmReadingTo = ''; /** * Sets the hostname to use in Message-Id and Received headers @@ -153,14 +156,14 @@ class PHPMailer { * by SERVER_NAME is used or 'localhost.localdomain'. * @var string */ - var $Hostname = ''; + public $Hostname = ''; /** * Sets the message ID to be used in the Message-Id header. * If empty, a unique id will be generated. * @var string */ - var $MessageID = ''; + public $MessageID = ''; ///////////////////////////////////////////////// // PROPERTIES FOR SMTP @@ -174,57 +177,57 @@ class PHPMailer { * Hosts will be tried in order. * @var string */ - var $Host = 'localhost'; + public $Host = 'localhost'; /** * Sets the default SMTP server port. * @var int */ - var $Port = 25; + public $Port = 25; /** * Sets the SMTP HELO of the message (Default is $Hostname). * @var string */ - var $Helo = ''; + public $Helo = ''; /** * Sets connection prefix. * Options are "", "ssl" or "tls" * @var string */ - var $SMTPSecure = ""; + public $SMTPSecure = ''; /** * Sets SMTP authentication. Utilizes the Username and Password variables. * @var bool */ - var $SMTPAuth = false; + public $SMTPAuth = false; /** * Sets SMTP username. * @var string */ - var $Username = ''; + public $Username = ''; /** * Sets SMTP password. * @var string */ - var $Password = ''; + public $Password = ''; /** - * Sets the SMTP server timeout in seconds. This function will not - * work with the win32 version. + * Sets the SMTP server timeout in seconds. + * This function will not work with the win32 version. * @var int */ - var $Timeout = 10; + public $Timeout = 10; /** * Sets SMTP class debugging on or off. * @var bool */ - var $SMTPDebug = false; + public $SMTPDebug = false; /** * Prevents the SMTP connection from being closed after each mail @@ -232,46 +235,75 @@ class PHPMailer { * requires an explicit call to SmtpClose(). * @var bool */ - var $SMTPKeepAlive = false; + public $SMTPKeepAlive = false; /** * Provides the ability to have the TO field process individual * emails, instead of sending to entire TO addresses * @var bool */ - var $SingleTo = false; + public $SingleTo = false; + + /** + * Provides the ability to change the line ending + * @var string + */ + public $LE = "\n"; + + /** + * Sets the PHPMailer Version number + * @var string + */ + public $Version = '5.0.2'; ///////////////////////////////////////////////// - // PROPERTIES, PRIVATE + // PROPERTIES, PRIVATE AND PROTECTED ///////////////////////////////////////////////// - var $smtp = NULL; - var $to = array(); - var $cc = array(); - var $bcc = array(); - var $ReplyTo = array(); - var $attachment = array(); - var $CustomHeader = array(); - var $message_type = ''; - var $boundary = array(); - var $language = array(); - var $error_count = 0; - var $LE = "\n"; - var $sign_cert_file = ""; - var $sign_key_file = ""; - var $sign_key_pass = ""; + 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; + + ///////////////////////////////////////////////// + // CONSTANTS + ///////////////////////////////////////////////// + + 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 ///////////////////////////////////////////////// // METHODS, VARIABLES ///////////////////////////////////////////////// + /** + * Constructor + * @param boolean $exceptions Should we throw external exceptions? + */ + public function __construct($exceptions = false) { + $this->exceptions = ($exceptions == true); + } + /** * Sets message type to HTML. - * @param bool $bool + * @param bool $ishtml * @return void */ - function IsHTML($bool) { - if($bool == true) { + public function IsHTML($ishtml = true) { + if ($ishtml) { $this->ContentType = 'text/html'; } else { $this->ContentType = 'text/plain'; @@ -282,7 +314,7 @@ class PHPMailer { * Sets Mailer to send message using SMTP. * @return void */ - function IsSMTP() { + public function IsSMTP() { $this->Mailer = 'smtp'; } @@ -290,7 +322,7 @@ class PHPMailer { * Sets Mailer to send message using PHP mail() function. * @return void */ - function IsMail() { + public function IsMail() { $this->Mailer = 'mail'; } @@ -298,7 +330,10 @@ class PHPMailer { * Sets Mailer to send message using the $Sendmail program. * @return void */ - function IsSendmail() { + public function IsSendmail() { + if (!stristr(ini_get('sendmail_path'), 'sendmail')) { + $this->Sendmail = '/var/qmail/bin/sendmail'; + } $this->Mailer = 'sendmail'; } @@ -306,8 +341,10 @@ class PHPMailer { * Sets Mailer to send message using the qmail MTA. * @return void */ - function IsQmail() { - $this->Sendmail = '/var/qmail/bin/sendmail'; + public function IsQmail() { + if (stristr(ini_get('sendmail_path'), 'qmail')) { + $this->Sendmail = '/var/qmail/bin/sendmail'; + } $this->Mailer = 'sendmail'; } @@ -319,52 +356,126 @@ class PHPMailer { * Adds a "To" address. * @param string $address * @param string $name - * @return void + * @return boolean true on success, false if address already used */ - function AddAddress($address, $name = '') { - $cur = count($this->to); - $this->to[$cur][0] = trim($address); - $this->to[$cur][1] = $name; + public function AddAddress($address, $name = '') { + return $this->AddAnAddress('to', $address, $name); } /** - * Adds a "Cc" address. Note: this function works - * with the SMTP mailer on win32, not with the "mail" - * mailer. + * Adds a "Cc" address. + * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer. * @param string $address * @param string $name - * @return void + * @return boolean true on success, false if address already used */ - function AddCC($address, $name = '') { - $cur = count($this->cc); - $this->cc[$cur][0] = trim($address); - $this->cc[$cur][1] = $name; + public function AddCC($address, $name = '') { + return $this->AddAnAddress('cc', $address, $name); } /** - * Adds a "Bcc" address. Note: this function works - * with the SMTP mailer on win32, not with the "mail" - * mailer. + * Adds a "Bcc" address. + * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer. * @param string $address * @param string $name - * @return void + * @return boolean true on success, false if address already used */ - function AddBCC($address, $name = '') { - $cur = count($this->bcc); - $this->bcc[$cur][0] = trim($address); - $this->bcc[$cur][1] = $name; + public function AddBCC($address, $name = '') { + return $this->AddAnAddress('bcc', $address, $name); } /** - * Adds a "Reply-To" address. + * Adds a "Reply-to" address. * @param string $address * @param string $name - * @return void + * @return boolean */ - function AddReplyTo($address, $name = '') { - $cur = count($this->ReplyTo); - $this->ReplyTo[$cur][0] = trim($address); - $this->ReplyTo[$cur][1] = $name; + public function AddReplyTo($address, $name = '') { + return $this->AddAnAddress('ReplyTo', $address, $name); + } + + /** + * Adds an address to one of the recipient arrays + * Addresses that have been added already return false, but do not throw exceptions + * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo' + * @param string $address The email address to send to + * @param string $name + * @return boolean true on success, false if address already used or invalid in some way + * @access private + */ + private function AddAnAddress($kind, $address, $name = '') { + if (!preg_match('/^(to|cc|bcc|ReplyTo)$/', $kind)) { + echo 'Invalid recipient array: ' . kind; + return false; + } + $address = trim($address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + if (!self::ValidateAddress($address)) { + $this->SetError($this->Lang('invalid_address').': '. $address); + if ($this->exceptions) { + throw new phpmailerException($this->Lang('invalid_address').': '.$address); + } + echo $this->Lang('invalid_address').': '.$address; + return false; + } + if ($kind != 'ReplyTo') { + if (!isset($this->all_recipients[strtolower($address)])) { + array_push($this->$kind, array($address, $name)); + $this->all_recipients[strtolower($address)] = true; + return true; + } + } else { + if (!array_key_exists(strtolower($address), $this->ReplyTo)) { + $this->ReplyTo[strtolower($address)] = array($address, $name); + return true; + } + } + return false; + } + +/** + * Set the From and FromName properties + * @param string $address + * @param string $name + * @return boolean + */ + public function SetFrom($address, $name = '') { + $address = trim($address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + if (!self::ValidateAddress($address)) { + $this->SetError($this->Lang('invalid_address').': '. $address); + if ($this->exceptions) { + throw new phpmailerException($this->Lang('invalid_address').': '.$address); + } + echo $this->Lang('invalid_address').': '.$address; + return false; + } + $this->From = $address; + $this->FromName = $name; + 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 + * @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; + } + } 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); + } } ///////////////////////////////////////////////// @@ -377,100 +488,88 @@ class PHPMailer { * variable to view description of the error. * @return bool */ - function Send() { - $header = ''; - $body = ''; - $result = true; + public function Send() { + try { + if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { + throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL); + } - if((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { - $this->SetError($this->Lang('provide_address')); + // Set whether the message is multipart/alternative + if(!empty($this->AltBody)) { + $this->ContentType = 'multipart/alternative'; + } + + $this->error_count = 0; // reset errors + $this->SetMessageType(); + $header = $this->CreateHeader(); + $body = $this->CreateBody(); + + 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); + } + + } catch (phpmailerException $e) { + $this->SetError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + echo $e->getMessage()."\n"; return false; } - - /* Set whether the message is multipart/alternative */ - if(!empty($this->AltBody)) { - $this->ContentType = 'multipart/alternative'; - } - - $this->error_count = 0; // reset errors - $this->SetMessageType(); - $header .= $this->CreateHeader(); - $body = $this->CreateBody(); - - if($body == '') { - return false; - } - - /* Choose the mailer */ - switch($this->Mailer) { - case 'sendmail': - $result = $this->SendmailSend($header, $body); - break; - case 'smtp': - $result = $this->SmtpSend($header, $body); - break; - case 'mail': - $result = $this->MailSend($header, $body); - break; - default: - $result = $this->MailSend($header, $body); - break; - //$this->SetError($this->Mailer . $this->Lang('mailer_not_supported')); - //$result = false; - //break; - } - - return $result; } /** * Sends mail using the $Sendmail program. - * @access private + * @param string $header The message headers + * @param string $body The message body + * @access protected * @return bool */ - function SendmailSend($header, $body) { + protected function SendmailSend($header, $body) { if ($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')) { - $this->SetError($this->Lang('execute') . $this->Sendmail); - return false; + throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } - fputs($mail, $header); fputs($mail, $body); - $result = pclose($mail); - if (version_compare(phpversion(), '4.2.3') == -1) { - $result = $result >> 8 & 0xFF; - } if($result != 0) { - $this->SetError($this->Lang('execute') . $this->Sendmail); - return false; + throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } return true; } /** * Sends mail using the PHP mail() function. - * @access private + * @param string $header The message headers + * @param string $body The message body + * @access protected * @return bool */ - function MailSend($header, $body) { - - $to = ''; - for($i = 0; $i < count($this->to); $i++) { - if($i != 0) { $to .= ', '; } - $to .= $this->AddrFormat($this->to[$i]); + protected function MailSend($header, $body) { + $toArr = array(); + foreach($this->to as $t) { + $toArr[] = $this->AddrFormat($t); } - - $toArr = split(',', $to); + $to = implode(', ', $toArr); $params = sprintf("-oi -f %s", $this->Sender); - if ($this->Sender != '' && strlen(ini_get('safe_mode')) < 1) { + if ($this->Sender != '' && strlen(ini_get('safe_mode'))< 1) { $old_from = ini_get('sendmail_from'); ini_set('sendmail_from', $this->Sender); if ($this->SingleTo === true && count($toArr) > 1) { @@ -489,145 +588,136 @@ class PHPMailer { $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header); } } - if (isset($old_from)) { ini_set('sendmail_from', $old_from); } - if(!$rt) { - $this->SetError($this->Lang('instantiate')); - return false; + throw new phpmailerException($this->Lang('instantiate'), self::STOP_CRITICAL); } - return true; } /** - * Sends mail via SMTP using PhpSMTP (Author: - * Chris Ryan). Returns bool. Returns false if there is a - * bad MAIL FROM, RCPT, or DATA input. - * @access private + * Sends mail via SMTP using PhpSMTP + * 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 + * @uses SMTP + * @access protected * @return bool */ - function SmtpSend($header, $body) { - include_once($this->PluginDir . 'class.smtp.php'); - $error = ''; + protected function SmtpSend($header, $body) { + require_once $this->PluginDir . 'class.smtp.php'; $bad_rcpt = array(); if(!$this->SmtpConnect()) { - return false; + throw new phpmailerException($this->Lang('smtp_connect_failed'), self::STOP_CRITICAL); } - $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; if(!$this->smtp->Mail($smtp_from)) { - $error = $this->Lang('from_failed') . $smtp_from; - $this->SetError($error); - $this->smtp->Reset(); - return false; + throw new phpmailerException($this->Lang('from_failed') . $smtp_from, self::STOP_CRITICAL); } - /* Attempt to send attach all recipients */ - for($i = 0; $i < count($this->to); $i++) { - if(!$this->smtp->Recipient($this->to[$i][0])) { - $bad_rcpt[] = $this->to[$i][0]; + // Attempt to send attach all recipients + foreach($this->to as $to) { + if (!$this->smtp->Recipient($to[0])) { + $bad_rcpt[] = $to[0]; } } - for($i = 0; $i < count($this->cc); $i++) { - if(!$this->smtp->Recipient($this->cc[$i][0])) { - $bad_rcpt[] = $this->cc[$i][0]; + foreach($this->cc as $cc) { + if (!$this->smtp->Recipient($cc[0])) { + $bad_rcpt[] = $cc[0]; } } - for($i = 0; $i < count($this->bcc); $i++) { - if(!$this->smtp->Recipient($this->bcc[$i][0])) { - $bad_rcpt[] = $this->bcc[$i][0]; + foreach($this->bcc as $bcc) { + if (!$this->smtp->Recipient($bcc[0])) { + $bad_rcpt[] = $bcc[0]; } } - - if(count($bad_rcpt) > 0) { // Create error message - for($i = 0; $i < count($bad_rcpt); $i++) { - if($i != 0) { - $error .= ', '; - } - $error .= $bad_rcpt[$i]; - } - $error = $this->Lang('recipients_failed') . $error; - $this->SetError($error); - $this->smtp->Reset(); - return false; + if (count($bad_rcpt) > 0 ) { //Create error message for any bad addresses + $badaddresses = implode(', ', $bad_rcpt); + throw new phpmailerException($this->Lang('recipients_failed') . $badaddresses); } - if(!$this->smtp->Data($header . $body)) { - $this->SetError($this->Lang('data_not_accepted')); - $this->smtp->Reset(); - return false; + throw new phpmailerException($this->Lang('data_not_accepted'), self::STOP_CRITICAL); } if($this->SMTPKeepAlive == true) { $this->smtp->Reset(); - } else { - $this->SmtpClose(); } - return true; } /** - * Initiates a connection to an SMTP server. Returns false if the - * operation failed. - * @access private + * Initiates a connection to an SMTP server. + * Returns false if the operation failed. + * @uses SMTP + * @access public * @return bool */ - function SmtpConnect() { - if($this->smtp == NULL) { + public function SmtpConnect() { + if(is_null($this->smtp)) { $this->smtp = new SMTP(); } $this->smtp->do_debug = $this->SMTPDebug; $hosts = explode(';', $this->Host); $index = 0; - $connection = ($this->smtp->Connected()); + $connection = $this->smtp->Connected(); - /* Retry while there is no connection */ - while($index < count($hosts) && $connection == false) { - $hostinfo = array(); - if(eregi('^(.+):([0-9]+)$', $hosts[$index], $hostinfo)) { - $host = $hostinfo[1]; - $port = $hostinfo[2]; - } else { - $host = $hosts[$index]; - $port = $this->Port; - } - - if($this->smtp->Connect(((!empty($this->SMTPSecure))?$this->SMTPSecure.'://':'').$host, $port, $this->Timeout)) { - if ($this->Helo != '') { - $this->smtp->Hello($this->Helo); + // Retry while there is no connection + try { + while($index < count($hosts) && !$connection) { + $hostinfo = array(); + if (preg_match('/^(.+):([0-9]+)$/', $hosts[$index], $hostinfo)) { + $host = $hostinfo[1]; + $port = $hostinfo[2]; } else { - $this->smtp->Hello($this->ServerHostname()); + $host = $hosts[$index]; + $port = $this->Port; } - $connection = true; - if($this->SMTPAuth) { - if(!$this->smtp->Authenticate($this->Username, $this->Password)) { - $this->SetError($this->Lang('authenticate')); - $this->smtp->Reset(); - $connection = false; + $tls = ($this->SMTPSecure == 'tls'); + $ssl = ($this->SMTPSecure == 'ssl'); + + if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) { + + $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname()); + $this->smtp->Hello($hello); + + if ($tls) { + if (!$this->smtp->StartTLS()) { + throw new phpmailerException($this->Lang('tls')); + } + + //We must resend HELO after tls negotiation + $this->smtp->Hello($hello); + } + + $connection = true; + if ($this->SMTPAuth) { + if (!$this->smtp->Authenticate($this->Username, $this->Password)) { + throw new phpmailerException($this->Lang('authenticate')); + } } } + $index++; + if (!$connection) { + throw new phpmailerException($this->Lang('connect_host')); + } } - $index++; + } catch (phpmailerException $e) { + $this->smtp->Reset(); + throw $e; } - if(!$connection) { - $this->SetError($this->Lang('connect_host')); - } - - return $connection; + return true; } /** * Closes the active SMTP session if one exists. * @return void */ - function SmtpClose() { - if($this->smtp != NULL) { + public function SmtpClose() { + if(!is_null($this->smtp)) { if($this->smtp->Connected()) { $this->smtp->Quit(); $this->smtp->Close(); @@ -636,38 +726,48 @@ class PHPMailer { } /** - * Sets the language for all class error messages. Returns false - * if it cannot load the language file. The default language type - * is English. - * @param string $lang_type Type of language (e.g. Portuguese: "br") - * @param string $lang_path Path to the language file directory - * @access public - * @return bool - */ - function SetLanguage($lang_type, $lang_path = 'language/') { - if(file_exists($lang_path.'phpmailer.lang-'.$lang_type.'.php')) { - include($lang_path.'phpmailer.lang-'.$lang_type.'.php'); - } elseif (file_exists($lang_path.'phpmailer.lang-en.php')) { - include($lang_path.'phpmailer.lang-en.php'); - } else { - $PHPMAILER_LANG = array(); - $PHPMAILER_LANG["provide_address"] = 'You must provide at least one ' . - $PHPMAILER_LANG["mailer_not_supported"] = ' mailer is not supported.'; - $PHPMAILER_LANG["execute"] = 'Could not execute: '; - $PHPMAILER_LANG["instantiate"] = 'Could not instantiate mail function.'; - $PHPMAILER_LANG["authenticate"] = 'SMTP Error: Could not authenticate.'; - $PHPMAILER_LANG["from_failed"] = 'The following From address failed: '; - $PHPMAILER_LANG["recipients_failed"] = 'SMTP Error: The following ' . - $PHPMAILER_LANG["data_not_accepted"] = 'SMTP Error: Data not accepted.'; - $PHPMAILER_LANG["connect_host"] = 'SMTP Error: Could not connect to SMTP host.'; - $PHPMAILER_LANG["file_access"] = 'Could not access file: '; - $PHPMAILER_LANG["file_open"] = 'File Error: Could not open file: '; - $PHPMAILER_LANG["encoding"] = 'Unknown encoding: '; - $PHPMAILER_LANG["signing"] = 'Signing Error: '; + * Sets the language for all class error messages. + * 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 + * @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.', + '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: ' + ); + //Overwrite language-specific strings. This way we'll never have missing translations - no more "language string failed to load"! + $l = true; + if ($langcode != 'en') { //There is no English translation file + $l = @include $lang_path.'phpmailer.lang-'.$langcode.'.php'; } $this->language = $PHPMAILER_LANG; + return ($l == true); //Returns false if language not found + } - return true; + /** + * Return the current array of language strings + * @return array + */ + public function GetTranslations() { + return $this->language; } ///////////////////////////////////////////////// @@ -676,17 +776,16 @@ class PHPMailer { /** * Creates recipient headers. - * @access private + * @access public * @return string */ - function AddrAppend($type, $addr) { + public function AddrAppend($type, $addr) { $addr_str = $type . ': '; - $addr_str .= $this->AddrFormat($addr[0]); - if(count($addr) > 1) { - for($i = 1; $i < count($addr); $i++) { - $addr_str .= ', ' . $this->AddrFormat($addr[$i]); - } + $addresses = array(); + foreach ($addr as $a) { + $addresses[] = $this->AddrFormat($a); } + $addr_str .= implode(', ', $addresses); $addr_str .= $this->LE; return $addr_str; @@ -694,27 +793,28 @@ class PHPMailer { /** * Formats an address correctly. - * @access private + * @access public * @return string */ - function AddrFormat($addr) { - if(empty($addr[1])) { - $formatted = $this->SecureHeader($addr[0]); + public function AddrFormat($addr) { + if (empty($addr[1])) { + return $this->SecureHeader($addr[0]); } else { - $formatted = $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">"; + return $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">"; } - - return $formatted; } /** * Wraps message for use with mailers that do not * automatically perform wrapping and for quoted-printable. * Original written by philippe. - * @access private + * @param string $message The message to wrap + * @param integer $length The line length to wrap to + * @param boolean $qp_mode Whether to run in Quoted-Printable mode + * @access public * @return string */ - function WrapText($message, $length, $qp_mode = false) { + public function WrapText($message, $length, $qp_mode = false) { $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; // If utf-8 encoding is used, we will need to make sure we don't // split multibyte characters when we wrap @@ -791,12 +891,12 @@ class PHPMailer { * Finds last character boundary prior to maxLength in a utf-8 * quoted (printable) encoded string. * Original written by Colin Brown. - * @access private + * @access public * @param string $encodedText utf-8 QP text * @param int $maxLength find last character boundary prior to this length * @return int */ - function UTF8CharBoundary($encodedText, $maxLength) { + public function UTF8CharBoundary($encodedText, $maxLength) { $foundSplitPos = false; $lookBack = 3; while (!$foundSplitPos) { @@ -828,19 +928,19 @@ class PHPMailer { return $maxLength; } + /** * Set the body wrapping. - * @access private + * @access public * @return void */ - function SetWordWrap() { + public function SetWordWrap() { if($this->WordWrap < 1) { return; } switch($this->message_type) { case 'alt': - /* fall through */ case 'alt_attachments': $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap); break; @@ -852,25 +952,25 @@ class PHPMailer { /** * Assembles message header. - * @access private - * @return string + * @access public + * @return string The assembled header */ - function CreateHeader() { + public function CreateHeader() { $result = ''; - /* Set the boundaries */ + // Set the boundaries $uniq_id = md5(uniqid(time())); $this->boundary[1] = 'b1_' . $uniq_id; $this->boundary[2] = 'b2_' . $uniq_id; - $result .= $this->HeaderLine('Date', $this->RFCDate()); + $result .= $this->HeaderLine('Date', self::RFCDate()); if($this->Sender == '') { $result .= $this->HeaderLine('Return-Path', trim($this->From)); } else { $result .= $this->HeaderLine('Return-Path', trim($this->Sender)); } - /* To be created automatically by mail() */ + // To be created automatically by mail() if($this->Mailer != 'mail') { if(count($this->to) > 0) { $result .= $this->AddrAppend('To', $this->to); @@ -884,21 +984,21 @@ class PHPMailer { $from[0][1] = $this->FromName; $result .= $this->AddrAppend('From', $from); - /* sendmail and mail() extract Cc from the header before sending */ - if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->cc) > 0)) { + // sendmail and mail() extract Cc from the header before sending + if(count($this->cc) > 0) { $result .= $this->AddrAppend('Cc', $this->cc); } - /* sendmail and mail() extract Bcc from the header before sending */ + // sendmail and mail() extract Bcc from the header before sending if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) { $result .= $this->AddrAppend('Bcc', $this->bcc); } if(count($this->ReplyTo) > 0) { - $result .= $this->AddrAppend('Reply-To', $this->ReplyTo); + $result .= $this->AddrAppend('Reply-to', $this->ReplyTo); } - /* mail() sets the subject itself */ + // mail() sets the subject itself if($this->Mailer != 'mail') { $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject))); } @@ -909,7 +1009,7 @@ class PHPMailer { $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 (phpmailer.sourceforge.net) [version ' . $this->Version . ']'); + $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (phpmailer.codeworxtech.com)'); if($this->ConfirmReadingTo != '') { $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>'); @@ -929,10 +1029,10 @@ class PHPMailer { /** * Returns the message MIME. - * @access private + * @access public * @return string */ - function GetMailMIME() { + public function GetMailMIME() { $result = ''; switch($this->message_type) { case 'plain': @@ -940,7 +1040,6 @@ class PHPMailer { $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet); break; case 'attachments': - /* fall through */ 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); @@ -964,84 +1063,83 @@ class PHPMailer { /** * Assembles the message body. Returns an empty string on failure. - * @access private - * @return string + * @access public + * @return string The assembled message body */ - function CreateBody() { - $result = ''; + public function CreateBody() { + $body = ''; + if ($this->sign_key_file) { - $result .= $this->GetMailMIME(); + $body .= $this->GetMailMIME(); } $this->SetWordWrap(); switch($this->message_type) { case 'alt': - $result .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); - $result .= $this->EncodeString($this->AltBody, $this->Encoding); - $result .= $this->LE.$this->LE; - $result .= $this->GetBoundary($this->boundary[1], '', 'text/html', ''); - $result .= $this->EncodeString($this->Body, $this->Encoding); - $result .= $this->LE.$this->LE; - $result .= $this->EndBoundary($this->boundary[1]); + $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[1], '', 'text/html', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->EndBoundary($this->boundary[1]); break; case 'plain': - $result .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->EncodeString($this->Body, $this->Encoding); break; case 'attachments': - $result .= $this->GetBoundary($this->boundary[1], '', '', ''); - $result .= $this->EncodeString($this->Body, $this->Encoding); - $result .= $this->LE; - $result .= $this->AttachAll(); + $body .= $this->GetBoundary($this->boundary[1], '', '', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE; + $body .= $this->AttachAll(); break; case 'alt_attachments': - $result .= sprintf("--%s%s", $this->boundary[1], $this->LE); - $result .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE); - $result .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body - $result .= $this->EncodeString($this->AltBody, $this->Encoding); - $result .= $this->LE.$this->LE; - $result .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body - $result .= $this->EncodeString($this->Body, $this->Encoding); - $result .= $this->LE.$this->LE; - $result .= $this->EndBoundary($this->boundary[2]); - $result .= $this->AttachAll(); + $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 + $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->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->EndBoundary($this->boundary[2]); + $body .= $this->AttachAll(); break; } - if($this->IsError()) { - $result = ''; - } else if ($this->sign_key_file) { - $file = tempnam("", "mail"); - $fp = fopen($file, "w"); - fwrite($fp, $result); - fclose($fp); - $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)) { - $fp = fopen($signed, "r"); - $result = fread($fp, filesize($this->sign_key_file)); - $result = ''; - while(!feof($fp)){ - $result = $result . fread($fp, 1024); + if ($this->IsError()) { + $body = ''; + } elseif ($this->sign_key_file) { + try { + $file = tempnam('', 'mail'); + file_put_contents($file, $body); //TODO check this worked + $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); + } else { + @unlink($file); + @unlink($signed); + throw new phpmailerException($this->Lang("signing").openssl_error_string()); + } + } catch (phpmailerException $e) { + $body = ''; + if ($this->exceptions) { + throw $e; } - fclose($fp); - } else { - $this->SetError($this->Lang("signing").openssl_error_string()); - $result = ''; } - - unlink($file); - unlink($signed); } - return $result; + return $body; } /** * Returns the start of a message boundary. * @access private */ - function GetBoundary($boundary, $charSet, $contentType, $encoding) { + private function GetBoundary($boundary, $charSet, $contentType, $encoding) { $result = ''; if($charSet == '') { $charSet = $this->CharSet; @@ -1065,7 +1163,7 @@ class PHPMailer { * Returns the end of a message boundary. * @access private */ - function EndBoundary($boundary) { + private function EndBoundary($boundary) { return $this->LE . '--' . $boundary . '--' . $this->LE; } @@ -1074,7 +1172,7 @@ class PHPMailer { * @access private * @return void */ - function SetMessageType() { + private function SetMessageType() { if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) { $this->message_type = 'plain'; } else { @@ -1090,20 +1188,21 @@ class PHPMailer { } } - /* Returns a formatted header line. - * @access private + /** + * Returns a formatted header line. + * @access public * @return string */ - function HeaderLine($name, $value) { + public function HeaderLine($name, $value) { return $name . ': ' . $value . $this->LE; } /** * Returns a formatted mail line. - * @access private + * @access public * @return string */ - function TextLine($value) { + public function TextLine($value) { return $value . $this->LE; } @@ -1121,56 +1220,80 @@ class PHPMailer { * @param string $type File extension (MIME) type. * @return bool */ - function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { - if(!@is_file($path)) { - $this->SetError($this->Lang('file_access') . $path); - return false; + public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { + try { + if ( !@is_file($path) ) { + throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE); + } + $filename = basename($path); + if ( $name == '' ) { + $name = $filename; + } + + $this->attachment[] = array( + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => 'attachment', + 7 => 0 + ); + + } catch (phpmailerException $e) { + $this->SetError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + echo $e->getMessage()."\n"; + if ( $e->getCode() == self::STOP_CRITICAL ) { + return false; + } } - - $filename = basename($path); - if($name == '') { - $name = $filename; - } - - $cur = count($this->attachment); - $this->attachment[$cur][0] = $path; - $this->attachment[$cur][1] = $filename; - $this->attachment[$cur][2] = $name; - $this->attachment[$cur][3] = $encoding; - $this->attachment[$cur][4] = $type; - $this->attachment[$cur][5] = false; // isStringAttachment - $this->attachment[$cur][6] = 'attachment'; - $this->attachment[$cur][7] = 0; - return true; } + /** + * Return the current array of attachments + * @return array + */ + public function GetAttachments() { + return $this->attachment; + } + /** * Attaches all fs, string, and binary attachments to the message. * Returns an empty string on failure. * @access private * @return string */ - function AttachAll() { - /* Return text of body */ + private function AttachAll() { + // Return text of body $mime = array(); + $cidUniq = array(); + $incl = array(); - /* Add all attachments */ - for($i = 0; $i < count($this->attachment); $i++) { - /* Check for string attachment */ - $bString = $this->attachment[$i][5]; + // Add all attachments + foreach ($this->attachment as $attachment) { + // Check for string attachment + $bString = $attachment[5]; if ($bString) { - $string = $this->attachment[$i][0]; + $string = $attachment[0]; } else { - $path = $this->attachment[$i][0]; + $path = $attachment[0]; } - $filename = $this->attachment[$i][1]; - $name = $this->attachment[$i][2]; - $encoding = $this->attachment[$i][3]; - $type = $this->attachment[$i][4]; - $disposition = $this->attachment[$i][6]; - $cid = $this->attachment[$i][7]; + 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); @@ -1182,7 +1305,7 @@ class PHPMailer { $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); - /* Encode as string attachment */ + // Encode as string attachment if($bString) { $mime[] = $this->EncodeString($string, $encoding); if($this->IsError()) { @@ -1204,42 +1327,56 @@ class PHPMailer { } /** - * Encodes attachment in requested format. Returns an - * empty string on failure. + * Encodes attachment in requested format. + * 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' + * @see EncodeFile() * @access private * @return string */ - function EncodeFile ($path, $encoding = 'base64') { - if(!@$fd = fopen($path, 'rb')) { - $this->SetError($this->Lang('file_open') . $path); + private 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 (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); } + return $file_buffer; + } catch (Exception $e) { + $this->SetError($e->getMessage()); return ''; } - $magic_quotes = get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - $file_buffer = fread($fd, filesize($path)); - $file_buffer = $this->EncodeString($file_buffer, $encoding); - fclose($fd); - set_magic_quotes_runtime($magic_quotes); - - return $file_buffer; } /** - * Encodes string to requested format. Returns an - * empty string on failure. - * @access private + * Encodes string to requested format. + * Returns an empty string on failure. + * @param string $str The text to encode + * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' + * @access public * @return string */ - function EncodeString ($str, $encoding = 'base64') { + public function EncodeString ($str, $encoding = 'base64') { $encoded = ''; switch(strtolower($encoding)) { case 'base64': - /* chunk_split is found in PHP >= 3.0.6 */ $encoded = chunk_split(base64_encode($str), 76, $this->LE); break; case '7bit': case '8bit': $encoded = $this->FixEOL($str); + //Make sure it ends with a line break if (substr($encoded, -(strlen($this->LE))) != $this->LE) $encoded .= $this->LE; break; @@ -1257,17 +1394,17 @@ class PHPMailer { } /** - * Encode a header string to best of Q, B, quoted or none. - * @access private + * Encode a header string to best (shortest) of Q, B, quoted or none. + * @access public * @return string */ - function EncodeHeader ($str, $position = 'text') { + public function EncodeHeader($str, $position = 'text') { $x = 0; switch (strtolower($position)) { case 'phrase': if (!preg_match('/[\200-\377]/', $str)) { - /* Can't use addslashes as we don't know what value has magic_quotes_sybase. */ + // Can't use addslashes as we don't know what value has magic_quotes_sybase $encoded = addcslashes($str, "\0..\37\177\\\""); if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { return ($encoded); @@ -1279,7 +1416,7 @@ class PHPMailer { break; case 'comment': $x = preg_match_all('/[()"]/', $str, $matches); - /* Fall-through */ + // Fall-through case 'text': default: $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); @@ -1291,12 +1428,12 @@ class PHPMailer { } $maxlen = 75 - 7 - strlen($this->CharSet); - /* Try to select the encoding which should produce the shortest output */ + // Try to select the encoding which should produce the shortest output if (strlen($str)/3 < $x) { $encoding = 'B'; 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 + // Use a custom function which correctly encodes and wraps long + // multibyte strings without breaking lines within a character $encoded = $this->Base64EncodeWrapMB($str); } else { $encoded = base64_encode($str); @@ -1318,15 +1455,15 @@ class PHPMailer { /** * Checks if a string contains multibyte characters. - * @access private + * @access public * @param string $str multi-byte text to wrap encode * @return bool */ - function HasMultiBytes($str) { + public function HasMultiBytes($str) { if (function_exists('mb_strlen')) { return (strlen($str) > mb_strlen($str, $this->CharSet)); } else { // Assume no multibytes (we can't handle without mbstring functions anyway) - return False; + return false; } } @@ -1334,11 +1471,11 @@ class PHPMailer { * Correctly encodes and wraps long multibyte strings for mail headers * without breaking lines within a character. * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php - * @access private + * @access public * @param string $str multi-byte text to wrap encode * @return string */ - function Base64EncodeWrapMB($str) { + public function Base64EncodeWrapMB($str) { $start = "=?".$this->CharSet."?B?"; $end = "?="; $encoded = ""; @@ -1371,11 +1508,14 @@ class PHPMailer { } /** - * Encode string to quoted-printable. - * @access private - * @return string - */ - function EncodeQP( $input = '', $line_max = 76, $space_conv = false ) { + * Encode string to quoted-printable. + * Only uses standard PHP, slow, but will always work + * @access public + * @param string $string the text to encode + * @param integer $line_max Number of chars allowed on a line before wrapping + * @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'); $lines = preg_split('/(?:\r\n|\r|\n)/', $input); $eol = "\r\n"; @@ -1416,14 +1556,50 @@ class PHPMailer { return $output; } + /** + * Encode string to RFC2045 (6.7) quoted-printable format + * Uses a PHP5 stream filter to do the encoding about 64x faster than the old version + * Also results in same content as you started with after decoding + * @see EncodeQPphp() + * @access public + * @param string $string the text to encode + * @param integer $line_max Number of chars allowed on a line before wrapping + * @param boolean $space_conv Dummy param for compatibility with existing EncodeQP function + * @return string + * @author Marcus Bointon + */ + public function EncodeQP($string, $line_max = 76, $space_conv = false) { + if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3) + return quoted_printable_encode($string); + } + $filters = stream_get_filters(); + if (!in_array('convert.*', $filters)) { //Got convert stream filter? + return $this->EncodeQPphp($string, $line_max, $space_conv); //Fall back to old implementation + } + $fp = fopen('php://temp/', 'r+'); + $string = preg_replace('/\r\n?/', $this->LE, $string); //Normalise line breaks + $params = array('line-length' => $line_max, 'line-break-chars' => $this->LE); + $s = stream_filter_append($fp, 'convert.quoted-printable-encode', STREAM_FILTER_READ, $params); + fputs($fp, $string); + rewind($fp); + $out = stream_get_contents($fp); + stream_filter_remove($s); + $out = preg_replace('/^\./m', '=2E', $out); //Encode . if it is first char on a line, workaround for bug in Exchange + fclose($fp); + return $out; + } + /** * Encode string to q encoding. - * @access private + * @link http://tools.ietf.org/html/rfc2047 + * @param string $str the text to encode + * @param string $position Where the text is going to be used, see the RFC for what that means + * @access public * @return string */ - 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 + $encoded = preg_replace('/[\r\n]*/', '', $str); switch (strtolower($position)) { case 'phrase': @@ -1433,13 +1609,14 @@ class PHPMailer { $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); case 'text': default: - /* Replace every high ascii, control =, ? and _ characters */ + // 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); break; } - /* Replace every spaces to _ (more readable than =20) */ + // Replace every spaces to _ (more readable than =20) $encoded = str_replace(' ', '_', $encoded); return $encoded; @@ -1455,17 +1632,18 @@ class PHPMailer { * @param string $type File extension (MIME) type. * @return void */ - function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') { - /* Append to $attachment array */ - $cur = count($this->attachment); - $this->attachment[$cur][0] = $string; - $this->attachment[$cur][1] = $filename; - $this->attachment[$cur][2] = $filename; - $this->attachment[$cur][3] = $encoding; - $this->attachment[$cur][4] = $type; - $this->attachment[$cur][5] = true; // isString - $this->attachment[$cur][6] = 'attachment'; - $this->attachment[$cur][7] = 0; + public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') { + // Append to $attachment array + $this->attachment[] = array( + 0 => $string, + 1 => $filename, + 2 => $filename, + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => 'attachment', + 7 => 0 + ); } /** @@ -1481,47 +1659,45 @@ class PHPMailer { * @param string $type File extension (MIME) type. * @return bool */ - function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { + public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { - if(!@is_file($path)) { + if ( !@is_file($path) ) { $this->SetError($this->Lang('file_access') . $path); return false; } $filename = basename($path); - if($name == '') { + if ( $name == '' ) { $name = $filename; } - /* Append to $attachment array */ - $cur = count($this->attachment); - $this->attachment[$cur][0] = $path; - $this->attachment[$cur][1] = $filename; - $this->attachment[$cur][2] = $name; - $this->attachment[$cur][3] = $encoding; - $this->attachment[$cur][4] = $type; - $this->attachment[$cur][5] = false; - $this->attachment[$cur][6] = 'inline'; - $this->attachment[$cur][7] = $cid; + // Append to $attachment array + $this->attachment[] = array( + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => 'inline', + 7 => $cid + ); return true; } /** * Returns true if an inline attachment is present. - * @access private + * @access public * @return bool */ - function InlineImageExists() { - $result = false; - for($i = 0; $i < count($this->attachment); $i++) { - if($this->attachment[$i][6] == 'inline') { - $result = true; - break; + public function InlineImageExists() { + foreach($this->attachment as $attachment) { + if ($attachment[6] == 'inline') { + return true; } } - - return $result; + return false; } ///////////////////////////////////////////////// @@ -1532,7 +1708,10 @@ class PHPMailer { * Clears all recipients assigned in the TO array. Returns void. * @return void */ - function ClearAddresses() { + public function ClearAddresses() { + foreach($this->to as $to) { + unset($this->all_recipients[strtolower($to[0])]); + } $this->to = array(); } @@ -1540,7 +1719,10 @@ class PHPMailer { * Clears all recipients assigned in the CC array. Returns void. * @return void */ - function ClearCCs() { + public function ClearCCs() { + foreach($this->cc as $cc) { + unset($this->all_recipients[strtolower($cc[0])]); + } $this->cc = array(); } @@ -1548,7 +1730,10 @@ class PHPMailer { * Clears all recipients assigned in the BCC array. Returns void. * @return void */ - function ClearBCCs() { + public function ClearBCCs() { + foreach($this->bcc as $bcc) { + unset($this->all_recipients[strtolower($bcc[0])]); + } $this->bcc = array(); } @@ -1556,7 +1741,7 @@ class PHPMailer { * Clears all recipients assigned in the ReplyTo array. Returns void. * @return void */ - function ClearReplyTos() { + public function ClearReplyTos() { $this->ReplyTo = array(); } @@ -1565,10 +1750,11 @@ class PHPMailer { * array. Returns void. * @return void */ - function ClearAllRecipients() { + public function ClearAllRecipients() { $this->to = array(); $this->cc = array(); $this->bcc = array(); + $this->all_recipients = array(); } /** @@ -1576,7 +1762,7 @@ class PHPMailer { * attachments. Returns void. * @return void */ - function ClearAttachments() { + public function ClearAttachments() { $this->attachment = array(); } @@ -1584,7 +1770,7 @@ class PHPMailer { * Clears all custom headers. Returns void. * @return void */ - function ClearCustomHeaders() { + public function ClearCustomHeaders() { $this->CustomHeader = array(); } @@ -1594,21 +1780,27 @@ class PHPMailer { /** * Adds the error message to the error container. - * Returns void. - * @access private + * @access protected * @return void */ - function SetError($msg) { + protected function SetError($msg) { $this->error_count++; + if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { + $lasterror = $this->smtp->getError(); + if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) { + $msg .= '

' . $this->Lang('smtp_error') . $lasterror['smtp_msg'] . "

\n"; + } + } $this->ErrorInfo = $msg; } /** * Returns the proper RFC 822 formatted date. - * @access private + * @access public * @return string + * @static */ - function RFCDate() { + public static function RFCDate() { $tz = date('Z'); $tzs = ($tz < 0) ? '-' : '+'; $tz = abs($tz); @@ -1618,41 +1810,16 @@ class PHPMailer { return $result; } - /** - * Returns the appropriate server variable. Should work with both - * PHP 4.1.0+ as well as older versions. Returns an empty string - * if nothing is found. - * @access private - * @return mixed - */ - function ServerVar($varName) { - global $HTTP_SERVER_VARS; - global $HTTP_ENV_VARS; - - if(!isset($_SERVER)) { - $_SERVER = $HTTP_SERVER_VARS; - if(!isset($_SERVER['REMOTE_ADDR'])) { - $_SERVER = $HTTP_ENV_VARS; // must be Apache - } - } - - if(isset($_SERVER[$varName])) { - return $_SERVER[$varName]; - } else { - return ''; - } - } - /** * Returns the server hostname or 'localhost.localdomain' if unknown. * @access private * @return string */ - function ServerHostname() { - if ($this->Hostname != '') { + private function ServerHostname() { + if (!empty($this->Hostname)) { $result = $this->Hostname; - } elseif ($this->ServerVar('SERVER_NAME') != '') { - $result = $this->ServerVar('SERVER_NAME'); + } elseif (isset($_SERVER['SERVER_NAME'])) { + $result = $_SERVER['SERVER_NAME']; } else { $result = 'localhost.localdomain'; } @@ -1665,7 +1832,7 @@ class PHPMailer { * @access private * @return string */ - function Lang($key) { + private function Lang($key) { if(count($this->language) < 1) { $this->SetLanguage('en'); // set the default language } @@ -1679,9 +1846,10 @@ class PHPMailer { /** * Returns true if an error occurred. + * @access public * @return bool */ - function IsError() { + public function IsError() { return ($this->error_count > 0); } @@ -1690,7 +1858,7 @@ class PHPMailer { * @access private * @return string */ - function FixEOL($str) { + private function FixEOL($str) { $str = str_replace("\r\n", "\n", $str); $str = str_replace("\r", "\n", $str); $str = str_replace("\n", $this->LE, $str); @@ -1699,9 +1867,10 @@ class PHPMailer { /** * Adds a custom header. + * @access public * @return void */ - function AddCustomHeader($custom_header) { + public function AddCustomHeader($custom_header) { $this->CustomHeader[] = explode(':', $custom_header, 2); } @@ -1710,19 +1879,18 @@ class PHPMailer { * @access public * @return $message */ - function MsgHTML($message,$basedir='') { + public function MsgHTML($message, $basedir = '') { 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][A-z]*:\/\//',$url)) { + if (!preg_match('#^[A-z]+://#',$url)) { $filename = basename($url); $directory = dirname($url); ($directory == '.')?$directory='':''; $cid = 'cid:' . md5($filename); - $fileParts = split("\.", $filename); - $ext = $fileParts[1]; - $mimeType = $this->_mime_types($ext); + $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) ) { @@ -1734,161 +1902,152 @@ class PHPMailer { $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) ) { + if (!empty($textMsg) && empty($this->AltBody)) { $this->AltBody = html_entity_decode($textMsg); } - if ( empty($this->AltBody) ) { - $this->AltBody = 'To view this email message, open the email in with HTML compatibility!' . "\n\n"; + if (empty($this->AltBody)) { + $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; } } /** - * Gets the mime type of the embedded or inline image - * @access private - * @return mime type of ext + * Gets the MIME type of the embedded or inline image + * @param string File extension + * @access public + * @return string MIME type of ext + * @static */ - function _mime_types($ext = '') { + public static function _mime_types($ext = '') { $mimes = array( - 'ai' => 'application/postscript', - 'aif' => 'audio/x-aiff', - 'aifc' => 'audio/x-aiff', - 'aiff' => 'audio/x-aiff', - 'avi' => 'video/x-msvideo', - 'bin' => 'application/macbinary', - 'bmp' => 'image/bmp', - 'class' => 'application/octet-stream', - 'cpt' => 'application/mac-compactpro', - 'css' => 'text/css', - 'dcr' => 'application/x-director', - 'dir' => 'application/x-director', - 'dll' => 'application/octet-stream', - 'dms' => 'application/octet-stream', - 'doc' => 'application/msword', - 'dvi' => 'application/x-dvi', - 'dxr' => 'application/x-director', - 'eml' => 'message/rfc822', - 'eps' => 'application/postscript', - 'exe' => 'application/octet-stream', - 'gif' => 'image/gif', - 'gtar' => 'application/x-gtar', - 'htm' => 'text/html', - 'html' => 'text/html', - 'jpe' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', 'hqx' => 'application/mac-binhex40', - 'js' => 'application/x-javascript', + 'cpt' => 'application/mac-compactpro', + 'doc' => 'application/msword', + 'bin' => 'application/macbinary', + 'dms' => 'application/octet-stream', 'lha' => 'application/octet-stream', - 'log' => 'text/plain', 'lzh' => 'application/octet-stream', - 'mid' => 'audio/midi', - 'midi' => 'audio/midi', - 'mif' => 'application/vnd.mif', - 'mov' => 'video/quicktime', - 'movie' => 'video/x-sgi-movie', - 'mp2' => 'audio/mpeg', - 'mp3' => 'audio/mpeg', - 'mpe' => 'video/mpeg', - 'mpeg' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'mpga' => 'audio/mpeg', + 'exe' => 'application/octet-stream', + 'class' => 'application/octet-stream', + 'psd' => 'application/octet-stream', + 'so' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'dll' => 'application/octet-stream', 'oda' => 'application/oda', 'pdf' => 'application/pdf', - 'php' => 'application/x-httpd-php', - 'php3' => 'application/x-httpd-php', - 'php4' => 'application/x-httpd-php', - 'phps' => 'application/x-httpd-php-source', - 'phtml' => 'application/x-httpd-php', - 'png' => 'image/png', - 'ppt' => 'application/vnd.ms-powerpoint', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', 'ps' => 'application/postscript', - 'psd' => 'application/octet-stream', - 'qt' => 'video/quicktime', - 'ra' => 'audio/x-realaudio', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'mif' => 'application/vnd.mif', + 'xls' => 'application/vnd.ms-excel', + 'ppt' => 'application/vnd.ms-powerpoint', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + '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', + 'phtml' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'js' => 'application/x-javascript', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'tar' => 'application/x-tar', + 'tgz' => 'application/x-tar', + 'xhtml' => 'application/xhtml+xml', + 'xht' => 'application/xhtml+xml', + 'zip' => 'application/zip', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', 'ram' => 'audio/x-pn-realaudio', 'rm' => 'audio/x-pn-realaudio', 'rpm' => 'audio/x-pn-realaudio-plugin', - 'rtf' => 'text/rtf', - 'rtx' => 'text/richtext', + 'ra' => 'audio/x-realaudio', 'rv' => 'video/vnd.rn-realvideo', - 'sea' => 'application/octet-stream', - 'shtml' => 'text/html', - 'sit' => 'application/x-stuffit', - 'so' => 'application/octet-stream', - 'smi' => 'application/smil', - 'smil' => 'application/smil', - 'swf' => 'application/x-shockwave-flash', - 'tar' => 'application/x-tar', - 'text' => 'text/plain', - 'txt' => 'text/plain', - 'tgz' => 'application/x-tar', - 'tif' => 'image/tiff', - 'tiff' => 'image/tiff', 'wav' => 'audio/x-wav', - 'wbxml' => 'application/vnd.wap.wbxml', - 'wmlc' => 'application/vnd.wap.wmlc', - 'word' => 'application/msword', - 'xht' => 'application/xhtml+xml', - 'xhtml' => 'application/xhtml+xml', - 'xl' => 'application/excel', - 'xls' => 'application/vnd.ms-excel', + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'png' => 'image/png', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'css' => 'text/css', + 'html' => 'text/html', + 'htm' => 'text/html', + 'shtml' => 'text/html', + 'txt' => 'text/plain', + 'text' => 'text/plain', + 'log' => 'text/plain', + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', 'xml' => 'text/xml', 'xsl' => 'text/xml', - 'zip' => 'application/zip' + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie', + 'doc' => 'application/msword', + 'word' => 'application/msword', + 'xl' => 'application/excel', + 'eml' => 'message/rfc822' ); - return ( ! isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)]; + return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)]; } /** - * Set (or reset) Class Objects (variables) - * - * Usage Example: - * $page->set('X-Priority', '3'); - * - * @access public - * @param string $name Parameter Name - * @param mixed $value Parameter Value - * NOTE: will not work with arrays, there are no arrays to set/reset - */ - function set ( $name, $value = '' ) { - if ( isset($this->$name) ) { - $this->$name = $value; - } else { - $this->SetError('Cannot set or reset variable ' . $name); - return false; - } - } - - /** - * Read a file from a supplied filename and return it. - * - * @access public - * @param string $filename Parameter File Name - */ - function getFile($filename) { - $return = ''; - if ($fp = fopen($filename, 'rb')) { - while (!feof($fp)) { - $return .= fread($fp, 1024); + * Set (or reset) Class Objects (variables) + * + * Usage Example: + * $page->set('X-Priority', '3'); + * + * @access public + * @param string $name Parameter Name + * @param mixed $value Parameter Value + * NOTE: will not work with arrays, there are no arrays to set/reset + * @todo Should this not be using __set() magic function? + */ + public function set($name, $value = '') { + try { + if (isset($this->$name) ) { + $this->$name = $value; + } else { + throw new phpmailerException($this->Lang('variable_set') . $name, self::STOP_CRITICAL); + } + } catch (Exception $e) { + $this->SetError($e->getMessage()); + if ($e->getCode() == self::STOP_CRITICAL) { + return false; } - fclose($fp); - return $return; - } else { - return false; } + return true; } /** * Strips newlines to prevent header injection. - * @access private + * @access public * @param string $str String * @return string */ - function SecureHeader($str) { - $str = trim($str); - $str = str_replace("\r", "", $str); - $str = str_replace("\n", "", $str); - return $str; + public function SecureHeader($str) { + $str = str_replace("\r", '', $str); + $str = str_replace("\n", '', $str); + return trim($str); } /** @@ -1898,12 +2057,17 @@ class PHPMailer { * @param string $key_filename Parameter File Name * @param string $key_pass Password for private key */ - function Sign($cert_filename, $key_filename, $key_pass) { + public function Sign($cert_filename, $key_filename, $key_pass) { $this->sign_cert_file = $cert_filename; $this->sign_key_file = $key_filename; $this->sign_key_pass = $key_pass; } - } +class phpmailerException extends Exception { + public function errorMessage() { + $errorMsg = '' . $this->getMessage() . "
\n"; + return $errorMsg; + } +} ?> \ No newline at end of file diff --git a/e107_handlers/phpmailer/class.pop3.php b/e107_handlers/phpmailer/class.pop3.php index 0a6d8d45a..e71bfc9da 100644 --- a/e107_handlers/phpmailer/class.pop3.php +++ b/e107_handlers/phpmailer/class.pop3.php @@ -2,14 +2,16 @@ /*~ class.pop3.php .---------------------------------------------------------------------------. | Software: PHPMailer - PHP email class | -| Version: 2.0.4 | +| 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/ | | ------------------------------------------------------------------------- | -| Author: Andy Prevost (project admininistrator) | -| Author: Brent R. Matzelle (original founder) | -| Copyright (c) 2004-2007, Andy Prevost. All Rights Reserved. | +| Admin: Andy Prevost (project admininistrator) | +| Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net | +| : Marcus Bointon (coolbru) coolbru@users.sourceforge.net | +| Founder: Brent R. Matzelle (original founder) | +| Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. | | Copyright (c) 2001-2003, Brent R. Matzelle | | ------------------------------------------------------------------------- | | License: Distributed under the Lesser General Public License (LGPL) | @@ -23,18 +25,32 @@ | - Technology Consulting | | - Oursourcing (highly qualified programmers and graphic designers) | '---------------------------------------------------------------------------' +*/ + +/** + * PHPMailer - PHP POP Before SMTP Authentication Class + * NOTE: Designed for use with PHP version 5 and up + * @package PHPMailer + * @author Andy Prevost + * @author Marcus Bointon + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) + * @version $Id: class.pop3.php,v 1.5 2009-09-01 19:53:08 e107steved Exp $ + */ /** * POP Before SMTP Authentication Class + * Version 5.0.0 * * Author: Richard Davey (rich@corephp.co.uk) + * Modifications: Andy Prevost * 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 me + * Does not yet work with APOP - if you have an APOP account, contact Richard Davey * and we can test changes to this script. * - * This class is based on the structure of the SMTP class by Chris Ryan + * This class is based on the structure of the SMTP class originally authored by Chris Ryan * * This class is rfc 1939 compliant and implements all the commands * required for POP3 connection, authentication and disconnection. @@ -43,112 +59,103 @@ * @author Richard Davey */ -class POP3 -{ +class POP3 { /** * Default POP3 port * @var int */ - var $POP3_PORT = 110; + public $POP3_PORT = 110; /** * Default Timeout * @var int */ - var $POP3_TIMEOUT = 30; + public $POP3_TIMEOUT = 30; /** * POP3 Carriage Return + Line Feed * @var string */ - var $CRLF = "\r\n"; + public $CRLF = "\r\n"; /** * Displaying Debug warnings? (0 = now, 1+ = yes) * @var int */ - var $do_debug = 2; + public $do_debug = 2; /** * POP3 Mail Server * @var string */ - var $host; + public $host; /** * POP3 Port * @var int */ - var $port; + public $port; /** * POP3 Timeout Value * @var int */ - var $tval; + public $tval; /** * POP3 Username * @var string */ - var $username; + public $username; /** * POP3 Password * @var string */ - var $password; + public $password; - /**#@+ - * @access private - */ - var $pop_conn; - var $connected; - var $error; // Error log array - /**#@-*/ + ///////////////////////////////////////////////// + // PROPERTIES, PRIVATE AND PROTECTED + ///////////////////////////////////////////////// + + private $pop_conn; + private $connected; + private $error; // Error log array /** * Constructor, sets the initial values - * + * @access public * @return POP3 */ - function POP3 () - { - $this->pop_conn = 0; - $this->connected = false; - $this->error = null; - } + public function __construct() { + $this->pop_conn = 0; + $this->connected = false; + $this->error = null; + } /** * Combination of public events - connect, login, disconnect - * + * @access public * @param string $host * @param integer $port * @param integer $tval * @param string $username * @param string $password */ - function Authorise ($host, $port = false, $tval = false, $username, $password, $debug_level = 0) - { + public function Authorise ($host, $port = false, $tval = false, $username, $password, $debug_level = 0) { $this->host = $host; // If no port value is passed, retrieve it - if ($port == false) - { + if ($port == false) { $this->port = $this->POP3_PORT; - } - else - { + } else { $this->port = $port; } // If no port value is passed, retrieve it - if ($tval == false) - { + if ($tval == false) { $this->tval = $this->POP3_TIMEOUT; - } - else - { + } else { $this->tval = $tval; } @@ -157,17 +164,15 @@ class POP3 $this->password = $password; // Refresh the error log - $this->error = null; + $this->error = null; - // Connect + // Connect $result = $this->Connect($this->host, $this->port, $this->tval); - if ($result) - { + if ($result) { $login_result = $this->Login($this->username, $this->password); - if ($login_result) - { + if ($login_result) { $this->Disconnect(); return true; @@ -183,23 +188,21 @@ class POP3 /** * Connect to the POP3 server - * + * @access public * @param string $host * @param integer $port * @param integer $tval * @return boolean */ - function Connect ($host, $port = false, $tval = 30) - { + public function Connect ($host, $port = false, $tval = 30) { // Are we already connected? - if ($this->connected) - { + if ($this->connected) { return true; } /* - On Windows this will raise a PHP Warning error if the hostname doesn't exist. - Rather than supress it with @fsockopen, let's capture it cleanly instead + On Windows this will raise a PHP Warning error if the hostname doesn't exist. + Rather than supress it with @fsockopen, let's capture it cleanly instead */ set_error_handler(array(&$this, 'catchWarning')); @@ -215,215 +218,183 @@ class POP3 restore_error_handler(); // Does the Error Log now contain anything? - if ($this->error && $this->do_debug >= 1) - { - $this->displayErrors(); + if ($this->error && $this->do_debug >= 1) { + $this->displayErrors(); } // Did we connect? - if ($this->pop_conn == false) - { - // It would appear not... - $this->error = array( - 'error' => "Failed to connect to server $host on port $port", - 'errno' => $errno, - 'errstr' => $errstr - ); + if ($this->pop_conn == false) { + // It would appear not... + $this->error = array( + 'error' => "Failed to connect to server $host on port $port", + 'errno' => $errno, + 'errstr' => $errstr + ); - if ($this->do_debug >= 1) - { - $this->displayErrors(); - } - - return false; - } - - // Increase the stream time-out - - // Check for PHP 4.3.0 or later - if (version_compare(phpversion(), '4.3.0', 'ge')) - { - stream_set_timeout($this->pop_conn, $tval, 0); - } - else - { - // Does not work on Windows - if (substr(PHP_OS, 0, 3) !== 'WIN') - { - socket_set_timeout($this->pop_conn, $tval, 0); - } - } - - // Get the POP3 server response - $pop3_response = $this->getResponse(); - - // Check for the +OK - if ($this->checkResponse($pop3_response)) - { - // The connection is established and the POP3 server is talking - $this->connected = true; - return true; + if ($this->do_debug >= 1) { + $this->displayErrors(); } + return false; } - /** - * Login to the POP3 server (does not support APOP yet) - * - * @param string $username - * @param string $password - * @return boolean - */ - function Login ($username = '', $password = '') - { - if ($this->connected == false) - { - $this->error = 'Not connected to POP3 server'; + // Increase the stream time-out - if ($this->do_debug >= 1) - { - $this->displayErrors(); - } + // Check for PHP 4.3.0 or later + if (version_compare(phpversion(), '5.0.0', 'ge')) { + stream_set_timeout($this->pop_conn, $tval, 0); + } else { + // Does not work on Windows + if (substr(PHP_OS, 0, 3) !== 'WIN') { + socket_set_timeout($this->pop_conn, $tval, 0); } + } - if (empty($username)) - { - $username = $this->username; - } + // Get the POP3 server response + $pop3_response = $this->getResponse(); - if (empty($password)) - { - $password = $this->password; + // Check for the +OK + if ($this->checkResponse($pop3_response)) { + // The connection is established and the POP3 server is talking + $this->connected = true; + return true; + } + + } + + /** + * Login to the POP3 server (does not support APOP yet) + * @access public + * @param string $username + * @param string $password + * @return boolean + */ + public function Login ($username = '', $password = '') { + if ($this->connected == false) { + $this->error = 'Not connected to POP3 server'; + + if ($this->do_debug >= 1) { + $this->displayErrors(); } + } + + if (empty($username)) { + $username = $this->username; + } + + if (empty($password)) { + $password = $this->password; + } $pop_username = "USER $username" . $this->CRLF; $pop_password = "PASS $password" . $this->CRLF; - // Send the Username - $this->sendString($pop_username); + // Send the Username + $this->sendString($pop_username); + $pop3_response = $this->getResponse(); + + if ($this->checkResponse($pop3_response)) { + // Send the Password + $this->sendString($pop_password); $pop3_response = $this->getResponse(); - if ($this->checkResponse($pop3_response)) - { - // Send the Password - $this->sendString($pop_password); - $pop3_response = $this->getResponse(); - - if ($this->checkResponse($pop3_response)) - { - return true; - } - else - { - return false; - } - } - else - { - return false; - } - } - - /** - * Disconnect from the POP3 server - */ - function Disconnect () - { - $this->sendString('QUIT'); - - fclose($this->pop_conn); - } - - /* - --------------- - Private Methods - --------------- - */ - - /** - * Get the socket response back. - * $size is the maximum number of bytes to retrieve - * - * @param integer $size - * @return string - */ - function getResponse ($size = 128) - { - $pop3_response = fgets($this->pop_conn, $size); - - return $pop3_response; - } - - /** - * Send a string down the open socket connection to the POP3 server - * - * @param string $string - * @return integer - */ - function sendString ($string) - { - $bytes_sent = fwrite($this->pop_conn, $string, strlen($string)); - - return $bytes_sent; - - } - - /** - * Checks the POP3 server response for +OK or -ERR - * - * @param string $string - * @return boolean - */ - function checkResponse ($string) - { - if (substr($string, 0, 3) !== '+OK') - { - $this->error = array( - 'error' => "Server reported an error: $string", - 'errno' => 0, - 'errstr' => '' - ); - - if ($this->do_debug >= 1) - { - $this->displayErrors(); - } - - return false; - } - else - { + if ($this->checkResponse($pop3_response)) { return true; + } else { + return false; + } + } else { + return false; + } + } + + /** + * Disconnect from the POP3 server + * @access public + */ + public function Disconnect () { + $this->sendString('QUIT'); + + fclose($this->pop_conn); + } + + ///////////////////////////////////////////////// + // Private Methods + ///////////////////////////////////////////////// + + /** + * Get the socket response back. + * $size is the maximum number of bytes to retrieve + * @access private + * @param integer $size + * @return string + */ + private function getResponse ($size = 128) { + $pop3_response = fgets($this->pop_conn, $size); + + return $pop3_response; + } + + /** + * Send a string down the open socket connection to the POP3 server + * @access private + * @param string $string + * @return integer + */ + private function sendString ($string) { + $bytes_sent = fwrite($this->pop_conn, $string, strlen($string)); + + return $bytes_sent; + } + + /** + * Checks the POP3 server response for +OK or -ERR + * @access private + * @param string $string + * @return boolean + */ + private function checkResponse ($string) { + if (substr($string, 0, 3) !== '+OK') { + $this->error = array( + 'error' => "Server reported an error: $string", + 'errno' => 0, + 'errstr' => '' + ); + + if ($this->do_debug >= 1) { + $this->displayErrors(); } + return false; + } else { + return true; } - /** - * If debug is enabled, display the error message array - * - */ - function displayErrors () - { - echo '
';
+  }
 
-      foreach ($this->error as $single_error)
-    {
-        print_r($single_error);
+  /**
+   * If debug is enabled, display the error message array
+   * @access private
+   */
+  private function displayErrors () {
+    echo '
';
+
+    foreach ($this->error as $single_error) {
+      print_r($single_error);
     }
 
-      echo '
'; - } + echo '
'; + } /** * Takes over from PHP for the socket warning handler - * + * @access private * @param integer $errno * @param string $errstr * @param string $errfile * @param integer $errline */ - function catchWarning ($errno, $errstr, $errfile, $errline) - { + private function catchWarning ($errno, $errstr, $errfile, $errline) { $this->error[] = array( 'error' => "Connecting to the POP3 server raised a PHP warning: ", 'errno' => $errno, @@ -433,4 +404,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 be084a116..d06aa10e7 100644 --- a/e107_handlers/phpmailer/class.smtp.php +++ b/e107_handlers/phpmailer/class.smtp.php @@ -2,14 +2,16 @@ /*~ class.smtp.php .---------------------------------------------------------------------------. | Software: PHPMailer - PHP email class | -| Version: 2.0.4 | +| 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/ | | ------------------------------------------------------------------------- | -| Author: Andy Prevost (project admininistrator) | -| Author: Brent R. Matzelle (original founder) | -| Copyright (c) 2004-2007, Andy Prevost. All Rights Reserved. | +| Admin: Andy Prevost (project admininistrator) | +| Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net | +| : Marcus Bointon (coolbru) coolbru@users.sourceforge.net | +| Founder: Brent R. Matzelle (original founder) | +| Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. | | Copyright (c) 2001-2003, Brent R. Matzelle | | ------------------------------------------------------------------------- | | License: Distributed under the Lesser General Public License (LGPL) | @@ -23,56 +25,66 @@ | - Technology Consulting | | - Oursourcing (highly qualified programmers and graphic designers) | '---------------------------------------------------------------------------' +*/ + +/** + * PHPMailer - PHP SMTP email transport class + * NOTE: Designed for use with PHP version 5 and up + * @package PHPMailer + * @author Andy Prevost + * @author Marcus Bointon + * @copyright 2004 - 2008 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) + * @version $Id: class.smtp.php,v 1.5 2009-09-01 19:53:08 e107steved Exp $ + */ /** * 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. - * @package PHPMailer - * @author Chris Ryan + * original author: Chris Ryan */ -class SMTP -{ +class SMTP { /** * SMTP server port * @var int */ - var $SMTP_PORT = 25; + public $SMTP_PORT = 25; /** * SMTP reply line ending * @var string */ - var $CRLF = "\r\n"; + public $CRLF = "\r\n"; /** * Sets whether debugging is turned on * @var bool */ - var $do_debug; # the level of debug to perform + public $do_debug; // the level of debug to perform /** * Sets VERP use on/off (default is off) * @var bool */ - var $do_verp = false; + public $do_verp = false; - /**#@+ - * @access private - */ - var $smtp_conn; # the socket to the server - var $error; # error if any on the last call - var $helo_rply; # the reply the server sent to us for HELO - /**#@-*/ + ///////////////////////////////////////////////// + // 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 /** * Initialize the class so that the data is in a known state. * @access public * @return void */ - function SMTP() { + public function __construct() { $this->smtp_conn = 0; $this->error = null; $this->helo_rply = null; @@ -80,9 +92,9 @@ class SMTP $this->do_debug = 0; } - /************************************************************* - * CONNECTION FUNCTIONS * - ***********************************************************/ + ///////////////////////////////////////////////// + // CONNECTION FUNCTIONS + ///////////////////////////////////////////////// /** * Connect to the server specified on the port specified. @@ -97,15 +109,13 @@ class SMTP * @access public * @return bool */ - function Connect($host,$port=0,$tval=30) { - # set the error val to null so there is no confusion + public function Connect($host, $port = 0, $tval = 30) { + // set the error val to null so there is no confusion $this->error = null; - # make sure we are __not__ connected + // make sure we are __not__ connected if($this->connected()) { - # ok we are connected! what should we do? - # for now we will just give an error saying we - # are already connected + // already connected, generate error $this->error = array("error" => "Already connected to a server"); return false; } @@ -114,39 +124,78 @@ class SMTP $port = $this->SMTP_PORT; } - #connect to the smtp server - $this->smtp_conn = fsockopen($host, # the host of the server - $port, # the port to use - $errno, # error number if any - $errstr, # error message if any - $tval); # give up after ? secs - # verify we connected properly + // connect to the smtp server + $this->smtp_conn = @fsockopen($host, // the host of the server + $port, // the port to use + $errno, // error number if any + $errstr, // error message if any + $tval); // give up after ? secs + // verify we connected properly if(empty($this->smtp_conn)) { $this->error = array("error" => "Failed to connect to server", "errno" => $errno, "errstr" => $errstr); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": $errstr ($errno)" . $this->CRLF; + echo "SMTP -> ERROR: " . $this->error["error"] . ": $errstr ($errno)" . $this->CRLF . '
'; } return false; } - # sometimes the SMTP server takes a little longer to respond - # so we will give it a longer timeout for the first read - // Windows still does not have support for this timeout function + // 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); - # get any announcement stuff + // get any announcement $announce = $this->get_lines(); - # set the timeout of any socket functions at 1/10 of a second - //if(function_exists("socket_set_timeout")) - // socket_set_timeout($this->smtp_conn, 0, 100000); + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $announce . $this->CRLF . '
'; + } + + return true; + } + + /** + * Initiate a TLS communication with the server. + * + * SMTP CODE 220 Ready to start TLS + * SMTP CODE 501 Syntax error (no parameters allowed) + * SMTP CODE 454 TLS not available due to temporary reason + * @access public + * @return bool success + */ + public function StartTLS() { + $this->error = null; # to avoid confusion + + if(!$this->connected()) { + $this->error = array("error" => "Called StartTLS() without being connected"); + return false; + } + + fputs($this->smtp_conn,"STARTTLS" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $announce; + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 220) { + $this->error = + array("error" => "STARTTLS 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; + } + + // Begin encrypted connection + if(!stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { + return false; } return true; @@ -158,7 +207,7 @@ class SMTP * @access public * @return bool */ - function Authenticate($username, $password) { + public function Authenticate($username, $password) { // Start authentication fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF); @@ -171,8 +220,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; + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; } return false; } @@ -189,8 +237,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; + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; } return false; } @@ -207,8 +254,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; + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; } return false; } @@ -218,23 +264,21 @@ class SMTP /** * Returns true if connected to a server otherwise false - * @access private + * @access public * @return bool */ - function Connected() { + public function Connected() { if(!empty($this->smtp_conn)) { $sock_status = socket_get_status($this->smtp_conn); if($sock_status["eof"]) { - # hmm this is an odd situation... the socket is - # valid but we are not connected anymore + // 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"; + echo "SMTP -> NOTICE:" . $this->CRLF . "EOF caught while checking if connected"; } $this->Close(); return false; } - return true; # everything looks good + return true; // everything looks good } return false; } @@ -246,19 +290,19 @@ class SMTP * @access public * @return void */ - function Close() { - $this->error = null; # so there is no confusion + public function Close() { + $this->error = null; // so there is no confusion $this->helo_rply = null; if(!empty($this->smtp_conn)) { - # close the connection and cleanup + // close the connection and cleanup fclose($this->smtp_conn); $this->smtp_conn = 0; } } - /*************************************************************** - * SMTP COMMANDS * - *************************************************************/ + ///////////////////////////////////////////////// + // SMTP COMMANDS + ///////////////////////////////////////////////// /** * Issues a data command and sends the msg_data to the server @@ -279,8 +323,8 @@ class SMTP * @access public * @return bool */ - function Data($msg_data) { - $this->error = null; # so no confusion is caused + public function Data($msg_data) { + $this->error = null; // so no confusion is caused if(!$this->connected()) { $this->error = array( @@ -294,7 +338,7 @@ class SMTP $code = substr($rply,0,3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; } if($code != 354) { @@ -303,69 +347,73 @@ class SMTP "smtp_code" => $code, "smtp_msg" => substr($rply,4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; } return false; } - # the server is ready to accept data! - # according to rfc 821 we should not send more than 1000 - # including the CRLF - # characters on a single line so we will break the data up - # into lines by \r and/or \n then if needed we will break - # each of those into smaller lines to fit within the limit. - # in addition we will be looking for lines that start with - # a period '.' and append and additional period '.' to that - # line. NOTE: this does not count towards are limit. + /* the server is ready to accept data! + * according to rfc 821 we should not send more than 1000 + * including the CRLF + * characters on a single line so we will break the data up + * into lines by \r and/or \n then if needed we will break + * each of those into smaller lines to fit within the limit. + * in addition we will be looking for lines that start with + * a period '.' and append and additional period '.' to that + * line. NOTE: this does not count towards limit. + */ - # normalize the line breaks so we know the explode works + // normalize the line breaks so we know the explode works $msg_data = str_replace("\r\n","\n",$msg_data); $msg_data = str_replace("\r","\n",$msg_data); $lines = explode("\n",$msg_data); - # we need to find a good way to determine is headers are - # in the msg_data or if it is a straight msg body - # currently I am assuming rfc 822 definitions of msg headers - # and if the first field of the first line (':' sperated) - # does not contain a space then it _should_ be a header - # and we can process all lines before a blank "" line as - # headers. + /* we need to find a good way to determine is headers are + * in the msg_data or if it is a straight msg body + * currently I am assuming rfc 822 definitions of msg headers + * and if the first field of the first line (':' sperated) + * does not contain a space then it _should_ be a header + * and we can process all lines before a blank "" line as + * headers. + */ + $field = substr($lines[0],0,strpos($lines[0],":")); $in_headers = false; if(!empty($field) && !strstr($field," ")) { $in_headers = true; } - $max_line_length = 998; # used below; set here for ease in change + $max_line_length = 998; // used below; set here for ease in change while(list(,$line) = @each($lines)) { $lines_out = null; if($line == "" && $in_headers) { $in_headers = false; } - # ok we need to break this line up into several - # smaller lines + // ok we need to break this line up into several smaller lines while(strlen($line) > $max_line_length) { $pos = strrpos(substr($line,0,$max_line_length)," "); - # Patch to fix DOS attack + // Patch to fix DOS attack if(!$pos) { $pos = $max_line_length - 1; + $lines_out[] = substr($line,0,$pos); + $line = substr($line,$pos); + } else { + $lines_out[] = substr($line,0,$pos); + $line = substr($line,$pos + 1); } - $lines_out[] = substr($line,0,$pos); - $line = substr($line,$pos + 1); - # if we are processing headers we need to - # add a LWSP-char to the front of the new line - # rfc 822 on long msg headers + /* if processing headers add a LWSP-char to the front of new line + * rfc 822 on long msg headers + */ if($in_headers) { $line = "\t" . $line; } } $lines_out[] = $line; - # now send the lines to the server + // send the lines to the server while(list(,$line_out) = @each($lines_out)) { if(strlen($line_out) > 0) { @@ -377,15 +425,14 @@ class SMTP } } - # ok all the message data has been sent so lets get this - # over with aleady + // message data has been sent fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF); $rply = $this->get_lines(); $code = substr($rply,0,3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; } if($code != 250) { @@ -394,69 +441,13 @@ class SMTP "smtp_code" => $code, "smtp_msg" => substr($rply,4)); if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; } return false; } return true; } - /** - * Expand takes the name and asks the server to list all the - * people who are members of the _list_. Expand will return - * back and array of the result or false if an error occurs. - * Each value in the array returned has the format of: - * [ ] - * The definition of is defined in rfc 821 - * - * Implements rfc 821: EXPN - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE FAILURE: 550 - * SMTP CODE ERROR : 500,501,502,504,421 - * @access public - * @return string array - */ - function Expand($name) { - $this->error = null; # so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Expand() without being connected"); - return false; - } - - fputs($this->smtp_conn,"EXPN " . $name . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 250) { - $this->error = - array("error" => "EXPN 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; - } - - # parse the reply and place in our array to return to user - $entries = explode($this->CRLF,$rply); - while(list(,$l) = @each($entries)) { - $list[] = substr($l,4); - } - - return $list; - } - /** * Sends the HELO command to the smtp server. * This makes sure that we and the server are in @@ -469,8 +460,8 @@ class SMTP * @access public * @return bool */ - function Hello($host="") { - $this->error = null; # so no confusion is caused + public function Hello($host = '') { + $this->error = null; // so no confusion is caused if(!$this->connected()) { $this->error = array( @@ -478,19 +469,17 @@ class SMTP return false; } - # if a hostname for the HELO was not specified determine - # a suitable one to send + // if hostname for HELO was not specified send default if(empty($host)) { - # we need to determine some sort of appopiate default - # to send to the server + // determine appropriate default to send to server $host = "localhost"; } // Send extended hello first (RFC 2821) - if(!$this->SendHello("EHLO", $host)) - { - if(!$this->SendHello("HELO", $host)) - return false; + if(!$this->SendHello("EHLO", $host)) { + if(!$this->SendHello("HELO", $host)) { + return false; + } } return true; @@ -501,14 +490,14 @@ class SMTP * @access private * @return bool */ - function SendHello($hello, $host) { + private function SendHello($hello, $host) { fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF); $rply = $this->get_lines(); $code = substr($rply,0,3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER: " . $this->CRLF . $rply; + echo "SMTP -> FROM SERVER: " . $rply . $this->CRLF . '
'; } if($code != 250) { @@ -517,8 +506,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; + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; } return false; } @@ -528,59 +516,6 @@ class SMTP return true; } - /** - * Gets help information on the keyword specified. If the keyword - * is not specified then returns generic help, ussually contianing - * A list of keywords that help is available on. This function - * returns the results back to the user. It is up to the user to - * handle the returned data. If an error occurs then false is - * returned with $this->error set appropiately. - * - * Implements rfc 821: HELP [ ] - * - * SMTP CODE SUCCESS: 211,214 - * SMTP CODE ERROR : 500,501,502,504,421 - * @access public - * @return string - */ - function Help($keyword="") { - $this->error = null; # to avoid confusion - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Help() without being connected"); - return false; - } - - $extra = ""; - if(!empty($keyword)) { - $extra = " " . $keyword; - } - - fputs($this->smtp_conn,"HELP" . $extra . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 211 && $code != 214) { - $this->error = - array("error" => "HELP 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 $rply; - } - /** * Starts a mail transaction from the email address specified in * $from. Returns true if successful or false otherwise. If True @@ -595,8 +530,8 @@ class SMTP * @access public * @return bool */ - function Mail($from) { - $this->error = null; # so no confusion is caused + public function Mail($from) { + $this->error = null; // so no confusion is caused if(!$this->connected()) { $this->error = array( @@ -611,7 +546,7 @@ class SMTP $code = substr($rply,0,3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; } if($code != 250) { @@ -620,50 +555,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; - } - return false; - } - return true; - } - - /** - * Sends the command NOOP to the SMTP server. - * - * Implements from rfc 821: NOOP - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE ERROR : 500, 421 - * @access public - * @return bool - */ - function Noop() { - $this->error = null; # so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Noop() without being connected"); - return false; - } - - fputs($this->smtp_conn,"NOOP" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 250) { - $this->error = - array("error" => "NOOP 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; + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; } return false; } @@ -681,8 +573,8 @@ class SMTP * @access public * @return bool */ - function Quit($close_on_error=true) { - $this->error = null; # so there is no confusion + public function Quit($close_on_error = true) { + $this->error = null; // so there is no confusion if(!$this->connected()) { $this->error = array( @@ -690,14 +582,14 @@ class SMTP return false; } - # send the quit command to the server + // send the quit command to the server fputs($this->smtp_conn,"quit" . $this->CRLF); - # get any good-bye messages + // get any good-bye messages $byemsg = $this->get_lines(); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $byemsg; + echo "SMTP -> FROM SERVER:" . $byemsg . $this->CRLF . '
'; } $rval = true; @@ -705,14 +597,13 @@ class SMTP $code = substr($byemsg,0,3); if($code != 221) { - # use e as a tmp var cause Close will overwrite $this->error + // use e as a tmp var cause Close will overwrite $this->error $e = array("error" => "SMTP server rejected quit command", "smtp_code" => $code, "smtp_rply" => substr($byemsg,4)); $rval = false; if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $e["error"] . ": " . - $byemsg . $this->CRLF; + echo "SMTP -> ERROR: " . $e["error"] . ": " . $byemsg . $this->CRLF . '
'; } } @@ -735,8 +626,8 @@ class SMTP * @access public * @return bool */ - function Recipient($to) { - $this->error = null; # so no confusion is caused + public function Recipient($to) { + $this->error = null; // so no confusion is caused if(!$this->connected()) { $this->error = array( @@ -750,7 +641,7 @@ class SMTP $code = substr($rply,0,3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; } if($code != 250 && $code != 251) { @@ -759,8 +650,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; + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; } return false; } @@ -779,8 +669,8 @@ class SMTP * @access public * @return bool */ - function Reset() { - $this->error = null; # so no confusion is caused + public function Reset() { + $this->error = null; // so no confusion is caused if(!$this->connected()) { $this->error = array( @@ -794,7 +684,7 @@ class SMTP $code = substr($rply,0,3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; } if($code != 250) { @@ -803,8 +693,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; + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; } return false; } @@ -812,54 +701,6 @@ class SMTP return true; } - /** - * Starts a mail transaction from the email address specified in - * $from. Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more Recipient - * commands may be called followed by a Data command. This command - * will send the message to the users terminal if they are logged - * in. - * - * Implements rfc 821: SEND FROM: - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE SUCCESS: 552,451,452 - * SMTP CODE SUCCESS: 500,501,502,421 - * @access public - * @return bool - */ - function Send($from) { - $this->error = null; # so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Send() without being connected"); - return false; - } - - fputs($this->smtp_conn,"SEND FROM:" . $from . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 250) { - $this->error = - array("error" => "SEND 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; - } - /** * Starts a mail transaction from the email address specified in * $from. Returns true if successful or false otherwise. If True @@ -876,8 +717,8 @@ class SMTP * @access public * @return bool */ - function SendAndMail($from) { - $this->error = null; # so no confusion is caused + public function SendAndMail($from) { + $this->error = null; // so no confusion is caused if(!$this->connected()) { $this->error = array( @@ -891,7 +732,7 @@ class SMTP $code = substr($rply,0,3); if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; } if($code != 250) { @@ -900,56 +741,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; - } - return false; - } - return true; - } - - /** - * Starts a mail transaction from the email address specified in - * $from. Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more Recipient - * commands may be called followed by a Data command. This command - * will send the message to the users terminal if they are logged - * in or mail it to them if they are not. - * - * Implements rfc 821: SOML FROM: - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE SUCCESS: 552,451,452 - * SMTP CODE SUCCESS: 500,501,502,421 - * @access public - * @return bool - */ - function SendOrMail($from) { - $this->error = null; # so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called SendOrMail() without being connected"); - return false; - } - - fputs($this->smtp_conn,"SOML FROM:" . $from . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 250) { - $this->error = - array("error" => "SOML 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; + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; } return false; } @@ -969,63 +761,27 @@ class SMTP * @access public * @return bool */ - function Turn() { + public function Turn() { $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; + echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF . '
'; } return false; } /** - * Verifies that the name is recognized by the server. - * Returns false if the name could not be verified otherwise - * the response from the server is returned. - * - * Implements rfc 821: VRFY - * - * SMTP CODE SUCCESS: 250,251 - * SMTP CODE FAILURE: 550,551,553 - * SMTP CODE ERROR : 500,501,502,421 - * @access public - * @return int - */ - function Verify($name) { - $this->error = null; # so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Verify() without being connected"); - return false; - } - - fputs($this->smtp_conn,"VRFY " . $name . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 250 && $code != 251) { - $this->error = - array("error" => "VRFY failed on name '$name'", - "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 $rply; + * Get the current error + * @access public + * @return array + */ + public function getError() { + return $this->error; } - /******************************************************************* - * INTERNAL FUNCTIONS * - ******************************************************************/ + ///////////////////////////////////////////////// + // INTERNAL FUNCTIONS + ///////////////////////////////////////////////// /** * Read in as many lines as possible @@ -1036,21 +792,18 @@ class SMTP * @access private * @return string */ - function get_lines() { + private function get_lines() { $data = ""; while($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; + echo "SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '
'; + echo "SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '
'; } $data .= $str; if($this->do_debug >= 4) { - echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF; + echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF . '
'; } - # if the 4th character is a space then we are done reading - # so just break the loop + // if 4th character is a space, we are done reading, break the loop if(substr($str,3,1) == " ") { break; } } return $data; @@ -1058,5 +811,4 @@ class SMTP } - - ?> +?> \ No newline at end of file diff --git a/e107_handlers/phpmailer/language/phpmailer.lang-en.php b/e107_handlers/phpmailer/language/phpmailer.lang-en.php deleted file mode 100644 index 770431936..000000000 --- a/e107_handlers/phpmailer/language/phpmailer.lang-en.php +++ /dev/null @@ -1,23 +0,0 @@ - \ No newline at end of file diff --git a/e107_languages/English/admin/lan_mailout.php b/e107_languages/English/admin/lan_mailout.php index 218ee9993..449de801e 100644 --- a/e107_languages/English/admin/lan_mailout.php +++ b/e107_languages/English/admin/lan_mailout.php @@ -4,8 +4,8 @@ | e107 website system - Language File. | | $Source: /cvs_backup/e107_0.8/e107_languages/English/admin/lan_mailout.php,v $ -| $Revision: 1.5 $ -| $Date: 2008-07-28 21:33:36 $ +| $Revision: 1.6 $ +| $Date: 2009-09-01 19:53:08 $ | $Author: e107steved $ +----------------------------------------------------------------------------+ */ @@ -122,7 +122,7 @@ define('LAN_MAILOUT_110','Send test email'); //define('LAN_MAILOUT_111','Clicking button will send test email to main admin email address'); 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_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');