EliteConf
 All Data Structures Namespaces Files Functions Variables Pages
class.phpmailer.php
Go to the documentation of this file.
1 <?php
2 /*~ class.phpmailer.php
3 .---------------------------------------------------------------------------.
4 | Software: PHPMailer - PHP email class |
5 | Version: 5.1 |
6 | Contact: via sourceforge.net support pages (also www.worxware.com) |
7 | Info: http://phpmailer.sourceforge.net |
8 | Support: http://sourceforge.net/projects/phpmailer/ |
9 | ------------------------------------------------------------------------- |
10 | Admin: Andy Prevost (project admininistrator) |
11 | Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net |
12 | : Marcus Bointon (coolbru) coolbru@users.sourceforge.net |
13 | Founder: Brent R. Matzelle (original founder) |
14 | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. |
15 | Copyright (c) 2001-2003, Brent R. Matzelle |
16 | ------------------------------------------------------------------------- |
17 | License: Distributed under the Lesser General Public License (LGPL) |
18 | http://www.gnu.org/copyleft/lesser.html |
19 | This program is distributed in the hope that it will be useful - WITHOUT |
20 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
21 | FITNESS FOR A PARTICULAR PURPOSE. |
22 | ------------------------------------------------------------------------- |
23 | We offer a number of paid services (www.worxware.com): |
24 | - Web Hosting on highly optimized fast and secure servers |
25 | - Technology Consulting |
26 | - Oursourcing (highly qualified programmers and graphic designers) |
27 '---------------------------------------------------------------------------'
28 */
29 
41 if (version_compare(PHP_VERSION, '5.0.0', '<') ) exit("Sorry, this version of PHPMailer will only run on PHP version 5 or greater!\n");
42 
43 class PHPMailer {
44 
46  // PROPERTIES, PUBLIC
48 
53  public $Priority = 3;
54 
59  public $CharSet = 'iso-8859-1';
60 
65  public $ContentType = 'text/plain';
66 
72  public $Encoding = '8bit';
73 
78  public $ErrorInfo = '';
79 
84  public $From = 'root@localhost';
85 
90  public $FromName = 'Root User';
91 
97  public $Sender = '';
98 
103  public $Subject = '';
104 
110  public $Body = '';
111 
119  public $AltBody = '';
120 
126  public $WordWrap = 0;
127 
132  public $Mailer = 'mail';
133 
138  public $Sendmail = '/usr/sbin/sendmail';
139 
145  public $PluginDir = '';
146 
151  public $ConfirmReadingTo = '';
152 
159  public $Hostname = '';
160 
166  public $MessageID = '';
167 
169  // PROPERTIES FOR SMTP
171 
180  public $Host = 'localhost';
181 
186  public $Port = 25;
187 
192  public $Helo = '';
193 
199  public $SMTPSecure = '';
200 
205  public $SMTPAuth = false;
206 
211  public $Username = '';
212 
217  public $Password = '';
218 
224  public $Timeout = 10;
225 
230  public $SMTPDebug = false;
231 
238  public $SMTPKeepAlive = false;
239 
245  public $SingleTo = false;
246 
251  public $SingleToArray = array();
252 
257  public $LE = "\n";
258 
263  public $DKIM_selector = 'phpmailer';
264 
270  public $DKIM_identity = '';
271 
277  public $DKIM_domain = '';
278 
284  public $DKIM_private = '';
285 
297  public $action_function = ''; //'callbackAction';
298 
303  public $Version = '5.1';
304 
306  // PROPERTIES, PRIVATE AND PROTECTED
308 
309  private $smtp = NULL;
310  private $to = array();
311  private $cc = array();
312  private $bcc = array();
313  private $ReplyTo = array();
314  private $all_recipients = array();
315  private $attachment = array();
316  private $CustomHeader = array();
317  private $message_type = '';
318  private $boundary = array();
319  protected $language = array();
320  private $error_count = 0;
321  private $sign_cert_file = "";
322  private $sign_key_file = "";
323  private $sign_key_pass = "";
324  private $exceptions = false;
325 
327  // CONSTANTS
329 
330  const STOP_MESSAGE = 0; // message only, continue processing
331  const STOP_CONTINUE = 1; // message?, likely ok to continue processing
332  const STOP_CRITICAL = 2; // message, plus full stop, critical error reached
333 
335  // METHODS, VARIABLES
337 
342  public function __construct($exceptions = false) {
343  $this->exceptions = ($exceptions == true);
344  }
345 
351  public function IsHTML($ishtml = true) {
352  if ($ishtml) {
353  $this->ContentType = 'text/html';
354  } else {
355  $this->ContentType = 'text/plain';
356  }
357  }
358 
363  public function IsSMTP() {
364  $this->Mailer = 'smtp';
365  }
366 
371  public function IsMail() {
372  $this->Mailer = 'mail';
373  }
374 
379  public function IsSendmail() {
380  if (!stristr(ini_get('sendmail_path'), 'sendmail')) {
381  $this->Sendmail = '/var/qmail/bin/sendmail';
382  }
383  $this->Mailer = 'sendmail';
384  }
385 
390  public function IsQmail() {
391  if (stristr(ini_get('sendmail_path'), 'qmail')) {
392  $this->Sendmail = '/var/qmail/bin/sendmail';
393  }
394  $this->Mailer = 'sendmail';
395  }
396 
398  // METHODS, RECIPIENTS
400 
407  public function AddAddress($address, $name = '') {
408  return $this->AddAnAddress('to', $address, $name);
409  }
410 
418  public function AddCC($address, $name = '') {
419  return $this->AddAnAddress('cc', $address, $name);
420  }
421 
429  public function AddBCC($address, $name = '') {
430  return $this->AddAnAddress('bcc', $address, $name);
431  }
432 
439  public function AddReplyTo($address, $name = '') {
440  return $this->AddAnAddress('ReplyTo', $address, $name);
441  }
442 
452  private function AddAnAddress($kind, $address, $name = '') {
453  if (!preg_match('/^(to|cc|bcc|ReplyTo)$/', $kind)) {
454  echo 'Invalid recipient array: ' . kind;
455  return false;
456  }
457  $address = trim($address);
458  $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
459  if (!self::ValidateAddress($address)) {
460  $this->SetError($this->Lang('invalid_address').': '. $address);
461  if ($this->exceptions) {
462  throw new phpmailerException($this->Lang('invalid_address').': '.$address);
463  }
464  echo $this->Lang('invalid_address').': '.$address;
465  return false;
466  }
467  if ($kind != 'ReplyTo') {
468  if (!isset($this->all_recipients[strtolower($address)])) {
469  array_push($this->$kind, array($address, $name));
470  $this->all_recipients[strtolower($address)] = true;
471  return true;
472  }
473  } else {
474  if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
475  $this->ReplyTo[strtolower($address)] = array($address, $name);
476  return true;
477  }
478  }
479  return false;
480 }
481 
488  public function SetFrom($address, $name = '',$auto=1) {
489  $address = trim($address);
490  $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
491  if (!self::ValidateAddress($address)) {
492  $this->SetError($this->Lang('invalid_address').': '. $address);
493  if ($this->exceptions) {
494  throw new phpmailerException($this->Lang('invalid_address').': '.$address);
495  }
496  echo $this->Lang('invalid_address').': '.$address;
497  return false;
498  }
499  $this->From = $address;
500  $this->FromName = $name;
501  if ($auto) {
502  if (empty($this->ReplyTo)) {
503  $this->AddAnAddress('ReplyTo', $address, $name);
504  }
505  if (empty($this->Sender)) {
506  $this->Sender = $address;
507  }
508  }
509  return true;
510  }
511 
523  public static function ValidateAddress($address) {
524  if (function_exists('filter_var')) { //Introduced in PHP 5.2
525  if(filter_var($address, FILTER_VALIDATE_EMAIL) === FALSE) {
526  return false;
527  } else {
528  return true;
529  }
530  } else {
531  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);
532  }
533  }
534 
536  // METHODS, MAIL SENDING
538 
545  public function Send() {
546  try {
547  if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
548  throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL);
549  }
550 
551  // Set whether the message is multipart/alternative
552  if(!empty($this->AltBody)) {
553  $this->ContentType = 'multipart/alternative';
554  }
555 
556  $this->error_count = 0; // reset errors
557  $this->SetMessageType();
558  $header = $this->CreateHeader();
559  $body = $this->CreateBody();
560 
561  if (empty($this->Body)) {
562  throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL);
563  }
564 
565  // digitally sign with DKIM if enabled
566  if ($this->DKIM_domain && $this->DKIM_private) {
567  $header_dkim = $this->DKIM_Add($header,$this->Subject,$body);
568  $header = str_replace("\r\n","\n",$header_dkim) . $header;
569  }
570 
571  // Choose the mailer and send through it
572  switch($this->Mailer) {
573  case 'sendmail':
574  return $this->SendmailSend($header, $body);
575  case 'smtp':
576  return $this->SmtpSend($header, $body);
577  default:
578  return $this->MailSend($header, $body);
579  }
580 
581  } catch (phpmailerException $e) {
582  $this->SetError($e->getMessage());
583  if ($this->exceptions) {
584  throw $e;
585  }
586  echo $e->getMessage()."\n";
587  return false;
588  }
589  }
590 
598  protected function SendmailSend($header, $body) {
599  if ($this->Sender != '') {
600  $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
601  } else {
602  $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail));
603  }
604  if ($this->SingleTo === true) {
605  foreach ($this->SingleToArray as $key => $val) {
606  if(!@$mail = popen($sendmail, 'w')) {
607  throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
608  }
609  fputs($mail, "To: " . $val . "\n");
610  fputs($mail, $header);
611  fputs($mail, $body);
612  $result = pclose($mail);
613  // implement call back function if it exists
614  $isSent = ($result == 0) ? 1 : 0;
615  $this->doCallback($isSent,$val,$this->cc,$this->bcc,$this->Subject,$body);
616  if($result != 0) {
617  throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
618  }
619  }
620  } else {
621  if(!@$mail = popen($sendmail, 'w')) {
622  throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
623  }
624  fputs($mail, $header);
625  fputs($mail, $body);
626  $result = pclose($mail);
627  // implement call back function if it exists
628  $isSent = ($result == 0) ? 1 : 0;
629  $this->doCallback($isSent,$this->to,$this->cc,$this->bcc,$this->Subject,$body);
630  if($result != 0) {
631  throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
632  }
633  }
634  return true;
635  }
636 
644  protected function MailSend($header, $body) {
645  $toArr = array();
646  foreach($this->to as $t) {
647  $toArr[] = $this->AddrFormat($t);
648  }
649  $to = implode(', ', $toArr);
650 
651  $params = sprintf("-oi -f %s", $this->Sender);
652  if ($this->Sender != '' && strlen(ini_get('safe_mode'))< 1) {
653  $old_from = ini_get('sendmail_from');
654  ini_set('sendmail_from', $this->Sender);
655  if ($this->SingleTo === true && count($toArr) > 1) {
656  foreach ($toArr as $key => $val) {
657  $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
658  // implement call back function if it exists
659  $isSent = ($rt == 1) ? 1 : 0;
660  $this->doCallback($isSent,$val,$this->cc,$this->bcc,$this->Subject,$body);
661  }
662  } else {
663  $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
664  // implement call back function if it exists
665  $isSent = ($rt == 1) ? 1 : 0;
666  $this->doCallback($isSent,$to,$this->cc,$this->bcc,$this->Subject,$body);
667  }
668  } else {
669  if ($this->SingleTo === true && count($toArr) > 1) {
670  foreach ($toArr as $key => $val) {
671  $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
672  // implement call back function if it exists
673  $isSent = ($rt == 1) ? 1 : 0;
674  $this->doCallback($isSent,$val,$this->cc,$this->bcc,$this->Subject,$body);
675  }
676  } else {
677  $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header);
678  // implement call back function if it exists
679  $isSent = ($rt == 1) ? 1 : 0;
680  $this->doCallback($isSent,$to,$this->cc,$this->bcc,$this->Subject,$body);
681  }
682  }
683  if (isset($old_from)) {
684  ini_set('sendmail_from', $old_from);
685  }
686  if(!$rt) {
687  throw new phpmailerException($this->Lang('instantiate'), self::STOP_CRITICAL);
688  }
689  return true;
690  }
691 
701  protected function SmtpSend($header, $body) {
702  require_once $this->PluginDir . 'class.smtp.php';
703  $bad_rcpt = array();
704 
705  if(!$this->SmtpConnect()) {
706  throw new phpmailerException($this->Lang('smtp_connect_failed'), self::STOP_CRITICAL);
707  }
708  $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
709  if(!$this->smtp->Mail($smtp_from)) {
710  throw new phpmailerException($this->Lang('from_failed') . $smtp_from, self::STOP_CRITICAL);
711  }
712 
713  // Attempt to send attach all recipients
714  foreach($this->to as $to) {
715  if (!$this->smtp->Recipient($to[0])) {
716  $bad_rcpt[] = $to[0];
717  // implement call back function if it exists
718  $isSent = 0;
719  $this->doCallback($isSent,$to[0],'','',$this->Subject,$body);
720  } else {
721  // implement call back function if it exists
722  $isSent = 1;
723  $this->doCallback($isSent,$to[0],'','',$this->Subject,$body);
724  }
725  }
726  foreach($this->cc as $cc) {
727  if (!$this->smtp->Recipient($cc[0])) {
728  $bad_rcpt[] = $cc[0];
729  // implement call back function if it exists
730  $isSent = 0;
731  $this->doCallback($isSent,'',$cc[0],'',$this->Subject,$body);
732  } else {
733  // implement call back function if it exists
734  $isSent = 1;
735  $this->doCallback($isSent,'',$cc[0],'',$this->Subject,$body);
736  }
737  }
738  foreach($this->bcc as $bcc) {
739  if (!$this->smtp->Recipient($bcc[0])) {
740  $bad_rcpt[] = $bcc[0];
741  // implement call back function if it exists
742  $isSent = 0;
743  $this->doCallback($isSent,'','',$bcc[0],$this->Subject,$body);
744  } else {
745  // implement call back function if it exists
746  $isSent = 1;
747  $this->doCallback($isSent,'','',$bcc[0],$this->Subject,$body);
748  }
749  }
750 
751 
752  if (count($bad_rcpt) > 0 ) { //Create error message for any bad addresses
753  $badaddresses = implode(', ', $bad_rcpt);
754  throw new phpmailerException($this->Lang('recipients_failed') . $badaddresses);
755  }
756  if(!$this->smtp->Data($header . $body)) {
757  throw new phpmailerException($this->Lang('data_not_accepted'), self::STOP_CRITICAL);
758  }
759  if($this->SMTPKeepAlive == true) {
760  $this->smtp->Reset();
761  }
762  return true;
763  }
764 
772  public function SmtpConnect() {
773  if(is_null($this->smtp)) {
774  $this->smtp = new SMTP();
775  }
776 
777  $this->smtp->do_debug = $this->SMTPDebug;
778  $hosts = explode(';', $this->Host);
779  $index = 0;
780  $connection = $this->smtp->Connected();
781 
782  // Retry while there is no connection
783  try {
784  while($index < count($hosts) && !$connection) {
785  $hostinfo = array();
786  if (preg_match('/^(.+):([0-9]+)$/', $hosts[$index], $hostinfo)) {
787  $host = $hostinfo[1];
788  $port = $hostinfo[2];
789  } else {
790  $host = $hosts[$index];
791  $port = $this->Port;
792  }
793 
794  $tls = ($this->SMTPSecure == 'tls');
795  $ssl = ($this->SMTPSecure == 'ssl');
796 
797  if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) {
798 
799  $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname());
800  $this->smtp->Hello($hello);
801 
802  if ($tls) {
803  if (!$this->smtp->StartTLS()) {
804  throw new phpmailerException($this->Lang('tls'));
805  }
806 
807  //We must resend HELO after tls negotiation
808  $this->smtp->Hello($hello);
809  }
810 
811  $connection = true;
812  if ($this->SMTPAuth) {
813  if (!$this->smtp->Authenticate($this->Username, $this->Password)) {
814  throw new phpmailerException($this->Lang('authenticate'));
815  }
816  }
817  }
818  $index++;
819  if (!$connection) {
820  throw new phpmailerException($this->Lang('connect_host'));
821  }
822  }
823  } catch (phpmailerException $e) {
824  $this->smtp->Reset();
825  throw $e;
826  }
827  return true;
828  }
829 
834  public function SmtpClose() {
835  if(!is_null($this->smtp)) {
836  if($this->smtp->Connected()) {
837  $this->smtp->Quit();
838  $this->smtp->Close();
839  }
840  }
841  }
842 
850  function SetLanguage($langcode = 'en', $lang_path = 'language/') {
851  //Define full set of translatable strings
852  $PHPMAILER_LANG = array(
853  'provide_address' => 'You must provide at least one recipient email address.',
854  'mailer_not_supported' => ' mailer is not supported.',
855  'execute' => 'Could not execute: ',
856  'instantiate' => 'Could not instantiate mail function.',
857  'authenticate' => 'SMTP Error: Could not authenticate.',
858  'from_failed' => 'The following From address failed: ',
859  'recipients_failed' => 'SMTP Error: The following recipients failed: ',
860  'data_not_accepted' => 'SMTP Error: Data not accepted.',
861  'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
862  'file_access' => 'Could not access file: ',
863  'file_open' => 'File Error: Could not open file: ',
864  'encoding' => 'Unknown encoding: ',
865  'signing' => 'Signing Error: ',
866  'smtp_error' => 'SMTP server error: ',
867  'empty_message' => 'Message body empty',
868  'invalid_address' => 'Invalid address',
869  'variable_set' => 'Cannot set or reset variable: '
870  );
871  //Overwrite language-specific strings. This way we'll never have missing translations - no more "language string failed to load"!
872  $l = true;
873  if ($langcode != 'en') { //There is no English translation file
874  $l = @include $lang_path.'phpmailer.lang-'.$langcode.'.php';
875  }
876  $this->language = $PHPMAILER_LANG;
877  return ($l == true); //Returns false if language not found
878  }
879 
884  public function GetTranslations() {
885  return $this->language;
886  }
887 
889  // METHODS, MESSAGE CREATION
891 
897  public function AddrAppend($type, $addr) {
898  $addr_str = $type . ': ';
899  $addresses = array();
900  foreach ($addr as $a) {
901  $addresses[] = $this->AddrFormat($a);
902  }
903  $addr_str .= implode(', ', $addresses);
904  $addr_str .= $this->LE;
905 
906  return $addr_str;
907  }
908 
914  public function AddrFormat($addr) {
915  if (empty($addr[1])) {
916  return $this->SecureHeader($addr[0]);
917  } else {
918  return $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">";
919  }
920  }
921 
932  public function WrapText($message, $length, $qp_mode = false) {
933  $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
934  // If utf-8 encoding is used, we will need to make sure we don't
935  // split multibyte characters when we wrap
936  $is_utf8 = (strtolower($this->CharSet) == "utf-8");
937 
938  $message = $this->FixEOL($message);
939  if (substr($message, -1) == $this->LE) {
940  $message = substr($message, 0, -1);
941  }
942 
943  $line = explode($this->LE, $message);
944  $message = '';
945  for ($i=0 ;$i < count($line); $i++) {
946  $line_part = explode(' ', $line[$i]);
947  $buf = '';
948  for ($e = 0; $e<count($line_part); $e++) {
949  $word = $line_part[$e];
950  if ($qp_mode and (strlen($word) > $length)) {
951  $space_left = $length - strlen($buf) - 1;
952  if ($e != 0) {
953  if ($space_left > 20) {
954  $len = $space_left;
955  if ($is_utf8) {
956  $len = $this->UTF8CharBoundary($word, $len);
957  } elseif (substr($word, $len - 1, 1) == "=") {
958  $len--;
959  } elseif (substr($word, $len - 2, 1) == "=") {
960  $len -= 2;
961  }
962  $part = substr($word, 0, $len);
963  $word = substr($word, $len);
964  $buf .= ' ' . $part;
965  $message .= $buf . sprintf("=%s", $this->LE);
966  } else {
967  $message .= $buf . $soft_break;
968  }
969  $buf = '';
970  }
971  while (strlen($word) > 0) {
972  $len = $length;
973  if ($is_utf8) {
974  $len = $this->UTF8CharBoundary($word, $len);
975  } elseif (substr($word, $len - 1, 1) == "=") {
976  $len--;
977  } elseif (substr($word, $len - 2, 1) == "=") {
978  $len -= 2;
979  }
980  $part = substr($word, 0, $len);
981  $word = substr($word, $len);
982 
983  if (strlen($word) > 0) {
984  $message .= $part . sprintf("=%s", $this->LE);
985  } else {
986  $buf = $part;
987  }
988  }
989  } else {
990  $buf_o = $buf;
991  $buf .= ($e == 0) ? $word : (' ' . $word);
992 
993  if (strlen($buf) > $length and $buf_o != '') {
994  $message .= $buf_o . $soft_break;
995  $buf = $word;
996  }
997  }
998  }
999  $message .= $buf . $this->LE;
1000  }
1001 
1002  return $message;
1003  }
1004 
1014  public function UTF8CharBoundary($encodedText, $maxLength) {
1015  $foundSplitPos = false;
1016  $lookBack = 3;
1017  while (!$foundSplitPos) {
1018  $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1019  $encodedCharPos = strpos($lastChunk, "=");
1020  if ($encodedCharPos !== false) {
1021  // Found start of encoded character byte within $lookBack block.
1022  // Check the encoded byte value (the 2 chars after the '=')
1023  $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1024  $dec = hexdec($hex);
1025  if ($dec < 128) { // Single byte character.
1026  // If the encoded char was found at pos 0, it will fit
1027  // otherwise reduce maxLength to start of the encoded char
1028  $maxLength = ($encodedCharPos == 0) ? $maxLength :
1029  $maxLength - ($lookBack - $encodedCharPos);
1030  $foundSplitPos = true;
1031  } elseif ($dec >= 192) { // First byte of a multi byte character
1032  // Reduce maxLength to split at start of character
1033  $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1034  $foundSplitPos = true;
1035  } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
1036  $lookBack += 3;
1037  }
1038  } else {
1039  // No encoded character found
1040  $foundSplitPos = true;
1041  }
1042  }
1043  return $maxLength;
1044  }
1045 
1046 
1052  public function SetWordWrap() {
1053  if($this->WordWrap < 1) {
1054  return;
1055  }
1056 
1057  switch($this->message_type) {
1058  case 'alt':
1059  case 'alt_attachments':
1060  $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);
1061  break;
1062  default:
1063  $this->Body = $this->WrapText($this->Body, $this->WordWrap);
1064  break;
1065  }
1066  }
1067 
1073  public function CreateHeader() {
1074  $result = '';
1075 
1076  // Set the boundaries
1077  $uniq_id = md5(uniqid(time()));
1078  $this->boundary[1] = 'b1_' . $uniq_id;
1079  $this->boundary[2] = 'b2_' . $uniq_id;
1080 
1081  $result .= $this->HeaderLine('Date', self::RFCDate());
1082  if($this->Sender == '') {
1083  $result .= $this->HeaderLine('Return-Path', trim($this->From));
1084  } else {
1085  $result .= $this->HeaderLine('Return-Path', trim($this->Sender));
1086  }
1087 
1088  // To be created automatically by mail()
1089  if($this->Mailer != 'mail') {
1090  if ($this->SingleTo === true) {
1091  foreach($this->to as $t) {
1092  $this->SingleToArray[] = $this->AddrFormat($t);
1093  }
1094  } else {
1095  if(count($this->to) > 0) {
1096  $result .= $this->AddrAppend('To', $this->to);
1097  } elseif (count($this->cc) == 0) {
1098  $result .= $this->HeaderLine('To', 'undisclosed-recipients:;');
1099  }
1100  }
1101  }
1102 
1103  $from = array();
1104  $from[0][0] = trim($this->From);
1105  $from[0][1] = $this->FromName;
1106  $result .= $this->AddrAppend('From', $from);
1107 
1108  // sendmail and mail() extract Cc from the header before sending
1109  if(count($this->cc) > 0) {
1110  $result .= $this->AddrAppend('Cc', $this->cc);
1111  }
1112 
1113  // sendmail and mail() extract Bcc from the header before sending
1114  if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) {
1115  $result .= $this->AddrAppend('Bcc', $this->bcc);
1116  }
1117 
1118  if(count($this->ReplyTo) > 0) {
1119  $result .= $this->AddrAppend('Reply-to', $this->ReplyTo);
1120  }
1121 
1122  // mail() sets the subject itself
1123  if($this->Mailer != 'mail') {
1124  $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject)));
1125  }
1126 
1127  if($this->MessageID != '') {
1128  $result .= $this->HeaderLine('Message-ID',$this->MessageID);
1129  } else {
1130  $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE);
1131  }
1132  $result .= $this->HeaderLine('X-Priority', $this->Priority);
1133  $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (phpmailer.sourceforge.net)');
1134 
1135  if($this->ConfirmReadingTo != '') {
1136  $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
1137  }
1138 
1139  // Add custom headers
1140  for($index = 0; $index < count($this->CustomHeader); $index++) {
1141  $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1])));
1142  }
1143  if (!$this->sign_key_file) {
1144  $result .= $this->HeaderLine('MIME-Version', '1.0');
1145  $result .= $this->GetMailMIME();
1146  }
1147 
1148  return $result;
1149  }
1150 
1156  public function GetMailMIME() {
1157  $result = '';
1158  switch($this->message_type) {
1159  case 'plain':
1160  $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);
1161  $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet);
1162  break;
1163  case 'attachments':
1164  case 'alt_attachments':
1165  if($this->InlineImageExists()){
1166  $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE);
1167  } else {
1168  $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;');
1169  $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
1170  }
1171  break;
1172  case 'alt':
1173  $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
1174  $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
1175  break;
1176  }
1177 
1178  if($this->Mailer != 'mail') {
1179  $result .= $this->LE.$this->LE;
1180  }
1181 
1182  return $result;
1183  }
1184 
1190  public function CreateBody() {
1191  $body = '';
1192 
1193  if ($this->sign_key_file) {
1194  $body .= $this->GetMailMIME();
1195  }
1196 
1197  $this->SetWordWrap();
1198 
1199  switch($this->message_type) {
1200  case 'alt':
1201  $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');
1202  $body .= $this->EncodeString($this->AltBody, $this->Encoding);
1203  $body .= $this->LE.$this->LE;
1204  $body .= $this->GetBoundary($this->boundary[1], '', 'text/html', '');
1205  $body .= $this->EncodeString($this->Body, $this->Encoding);
1206  $body .= $this->LE.$this->LE;
1207  $body .= $this->EndBoundary($this->boundary[1]);
1208  break;
1209  case 'plain':
1210  $body .= $this->EncodeString($this->Body, $this->Encoding);
1211  break;
1212  case 'attachments':
1213  $body .= $this->GetBoundary($this->boundary[1], '', '', '');
1214  $body .= $this->EncodeString($this->Body, $this->Encoding);
1215  $body .= $this->LE;
1216  $body .= $this->AttachAll();
1217  break;
1218  case 'alt_attachments':
1219  $body .= sprintf("--%s%s", $this->boundary[1], $this->LE);
1220  $body .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE);
1221  $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body
1222  $body .= $this->EncodeString($this->AltBody, $this->Encoding);
1223  $body .= $this->LE.$this->LE;
1224  $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body
1225  $body .= $this->EncodeString($this->Body, $this->Encoding);
1226  $body .= $this->LE.$this->LE;
1227  $body .= $this->EndBoundary($this->boundary[2]);
1228  $body .= $this->AttachAll();
1229  break;
1230  }
1231 
1232  if ($this->IsError()) {
1233  $body = '';
1234  } elseif ($this->sign_key_file) {
1235  try {
1236  $file = tempnam('', 'mail');
1237  file_put_contents($file, $body); //TODO check this worked
1238  $signed = tempnam("", "signed");
1239  if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) {
1240  @unlink($file);
1241  @unlink($signed);
1242  $body = file_get_contents($signed);
1243  } else {
1244  @unlink($file);
1245  @unlink($signed);
1246  throw new phpmailerException($this->Lang("signing").openssl_error_string());
1247  }
1248  } catch (phpmailerException $e) {
1249  $body = '';
1250  if ($this->exceptions) {
1251  throw $e;
1252  }
1253  }
1254  }
1255 
1256  return $body;
1257  }
1258 
1263  private function GetBoundary($boundary, $charSet, $contentType, $encoding) {
1264  $result = '';
1265  if($charSet == '') {
1266  $charSet = $this->CharSet;
1267  }
1268  if($contentType == '') {
1269  $contentType = $this->ContentType;
1270  }
1271  if($encoding == '') {
1272  $encoding = $this->Encoding;
1273  }
1274  $result .= $this->TextLine('--' . $boundary);
1275  $result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet);
1276  $result .= $this->LE;
1277  $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding);
1278  $result .= $this->LE;
1279 
1280  return $result;
1281  }
1282 
1287  private function EndBoundary($boundary) {
1288  return $this->LE . '--' . $boundary . '--' . $this->LE;
1289  }
1290 
1296  private function SetMessageType() {
1297  if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) {
1298  $this->message_type = 'plain';
1299  } else {
1300  if(count($this->attachment) > 0) {
1301  $this->message_type = 'attachments';
1302  }
1303  if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) {
1304  $this->message_type = 'alt';
1305  }
1306  if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) {
1307  $this->message_type = 'alt_attachments';
1308  }
1309  }
1310  }
1311 
1317  public function HeaderLine($name, $value) {
1318  return $name . ': ' . $value . $this->LE;
1319  }
1320 
1326  public function TextLine($value) {
1327  return $value . $this->LE;
1328  }
1329 
1331  // CLASS METHODS, ATTACHMENTS
1333 
1344  public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
1345  try {
1346  if ( !@is_file($path) ) {
1347  throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE);
1348  }
1349  $filename = basename($path);
1350  if ( $name == '' ) {
1351  $name = $filename;
1352  }
1353 
1354  $this->attachment[] = array(
1355  0 => $path,
1356  1 => $filename,
1357  2 => $name,
1358  3 => $encoding,
1359  4 => $type,
1360  5 => false, // isStringAttachment
1361  6 => 'attachment',
1362  7 => 0
1363  );
1364 
1365  } catch (phpmailerException $e) {
1366  $this->SetError($e->getMessage());
1367  if ($this->exceptions) {
1368  throw $e;
1369  }
1370  echo $e->getMessage()."\n";
1371  if ( $e->getCode() == self::STOP_CRITICAL ) {
1372  return false;
1373  }
1374  }
1375  return true;
1376  }
1377 
1382  public function GetAttachments() {
1383  return $this->attachment;
1384  }
1385 
1392  private function AttachAll() {
1393  // Return text of body
1394  $mime = array();
1395  $cidUniq = array();
1396  $incl = array();
1397 
1398  // Add all attachments
1399  foreach ($this->attachment as $attachment) {
1400  // Check for string attachment
1401  $bString = $attachment[5];
1402  if ($bString) {
1403  $string = $attachment[0];
1404  } else {
1405  $path = $attachment[0];
1406  }
1407 
1408  if (in_array($attachment[0], $incl)) { continue; }
1409  $filename = $attachment[1];
1410  $name = $attachment[2];
1411  $encoding = $attachment[3];
1412  $type = $attachment[4];
1413  $disposition = $attachment[6];
1414  $cid = $attachment[7];
1415  $incl[] = $attachment[0];
1416  if ( $disposition == 'inline' && isset($cidUniq[$cid]) ) { continue; }
1417  $cidUniq[$cid] = true;
1418 
1419  $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);
1420  $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE);
1421  $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
1422 
1423  if($disposition == 'inline') {
1424  $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
1425  }
1426 
1427  $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE);
1428 
1429  // Encode as string attachment
1430  if($bString) {
1431  $mime[] = $this->EncodeString($string, $encoding);
1432  if($this->IsError()) {
1433  return '';
1434  }
1435  $mime[] = $this->LE.$this->LE;
1436  } else {
1437  $mime[] = $this->EncodeFile($path, $encoding);
1438  if($this->IsError()) {
1439  return '';
1440  }
1441  $mime[] = $this->LE.$this->LE;
1442  }
1443  }
1444 
1445  $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE);
1446 
1447  return join('', $mime);
1448  }
1449 
1459  private function EncodeFile($path, $encoding = 'base64') {
1460  try {
1461  if (!is_readable($path)) {
1462  throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE);
1463  }
1464  if (function_exists('get_magic_quotes')) {
1465  function get_magic_quotes() {
1466  return false;
1467  }
1468  }
1469  if (PHP_VERSION < 6) {
1470  $magic_quotes = get_magic_quotes_runtime();
1471  set_magic_quotes_runtime(0);
1472  }
1473  $file_buffer = file_get_contents($path);
1474  $file_buffer = $this->EncodeString($file_buffer, $encoding);
1475  if (PHP_VERSION < 6) { set_magic_quotes_runtime($magic_quotes); }
1476  return $file_buffer;
1477  } catch (Exception $e) {
1478  $this->SetError($e->getMessage());
1479  return '';
1480  }
1481  }
1482 
1491  public function EncodeString ($str, $encoding = 'base64') {
1492  $encoded = '';
1493  switch(strtolower($encoding)) {
1494  case 'base64':
1495  $encoded = chunk_split(base64_encode($str), 76, $this->LE);
1496  break;
1497  case '7bit':
1498  case '8bit':
1499  $encoded = $this->FixEOL($str);
1500  //Make sure it ends with a line break
1501  if (substr($encoded, -(strlen($this->LE))) != $this->LE)
1502  $encoded .= $this->LE;
1503  break;
1504  case 'binary':
1505  $encoded = $str;
1506  break;
1507  case 'quoted-printable':
1508  $encoded = $this->EncodeQP($str);
1509  break;
1510  default:
1511  $this->SetError($this->Lang('encoding') . $encoding);
1512  break;
1513  }
1514  return $encoded;
1515  }
1516 
1522  public function EncodeHeader($str, $position = 'text') {
1523  $x = 0;
1524 
1525  switch (strtolower($position)) {
1526  case 'phrase':
1527  if (!preg_match('/[\200-\377]/', $str)) {
1528  // Can't use addslashes as we don't know what value has magic_quotes_sybase
1529  $encoded = addcslashes($str, "\0..\37\177\\\"");
1530  if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
1531  return ($encoded);
1532  } else {
1533  return ("\"$encoded\"");
1534  }
1535  }
1536  $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
1537  break;
1538  case 'comment':
1539  $x = preg_match_all('/[()"]/', $str, $matches);
1540  // Fall-through
1541  case 'text':
1542  default:
1543  $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
1544  break;
1545  }
1546 
1547  if ($x == 0) {
1548  return ($str);
1549  }
1550 
1551  $maxlen = 75 - 7 - strlen($this->CharSet);
1552  // Try to select the encoding which should produce the shortest output
1553  if (strlen($str)/3 < $x) {
1554  $encoding = 'B';
1555  if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) {
1556  // Use a custom function which correctly encodes and wraps long
1557  // multibyte strings without breaking lines within a character
1558  $encoded = $this->Base64EncodeWrapMB($str);
1559  } else {
1560  $encoded = base64_encode($str);
1561  $maxlen -= $maxlen % 4;
1562  $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
1563  }
1564  } else {
1565  $encoding = 'Q';
1566  $encoded = $this->EncodeQ($str, $position);
1567  $encoded = $this->WrapText($encoded, $maxlen, true);
1568  $encoded = str_replace('='.$this->LE, "\n", trim($encoded));
1569  }
1570 
1571  $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);
1572  $encoded = trim(str_replace("\n", $this->LE, $encoded));
1573 
1574  return $encoded;
1575  }
1576 
1583  public function HasMultiBytes($str) {
1584  if (function_exists('mb_strlen')) {
1585  return (strlen($str) > mb_strlen($str, $this->CharSet));
1586  } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
1587  return false;
1588  }
1589  }
1590 
1599  public function Base64EncodeWrapMB($str) {
1600  $start = "=?".$this->CharSet."?B?";
1601  $end = "?=";
1602  $encoded = "";
1603 
1604  $mb_length = mb_strlen($str, $this->CharSet);
1605  // Each line must have length <= 75, including $start and $end
1606  $length = 75 - strlen($start) - strlen($end);
1607  // Average multi-byte ratio
1608  $ratio = $mb_length / strlen($str);
1609  // Base64 has a 4:3 ratio
1610  $offset = $avgLength = floor($length * $ratio * .75);
1611 
1612  for ($i = 0; $i < $mb_length; $i += $offset) {
1613  $lookBack = 0;
1614 
1615  do {
1616  $offset = $avgLength - $lookBack;
1617  $chunk = mb_substr($str, $i, $offset, $this->CharSet);
1618  $chunk = base64_encode($chunk);
1619  $lookBack++;
1620  }
1621  while (strlen($chunk) > $length);
1622 
1623  $encoded .= $chunk . $this->LE;
1624  }
1625 
1626  // Chomp the last linefeed
1627  $encoded = substr($encoded, 0, -strlen($this->LE));
1628  return $encoded;
1629  }
1630 
1639  public function EncodeQPphp( $input = '', $line_max = 76, $space_conv = false) {
1640  $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
1641  $lines = preg_split('/(?:\r\n|\r|\n)/', $input);
1642  $eol = "\r\n";
1643  $escape = '=';
1644  $output = '';
1645  while( list(, $line) = each($lines) ) {
1646  $linlen = strlen($line);
1647  $newline = '';
1648  for($i = 0; $i < $linlen; $i++) {
1649  $c = substr( $line, $i, 1 );
1650  $dec = ord( $c );
1651  if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E
1652  $c = '=2E';
1653  }
1654  if ( $dec == 32 ) {
1655  if ( $i == ( $linlen - 1 ) ) { // convert space at eol only
1656  $c = '=20';
1657  } else if ( $space_conv ) {
1658  $c = '=20';
1659  }
1660  } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required
1661  $h2 = floor($dec/16);
1662  $h1 = floor($dec%16);
1663  $c = $escape.$hex[$h2].$hex[$h1];
1664  }
1665  if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted
1666  $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay
1667  $newline = '';
1668  // check if newline first character will be point or not
1669  if ( $dec == 46 ) {
1670  $c = '=2E';
1671  }
1672  }
1673  $newline .= $c;
1674  } // end of for
1675  $output .= $newline.$eol;
1676  } // end of while
1677  return $output;
1678  }
1679 
1692  public function EncodeQP($string, $line_max = 76, $space_conv = false) {
1693  if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3)
1694  return quoted_printable_encode($string);
1695  }
1696  $filters = stream_get_filters();
1697  if (!in_array('convert.*', $filters)) { //Got convert stream filter?
1698  return $this->EncodeQPphp($string, $line_max, $space_conv); //Fall back to old implementation
1699  }
1700  $fp = fopen('php://temp/', 'r+');
1701  $string = preg_replace('/\r\n?/', $this->LE, $string); //Normalise line breaks
1702  $params = array('line-length' => $line_max, 'line-break-chars' => $this->LE);
1703  $s = stream_filter_append($fp, 'convert.quoted-printable-encode', STREAM_FILTER_READ, $params);
1704  fputs($fp, $string);
1705  rewind($fp);
1706  $out = stream_get_contents($fp);
1707  stream_filter_remove($s);
1708  $out = preg_replace('/^\./m', '=2E', $out); //Encode . if it is first char on a line, workaround for bug in Exchange
1709  fclose($fp);
1710  return $out;
1711  }
1712 
1721  public function EncodeQ ($str, $position = 'text') {
1722  // There should not be any EOL in the string
1723  $encoded = preg_replace('/[\r\n]*/', '', $str);
1724 
1725  switch (strtolower($position)) {
1726  case 'phrase':
1727  $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
1728  break;
1729  case 'comment':
1730  $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
1731  case 'text':
1732  default:
1733  // Replace every high ascii, control =, ? and _ characters
1734  //TODO using /e (equivalent to eval()) is probably not a good idea
1735  $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e',
1736  "'='.sprintf('%02X', ord('\\1'))", $encoded);
1737  break;
1738  }
1739 
1740  // Replace every spaces to _ (more readable than =20)
1741  $encoded = str_replace(' ', '_', $encoded);
1742 
1743  return $encoded;
1744  }
1745 
1756  public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') {
1757  // Append to $attachment array
1758  $this->attachment[] = array(
1759  0 => $string,
1760  1 => $filename,
1761  2 => basename($filename),
1762  3 => $encoding,
1763  4 => $type,
1764  5 => true, // isStringAttachment
1765  6 => 'attachment',
1766  7 => 0
1767  );
1768  }
1769 
1783  public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
1784 
1785  if ( !@is_file($path) ) {
1786  $this->SetError($this->Lang('file_access') . $path);
1787  return false;
1788  }
1789 
1790  $filename = basename($path);
1791  if ( $name == '' ) {
1792  $name = $filename;
1793  }
1794 
1795  // Append to $attachment array
1796  $this->attachment[] = array(
1797  0 => $path,
1798  1 => $filename,
1799  2 => $name,
1800  3 => $encoding,
1801  4 => $type,
1802  5 => false, // isStringAttachment
1803  6 => 'inline',
1804  7 => $cid
1805  );
1806 
1807  return true;
1808  }
1809 
1815  public function InlineImageExists() {
1816  foreach($this->attachment as $attachment) {
1817  if ($attachment[6] == 'inline') {
1818  return true;
1819  }
1820  }
1821  return false;
1822  }
1823 
1825  // CLASS METHODS, MESSAGE RESET
1827 
1832  public function ClearAddresses() {
1833  foreach($this->to as $to) {
1834  unset($this->all_recipients[strtolower($to[0])]);
1835  }
1836  $this->to = array();
1837  }
1838 
1843  public function ClearCCs() {
1844  foreach($this->cc as $cc) {
1845  unset($this->all_recipients[strtolower($cc[0])]);
1846  }
1847  $this->cc = array();
1848  }
1849 
1854  public function ClearBCCs() {
1855  foreach($this->bcc as $bcc) {
1856  unset($this->all_recipients[strtolower($bcc[0])]);
1857  }
1858  $this->bcc = array();
1859  }
1860 
1865  public function ClearReplyTos() {
1866  $this->ReplyTo = array();
1867  }
1868 
1874  public function ClearAllRecipients() {
1875  $this->to = array();
1876  $this->cc = array();
1877  $this->bcc = array();
1878  $this->all_recipients = array();
1879  }
1880 
1886  public function ClearAttachments() {
1887  $this->attachment = array();
1888  }
1889 
1894  public function ClearCustomHeaders() {
1895  $this->CustomHeader = array();
1896  }
1897 
1899  // CLASS METHODS, MISCELLANEOUS
1901 
1907  protected function SetError($msg) {
1908  $this->error_count++;
1909  if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
1910  $lasterror = $this->smtp->getError();
1911  if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {
1912  $msg .= '<p>' . $this->Lang('smtp_error') . $lasterror['smtp_msg'] . "</p>\n";
1913  }
1914  }
1915  $this->ErrorInfo = $msg;
1916  }
1917 
1924  public static function RFCDate() {
1925  $tz = date('Z');
1926  $tzs = ($tz < 0) ? '-' : '+';
1927  $tz = abs($tz);
1928  $tz = (int)($tz/3600)*100 + ($tz%3600)/60;
1929  $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz);
1930 
1931  return $result;
1932  }
1933 
1939  private function ServerHostname() {
1940  if (!empty($this->Hostname)) {
1942  } elseif (isset($_SERVER['SERVER_NAME'])) {
1943  $result = $_SERVER['SERVER_NAME'];
1944  } else {
1945  $result = 'localhost.localdomain';
1946  }
1947 
1948  return $result;
1949  }
1950 
1956  private function Lang($key) {
1957  if(count($this->language) < 1) {
1958  $this->SetLanguage('en'); // set the default language
1959  }
1960 
1961  if(isset($this->language[$key])) {
1962  return $this->language[$key];
1963  } else {
1964  return 'Language string failed to load: ' . $key;
1965  }
1966  }
1967 
1973  public function IsError() {
1974  return ($this->error_count > 0);
1975  }
1976 
1982  private function FixEOL($str) {
1983  $str = str_replace("\r\n", "\n", $str);
1984  $str = str_replace("\r", "\n", $str);
1985  $str = str_replace("\n", $this->LE, $str);
1986  return $str;
1987  }
1988 
1994  public function AddCustomHeader($custom_header) {
1995  $this->CustomHeader[] = explode(':', $custom_header, 2);
1996  }
1997 
2003  public function MsgHTML($message, $basedir = '') {
2004  preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images);
2005  if(isset($images[2])) {
2006  foreach($images[2] as $i => $url) {
2007  // do not change urls for absolute images (thanks to corvuscorax)
2008  if (!preg_match('#^[A-z]+://#',$url)) {
2009  $filename = basename($url);
2010  $directory = dirname($url);
2011  ($directory == '.')?$directory='':'';
2012  $cid = 'cid:' . md5($filename);
2013  $ext = pathinfo($filename, PATHINFO_EXTENSION);
2014  $mimeType = self::_mime_types($ext);
2015  if ( strlen($basedir) > 1 && substr($basedir,-1) != '/') { $basedir .= '/'; }
2016  if ( strlen($directory) > 1 && substr($directory,-1) != '/') { $directory .= '/'; }
2017  if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64',$mimeType) ) {
2018  $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message);
2019  }
2020  }
2021  }
2022  }
2023  $this->IsHTML(true);
2024  $this->Body = $message;
2025  $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$message)));
2026  if (!empty($textMsg) && empty($this->AltBody)) {
2027  $this->AltBody = html_entity_decode($textMsg);
2028  }
2029  if (empty($this->AltBody)) {
2030  $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n";
2031  }
2032  }
2033 
2041  public static function _mime_types($ext = '') {
2042  $mimes = array(
2043  'hqx' => 'application/mac-binhex40',
2044  'cpt' => 'application/mac-compactpro',
2045  'doc' => 'application/msword',
2046  'bin' => 'application/macbinary',
2047  'dms' => 'application/octet-stream',
2048  'lha' => 'application/octet-stream',
2049  'lzh' => 'application/octet-stream',
2050  'exe' => 'application/octet-stream',
2051  'class' => 'application/octet-stream',
2052  'psd' => 'application/octet-stream',
2053  'so' => 'application/octet-stream',
2054  'sea' => 'application/octet-stream',
2055  'dll' => 'application/octet-stream',
2056  'oda' => 'application/oda',
2057  'pdf' => 'application/pdf',
2058  'ai' => 'application/postscript',
2059  'eps' => 'application/postscript',
2060  'ps' => 'application/postscript',
2061  'smi' => 'application/smil',
2062  'smil' => 'application/smil',
2063  'mif' => 'application/vnd.mif',
2064  'xls' => 'application/vnd.ms-excel',
2065  'ppt' => 'application/vnd.ms-powerpoint',
2066  'wbxml' => 'application/vnd.wap.wbxml',
2067  'wmlc' => 'application/vnd.wap.wmlc',
2068  'dcr' => 'application/x-director',
2069  'dir' => 'application/x-director',
2070  'dxr' => 'application/x-director',
2071  'dvi' => 'application/x-dvi',
2072  'gtar' => 'application/x-gtar',
2073  'php' => 'application/x-httpd-php',
2074  'php4' => 'application/x-httpd-php',
2075  'php3' => 'application/x-httpd-php',
2076  'phtml' => 'application/x-httpd-php',
2077  'phps' => 'application/x-httpd-php-source',
2078  'js' => 'application/x-javascript',
2079  'swf' => 'application/x-shockwave-flash',
2080  'sit' => 'application/x-stuffit',
2081  'tar' => 'application/x-tar',
2082  'tgz' => 'application/x-tar',
2083  'xhtml' => 'application/xhtml+xml',
2084  'xht' => 'application/xhtml+xml',
2085  'zip' => 'application/zip',
2086  'mid' => 'audio/midi',
2087  'midi' => 'audio/midi',
2088  'mpga' => 'audio/mpeg',
2089  'mp2' => 'audio/mpeg',
2090  'mp3' => 'audio/mpeg',
2091  'aif' => 'audio/x-aiff',
2092  'aiff' => 'audio/x-aiff',
2093  'aifc' => 'audio/x-aiff',
2094  'ram' => 'audio/x-pn-realaudio',
2095  'rm' => 'audio/x-pn-realaudio',
2096  'rpm' => 'audio/x-pn-realaudio-plugin',
2097  'ra' => 'audio/x-realaudio',
2098  'rv' => 'video/vnd.rn-realvideo',
2099  'wav' => 'audio/x-wav',
2100  'bmp' => 'image/bmp',
2101  'gif' => 'image/gif',
2102  'jpeg' => 'image/jpeg',
2103  'jpg' => 'image/jpeg',
2104  'jpe' => 'image/jpeg',
2105  'png' => 'image/png',
2106  'tiff' => 'image/tiff',
2107  'tif' => 'image/tiff',
2108  'css' => 'text/css',
2109  'html' => 'text/html',
2110  'htm' => 'text/html',
2111  'shtml' => 'text/html',
2112  'txt' => 'text/plain',
2113  'text' => 'text/plain',
2114  'log' => 'text/plain',
2115  'rtx' => 'text/richtext',
2116  'rtf' => 'text/rtf',
2117  'xml' => 'text/xml',
2118  'xsl' => 'text/xml',
2119  'mpeg' => 'video/mpeg',
2120  'mpg' => 'video/mpeg',
2121  'mpe' => 'video/mpeg',
2122  'qt' => 'video/quicktime',
2123  'mov' => 'video/quicktime',
2124  'avi' => 'video/x-msvideo',
2125  'movie' => 'video/x-sgi-movie',
2126  'doc' => 'application/msword',
2127  'word' => 'application/msword',
2128  'xl' => 'application/excel',
2129  'eml' => 'message/rfc822'
2130  );
2131  return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)];
2132  }
2133 
2146  public function set($name, $value = '') {
2147  try {
2148  if (isset($this->$name) ) {
2149  $this->$name = $value;
2150  } else {
2151  throw new phpmailerException($this->Lang('variable_set') . $name, self::STOP_CRITICAL);
2152  }
2153  } catch (Exception $e) {
2154  $this->SetError($e->getMessage());
2155  if ($e->getCode() == self::STOP_CRITICAL) {
2156  return false;
2157  }
2158  }
2159  return true;
2160  }
2161 
2168  public function SecureHeader($str) {
2169  $str = str_replace("\r", '', $str);
2170  $str = str_replace("\n", '', $str);
2171  return trim($str);
2172  }
2173 
2181  public function Sign($cert_filename, $key_filename, $key_pass) {
2182  $this->sign_cert_file = $cert_filename;
2183  $this->sign_key_file = $key_filename;
2184  $this->sign_key_pass = $key_pass;
2185  }
2186 
2194  public function DKIM_QP($txt) {
2195  $tmp="";
2196  $line="";
2197  for ($i=0;$i<strlen($txt);$i++) {
2198  $ord=ord($txt[$i]);
2199  if ( ((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E)) ) {
2200  $line.=$txt[$i];
2201  } else {
2202  $line.="=".sprintf("%02X",$ord);
2203  }
2204  }
2205  return $line;
2206  }
2207 
2214  public function DKIM_Sign($s) {
2215  $privKeyStr = file_get_contents($this->DKIM_private);
2216  if ($this->DKIM_passphrase!='') {
2217  $privKey = openssl_pkey_get_private($privKeyStr,$this->DKIM_passphrase);
2218  } else {
2219  $privKey = $privKeyStr;
2220  }
2221  if (openssl_sign($s, $signature, $privKey)) {
2222  return base64_encode($signature);
2223  }
2224  }
2225 
2232  public function DKIM_HeaderC($s) {
2233  $s=preg_replace("/\r\n\s+/"," ",$s);
2234  $lines=explode("\r\n",$s);
2235  foreach ($lines as $key=>$line) {
2236  list($heading,$value)=explode(":",$line,2);
2237  $heading=strtolower($heading);
2238  $value=preg_replace("/\s+/"," ",$value) ; // Compress useless spaces
2239  $lines[$key]=$heading.":".trim($value) ; // Don't forget to remove WSP around the value
2240  }
2241  $s=implode("\r\n",$lines);
2242  return $s;
2243  }
2244 
2251  public function DKIM_BodyC($body) {
2252  if ($body == '') return "\r\n";
2253  // stabilize line endings
2254  $body=str_replace("\r\n","\n",$body);
2255  $body=str_replace("\n","\r\n",$body);
2256  // END stabilize line endings
2257  while (substr($body,strlen($body)-4,4) == "\r\n\r\n") {
2258  $body=substr($body,0,strlen($body)-2);
2259  }
2260  return $body;
2261  }
2262 
2271  public function DKIM_Add($headers_line,$subject,$body) {
2272  $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms
2273  $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
2274  $DKIMquery = 'dns/txt'; // Query method
2275  $DKIMtime = time() ; // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
2276  $subject_header = "Subject: $subject";
2277  $headers = explode("\r\n",$headers_line);
2278  foreach($headers as $header) {
2279  if (strpos($header,'From:') === 0) {
2280  $from_header=$header;
2281  } elseif (strpos($header,'To:') === 0) {
2282  $to_header=$header;
2283  }
2284  }
2285  $from = str_replace('|','=7C',$this->DKIM_QP($from_header));
2286  $to = str_replace('|','=7C',$this->DKIM_QP($to_header));
2287  $subject = str_replace('|','=7C',$this->DKIM_QP($subject_header)) ; // Copied header fields (dkim-quoted-printable
2288  $body = $this->DKIM_BodyC($body);
2289  $DKIMlen = strlen($body) ; // Length of body
2290  $DKIMb64 = base64_encode(pack("H*", sha1($body))) ; // Base64 of packed binary SHA-1 hash of body
2291  $ident = ($this->DKIM_identity == '')? '' : " i=" . $this->DKIM_identity . ";";
2292  $dkimhdrs = "DKIM-Signature: v=1; a=" . $DKIMsignatureType . "; q=" . $DKIMquery . "; l=" . $DKIMlen . "; s=" . $this->DKIM_selector . ";\r\n".
2293  "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n".
2294  "\th=From:To:Subject;\r\n".
2295  "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n".
2296  "\tz=$from\r\n".
2297  "\t|$to\r\n".
2298  "\t|$subject;\r\n".
2299  "\tbh=" . $DKIMb64 . ";\r\n".
2300  "\tb=";
2301  $toSign = $this->DKIM_HeaderC($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs);
2302  $signed = $this->DKIM_Sign($toSign);
2303  return "X-PHPMAILER-DKIM: phpmailer.worxware.com\r\n".$dkimhdrs.$signed."\r\n";
2304  }
2305 
2306  protected function doCallback($isSent,$to,$cc,$bcc,$subject,$body) {
2307  if (!empty($this->action_function) && function_exists($this->action_function)) {
2308  $params = array($isSent,$to,$cc,$bcc,$subject,$body);
2309  call_user_func_array($this->action_function,$params);
2310  }
2311  }
2312 }
2313 
2314 class phpmailerException extends Exception {
2315  public function errorMessage() {
2316  $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
2317  return $errorMsg;
2318  }
2319 }
2320 ?>