class.phpmailer.php Quellcode

class.phpmailer.php
gehe zur Dokumentation dieser Datei
1 <?php
28 class PHPMailer
29 {
34  public $Version = '5.2.9';
35 
41  public $Priority = 3;
42 
47  public $CharSet = 'iso-8859-1';
48 
53  public $ContentType = 'text/plain';
54 
60  public $Encoding = '8bit';
61 
66  public $ErrorInfo = '';
67 
72  public $From = 'root@localhost';
73 
78  public $FromName = 'Root User';
79 
85  public $Sender = '';
86 
95  public $ReturnPath = '';
96 
101  public $Subject = '';
102 
108  public $Body = '';
109 
117  public $AltBody = '';
118 
127  public $Ical = '';
128 
134  protected $MIMEBody = '';
135 
141  protected $MIMEHeader = '';
142 
148  protected $mailHeader = '';
149 
154  public $WordWrap = 0;
155 
161  public $Mailer = 'mail';
162 
167  public $Sendmail = '/usr/sbin/sendmail';
168 
174  public $UseSendmailOptions = true;
175 
182  public $PluginDir = '';
183 
188  public $ConfirmReadingTo = '';
189 
197  public $Hostname = '';
198 
204  public $MessageID = '';
205 
211  public $MessageDate = '';
212 
224  public $Host = 'localhost';
225 
231  public $Port = 25;
232 
239  public $Helo = '';
240 
246  public $SMTPSecure = '';
247 
255  public $SMTPAuth = false;
256 
261  public $Username = '';
262 
267  public $Password = '';
268 
274  public $AuthType = '';
275 
281  public $Realm = '';
282 
288  public $Workstation = '';
289 
294  public $Timeout = 10;
295 
308  public $SMTPDebug = 0;
309 
324  public $Debugoutput = 'echo';
325 
332  public $SMTPKeepAlive = false;
333 
339  public $SingleTo = false;
340 
346  public $SingleToArray = array();
347 
355  public $do_verp = false;
356 
361  public $AllowEmpty = false;
362 
369  public $LE = "\n";
370 
375  public $DKIM_selector = '';
376 
382  public $DKIM_identity = '';
383 
389  public $DKIM_passphrase = '';
390 
396  public $DKIM_domain = '';
397 
402  public $DKIM_private = '';
403 
422  public $action_function = '';
423 
429  public $XMailer = '';
430 
436  protected $smtp = null;
437 
443  protected $to = array();
444 
450  protected $cc = array();
451 
457  protected $bcc = array();
458 
464  protected $ReplyTo = array();
465 
472  protected $all_recipients = array();
473 
479  protected $attachment = array();
480 
486  protected $CustomHeader = array();
487 
493  protected $lastMessageID = '';
494 
500  protected $message_type = '';
501 
507  protected $boundary = array();
508 
514  protected $language = array();
515 
521  protected $error_count = 0;
522 
528  protected $sign_cert_file = '';
529 
535  protected $sign_key_file = '';
536 
543  protected $sign_key_pass = '';
544 
550  protected $exceptions = false;
551 
555  const STOP_MESSAGE = 0;
556 
560  const STOP_CONTINUE = 1;
561 
565  const STOP_CRITICAL = 2;
566 
570  const CRLF = "\r\n";
571 
576  public function __construct($exceptions = false)
577  {
578  $this->exceptions = ($exceptions == true);
579  }
580 
584  public function __destruct()
585  {
586  if ($this->Mailer == 'smtp') { //close any open SMTP connection nicely
587  $this->smtpClose();
588  }
589  }
590 
604  private function mailPassthru($to, $subject, $body, $header, $params)
605  {
606  //Check overloading of mail function to avoid double-encoding
607  if (ini_get('mbstring.func_overload') & 1) {
608  $subject = $this->secureHeader($subject);
609  } else {
610  $subject = $this->encodeHeader($this->secureHeader($subject));
611  }
612  if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
613  $result = @mail($to, $subject, $body, $header);
614  } else {
615  $result = @mail($to, $subject, $body, $header, $params);
616  }
617  return $result;
618  }
619 
627  protected function edebug($str)
628  {
629  if ($this->SMTPDebug <= 0) {
630  return;
631  }
632  if (is_callable($this->Debugoutput)) {
633  call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
634  return;
635  }
636  switch ($this->Debugoutput) {
637  case 'error_log':
638  //Don't output, just log
639  error_log($str);
640  break;
641  case 'html':
642  //Cleans up output a bit for a better looking, HTML-safe output
643  echo htmlentities(
644  preg_replace('/[\r\n]+/', '', $str),
645  ENT_QUOTES,
646  'UTF-8'
647  )
648  . "<br>\n";
649  break;
650  case 'echo':
651  default:
652  //Normalize line breaks
653  $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
654  echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
655  "\n",
656  "\n \t ",
657  trim($str)
658  ) . "\n";
659  }
660  }
661 
667  public function isHTML($isHtml = true)
668  {
669  if ($isHtml) {
670  $this->ContentType = 'text/html';
671  } else {
672  $this->ContentType = 'text/plain';
673  }
674  }
675 
680  public function isSMTP()
681  {
682  $this->Mailer = 'smtp';
683  }
684 
689  public function isMail()
690  {
691  $this->Mailer = 'mail';
692  }
693 
698  public function isSendmail()
699  {
700  $ini_sendmail_path = ini_get('sendmail_path');
701 
702  if (!stristr($ini_sendmail_path, 'sendmail')) {
703  $this->Sendmail = '/usr/sbin/sendmail';
704  } else {
705  $this->Sendmail = $ini_sendmail_path;
706  }
707  $this->Mailer = 'sendmail';
708  }
709 
714  public function isQmail()
715  {
716  $ini_sendmail_path = ini_get('sendmail_path');
717 
718  if (!stristr($ini_sendmail_path, 'qmail')) {
719  $this->Sendmail = '/var/qmail/bin/qmail-inject';
720  } else {
721  $this->Sendmail = $ini_sendmail_path;
722  }
723  $this->Mailer = 'qmail';
724  }
725 
732  public function addAddress($address, $name = '')
733  {
734  return $this->addAnAddress('to', $address, $name);
735  }
736 
744  public function addCC($address, $name = '')
745  {
746  return $this->addAnAddress('cc', $address, $name);
747  }
748 
756  public function addBCC($address, $name = '')
757  {
758  return $this->addAnAddress('bcc', $address, $name);
759  }
760 
767  public function addReplyTo($address, $name = '')
768  {
769  return $this->addAnAddress('Reply-To', $address, $name);
770  }
771 
782  protected function addAnAddress($kind, $address, $name = '')
783  {
784  if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) {
785  $this->setError($this->lang('Invalid recipient array') . ': ' . $kind);
786  $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);
787  if ($this->exceptions) {
788  throw new phpmailerException('Invalid recipient array: ' . $kind);
789  }
790  return false;
791  }
792  $address = trim($address);
793  $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
794  if (!$this->validateAddress($address)) {
795  $this->setError($this->lang('invalid_address') . ': ' . $address);
796  $this->edebug($this->lang('invalid_address') . ': ' . $address);
797  if ($this->exceptions) {
798  throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
799  }
800  return false;
801  }
802  if ($kind != 'Reply-To') {
803  if (!isset($this->all_recipients[strtolower($address)])) {
804  array_push($this->$kind, array($address, $name));
805  $this->all_recipients[strtolower($address)] = true;
806  return true;
807  }
808  } else {
809  if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
810  $this->ReplyTo[strtolower($address)] = array($address, $name);
811  return true;
812  }
813  }
814  return false;
815  }
816 
825  public function setFrom($address, $name = '', $auto = true)
826  {
827  $address = trim($address);
828  $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
829  if (!$this->validateAddress($address)) {
830  $this->setError($this->lang('invalid_address') . ': ' . $address);
831  $this->edebug($this->lang('invalid_address') . ': ' . $address);
832  if ($this->exceptions) {
833  throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
834  }
835  return false;
836  }
837  $this->From = $address;
838  $this->FromName = $name;
839  if ($auto) {
840  if (empty($this->Sender)) {
841  $this->Sender = $address;
842  }
843  }
844  return true;
845  }
846 
854  public function getLastMessageID()
855  {
856  return $this->lastMessageID;
857  }
858 
873  public static function validateAddress($address, $patternselect = 'auto')
874  {
875  if (!$patternselect or $patternselect == 'auto') {
876  //Check this constant first so it works when extension_loaded() is disabled by safe mode
877  //Constant was added in PHP 5.2.4
878  if (defined('PCRE_VERSION')) {
879  //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
880  if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
881  $patternselect = 'pcre8';
882  } else {
883  $patternselect = 'pcre';
884  }
885  } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
886  //Fall back to older PCRE
887  $patternselect = 'pcre';
888  } else {
889  //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
890  if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
891  $patternselect = 'php';
892  } else {
893  $patternselect = 'noregex';
894  }
895  }
896  }
897  switch ($patternselect) {
898  case 'pcre8':
905  return (boolean)preg_match(
906  '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
907  '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
908  '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
909  '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
910  '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
911  '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
912  '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
913  '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
914  '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
915  $address
916  );
917  case 'pcre':
918  //An older regex that doesn't need a recent PCRE
919  return (boolean)preg_match(
920  '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
921  '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
922  '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
923  '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
924  '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
925  '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
926  '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
927  '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
928  '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
929  '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
930  $address
931  );
932  case 'html5':
937  return (boolean)preg_match(
938  '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
939  '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
940  $address
941  );
942  case 'noregex':
943  //No PCRE! Do something _very_ approximate!
944  //Check the address is 3 chars or longer and contains an @ that's not the first or last char
945  return (strlen($address) >= 3
946  and strpos($address, '@') >= 1
947  and strpos($address, '@') != strlen($address) - 1);
948  case 'php':
949  default:
950  return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
951  }
952  }
953 
960  public function send()
961  {
962  try {
963  if (!$this->preSend()) {
964  return false;
965  }
966  return $this->postSend();
967  } catch (phpmailerException $exc) {
968  $this->mailHeader = '';
969  $this->setError($exc->getMessage());
970  if ($this->exceptions) {
971  throw $exc;
972  }
973  return false;
974  }
975  }
976 
982  public function preSend()
983  {
984  try {
985  $this->mailHeader = '';
986  if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
987  throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
988  }
989 
990  // Set whether the message is multipart/alternative
991  if (!empty($this->AltBody)) {
992  $this->ContentType = 'multipart/alternative';
993  }
994 
995  $this->error_count = 0; // reset errors
996  $this->setMessageType();
997  // Refuse to send an empty message unless we are specifically allowing it
998  if (!$this->AllowEmpty and empty($this->Body)) {
999  throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
1000  }
1001 
1002  $this->MIMEHeader = $this->createHeader();
1003  $this->MIMEBody = $this->createBody();
1004 
1005  // To capture the complete message when using mail(), create
1006  // an extra header list which createHeader() doesn't fold in
1007  if ($this->Mailer == 'mail') {
1008  if (count($this->to) > 0) {
1009  $this->mailHeader .= $this->addrAppend('To', $this->to);
1010  } else {
1011  $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
1012  }
1013  $this->mailHeader .= $this->headerLine(
1014  'Subject',
1015  $this->encodeHeader($this->secureHeader(trim($this->Subject)))
1016  );
1017  }
1018 
1019  // Sign with DKIM if enabled
1020  if (!empty($this->DKIM_domain)
1021  && !empty($this->DKIM_private)
1022  && !empty($this->DKIM_selector)
1023  && !empty($this->DKIM_domain)
1024  && file_exists($this->DKIM_private)) {
1025  $header_dkim = $this->DKIM_Add(
1026  $this->MIMEHeader . $this->mailHeader,
1027  $this->encodeHeader($this->secureHeader($this->Subject)),
1028  $this->MIMEBody
1029  );
1030  $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
1031  str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
1032  }
1033  return true;
1034 
1035  } catch (phpmailerException $exc) {
1036  $this->setError($exc->getMessage());
1037  if ($this->exceptions) {
1038  throw $exc;
1039  }
1040  return false;
1041  }
1042  }
1043 
1050  public function postSend()
1051  {
1052  try {
1053  // Choose the mailer and send through it
1054  switch ($this->Mailer) {
1055  case 'sendmail':
1056  case 'qmail':
1057  return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1058  case 'smtp':
1059  return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1060  case 'mail':
1061  return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1062  default:
1063  $sendMethod = $this->Mailer.'Send';
1064  if (method_exists($this, $sendMethod)) {
1065  return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
1066  }
1067 
1068  return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1069  }
1070  } catch (phpmailerException $exc) {
1071  $this->setError($exc->getMessage());
1072  $this->edebug($exc->getMessage());
1073  if ($this->exceptions) {
1074  throw $exc;
1075  }
1076  }
1077  return false;
1078  }
1079 
1089  protected function sendmailSend($header, $body)
1090  {
1091  if ($this->Sender != '') {
1092  if ($this->Mailer == 'qmail') {
1093  $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1094  } else {
1095  $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1096  }
1097  } else {
1098  if ($this->Mailer == 'qmail') {
1099  $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
1100  } else {
1101  $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
1102  }
1103  }
1104  if ($this->SingleTo === true) {
1105  foreach ($this->SingleToArray as $toAddr) {
1106  if (!@$mail = popen($sendmail, 'w')) {
1107  throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1108  }
1109  fputs($mail, 'To: ' . $toAddr . "\n");
1110  fputs($mail, $header);
1111  fputs($mail, $body);
1112  $result = pclose($mail);
1113  $this->doCallback(
1114  ($result == 0),
1115  array($toAddr),
1116  $this->cc,
1117  $this->bcc,
1118  $this->Subject,
1119  $body,
1120  $this->From
1121  );
1122  if ($result != 0) {
1123  throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1124  }
1125  }
1126  } else {
1127  if (!@$mail = popen($sendmail, 'w')) {
1128  throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1129  }
1130  fputs($mail, $header);
1131  fputs($mail, $body);
1132  $result = pclose($mail);
1133  $this->doCallback(($result == 0), $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1134  if ($result != 0) {
1135  throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1136  }
1137  }
1138  return true;
1139  }
1140 
1150  protected function mailSend($header, $body)
1151  {
1152  $toArr = array();
1153  foreach ($this->to as $toaddr) {
1154  $toArr[] = $this->addrFormat($toaddr);
1155  }
1156  $to = implode(', ', $toArr);
1157 
1158  if (empty($this->Sender)) {
1159  $params = ' ';
1160  } else {
1161  $params = sprintf('-f%s', $this->Sender);
1162  }
1163  if ($this->Sender != '' and !ini_get('safe_mode')) {
1164  $old_from = ini_get('sendmail_from');
1165  ini_set('sendmail_from', $this->Sender);
1166  }
1167  $result = false;
1168  if ($this->SingleTo === true && count($toArr) > 1) {
1169  foreach ($toArr as $toAddr) {
1170  $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
1171  $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1172  }
1173  } else {
1174  $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1175  $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1176  }
1177  if (isset($old_from)) {
1178  ini_set('sendmail_from', $old_from);
1179  }
1180  if (!$result) {
1181  throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1182  }
1183  return true;
1184  }
1185 
1191  public function getSMTPInstance()
1192  {
1193  if (!is_object($this->smtp)) {
1194  $this->smtp = new SMTP;
1195  }
1196  return $this->smtp;
1197  }
1198 
1211  protected function smtpSend($header, $body)
1212  {
1213  $bad_rcpt = array();
1214 
1215  if (!$this->smtpConnect()) {
1216  throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1217  }
1218  $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
1219  if (!$this->smtp->mail($smtp_from)) {
1220  $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1221  throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1222  }
1223 
1224  // Attempt to send to all recipients
1225  foreach ($this->to as $to) {
1226  if (!$this->smtp->recipient($to[0])) {
1227  $bad_rcpt[] = $to[0];
1228  $isSent = false;
1229  } else {
1230  $isSent = true;
1231  }
1232  $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
1233  }
1234  foreach ($this->cc as $cc) {
1235  if (!$this->smtp->recipient($cc[0])) {
1236  $bad_rcpt[] = $cc[0];
1237  $isSent = false;
1238  } else {
1239  $isSent = true;
1240  }
1241  $this->doCallback($isSent, array(), array($cc[0]), array(), $this->Subject, $body, $this->From);
1242  }
1243  foreach ($this->bcc as $bcc) {
1244  if (!$this->smtp->recipient($bcc[0])) {
1245  $bad_rcpt[] = $bcc[0];
1246  $isSent = false;
1247  } else {
1248  $isSent = true;
1249  }
1250  $this->doCallback($isSent, array(), array(), array($bcc[0]), $this->Subject, $body, $this->From);
1251  }
1252 
1253  // Only send the DATA command if we have viable recipients
1254  if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
1255  throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1256  }
1257  if ($this->SMTPKeepAlive == true) {
1258  $this->smtp->reset();
1259  } else {
1260  $this->smtp->quit();
1261  $this->smtp->close();
1262  }
1263  if (count($bad_rcpt) > 0) { // Create error message for any bad addresses
1264  throw new phpmailerException(
1265  $this->lang('recipients_failed') . implode(', ', $bad_rcpt),
1266  self::STOP_CONTINUE
1267  );
1268  }
1269  return true;
1270  }
1271 
1281  public function smtpConnect($options = array())
1282  {
1283  if (is_null($this->smtp)) {
1284  $this->smtp = $this->getSMTPInstance();
1285  }
1286 
1287  // Already connected?
1288  if ($this->smtp->connected()) {
1289  return true;
1290  }
1291 
1292  $this->smtp->setTimeout($this->Timeout);
1293  $this->smtp->setDebugLevel($this->SMTPDebug);
1294  $this->smtp->setDebugOutput($this->Debugoutput);
1295  $this->smtp->setVerp($this->do_verp);
1296  $hosts = explode(';', $this->Host);
1297  $lastexception = null;
1298 
1299  foreach ($hosts as $hostentry) {
1300  $hostinfo = array();
1301  if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
1302  // Not a valid host entry
1303  continue;
1304  }
1305  // $hostinfo[2]: optional ssl or tls prefix
1306  // $hostinfo[3]: the hostname
1307  // $hostinfo[4]: optional port number
1308  // The host string prefix can temporarily override the current setting for SMTPSecure
1309  // If it's not specified, the default value is used
1310  $prefix = '';
1311  $tls = ($this->SMTPSecure == 'tls');
1312  if ($hostinfo[2] == 'ssl' or ($hostinfo[2] == '' and $this->SMTPSecure == 'ssl')) {
1313  $prefix = 'ssl://';
1314  $tls = false; // Can't have SSL and TLS at once
1315  } elseif ($hostinfo[2] == 'tls') {
1316  $tls = true;
1317  // tls doesn't use a prefix
1318  }
1319  $host = $hostinfo[3];
1320  $port = $this->Port;
1321  $tport = (integer)$hostinfo[4];
1322  if ($tport > 0 and $tport < 65536) {
1323  $port = $tport;
1324  }
1325  if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
1326  try {
1327  if ($this->Helo) {
1328  $hello = $this->Helo;
1329  } else {
1330  $hello = $this->serverHostname();
1331  }
1332  $this->smtp->hello($hello);
1333 
1334  if ($tls) {
1335  if (!$this->smtp->startTLS()) {
1336  throw new phpmailerException($this->lang('connect_host'));
1337  }
1338  // We must resend HELO after tls negotiation
1339  $this->smtp->hello($hello);
1340  }
1341  if ($this->SMTPAuth) {
1342  if (!$this->smtp->authenticate(
1343  $this->Username,
1344  $this->Password,
1345  $this->AuthType,
1346  $this->Realm,
1347  $this->Workstation
1348  )
1349  ) {
1350  throw new phpmailerException($this->lang('authenticate'));
1351  }
1352  }
1353  return true;
1354  } catch (phpmailerException $exc) {
1355  $lastexception = $exc;
1356  // We must have connected, but then failed TLS or Auth, so close connection nicely
1357  $this->smtp->quit();
1358  }
1359  }
1360  }
1361  // If we get here, all connection attempts have failed, so close connection hard
1362  $this->smtp->close();
1363  // As we've caught all exceptions, just report whatever the last one was
1364  if ($this->exceptions and !is_null($lastexception)) {
1365  throw $lastexception;
1366  }
1367  return false;
1368  }
1369 
1374  public function smtpClose()
1375  {
1376  if ($this->smtp !== null) {
1377  if ($this->smtp->connected()) {
1378  $this->smtp->quit();
1379  $this->smtp->close();
1380  }
1381  }
1382  }
1383 
1393  public function setLanguage($langcode = 'en', $lang_path = '')
1394  {
1395  // Define full set of translatable strings in English
1396  $PHPMAILER_LANG = array(
1397  'authenticate' => 'SMTP Error: Could not authenticate.',
1398  'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1399  'data_not_accepted' => 'SMTP Error: data not accepted.',
1400  'empty_message' => 'Message body empty',
1401  'encoding' => 'Unknown encoding: ',
1402  'execute' => 'Could not execute: ',
1403  'file_access' => 'Could not access file: ',
1404  'file_open' => 'File Error: Could not open file: ',
1405  'from_failed' => 'The following From address failed: ',
1406  'instantiate' => 'Could not instantiate mail function.',
1407  'invalid_address' => 'Invalid address',
1408  'mailer_not_supported' => ' mailer is not supported.',
1409  'provide_address' => 'You must provide at least one recipient email address.',
1410  'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1411  'signing' => 'Signing Error: ',
1412  'smtp_connect_failed' => 'SMTP connect() failed.',
1413  'smtp_error' => 'SMTP server error: ',
1414  'variable_set' => 'Cannot set or reset variable: '
1415  );
1416  if (empty($lang_path)) {
1417  // Calculate an absolute path so it can work if CWD is not here
1418  $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
1419  }
1420  $foundlang = true;
1421  $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
1422  if ($langcode != 'en') { // There is no English translation file
1423  // Make sure language file path is readable
1424  if (!is_readable($lang_file)) {
1425  $foundlang = false;
1426  } else {
1427  // Overwrite language-specific strings.
1428  // This way we'll never have missing translations.
1429  $foundlang = include $lang_file;
1430  }
1431  }
1432  $this->language = $PHPMAILER_LANG;
1433  return ($foundlang == true); // Returns false if language not found
1434  }
1435 
1440  public function getTranslations()
1441  {
1442  return $this->language;
1443  }
1444 
1455  public function addrAppend($type, $addr)
1456  {
1457  $addresses = array();
1458  foreach ($addr as $address) {
1459  $addresses[] = $this->addrFormat($address);
1460  }
1461  return $type . ': ' . implode(', ', $addresses) . $this->LE;
1462  }
1463 
1471  public function addrFormat($addr)
1472  {
1473  if (empty($addr[1])) { // No name provided
1474  return $this->secureHeader($addr[0]);
1475  } else {
1476  return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
1477  $addr[0]
1478  ) . '>';
1479  }
1480  }
1481 
1493  public function wrapText($message, $length, $qp_mode = false)
1494  {
1495  $soft_break = ($qp_mode) ? sprintf(' =%s', $this->LE) : $this->LE;
1496  // If utf-8 encoding is used, we will need to make sure we don't
1497  // split multibyte characters when we wrap
1498  $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
1499  $lelen = strlen($this->LE);
1500  $crlflen = strlen(self::CRLF);
1501 
1502  $message = $this->fixEOL($message);
1503  if (substr($message, -$lelen) == $this->LE) {
1504  $message = substr($message, 0, -$lelen);
1505  }
1506 
1507  $line = explode($this->LE, $message); // Magic. We know fixEOL uses $LE
1508  $message = '';
1509  for ($i = 0; $i < count($line); $i++) {
1510  $line_part = explode(' ', $line[$i]);
1511  $buf = '';
1512  for ($e = 0; $e < count($line_part); $e++) {
1513  $word = $line_part[$e];
1514  if ($qp_mode and (strlen($word) > $length)) {
1515  $space_left = $length - strlen($buf) - $crlflen;
1516  if ($e != 0) {
1517  if ($space_left > 20) {
1518  $len = $space_left;
1519  if ($is_utf8) {
1520  $len = $this->utf8CharBoundary($word, $len);
1521  } elseif (substr($word, $len - 1, 1) == '=') {
1522  $len--;
1523  } elseif (substr($word, $len - 2, 1) == '=') {
1524  $len -= 2;
1525  }
1526  $part = substr($word, 0, $len);
1527  $word = substr($word, $len);
1528  $buf .= ' ' . $part;
1529  $message .= $buf . sprintf('=%s', self::CRLF);
1530  } else {
1531  $message .= $buf . $soft_break;
1532  }
1533  $buf = '';
1534  }
1535  while (strlen($word) > 0) {
1536  if ($length <= 0) {
1537  break;
1538  }
1539  $len = $length;
1540  if ($is_utf8) {
1541  $len = $this->utf8CharBoundary($word, $len);
1542  } elseif (substr($word, $len - 1, 1) == '=') {
1543  $len--;
1544  } elseif (substr($word, $len - 2, 1) == '=') {
1545  $len -= 2;
1546  }
1547  $part = substr($word, 0, $len);
1548  $word = substr($word, $len);
1549 
1550  if (strlen($word) > 0) {
1551  $message .= $part . sprintf('=%s', self::CRLF);
1552  } else {
1553  $buf = $part;
1554  }
1555  }
1556  } else {
1557  $buf_o = $buf;
1558  $buf .= ($e == 0) ? $word : (' ' . $word);
1559 
1560  if (strlen($buf) > $length and $buf_o != '') {
1561  $message .= $buf_o . $soft_break;
1562  $buf = $word;
1563  }
1564  }
1565  }
1566  $message .= $buf . self::CRLF;
1567  }
1568 
1569  return $message;
1570  }
1571 
1581  public function utf8CharBoundary($encodedText, $maxLength)
1582  {
1583  $foundSplitPos = false;
1584  $lookBack = 3;
1585  while (!$foundSplitPos) {
1586  $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1587  $encodedCharPos = strpos($lastChunk, '=');
1588  if ($encodedCharPos !== false) {
1589  // Found start of encoded character byte within $lookBack block.
1590  // Check the encoded byte value (the 2 chars after the '=')
1591  $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1592  $dec = hexdec($hex);
1593  if ($dec < 128) { // Single byte character.
1594  // If the encoded char was found at pos 0, it will fit
1595  // otherwise reduce maxLength to start of the encoded char
1596  $maxLength = ($encodedCharPos == 0) ? $maxLength :
1597  $maxLength - ($lookBack - $encodedCharPos);
1598  $foundSplitPos = true;
1599  } elseif ($dec >= 192) { // First byte of a multi byte character
1600  // Reduce maxLength to split at start of character
1601  $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1602  $foundSplitPos = true;
1603  } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
1604  $lookBack += 3;
1605  }
1606  } else {
1607  // No encoded character found
1608  $foundSplitPos = true;
1609  }
1610  }
1611  return $maxLength;
1612  }
1613 
1619  public function setWordWrap()
1620  {
1621  if ($this->WordWrap < 1) {
1622  return;
1623  }
1624 
1625  switch ($this->message_type) {
1626  case 'alt':
1627  case 'alt_inline':
1628  case 'alt_attach':
1629  case 'alt_inline_attach':
1630  $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
1631  break;
1632  default:
1633  $this->Body = $this->wrapText($this->Body, $this->WordWrap);
1634  break;
1635  }
1636  }
1637 
1643  public function createHeader()
1644  {
1645  $result = '';
1646 
1647  // Set the boundaries
1648  $uniq_id = md5(uniqid(time()));
1649  $this->boundary[1] = 'b1_' . $uniq_id;
1650  $this->boundary[2] = 'b2_' . $uniq_id;
1651  $this->boundary[3] = 'b3_' . $uniq_id;
1652 
1653  if ($this->MessageDate == '') {
1654  $this->MessageDate = self::rfcDate();
1655  }
1656  $result .= $this->headerLine('Date', $this->MessageDate);
1657 
1658 
1659  // To be created automatically by mail()
1660  if ($this->SingleTo === true) {
1661  if ($this->Mailer != 'mail') {
1662  foreach ($this->to as $toaddr) {
1663  $this->SingleToArray[] = $this->addrFormat($toaddr);
1664  }
1665  }
1666  } else {
1667  if (count($this->to) > 0) {
1668  if ($this->Mailer != 'mail') {
1669  $result .= $this->addrAppend('To', $this->to);
1670  }
1671  } elseif (count($this->cc) == 0) {
1672  $result .= $this->headerLine('To', 'undisclosed-recipients:;');
1673  }
1674  }
1675 
1676  $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
1677 
1678  // sendmail and mail() extract Cc from the header before sending
1679  if (count($this->cc) > 0) {
1680  $result .= $this->addrAppend('Cc', $this->cc);
1681  }
1682 
1683  // sendmail and mail() extract Bcc from the header before sending
1684  if ((
1685  $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
1686  )
1687  and count($this->bcc) > 0
1688  ) {
1689  $result .= $this->addrAppend('Bcc', $this->bcc);
1690  }
1691 
1692  if (count($this->ReplyTo) > 0) {
1693  $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
1694  }
1695 
1696  // mail() sets the subject itself
1697  if ($this->Mailer != 'mail') {
1698  $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
1699  }
1700 
1701  if ($this->MessageID != '') {
1702  $this->lastMessageID = $this->MessageID;
1703  } else {
1704  $this->lastMessageID = sprintf('<%s@%s>', $uniq_id, $this->ServerHostname());
1705  }
1706  $result .= $this->HeaderLine('Message-ID', $this->lastMessageID);
1707  $result .= $this->headerLine('X-Priority', $this->Priority);
1708  if ($this->XMailer == '') {
1709  $result .= $this->headerLine(
1710  'X-Mailer',
1711  'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer/)'
1712  );
1713  } else {
1714  $myXmailer = trim($this->XMailer);
1715  if ($myXmailer) {
1716  $result .= $this->headerLine('X-Mailer', $myXmailer);
1717  }
1718  }
1719 
1720  if ($this->ConfirmReadingTo != '') {
1721  $result .= $this->headerLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
1722  }
1723 
1724  // Add custom headers
1725  for ($index = 0; $index < count($this->CustomHeader); $index++) {
1726  $result .= $this->headerLine(
1727  trim($this->CustomHeader[$index][0]),
1728  $this->encodeHeader(trim($this->CustomHeader[$index][1]))
1729  );
1730  }
1731  if (!$this->sign_key_file) {
1732  $result .= $this->headerLine('MIME-Version', '1.0');
1733  $result .= $this->getMailMIME();
1734  }
1735 
1736  return $result;
1737  }
1738 
1744  public function getMailMIME()
1745  {
1746  $result = '';
1747  $ismultipart = true;
1748  switch ($this->message_type) {
1749  case 'inline':
1750  $result .= $this->headerLine('Content-Type', 'multipart/related;');
1751  $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1752  break;
1753  case 'attach':
1754  case 'inline_attach':
1755  case 'alt_attach':
1756  case 'alt_inline_attach':
1757  $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
1758  $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1759  break;
1760  case 'alt':
1761  case 'alt_inline':
1762  $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
1763  $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1764  break;
1765  default:
1766  // Catches case 'plain': and case '':
1767  $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
1768  $ismultipart = false;
1769  break;
1770  }
1771  // RFC1341 part 5 says 7bit is assumed if not specified
1772  if ($this->Encoding != '7bit') {
1773  // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
1774  if ($ismultipart) {
1775  if ($this->Encoding == '8bit') {
1776  $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
1777  }
1778  // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
1779  } else {
1780  $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
1781  }
1782  }
1783 
1784  if ($this->Mailer != 'mail') {
1785  $result .= $this->LE;
1786  }
1787 
1788  return $result;
1789  }
1790 
1799  public function getSentMIMEMessage()
1800  {
1801  return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;
1802  }
1803 
1804 
1812  public function createBody()
1813  {
1814  $body = '';
1815 
1816  if ($this->sign_key_file) {
1817  $body .= $this->getMailMIME() . $this->LE;
1818  }
1819 
1820  $this->setWordWrap();
1821 
1822  $bodyEncoding = $this->Encoding;
1823  $bodyCharSet = $this->CharSet;
1824  if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
1825  $bodyEncoding = '7bit';
1826  $bodyCharSet = 'us-ascii';
1827  }
1828  $altBodyEncoding = $this->Encoding;
1829  $altBodyCharSet = $this->CharSet;
1830  if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
1831  $altBodyEncoding = '7bit';
1832  $altBodyCharSet = 'us-ascii';
1833  }
1834  switch ($this->message_type) {
1835  case 'inline':
1836  $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
1837  $body .= $this->encodeString($this->Body, $bodyEncoding);
1838  $body .= $this->LE . $this->LE;
1839  $body .= $this->attachAll('inline', $this->boundary[1]);
1840  break;
1841  case 'attach':
1842  $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
1843  $body .= $this->encodeString($this->Body, $bodyEncoding);
1844  $body .= $this->LE . $this->LE;
1845  $body .= $this->attachAll('attachment', $this->boundary[1]);
1846  break;
1847  case 'inline_attach':
1848  $body .= $this->textLine('--' . $this->boundary[1]);
1849  $body .= $this->headerLine('Content-Type', 'multipart/related;');
1850  $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1851  $body .= $this->LE;
1852  $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
1853  $body .= $this->encodeString($this->Body, $bodyEncoding);
1854  $body .= $this->LE . $this->LE;
1855  $body .= $this->attachAll('inline', $this->boundary[2]);
1856  $body .= $this->LE;
1857  $body .= $this->attachAll('attachment', $this->boundary[1]);
1858  break;
1859  case 'alt':
1860  $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
1861  $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1862  $body .= $this->LE . $this->LE;
1863  $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
1864  $body .= $this->encodeString($this->Body, $bodyEncoding);
1865  $body .= $this->LE . $this->LE;
1866  if (!empty($this->Ical)) {
1867  $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
1868  $body .= $this->encodeString($this->Ical, $this->Encoding);
1869  $body .= $this->LE . $this->LE;
1870  }
1871  $body .= $this->endBoundary($this->boundary[1]);
1872  break;
1873  case 'alt_inline':
1874  $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
1875  $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1876  $body .= $this->LE . $this->LE;
1877  $body .= $this->textLine('--' . $this->boundary[1]);
1878  $body .= $this->headerLine('Content-Type', 'multipart/related;');
1879  $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1880  $body .= $this->LE;
1881  $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
1882  $body .= $this->encodeString($this->Body, $bodyEncoding);
1883  $body .= $this->LE . $this->LE;
1884  $body .= $this->attachAll('inline', $this->boundary[2]);
1885  $body .= $this->LE;
1886  $body .= $this->endBoundary($this->boundary[1]);
1887  break;
1888  case 'alt_attach':
1889  $body .= $this->textLine('--' . $this->boundary[1]);
1890  $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
1891  $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1892  $body .= $this->LE;
1893  $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
1894  $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1895  $body .= $this->LE . $this->LE;
1896  $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
1897  $body .= $this->encodeString($this->Body, $bodyEncoding);
1898  $body .= $this->LE . $this->LE;
1899  $body .= $this->endBoundary($this->boundary[2]);
1900  $body .= $this->LE;
1901  $body .= $this->attachAll('attachment', $this->boundary[1]);
1902  break;
1903  case 'alt_inline_attach':
1904  $body .= $this->textLine('--' . $this->boundary[1]);
1905  $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
1906  $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1907  $body .= $this->LE;
1908  $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
1909  $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
1910  $body .= $this->LE . $this->LE;
1911  $body .= $this->textLine('--' . $this->boundary[2]);
1912  $body .= $this->headerLine('Content-Type', 'multipart/related;');
1913  $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
1914  $body .= $this->LE;
1915  $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
1916  $body .= $this->encodeString($this->Body, $bodyEncoding);
1917  $body .= $this->LE . $this->LE;
1918  $body .= $this->attachAll('inline', $this->boundary[3]);
1919  $body .= $this->LE;
1920  $body .= $this->endBoundary($this->boundary[2]);
1921  $body .= $this->LE;
1922  $body .= $this->attachAll('attachment', $this->boundary[1]);
1923  break;
1924  default:
1925  // catch case 'plain' and case ''
1926  $body .= $this->encodeString($this->Body, $bodyEncoding);
1927  break;
1928  }
1929 
1930  if ($this->isError()) {
1931  $body = '';
1932  } elseif ($this->sign_key_file) {
1933  try {
1934  if (!defined('PKCS7_TEXT')) {
1935  throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
1936  }
1937  // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
1938  $file = tempnam(sys_get_temp_dir(), 'mail');
1939  file_put_contents($file, $body); // @TODO check this worked
1940  $signed = tempnam(sys_get_temp_dir(), 'signed');
1941  if (@openssl_pkcs7_sign(
1942  $file,
1943  $signed,
1944  'file://' . realpath($this->sign_cert_file),
1945  array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
1946  null
1947  )
1948  ) {
1949  @unlink($file);
1950  $body = file_get_contents($signed);
1951  @unlink($signed);
1952  } else {
1953  @unlink($file);
1954  @unlink($signed);
1955  throw new phpmailerException($this->lang('signing') . openssl_error_string());
1956  }
1957  } catch (phpmailerException $exc) {
1958  $body = '';
1959  if ($this->exceptions) {
1960  throw $exc;
1961  }
1962  }
1963  }
1964  return $body;
1965  }
1966 
1976  protected function getBoundary($boundary, $charSet, $contentType, $encoding)
1977  {
1978  $result = '';
1979  if ($charSet == '') {
1980  $charSet = $this->CharSet;
1981  }
1982  if ($contentType == '') {
1983  $contentType = $this->ContentType;
1984  }
1985  if ($encoding == '') {
1986  $encoding = $this->Encoding;
1987  }
1988  $result .= $this->textLine('--' . $boundary);
1989  $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
1990  $result .= $this->LE;
1991  // RFC1341 part 5 says 7bit is assumed if not specified
1992  if ($encoding != '7bit') {
1993  $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
1994  }
1995  $result .= $this->LE;
1996 
1997  return $result;
1998  }
1999 
2006  protected function endBoundary($boundary)
2007  {
2008  return $this->LE . '--' . $boundary . '--' . $this->LE;
2009  }
2010 
2018  protected function setMessageType()
2019  {
2020  $type = array();
2021  if ($this->alternativeExists()) {
2022  $type[] = 'alt';
2023  }
2024  if ($this->inlineImageExists()) {
2025  $type[] = 'inline';
2026  }
2027  if ($this->attachmentExists()) {
2028  $type[] = 'attach';
2029  }
2030  $this->message_type = implode('_', $type);
2031  if ($this->message_type == '') {
2032  $this->message_type = 'plain';
2033  }
2034  }
2035 
2043  public function headerLine($name, $value)
2044  {
2045  return $name . ': ' . $value . $this->LE;
2046  }
2047 
2054  public function textLine($value)
2055  {
2056  return $value . $this->LE;
2057  }
2058 
2070  public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2071  {
2072  try {
2073  if (!@is_file($path)) {
2074  throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
2075  }
2076 
2077  // If a MIME type is not specified, try to work it out from the file name
2078  if ($type == '') {
2079  $type = self::filenameToType($path);
2080  }
2081 
2082  $filename = basename($path);
2083  if ($name == '') {
2084  $name = $filename;
2085  }
2086 
2087  $this->attachment[] = array(
2088  0 => $path,
2089  1 => $filename,
2090  2 => $name,
2091  3 => $encoding,
2092  4 => $type,
2093  5 => false, // isStringAttachment
2094  6 => $disposition,
2095  7 => 0
2096  );
2097 
2098  } catch (phpmailerException $exc) {
2099  $this->setError($exc->getMessage());
2100  $this->edebug($exc->getMessage());
2101  if ($this->exceptions) {
2102  throw $exc;
2103  }
2104  return false;
2105  }
2106  return true;
2107  }
2108 
2113  public function getAttachments()
2114  {
2115  return $this->attachment;
2116  }
2117 
2126  protected function attachAll($disposition_type, $boundary)
2127  {
2128  // Return text of body
2129  $mime = array();
2130  $cidUniq = array();
2131  $incl = array();
2132 
2133  // Add all attachments
2134  foreach ($this->attachment as $attachment) {
2135  // Check if it is a valid disposition_filter
2136  if ($attachment[6] == $disposition_type) {
2137  // Check for string attachment
2138  $string = '';
2139  $path = '';
2140  $bString = $attachment[5];
2141  if ($bString) {
2142  $string = $attachment[0];
2143  } else {
2144  $path = $attachment[0];
2145  }
2146 
2147  $inclhash = md5(serialize($attachment));
2148  if (in_array($inclhash, $incl)) {
2149  continue;
2150  }
2151  $incl[] = $inclhash;
2152  $name = $attachment[2];
2153  $encoding = $attachment[3];
2154  $type = $attachment[4];
2155  $disposition = $attachment[6];
2156  $cid = $attachment[7];
2157  if ($disposition == 'inline' && isset($cidUniq[$cid])) {
2158  continue;
2159  }
2160  $cidUniq[$cid] = true;
2161 
2162  $mime[] = sprintf('--%s%s', $boundary, $this->LE);
2163  $mime[] = sprintf(
2164  'Content-Type: %s; name="%s"%s',
2165  $type,
2166  $this->encodeHeader($this->secureHeader($name)),
2167  $this->LE
2168  );
2169  // RFC1341 part 5 says 7bit is assumed if not specified
2170  if ($encoding != '7bit') {
2171  $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
2172  }
2173 
2174  if ($disposition == 'inline') {
2175  $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
2176  }
2177 
2178  // If a filename contains any of these chars, it should be quoted,
2179  // but not otherwise: RFC2183 & RFC2045 5.1
2180  // Fixes a warning in IETF's msglint MIME checker
2181  // Allow for bypassing the Content-Disposition header totally
2182  if (!(empty($disposition))) {
2183  $encoded_name = $this->encodeHeader($this->secureHeader($name));
2184  if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
2185  $mime[] = sprintf(
2186  'Content-Disposition: %s; filename="%s"%s',
2187  $disposition,
2188  $encoded_name,
2189  $this->LE . $this->LE
2190  );
2191  } else {
2192  $mime[] = sprintf(
2193  'Content-Disposition: %s; filename=%s%s',
2194  $disposition,
2195  $encoded_name,
2196  $this->LE . $this->LE
2197  );
2198  }
2199  } else {
2200  $mime[] = $this->LE;
2201  }
2202 
2203  // Encode as string attachment
2204  if ($bString) {
2205  $mime[] = $this->encodeString($string, $encoding);
2206  if ($this->isError()) {
2207  return '';
2208  }
2209  $mime[] = $this->LE . $this->LE;
2210  } else {
2211  $mime[] = $this->encodeFile($path, $encoding);
2212  if ($this->isError()) {
2213  return '';
2214  }
2215  $mime[] = $this->LE . $this->LE;
2216  }
2217  }
2218  }
2219 
2220  $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
2221 
2222  return implode('', $mime);
2223  }
2224 
2235  protected function encodeFile($path, $encoding = 'base64')
2236  {
2237  try {
2238  if (!is_readable($path)) {
2239  throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
2240  }
2241  $magic_quotes = get_magic_quotes_runtime();
2242  if ($magic_quotes) {
2243  if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2244  set_magic_quotes_runtime(false);
2245  } else {
2246  //Doesn't exist in PHP 5.4, but we don't need to check because
2247  //get_magic_quotes_runtime always returns false in 5.4+
2248  //so it will never get here
2249  ini_set('magic_quotes_runtime', 0);
2250  }
2251  }
2252  $file_buffer = file_get_contents($path);
2253  $file_buffer = $this->encodeString($file_buffer, $encoding);
2254  if ($magic_quotes) {
2255  if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2256  set_magic_quotes_runtime($magic_quotes);
2257  } else {
2258  ini_set('magic_quotes_runtime', ($magic_quotes?'1':'0'));
2259  }
2260  }
2261  return $file_buffer;
2262  } catch (Exception $exc) {
2263  $this->setError($exc->getMessage());
2264  return '';
2265  }
2266  }
2267 
2276  public function encodeString($str, $encoding = 'base64')
2277  {
2278  $encoded = '';
2279  switch (strtolower($encoding)) {
2280  case 'base64':
2281  $encoded = chunk_split(base64_encode($str), 76, $this->LE);
2282  break;
2283  case '7bit':
2284  case '8bit':
2285  $encoded = $this->fixEOL($str);
2286  // Make sure it ends with a line break
2287  if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
2288  $encoded .= $this->LE;
2289  }
2290  break;
2291  case 'binary':
2292  $encoded = $str;
2293  break;
2294  case 'quoted-printable':
2295  $encoded = $this->encodeQP($str);
2296  break;
2297  default:
2298  $this->setError($this->lang('encoding') . $encoding);
2299  break;
2300  }
2301  return $encoded;
2302  }
2303 
2312  public function encodeHeader($str, $position = 'text')
2313  {
2314  $matchcount = 0;
2315  switch (strtolower($position)) {
2316  case 'phrase':
2317  if (!preg_match('/[\200-\377]/', $str)) {
2318  // Can't use addslashes as we don't know the value of magic_quotes_sybase
2319  $encoded = addcslashes($str, "\0..\37\177\\\"");
2320  if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
2321  return ($encoded);
2322  } else {
2323  return ("\"$encoded\"");
2324  }
2325  }
2326  $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
2327  break;
2329  case 'comment':
2330  $matchcount = preg_match_all('/[()"]/', $str, $matches);
2331  // Intentional fall-through
2332  case 'text':
2333  default:
2334  $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
2335  break;
2336  }
2337 
2338  if ($matchcount == 0) { // There are no chars that need encoding
2339  return ($str);
2340  }
2341 
2342  $maxlen = 75 - 7 - strlen($this->CharSet);
2343  // Try to select the encoding which should produce the shortest output
2344  if ($matchcount > strlen($str) / 3) {
2345  // More than a third of the content will need encoding, so B encoding will be most efficient
2346  $encoding = 'B';
2347  if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
2348  // Use a custom function which correctly encodes and wraps long
2349  // multibyte strings without breaking lines within a character
2350  $encoded = $this->base64EncodeWrapMB($str, "\n");
2351  } else {
2352  $encoded = base64_encode($str);
2353  $maxlen -= $maxlen % 4;
2354  $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
2355  }
2356  } else {
2357  $encoding = 'Q';
2358  $encoded = $this->encodeQ($str, $position);
2359  $encoded = $this->wrapText($encoded, $maxlen, true);
2360  $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
2361  }
2362 
2363  $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
2364  $encoded = trim(str_replace("\n", $this->LE, $encoded));
2365 
2366  return $encoded;
2367  }
2368 
2375  public function hasMultiBytes($str)
2376  {
2377  if (function_exists('mb_strlen')) {
2378  return (strlen($str) > mb_strlen($str, $this->CharSet));
2379  } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
2380  return false;
2381  }
2382  }
2383 
2389  public function has8bitChars($text)
2390  {
2391  return (boolean)preg_match('/[\x80-\xFF]/', $text);
2392  }
2393 
2404  public function base64EncodeWrapMB($str, $linebreak = null)
2405  {
2406  $start = '=?' . $this->CharSet . '?B?';
2407  $end = '?=';
2408  $encoded = '';
2409  if ($linebreak === null) {
2410  $linebreak = $this->LE;
2411  }
2412 
2413  $mb_length = mb_strlen($str, $this->CharSet);
2414  // Each line must have length <= 75, including $start and $end
2415  $length = 75 - strlen($start) - strlen($end);
2416  // Average multi-byte ratio
2417  $ratio = $mb_length / strlen($str);
2418  // Base64 has a 4:3 ratio
2419  $avgLength = floor($length * $ratio * .75);
2420 
2421  for ($i = 0; $i < $mb_length; $i += $offset) {
2422  $lookBack = 0;
2423  do {
2424  $offset = $avgLength - $lookBack;
2425  $chunk = mb_substr($str, $i, $offset, $this->CharSet);
2426  $chunk = base64_encode($chunk);
2427  $lookBack++;
2428  } while (strlen($chunk) > $length);
2429  $encoded .= $chunk . $linebreak;
2430  }
2431 
2432  // Chomp the last linefeed
2433  $encoded = substr($encoded, 0, -strlen($linebreak));
2434  return $encoded;
2435  }
2436 
2446  public function encodeQP($string, $line_max = 76)
2447  {
2448  if (function_exists('quoted_printable_encode')) { // Use native function if it's available (>= PHP5.3)
2449  return $this->fixEOL(quoted_printable_encode($string));
2450  }
2451  // Fall back to a pure PHP implementation
2452  $string = str_replace(
2453  array('%20', '%0D%0A.', '%0D%0A', '%'),
2454  array(' ', "\r\n=2E", "\r\n", '='),
2455  rawurlencode($string)
2456  );
2457  $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
2458  return $this->fixEOL($string);
2459  }
2460 
2471  public function encodeQPphp(
2472  $string,
2473  $line_max = 76, $space_conv = false
2475  ) {
2476  return $this->encodeQP($string, $line_max);
2477  }
2478 
2487  public function encodeQ($str, $position = 'text')
2488  {
2489  // There should not be any EOL in the string
2490  $pattern = '';
2491  $encoded = str_replace(array("\r", "\n"), '', $str);
2492  switch (strtolower($position)) {
2493  case 'phrase':
2494  // RFC 2047 section 5.3
2495  $pattern = '^A-Za-z0-9!*+\/ -';
2496  break;
2498  case 'comment':
2499  // RFC 2047 section 5.2
2500  $pattern = '\(\)"';
2501  // intentional fall-through
2502  // for this reason we build the $pattern without including delimiters and []
2503  case 'text':
2504  default:
2505  // RFC 2047 section 5.1
2506  // Replace every high ascii, control, =, ? and _ characters
2507  $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
2508  break;
2509  }
2510  $matches = array();
2511  if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
2512  // If the string contains an '=', make sure it's the first thing we replace
2513  // so as to avoid double-encoding
2514  $eqkey = array_search('=', $matches[0]);
2515  if ($eqkey !== false) {
2516  unset($matches[0][$eqkey]);
2517  array_unshift($matches[0], '=');
2518  }
2519  foreach (array_unique($matches[0]) as $char) {
2520  $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
2521  }
2522  }
2523  // Replace every spaces to _ (more readable than =20)
2524  return str_replace(' ', '_', $encoded);
2525  }
2526 
2527 
2539  public function addStringAttachment(
2540  $string,
2541  $filename,
2542  $encoding = 'base64',
2543  $type = '',
2544  $disposition = 'attachment'
2545  ) {
2546  // If a MIME type is not specified, try to work it out from the file name
2547  if ($type == '') {
2548  $type = self::filenameToType($filename);
2549  }
2550  // Append to $attachment array
2551  $this->attachment[] = array(
2552  0 => $string,
2553  1 => $filename,
2554  2 => basename($filename),
2555  3 => $encoding,
2556  4 => $type,
2557  5 => true, // isStringAttachment
2558  6 => $disposition,
2559  7 => 0
2560  );
2561  }
2562 
2579  public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
2580  {
2581  if (!@is_file($path)) {
2582  $this->setError($this->lang('file_access') . $path);
2583  return false;
2584  }
2585 
2586  // If a MIME type is not specified, try to work it out from the file name
2587  if ($type == '') {
2588  $type = self::filenameToType($path);
2589  }
2590 
2591  $filename = basename($path);
2592  if ($name == '') {
2593  $name = $filename;
2594  }
2595 
2596  // Append to $attachment array
2597  $this->attachment[] = array(
2598  0 => $path,
2599  1 => $filename,
2600  2 => $name,
2601  3 => $encoding,
2602  4 => $type,
2603  5 => false, // isStringAttachment
2604  6 => $disposition,
2605  7 => $cid
2606  );
2607  return true;
2608  }
2609 
2624  public function addStringEmbeddedImage(
2625  $string,
2626  $cid,
2627  $name = '',
2628  $encoding = 'base64',
2629  $type = '',
2630  $disposition = 'inline'
2631  ) {
2632  // If a MIME type is not specified, try to work it out from the name
2633  if ($type == '') {
2634  $type = self::filenameToType($name);
2635  }
2636 
2637  // Append to $attachment array
2638  $this->attachment[] = array(
2639  0 => $string,
2640  1 => $name,
2641  2 => $name,
2642  3 => $encoding,
2643  4 => $type,
2644  5 => true, // isStringAttachment
2645  6 => $disposition,
2646  7 => $cid
2647  );
2648  return true;
2649  }
2650 
2656  public function inlineImageExists()
2657  {
2658  foreach ($this->attachment as $attachment) {
2659  if ($attachment[6] == 'inline') {
2660  return true;
2661  }
2662  }
2663  return false;
2664  }
2665 
2670  public function attachmentExists()
2671  {
2672  foreach ($this->attachment as $attachment) {
2673  if ($attachment[6] == 'attachment') {
2674  return true;
2675  }
2676  }
2677  return false;
2678  }
2679 
2684  public function alternativeExists()
2685  {
2686  return !empty($this->AltBody);
2687  }
2688 
2693  public function clearAddresses()
2694  {
2695  foreach ($this->to as $to) {
2696  unset($this->all_recipients[strtolower($to[0])]);
2697  }
2698  $this->to = array();
2699  }
2700 
2705  public function clearCCs()
2706  {
2707  foreach ($this->cc as $cc) {
2708  unset($this->all_recipients[strtolower($cc[0])]);
2709  }
2710  $this->cc = array();
2711  }
2712 
2717  public function clearBCCs()
2718  {
2719  foreach ($this->bcc as $bcc) {
2720  unset($this->all_recipients[strtolower($bcc[0])]);
2721  }
2722  $this->bcc = array();
2723  }
2724 
2729  public function clearReplyTos()
2730  {
2731  $this->ReplyTo = array();
2732  }
2733 
2738  public function clearAllRecipients()
2739  {
2740  $this->to = array();
2741  $this->cc = array();
2742  $this->bcc = array();
2743  $this->all_recipients = array();
2744  }
2745 
2750  public function clearAttachments()
2751  {
2752  $this->attachment = array();
2753  }
2754 
2759  public function clearCustomHeaders()
2760  {
2761  $this->CustomHeader = array();
2762  }
2763 
2770  protected function setError($msg)
2771  {
2772  $this->error_count++;
2773  if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
2774  $lasterror = $this->smtp->getError();
2775  if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {
2776  $msg .= '<p>' . $this->lang('smtp_error') . $lasterror['smtp_msg'] . "</p>\n";
2777  }
2778  }
2779  $this->ErrorInfo = $msg;
2780  }
2781 
2788  public static function rfcDate()
2789  {
2790  // Set the time zone to whatever the default is to avoid 500 errors
2791  // Will default to UTC if it's not set properly in php.ini
2792  date_default_timezone_set(@date_default_timezone_get());
2793  return date('D, j M Y H:i:s O');
2794  }
2795 
2802  protected function serverHostname()
2803  {
2804  $result = 'localhost.localdomain';
2805  if (!empty($this->Hostname)) {
2806  $result = $this->Hostname;
2807  } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
2808  $result = $_SERVER['SERVER_NAME'];
2809  } elseif (function_exists('gethostname') && gethostname() !== false) {
2810  $result = gethostname();
2811  } elseif (php_uname('n') !== false) {
2812  $result = php_uname('n');
2813  }
2814  return $result;
2815  }
2816 
2823  protected function lang($key)
2824  {
2825  if (count($this->language) < 1) {
2826  $this->setLanguage('en'); // set the default language
2827  }
2828 
2829  if (isset($this->language[$key])) {
2830  return $this->language[$key];
2831  } else {
2832  return 'Language string failed to load: ' . $key;
2833  }
2834  }
2835 
2841  public function isError()
2842  {
2843  return ($this->error_count > 0);
2844  }
2845 
2853  public function fixEOL($str)
2854  {
2855  // Normalise to \n
2856  $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
2857  // Now convert LE as needed
2858  if ($this->LE !== "\n") {
2859  $nstr = str_replace("\n", $this->LE, $nstr);
2860  }
2861  return $nstr;
2862  }
2863 
2873  public function addCustomHeader($name, $value = null)
2874  {
2875  if ($value === null) {
2876  // Value passed in as name:value
2877  $this->CustomHeader[] = explode(':', $name, 2);
2878  } else {
2879  $this->CustomHeader[] = array($name, $value);
2880  }
2881  }
2882 
2894  public function msgHTML($message, $basedir = '', $advanced = false)
2895  {
2896  preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
2897  if (isset($images[2])) {
2898  foreach ($images[2] as $imgindex => $url) {
2899  // Convert data URIs into embedded images
2900  if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
2901  $data = substr($url, strpos($url, ','));
2902  if ($match[2]) {
2903  $data = base64_decode($data);
2904  } else {
2905  $data = rawurldecode($data);
2906  }
2907  $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
2908  if ($this->addStringEmbeddedImage($data, $cid, '', 'base64', $match[1])) {
2909  $message = preg_replace(
2910  '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
2911  $images[1][$imgindex] . '="cid:' . $cid . '"',
2912  $message
2913  );
2914  }
2915  } elseif (!preg_match('#^[A-z]+://#', $url)) {
2916  // Do not change urls for absolute images (thanks to corvuscorax)
2917  $filename = basename($url);
2918  $directory = dirname($url);
2919  if ($directory == '.') {
2920  $directory = '';
2921  }
2922  $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
2923  if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
2924  $basedir .= '/';
2925  }
2926  if (strlen($directory) > 1 && substr($directory, -1) != '/') {
2927  $directory .= '/';
2928  }
2929  if ($this->addEmbeddedImage(
2930  $basedir . $directory . $filename,
2931  $cid,
2932  $filename,
2933  'base64',
2934  self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION))
2935  )
2936  ) {
2937  $message = preg_replace(
2938  '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
2939  $images[1][$imgindex] . '="cid:' . $cid . '"',
2940  $message
2941  );
2942  }
2943  }
2944  }
2945  }
2946  $this->isHTML(true);
2947  // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
2948  $this->Body = $this->normalizeBreaks($message);
2949  $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
2950  if (empty($this->AltBody)) {
2951  $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
2952  self::CRLF . self::CRLF;
2953  }
2954  return $this->Body;
2955  }
2956 
2963  public function html2text($html, $advanced = false)
2964  {
2965  if ($advanced) {
2966  require_once 'extras/class.html2text.php';
2967  $htmlconverter = new html2text($html);
2968  return $htmlconverter->get_text();
2969  }
2970  return html_entity_decode(
2971  trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
2972  ENT_QUOTES,
2973  $this->CharSet
2974  );
2975  }
2976 
2984  public static function _mime_types($ext = '')
2985  {
2986  $mimes = array(
2987  'xl' => 'application/excel',
2988  'hqx' => 'application/mac-binhex40',
2989  'cpt' => 'application/mac-compactpro',
2990  'bin' => 'application/macbinary',
2991  'doc' => 'application/msword',
2992  'word' => 'application/msword',
2993  'class' => 'application/octet-stream',
2994  'dll' => 'application/octet-stream',
2995  'dms' => 'application/octet-stream',
2996  'exe' => 'application/octet-stream',
2997  'lha' => 'application/octet-stream',
2998  'lzh' => 'application/octet-stream',
2999  'psd' => 'application/octet-stream',
3000  'sea' => 'application/octet-stream',
3001  'so' => 'application/octet-stream',
3002  'oda' => 'application/oda',
3003  'pdf' => 'application/pdf',
3004  'ai' => 'application/postscript',
3005  'eps' => 'application/postscript',
3006  'ps' => 'application/postscript',
3007  'smi' => 'application/smil',
3008  'smil' => 'application/smil',
3009  'mif' => 'application/vnd.mif',
3010  'xls' => 'application/vnd.ms-excel',
3011  'ppt' => 'application/vnd.ms-powerpoint',
3012  'wbxml' => 'application/vnd.wap.wbxml',
3013  'wmlc' => 'application/vnd.wap.wmlc',
3014  'dcr' => 'application/x-director',
3015  'dir' => 'application/x-director',
3016  'dxr' => 'application/x-director',
3017  'dvi' => 'application/x-dvi',
3018  'gtar' => 'application/x-gtar',
3019  'php3' => 'application/x-httpd-php',
3020  'php4' => 'application/x-httpd-php',
3021  'php' => 'application/x-httpd-php',
3022  'phtml' => 'application/x-httpd-php',
3023  'phps' => 'application/x-httpd-php-source',
3024  'js' => 'application/x-javascript',
3025  'swf' => 'application/x-shockwave-flash',
3026  'sit' => 'application/x-stuffit',
3027  'tar' => 'application/x-tar',
3028  'tgz' => 'application/x-tar',
3029  'xht' => 'application/xhtml+xml',
3030  'xhtml' => 'application/xhtml+xml',
3031  'zip' => 'application/zip',
3032  'mid' => 'audio/midi',
3033  'midi' => 'audio/midi',
3034  'mp2' => 'audio/mpeg',
3035  'mp3' => 'audio/mpeg',
3036  'mpga' => 'audio/mpeg',
3037  'aif' => 'audio/x-aiff',
3038  'aifc' => 'audio/x-aiff',
3039  'aiff' => 'audio/x-aiff',
3040  'ram' => 'audio/x-pn-realaudio',
3041  'rm' => 'audio/x-pn-realaudio',
3042  'rpm' => 'audio/x-pn-realaudio-plugin',
3043  'ra' => 'audio/x-realaudio',
3044  'wav' => 'audio/x-wav',
3045  'bmp' => 'image/bmp',
3046  'gif' => 'image/gif',
3047  'jpeg' => 'image/jpeg',
3048  'jpe' => 'image/jpeg',
3049  'jpg' => 'image/jpeg',
3050  'png' => 'image/png',
3051  'tiff' => 'image/tiff',
3052  'tif' => 'image/tiff',
3053  'eml' => 'message/rfc822',
3054  'css' => 'text/css',
3055  'html' => 'text/html',
3056  'htm' => 'text/html',
3057  'shtml' => 'text/html',
3058  'log' => 'text/plain',
3059  'text' => 'text/plain',
3060  'txt' => 'text/plain',
3061  'rtx' => 'text/richtext',
3062  'rtf' => 'text/rtf',
3063  'vcf' => 'text/vcard',
3064  'vcard' => 'text/vcard',
3065  'xml' => 'text/xml',
3066  'xsl' => 'text/xml',
3067  'mpeg' => 'video/mpeg',
3068  'mpe' => 'video/mpeg',
3069  'mpg' => 'video/mpeg',
3070  'mov' => 'video/quicktime',
3071  'qt' => 'video/quicktime',
3072  'rv' => 'video/vnd.rn-realvideo',
3073  'avi' => 'video/x-msvideo',
3074  'movie' => 'video/x-sgi-movie'
3075  );
3076  return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)]: 'application/octet-stream');
3077  }
3078 
3086  public static function filenameToType($filename)
3087  {
3088  // In case the path is a URL, strip any query string before getting extension
3089  $qpos = strpos($filename, '?');
3090  if ($qpos !== false) {
3091  $filename = substr($filename, 0, $qpos);
3092  }
3093  $pathinfo = self::mb_pathinfo($filename);
3094  return self::_mime_types($pathinfo['extension']);
3095  }
3096 
3108  public static function mb_pathinfo($path, $options = null)
3109  {
3110  $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
3111  $pathinfo = array();
3112  if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
3113  if (array_key_exists(1, $pathinfo)) {
3114  $ret['dirname'] = $pathinfo[1];
3115  }
3116  if (array_key_exists(2, $pathinfo)) {
3117  $ret['basename'] = $pathinfo[2];
3118  }
3119  if (array_key_exists(5, $pathinfo)) {
3120  $ret['extension'] = $pathinfo[5];
3121  }
3122  if (array_key_exists(3, $pathinfo)) {
3123  $ret['filename'] = $pathinfo[3];
3124  }
3125  }
3126  switch ($options) {
3127  case PATHINFO_DIRNAME:
3128  case 'dirname':
3129  return $ret['dirname'];
3130  case PATHINFO_BASENAME:
3131  case 'basename':
3132  return $ret['basename'];
3133  case PATHINFO_EXTENSION:
3134  case 'extension':
3135  return $ret['extension'];
3136  case PATHINFO_FILENAME:
3137  case 'filename':
3138  return $ret['filename'];
3139  default:
3140  return $ret;
3141  }
3142  }
3143 
3158  public function set($name, $value = '')
3159  {
3160  try {
3161  if (isset($this->$name)) {
3162  $this->$name = $value;
3163  } else {
3164  throw new phpmailerException($this->lang('variable_set') . $name, self::STOP_CRITICAL);
3165  }
3166  } catch (Exception $exc) {
3167  $this->setError($exc->getMessage());
3168  if ($exc->getCode() == self::STOP_CRITICAL) {
3169  return false;
3170  }
3171  }
3172  return true;
3173  }
3174 
3181  public function secureHeader($str)
3182  {
3183  return trim(str_replace(array("\r", "\n"), '', $str));
3184  }
3185 
3196  public static function normalizeBreaks($text, $breaktype = "\r\n")
3197  {
3198  return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
3199  }
3200 
3201 
3209  public function sign($cert_filename, $key_filename, $key_pass)
3210  {
3211  $this->sign_cert_file = $cert_filename;
3212  $this->sign_key_file = $key_filename;
3213  $this->sign_key_pass = $key_pass;
3214  }
3215 
3222  public function DKIM_QP($txt)
3223  {
3224  $line = '';
3225  for ($i = 0; $i < strlen($txt); $i++) {
3226  $ord = ord($txt[$i]);
3227  if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
3228  $line .= $txt[$i];
3229  } else {
3230  $line .= '=' . sprintf('%02X', $ord);
3231  }
3232  }
3233  return $line;
3234  }
3235 
3243  public function DKIM_Sign($signHeader)
3244  {
3245  if (!defined('PKCS7_TEXT')) {
3246  if ($this->exceptions) {
3247  throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
3248  }
3249  return '';
3250  }
3251  $privKeyStr = file_get_contents($this->DKIM_private);
3252  if ($this->DKIM_passphrase != '') {
3253  $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
3254  } else {
3255  $privKey = $privKeyStr;
3256  }
3257  if (openssl_sign($signHeader, $signature, $privKey)) {
3258  return base64_encode($signature);
3259  }
3260  return '';
3261  }
3262 
3269  public function DKIM_HeaderC($signHeader)
3270  {
3271  $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
3272  $lines = explode("\r\n", $signHeader);
3273  foreach ($lines as $key => $line) {
3274  list($heading, $value) = explode(':', $line, 2);
3275  $heading = strtolower($heading);
3276  $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces
3277  $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
3278  }
3279  $signHeader = implode("\r\n", $lines);
3280  return $signHeader;
3281  }
3282 
3289  public function DKIM_BodyC($body)
3290  {
3291  if ($body == '') {
3292  return "\r\n";
3293  }
3294  // stabilize line endings
3295  $body = str_replace("\r\n", "\n", $body);
3296  $body = str_replace("\n", "\r\n", $body);
3297  // END stabilize line endings
3298  while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
3299  $body = substr($body, 0, strlen($body) - 2);
3300  }
3301  return $body;
3302  }
3303 
3312  public function DKIM_Add($headers_line, $subject, $body)
3313  {
3314  $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms
3315  $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
3316  $DKIMquery = 'dns/txt'; // Query method
3317  $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
3318  $subject_header = "Subject: $subject";
3319  $headers = explode($this->LE, $headers_line);
3320  $from_header = '';
3321  $to_header = '';
3322  $current = '';
3323  foreach ($headers as $header) {
3324  if (strpos($header, 'From:') === 0) {
3325  $from_header = $header;
3326  $current = 'from_header';
3327  } elseif (strpos($header, 'To:') === 0) {
3328  $to_header = $header;
3329  $current = 'to_header';
3330  } else {
3331  if ($current && strpos($header, ' =?') === 0) {
3332  $current .= $header;
3333  } else {
3334  $current = '';
3335  }
3336  }
3337  }
3338  $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
3339  $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
3340  $subject = str_replace(
3341  '|',
3342  '=7C',
3343  $this->DKIM_QP($subject_header)
3344  ); // Copied header fields (dkim-quoted-printable)
3345  $body = $this->DKIM_BodyC($body);
3346  $DKIMlen = strlen($body); // Length of body
3347  $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body
3348  $ident = ($this->DKIM_identity == '') ? '' : ' i=' . $this->DKIM_identity . ';';
3349  $dkimhdrs = 'DKIM-Signature: v=1; a=' .
3350  $DKIMsignatureType . '; q=' .
3351  $DKIMquery . '; l=' .
3352  $DKIMlen . '; s=' .
3353  $this->DKIM_selector .
3354  ";\r\n" .
3355  "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
3356  "\th=From:To:Subject;\r\n" .
3357  "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
3358  "\tz=$from\r\n" .
3359  "\t|$to\r\n" .
3360  "\t|$subject;\r\n" .
3361  "\tbh=" . $DKIMb64 . ";\r\n" .
3362  "\tb=";
3363  $toSign = $this->DKIM_HeaderC(
3364  $from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs
3365  );
3366  $signed = $this->DKIM_Sign($toSign);
3367  return $dkimhdrs . $signed . "\r\n";
3368  }
3369 
3375  public function getToAddresses()
3376  {
3377  return $this->to;
3378  }
3379 
3385  public function getCcAddresses()
3386  {
3387  return $this->cc;
3388  }
3389 
3395  public function getBccAddresses()
3396  {
3397  return $this->bcc;
3398  }
3399 
3405  public function getReplyToAddresses()
3406  {
3407  return $this->ReplyTo;
3408  }
3409 
3415  public function getAllRecipientAddresses()
3416  {
3417  return $this->all_recipients;
3418  }
3419 
3430  protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
3431  {
3432  if (!empty($this->action_function) && is_callable($this->action_function)) {
3433  $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
3434  call_user_func_array($this->action_function, $params);
3435  }
3436  }
3437 }
3438 
3443 class phpmailerException extends Exception
3444 {
3449  public function errorMessage()
3450  {
3451  $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
3452  return $errorMsg;
3453  }
3454 }




Korrekturen, Hinweise und Ergänzungen

Bitte scheuen Sie sich nicht und melden Sie, was auf dieser Seite sachlich falsch oder irreführend ist, was ergänzt werden sollte, was fehlt usw. Dazu bitte oben aus dem Menü Seite den Eintrag Support Forum wählen. Es ist eine kostenlose Anmeldung erforderlich, um Anmerkungen zu posten. Unpassende Postings, Spam usw. werden kommentarlos entfernt.