3 * PHPMailer - PHP email creation and transport class.
7 * @link https://github.com/PHPMailer/PHPMailer/
8 * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk>
9 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
10 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
11 * @author Brent R. Matzelle (original founder)
12 * @copyright 2013 Marcus Bointon
13 * @copyright 2010 - 2012 Jim Jagielski
14 * @copyright 2004 - 2009 Andy Prevost
15 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
16 * @note This program is distributed in the hope that it will be useful - WITHOUT
17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE.
21 if (version_compare(PHP_VERSION, '5.0.0', '<')) {
22 exit("Sorry, PHPMailer will only run on PHP version 5 or greater!\n");
26 * PHPMailer - PHP email creation and transport class.
29 * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk>
30 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
31 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
32 * @author Brent R. Matzelle (original founder)
33 * @copyright 2013 Marcus Bointon
34 * @copyright 2010 - 2012 Jim Jagielski
35 * @copyright 2004 - 2009 Andy Prevost
40 * The PHPMailer Version number.
43 public $Version = '5.2.7';
47 * Options: 1 = High, 3 = Normal, 5 = low.
53 * The character set of the message.
56 public $CharSet = 'iso-8859-1';
59 * The MIME Content-type of the message.
62 public $ContentType = 'text/plain';
65 * The message encoding.
66 * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
69 public $Encoding = '8bit';
72 * Holds the most recent mailer error message.
75 public $ErrorInfo = '';
78 * The From email address for the message.
81 public $From = 'root@localhost';
84 * The From name of the message.
87 public $FromName = 'Root User';
90 * The Sender email (Return-Path) of the message.
91 * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
97 * The Return-Path of the message.
98 * If empty, it will be set to either From or Sender.
101 public $ReturnPath = '';
104 * The Subject of the message.
107 public $Subject = '';
110 * An HTML or plain text message body.
111 * If HTML then call isHTML(true).
117 * The plain-text message body.
118 * This body can be read by mail clients that do not have HTML email
119 * capability such as mutt & Eudora.
120 * Clients that can read HTML will view the normal Body.
123 public $AltBody = '';
126 * An iCal message part body.
127 * Only supported in simple alt or alt_inline message types
128 * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
129 * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
130 * @link http://kigkonsult.se/iCalcreator/
136 * The complete compiled MIME message body.
140 protected $MIMEBody = '';
143 * The complete compiled MIME message headers.
147 protected $MIMEHeader = '';
150 * Extra headers that createHeader() doesn't fold in.
154 protected $mailHeader = '';
157 * Word-wrap the message body to this number of chars.
160 public $WordWrap = 0;
163 * Which method to use to send mail.
164 * Options: "mail", "sendmail", or "smtp".
167 public $Mailer = 'mail';
170 * The path to the sendmail program.
173 public $Sendmail = '/usr/sbin/sendmail';
176 * Whether mail() uses a fully sendmail-compatible MTA.
177 * One which supports sendmail's "-oi -f" options.
180 public $UseSendmailOptions = true;
183 * Path to PHPMailer plugins.
184 * Useful if the SMTP class is not in the PHP include path.
186 * @deprecated Should not be needed now there is an autoloader.
188 public $PluginDir = '';
191 * The email address that a reading confirmation should be sent to.
194 public $ConfirmReadingTo = '';
197 * The hostname to use in Message-Id and Received headers
198 * and as default HELO string.
199 * If empty, the value returned
200 * by SERVER_NAME is used or 'localhost.localdomain'.
203 public $Hostname = '';
206 * An ID to be used in the Message-Id header.
207 * If empty, a unique id will be generated.
210 public $MessageID = '';
213 * The message Date to be used in the Date header.
214 * If empty, the current date will be added.
217 public $MessageDate = '';
221 * Either a single hostname or multiple semicolon-delimited hostnames.
222 * You can also specify a different port
223 * for each host by using this format: [hostname:port]
224 * (e.g. "smtp1.example.com:25;smtp2.example.com").
225 * Hosts will be tried in order.
228 public $Host = 'localhost';
231 * The default SMTP server port.
233 * @Todo Why is this needed when the SMTP class takes care of it?
238 * The SMTP HELO of the message.
239 * Default is $Hostname.
241 * @see PHPMailer::$Hostname
246 * The secure connection prefix.
247 * Options: "", "ssl" or "tls"
250 public $SMTPSecure = '';
253 * Whether to use SMTP authentication.
254 * Uses the Username and Password properties.
256 * @see PHPMailer::$Username
257 * @see PHPMailer::$Password
259 public $SMTPAuth = false;
265 public $Username = '';
271 public $Password = '';
275 * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
278 public $AuthType = '';
292 public $Workstation = '';
295 * The SMTP server timeout in seconds.
298 public $Timeout = 10;
301 * SMTP class debug output mode.
302 * Options: 0 = off, 1 = commands, 2 = commands and data
304 * @see SMTP::$do_debug
306 public $SMTPDebug = 0;
309 * The function/method to use for debugging output.
310 * Options: "echo" or "error_log"
312 * @see SMTP::$Debugoutput
314 public $Debugoutput = "echo";
317 * Whether to keep SMTP connection open after each message.
318 * If this is set to true then to close the connection
319 * requires an explicit call to smtpClose().
322 public $SMTPKeepAlive = false;
325 * Whether to split multiple to addresses into multiple messages
326 * or send them all in one message.
329 public $SingleTo = false;
332 * Storage for addresses when SingleTo is enabled.
334 * @todo This should really not be public
336 public $SingleToArray = array();
339 * Whether to generate VERP addresses on send.
340 * Only applicable when sending via SMTP.
341 * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
344 public $do_verp = false;
347 * Whether to allow sending messages with an empty body.
350 public $AllowEmpty = false;
353 * The default line ending.
354 * @note The default remains "\n". We force CRLF where we know
355 * it must be used via self::CRLF.
364 public $DKIM_selector = '';
368 * Usually the email address used as the source of the email
371 public $DKIM_identity = '';
375 * Used if your key is encrypted.
378 public $DKIM_passphrase = '';
381 * DKIM signing domain name.
382 * @example 'example.com'
385 public $DKIM_domain = '';
388 * DKIM private key file path.
391 public $DKIM_private = '';
394 * Callback Action function name.
396 * The function that handles the result of the send email action.
397 * It is called out by send() for each email sent.
400 * - 'function_name' for function names
401 * - 'Class::Method' for static method calls
402 * - array($object, 'Method') for calling methods on $object
403 * See http://php.net/is_callable manual page for more details.
406 * bool $result result of the send action
407 * string $to email address of the recipient
408 * string $cc cc email addresses
409 * string $bcc bcc email addresses
410 * string $subject the subject
411 * string $body the email body
412 * string $from email address of sender
416 public $action_function = '';
419 * What to use in the X-Mailer header.
420 * Options: null for default, whitespace for none, or a string to use
423 public $XMailer = '';
426 * An instance of the SMTP sender class.
430 protected $smtp = null;
433 * The array of 'to' addresses.
437 protected $to = array();
440 * The array of 'cc' addresses.
444 protected $cc = array();
447 * The array of 'bcc' addresses.
451 protected $bcc = array();
454 * The array of reply-to names and addresses.
458 protected $ReplyTo = array();
461 * An array of all kinds of addresses.
462 * Includes all of $to, $cc, $bcc, $replyto
466 protected $all_recipients = array();
469 * The array of attachments.
473 protected $attachment = array();
476 * The array of custom headers.
480 protected $CustomHeader = array();
483 * The most recent Message-ID (including angular brackets).
487 protected $lastMessageID = '';
490 * The message's MIME type.
494 protected $message_type = '';
497 * The array of MIME boundary strings.
501 protected $boundary = array();
504 * The array of available languages.
508 protected $language = array();
511 * The number of errors encountered.
515 protected $error_count = 0;
518 * The S/MIME certificate file path.
522 protected $sign_cert_file = '';
525 * The S/MIME key file path.
529 protected $sign_key_file = '';
532 * The S/MIME password for the key.
533 * Used only if the key is encrypted.
537 protected $sign_key_pass = '';
540 * Whether to throw exceptions for errors.
544 protected $exceptions = false;
547 * Error severity: message only, continue processing
549 const STOP_MESSAGE = 0;
552 * Error severity: message, likely ok to continue processing
554 const STOP_CONTINUE = 1;
557 * Error severity: message, plus full stop, critical error reached
559 const STOP_CRITICAL = 2;
562 * SMTP RFC standard line ending
568 * @param bool $exceptions Should we throw external exceptions?
570 public function __construct($exceptions = false)
572 $this->exceptions = ($exceptions == true);
578 public function __destruct()
580 if ($this->Mailer == 'smtp') { //close any open SMTP connection nicely
586 * Call mail() in a safe_mode-aware fashion.
587 * Also, unless sendmail_path points to sendmail (or something that
588 * claims to be sendmail), don't pass params (not a perfect fix,
590 * @param string $to To
591 * @param string $subject Subject
592 * @param string $body Message Body
593 * @param string $header Additional Header(s)
594 * @param string $params Params
598 private function mailPassthru($to, $subject, $body, $header, $params)
600 if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
601 $rt = @mail($to, $this->encodeHeader($this->secureHeader($subject)), $body, $header);
603 $rt = @mail($to, $this->encodeHeader($this->secureHeader($subject)), $body, $header, $params);
609 * Output debugging info via user-defined method.
610 * Only if debug output is enabled.
611 * @see PHPMailer::$Debugoutput
612 * @see PHPMailer::$SMTPDebug
615 protected function edebug($str)
617 if (!$this->SMTPDebug) {
620 switch ($this->Debugoutput) {
625 //Cleans up output a bit for a better looking display that's HTML-safe
626 echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, $this->CharSet) . "<br>\n";
630 //Just echoes exactly what was received
636 * Sets message type to HTML or plain.
637 * @param bool $ishtml True for HTML mode.
640 public function isHTML($ishtml = true)
643 $this->ContentType = 'text/html';
645 $this->ContentType = 'text/plain';
650 * Send messages using SMTP.
653 public function isSMTP()
655 $this->Mailer = 'smtp';
659 * Send messages using PHP's mail() function.
662 public function isMail()
664 $this->Mailer = 'mail';
668 * Send messages using $Sendmail.
671 public function isSendmail()
673 if (!stristr(ini_get('sendmail_path'), 'sendmail')) {
674 $this->Sendmail = '/var/qmail/bin/sendmail';
676 $this->Mailer = 'sendmail';
680 * Send messages using qmail.
683 public function isQmail()
685 if (stristr(ini_get('sendmail_path'), 'qmail')) {
686 $this->Sendmail = '/var/qmail/bin/sendmail';
688 $this->Mailer = 'sendmail';
692 * Add a "To" address.
693 * @param string $address
694 * @param string $name
695 * @return bool true on success, false if address already used
697 public function addAddress($address, $name = '')
699 return $this->addAnAddress('to', $address, $name);
703 * Add a "CC" address.
704 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
705 * @param string $address
706 * @param string $name
707 * @return bool true on success, false if address already used
709 public function addCC($address, $name = '')
711 return $this->addAnAddress('cc', $address, $name);
715 * Add a "BCC" address.
716 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
717 * @param string $address
718 * @param string $name
719 * @return bool true on success, false if address already used
721 public function addBCC($address, $name = '')
723 return $this->addAnAddress('bcc', $address, $name);
727 * Add a "Reply-to" address.
728 * @param string $address
729 * @param string $name
732 public function addReplyTo($address, $name = '')
734 return $this->addAnAddress('Reply-To', $address, $name);
738 * Add an address to one of the recipient arrays.
739 * Addresses that have been added already return false, but do not throw exceptions
740 * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo'
741 * @param string $address The email address to send to
742 * @param string $name
743 * @throws phpmailerException
744 * @return bool true on success, false if address already used or invalid in some way
747 protected function addAnAddress($kind, $address, $name = '')
749 if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) {
750 $this->setError($this->lang('Invalid recipient array') . ': ' . $kind);
751 if ($this->exceptions) {
752 throw new phpmailerException('Invalid recipient array: ' . $kind);
754 $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);
757 $address = trim($address);
758 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
759 if (!$this->validateAddress($address)) {
760 $this->setError($this->lang('invalid_address') . ': ' . $address);
761 if ($this->exceptions) {
762 throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
764 $this->edebug($this->lang('invalid_address') . ': ' . $address);
767 if ($kind != 'Reply-To') {
768 if (!isset($this->all_recipients[strtolower($address)])) {
769 array_push($this->$kind, array($address, $name));
770 $this->all_recipients[strtolower($address)] = true;
774 if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
775 $this->ReplyTo[strtolower($address)] = array($address, $name);
783 * Set the From and FromName properties.
784 * @param string $address
785 * @param string $name
786 * @param bool $auto Whether to also set the Sender address, defaults to true
787 * @throws phpmailerException
790 public function setFrom($address, $name = '', $auto = true)
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 if ($this->exceptions) {
797 throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
799 $this->edebug($this->lang('invalid_address') . ': ' . $address);
802 $this->From = $address;
803 $this->FromName = $name;
805 if (empty($this->Sender)) {
806 $this->Sender = $address;
813 * Return the Message-ID header of the last email.
814 * Technically this is the value from the last time the headers were created,
815 * but it's also the message ID of the last sent message except in
816 * pathological cases.
819 public function getLastMessageID()
821 return $this->lastMessageID;
825 * Check that a string looks like an email address.
826 * @param string $address The email address to check
827 * @param string $patternselect A selector for the validation pattern to use :
828 * 'auto' - pick best one automatically;
829 * 'pcre8' - use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
830 * 'pcre' - use old PCRE implementation;
831 * 'php' - use PHP built-in FILTER_VALIDATE_EMAIL; faster, less thorough;
832 * 'noregex' - super fast, really dumb.
837 public static function validateAddress($address, $patternselect = 'auto')
839 if ($patternselect == 'auto') {
843 ) { //Check this instead of extension_loaded so it works when that function is disabled
844 if (version_compare(PCRE_VERSION, '8.0') >= 0) {
845 $patternselect = 'pcre8';
847 $patternselect = 'pcre';
850 //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
851 if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
852 $patternselect = 'php';
854 $patternselect = 'noregex';
858 switch ($patternselect) {
861 * Conforms to RFC5322: Uses *correct* regex on which FILTER_VALIDATE_EMAIL is
862 * based; So why not use FILTER_VALIDATE_EMAIL? Because it was broken to
863 * not allow a@b type valid addresses :(
864 * @link http://squiloople.com/2009/12/20/email-address-validation/
865 * @copyright 2009-2010 Michael Rushton
866 * Feel free to use and redistribute this code. But please keep this copyright notice.
868 return (bool)preg_match(
869 '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
870 '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
871 '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
872 '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
873 '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
874 '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
875 '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
876 '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
877 '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
882 //An older regex that doesn't need a recent PCRE
883 return (bool)preg_match(
884 '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
885 '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
886 '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
887 '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
888 '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
889 '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
890 '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
891 '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
892 '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
893 '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
899 return (bool)filter_var($address, FILTER_VALIDATE_EMAIL);
902 //No PCRE! Do something _very_ approximate!
903 //Check the address is 3 chars or longer and contains an @ that's not the first or last char
904 return (strlen($address) >= 3
905 and strpos($address, '@') >= 1
906 and strpos($address, '@') != strlen($address) - 1);
912 * Create a message and send it.
913 * Uses the sending method specified by $Mailer.
914 * Returns false on error - Use the ErrorInfo variable to view description of the error.
915 * @throws phpmailerException
918 public function send()
921 if (!$this->preSend()) {
924 return $this->postSend();
925 } catch (phpmailerException $e) {
926 $this->mailHeader = '';
927 $this->setError($e->getMessage());
928 if ($this->exceptions) {
936 * Prepare a message for sending.
937 * @throws phpmailerException
940 public function preSend()
943 $this->mailHeader = "";
944 if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
945 throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
948 // Set whether the message is multipart/alternative
949 if (!empty($this->AltBody)) {
950 $this->ContentType = 'multipart/alternative';
953 $this->error_count = 0; // reset errors
954 $this->setMessageType();
955 // Refuse to send an empty message unless we are specifically allowing it
956 if (!$this->AllowEmpty and empty($this->Body)) {
957 throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
960 $this->MIMEHeader = $this->createHeader();
961 $this->MIMEBody = $this->createBody();
963 // To capture the complete message when using mail(), create
964 // an extra header list which createHeader() doesn't fold in
965 if ($this->Mailer == 'mail') {
966 if (count($this->to) > 0) {
967 $this->mailHeader .= $this->addrAppend("To", $this->to);
969 $this->mailHeader .= $this->headerLine("To", "undisclosed-recipients:;");
971 $this->mailHeader .= $this->headerLine(
973 $this->encodeHeader($this->secureHeader(trim($this->Subject)))
977 // Sign with DKIM if enabled
978 if (!empty($this->DKIM_domain)
979 && !empty($this->DKIM_private)
980 && !empty($this->DKIM_selector)
981 && !empty($this->DKIM_domain)
982 && file_exists($this->DKIM_private)) {
983 $header_dkim = $this->DKIM_Add(
984 $this->MIMEHeader . $this->mailHeader,
985 $this->encodeHeader($this->secureHeader($this->Subject)),
988 $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
989 str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
993 } catch (phpmailerException $e) {
994 $this->setError($e->getMessage());
995 if ($this->exceptions) {
1003 * Actually send a message.
1004 * Send the email via the selected mechanism
1005 * @throws phpmailerException
1008 public function postSend()
1011 // Choose the mailer and send through it
1012 switch ($this->Mailer) {
1014 return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1016 return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1018 return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1020 return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1022 } catch (phpmailerException $e) {
1023 $this->setError($e->getMessage());
1024 if ($this->exceptions) {
1027 $this->edebug($e->getMessage() . "\n");
1033 * Send mail using the $Sendmail program.
1034 * @param string $header The message headers
1035 * @param string $body The message body
1036 * @see PHPMailer::$Sendmail
1037 * @throws phpmailerException
1041 protected function sendmailSend($header, $body)
1043 if ($this->Sender != '') {
1044 $sendmail = sprintf("%s -oi -f%s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1046 $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail));
1048 if ($this->SingleTo === true) {
1049 foreach ($this->SingleToArray as $val) {
1050 if (!@$mail = popen($sendmail, 'w')) {
1051 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1053 fputs($mail, "To: " . $val . "\n");
1054 fputs($mail, $header);
1055 fputs($mail, $body);
1056 $result = pclose($mail);
1057 // implement call back function if it exists
1058 $isSent = ($result == 0) ? 1 : 0;
1059 $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1061 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1065 if (!@$mail = popen($sendmail, 'w')) {
1066 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1068 fputs($mail, $header);
1069 fputs($mail, $body);
1070 $result = pclose($mail);
1071 // implement call back function if it exists
1072 $isSent = ($result == 0) ? 1 : 0;
1073 $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1075 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1082 * Send mail using the PHP mail() function.
1083 * @param string $header The message headers
1084 * @param string $body The message body
1085 * @link http://www.php.net/manual/en/book.mail.php
1086 * @throws phpmailerException
1090 protected function mailSend($header, $body)
1093 foreach ($this->to as $t) {
1094 $toArr[] = $this->addrFormat($t);
1096 $to = implode(', ', $toArr);
1098 if (empty($this->Sender)) {
1101 $params = sprintf("-f%s", $this->Sender);
1103 if ($this->Sender != '' and !ini_get('safe_mode')) {
1104 $old_from = ini_get('sendmail_from');
1105 ini_set('sendmail_from', $this->Sender);
1108 if ($this->SingleTo === true && count($toArr) > 1) {
1109 foreach ($toArr as $val) {
1110 $rt = $this->mailPassthru($val, $this->Subject, $body, $header, $params);
1111 // implement call back function if it exists
1112 $isSent = ($rt == 1) ? 1 : 0;
1113 $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1116 $rt = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1117 // implement call back function if it exists
1118 $isSent = ($rt == 1) ? 1 : 0;
1119 $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1121 if (isset($old_from)) {
1122 ini_set('sendmail_from', $old_from);
1125 throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1131 * Get an instance to use for SMTP operations.
1132 * Override this function to load your own SMTP implementation
1135 public function getSMTPInstance()
1137 if (!is_object($this->smtp)) {
1138 require_once 'class-smtp.php';
1139 $this->smtp = new SMTP;
1145 * Send mail via SMTP.
1146 * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
1147 * Uses the PHPMailerSMTP class by default.
1148 * @see PHPMailer::getSMTPInstance() to use a different class.
1149 * @param string $header The message headers
1150 * @param string $body The message body
1151 * @throws phpmailerException
1156 protected function smtpSend($header, $body)
1158 $bad_rcpt = array();
1160 if (!$this->smtpConnect()) {
1161 throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1163 $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
1164 if (!$this->smtp->mail($smtp_from)) {
1165 $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1166 throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1169 // Attempt to send attach all recipients
1170 foreach ($this->to as $to) {
1171 if (!$this->smtp->recipient($to[0])) {
1172 $bad_rcpt[] = $to[0];
1177 $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body, $this->From);
1179 foreach ($this->cc as $cc) {
1180 if (!$this->smtp->recipient($cc[0])) {
1181 $bad_rcpt[] = $cc[0];
1186 $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body, $this->From);
1188 foreach ($this->bcc as $bcc) {
1189 if (!$this->smtp->recipient($bcc[0])) {
1190 $bad_rcpt[] = $bcc[0];
1195 $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body, $this->From);
1198 if (count($bad_rcpt) > 0) { //Create error message for any bad addresses
1199 throw new phpmailerException($this->lang('recipients_failed') . implode(', ', $bad_rcpt));
1201 if (!$this->smtp->data($header . $body)) {
1202 throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1204 if ($this->SMTPKeepAlive == true) {
1205 $this->smtp->reset();
1207 $this->smtp->quit();
1208 $this->smtp->close();
1214 * Initiate a connection to an SMTP server.
1215 * Returns false if the operation failed.
1216 * @param array $options An array of options compatible with stream_context_create()
1219 * @throws phpmailerException
1222 public function smtpConnect($options = array())
1224 if (is_null($this->smtp)) {
1225 $this->smtp = $this->getSMTPInstance();
1228 //Already connected?
1229 if ($this->smtp->connected()) {
1233 $this->smtp->setTimeout($this->Timeout);
1234 $this->smtp->setDebugLevel($this->SMTPDebug);
1235 $this->smtp->setDebugOutput($this->Debugoutput);
1236 $this->smtp->setVerp($this->do_verp);
1237 $tls = ($this->SMTPSecure == 'tls');
1238 $ssl = ($this->SMTPSecure == 'ssl');
1239 $hosts = explode(';', $this->Host);
1240 $lastexception = null;
1242 foreach ($hosts as $hostentry) {
1243 $hostinfo = array();
1245 $port = $this->Port;
1247 '/^(.+):([0-9]+)$/',
1251 ) { //If $hostentry contains 'address:port', override default
1252 $host = $hostinfo[1];
1253 $port = $hostinfo[2];
1255 if ($this->smtp->connect(($ssl ? 'ssl://' : '') . $host, $port, $this->Timeout, $options)) {
1258 $hello = $this->Helo;
1260 $hello = $this->serverHostname();
1262 $this->smtp->hello($hello);
1265 if (!$this->smtp->startTLS()) {
1266 throw new phpmailerException($this->lang('connect_host'));
1268 //We must resend HELO after tls negotiation
1269 $this->smtp->hello($hello);
1271 if ($this->SMTPAuth) {
1272 if (!$this->smtp->authenticate(
1280 throw new phpmailerException($this->lang('authenticate'));
1284 } catch (phpmailerException $e) {
1285 $lastexception = $e;
1286 //We must have connected, but then failed TLS or Auth, so close connection nicely
1287 $this->smtp->quit();
1291 //If we get here, all connection attempts have failed, so close connection hard
1292 $this->smtp->close();
1293 //As we've caught all exceptions, just report whatever the last one was
1294 if ($this->exceptions and !is_null($lastexception)) {
1295 throw $lastexception;
1301 * Close the active SMTP session if one exists.
1304 public function smtpClose()
1306 if ($this->smtp !== null) {
1307 if ($this->smtp->connected()) {
1308 $this->smtp->quit();
1309 $this->smtp->close();
1315 * Set the language for error messages.
1316 * Returns false if it cannot load the language file.
1317 * The default language is English.
1318 * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
1319 * @param string $lang_path Path to the language file directory, with trailing separator (slash)
1323 public function setLanguage($langcode = 'en', $lang_path = 'language/')
1325 //Define full set of translatable strings
1326 $PHPMAILER_LANG = array(
1327 'authenticate' => 'SMTP Error: Could not authenticate.',
1328 'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1329 'data_not_accepted' => 'SMTP Error: data not accepted.',
1330 'empty_message' => 'Message body empty',
1331 'encoding' => 'Unknown encoding: ',
1332 'execute' => 'Could not execute: ',
1333 'file_access' => 'Could not access file: ',
1334 'file_open' => 'File Error: Could not open file: ',
1335 'from_failed' => 'The following From address failed: ',
1336 'instantiate' => 'Could not instantiate mail function.',
1337 'invalid_address' => 'Invalid address',
1338 'mailer_not_supported' => ' mailer is not supported.',
1339 'provide_address' => 'You must provide at least one recipient email address.',
1340 'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1341 'signing' => 'Signing Error: ',
1342 'smtp_connect_failed' => 'SMTP connect() failed.',
1343 'smtp_error' => 'SMTP server error: ',
1344 'variable_set' => 'Cannot set or reset variable: '
1346 //Overwrite language-specific strings.
1347 //This way we'll never have missing translations - no more "language string failed to load"!
1349 if ($langcode != 'en') { //There is no English translation file
1350 $l = @include $lang_path . 'phpmailer.lang-' . $langcode . '.php';
1352 $this->language = $PHPMAILER_LANG;
1353 return ($l == true); //Returns false if language not found
1357 * Get the array of strings for the current language.
1360 public function getTranslations()
1362 return $this->language;
1366 * Create recipient headers.
1368 * @param string $type
1369 * @param array $addr An array of recipient,
1370 * where each recipient is a 2-element indexed array with element 0 containing an address
1371 * and element 1 containing a name, like:
1372 * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
1375 public function addrAppend($type, $addr)
1377 $addresses = array();
1378 foreach ($addr as $a) {
1379 $addresses[] = $this->addrFormat($a);
1381 return $type . ': ' . implode(', ', $addresses) . $this->LE;
1385 * Format an address for use in a message header.
1387 * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
1388 * like array('joe@example.com', 'Joe User')
1391 public function addrFormat($addr)
1393 if (empty($addr[1])) { // No name provided
1394 return $this->secureHeader($addr[0]);
1396 return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . " <" . $this->secureHeader(
1403 * Word-wrap message.
1404 * For use with mailers that do not automatically perform wrapping
1405 * and for quoted-printable encoded messages.
1406 * Original written by philippe.
1407 * @param string $message The message to wrap
1408 * @param integer $length The line length to wrap to
1409 * @param bool $qp_mode Whether to run in Quoted-Printable mode
1413 public function wrapText($message, $length, $qp_mode = false)
1415 $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
1416 // If utf-8 encoding is used, we will need to make sure we don't
1417 // split multibyte characters when we wrap
1418 $is_utf8 = (strtolower($this->CharSet) == "utf-8");
1419 $lelen = strlen($this->LE);
1420 $crlflen = strlen(self::CRLF);
1422 $message = $this->fixEOL($message);
1423 if (substr($message, -$lelen) == $this->LE) {
1424 $message = substr($message, 0, -$lelen);
1427 $line = explode($this->LE, $message); // Magic. We know fixEOL uses $LE
1429 for ($i = 0; $i < count($line); $i++) {
1430 $line_part = explode(' ', $line[$i]);
1432 for ($e = 0; $e < count($line_part); $e++) {
1433 $word = $line_part[$e];
1434 if ($qp_mode and (strlen($word) > $length)) {
1435 $space_left = $length - strlen($buf) - $crlflen;
1437 if ($space_left > 20) {
1440 $len = $this->utf8CharBoundary($word, $len);
1441 } elseif (substr($word, $len - 1, 1) == "=") {
1443 } elseif (substr($word, $len - 2, 1) == "=") {
1446 $part = substr($word, 0, $len);
1447 $word = substr($word, $len);
1448 $buf .= ' ' . $part;
1449 $message .= $buf . sprintf("=%s", self::CRLF);
1451 $message .= $buf . $soft_break;
1455 while (strlen($word) > 0) {
1461 $len = $this->utf8CharBoundary($word, $len);
1462 } elseif (substr($word, $len - 1, 1) == "=") {
1464 } elseif (substr($word, $len - 2, 1) == "=") {
1467 $part = substr($word, 0, $len);
1468 $word = substr($word, $len);
1470 if (strlen($word) > 0) {
1471 $message .= $part . sprintf("=%s", self::CRLF);
1478 $buf .= ($e == 0) ? $word : (' ' . $word);
1480 if (strlen($buf) > $length and $buf_o != '') {
1481 $message .= $buf_o . $soft_break;
1486 $message .= $buf . self::CRLF;
1493 * Find the last character boundary prior to $maxLength in a utf-8
1494 * quoted (printable) encoded string.
1495 * Original written by Colin Brown.
1497 * @param string $encodedText utf-8 QP text
1498 * @param int $maxLength find last character boundary prior to this length
1501 public function utf8CharBoundary($encodedText, $maxLength)
1503 $foundSplitPos = false;
1505 while (!$foundSplitPos) {
1506 $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1507 $encodedCharPos = strpos($lastChunk, "=");
1508 if ($encodedCharPos !== false) {
1509 // Found start of encoded character byte within $lookBack block.
1510 // Check the encoded byte value (the 2 chars after the '=')
1511 $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1512 $dec = hexdec($hex);
1513 if ($dec < 128) { // Single byte character.
1514 // If the encoded char was found at pos 0, it will fit
1515 // otherwise reduce maxLength to start of the encoded char
1516 $maxLength = ($encodedCharPos == 0) ? $maxLength :
1517 $maxLength - ($lookBack - $encodedCharPos);
1518 $foundSplitPos = true;
1519 } elseif ($dec >= 192) { // First byte of a multi byte character
1520 // Reduce maxLength to split at start of character
1521 $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1522 $foundSplitPos = true;
1523 } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
1527 // No encoded character found
1528 $foundSplitPos = true;
1536 * Set the body wrapping.
1540 public function setWordWrap()
1542 if ($this->WordWrap < 1) {
1546 switch ($this->message_type) {
1550 case 'alt_inline_attach':
1551 $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
1554 $this->Body = $this->wrapText($this->Body, $this->WordWrap);
1560 * Assemble message headers.
1562 * @return string The assembled headers
1564 public function createHeader()
1568 // Set the boundaries
1569 $uniq_id = md5(uniqid(time()));
1570 $this->boundary[1] = 'b1_' . $uniq_id;
1571 $this->boundary[2] = 'b2_' . $uniq_id;
1572 $this->boundary[3] = 'b3_' . $uniq_id;
1574 if ($this->MessageDate == '') {
1575 $result .= $this->headerLine('Date', self::rfcDate());
1577 $result .= $this->headerLine('Date', $this->MessageDate);
1580 if ($this->ReturnPath) {
1581 $result .= $this->headerLine('Return-Path', '<' . trim($this->ReturnPath) . '>');
1582 } elseif ($this->Sender == '') {
1583 $result .= $this->headerLine('Return-Path', '<' . trim($this->From) . '>');
1585 $result .= $this->headerLine('Return-Path', '<' . trim($this->Sender) . '>');
1588 // To be created automatically by mail()
1589 if ($this->Mailer != 'mail') {
1590 if ($this->SingleTo === true) {
1591 foreach ($this->to as $t) {
1592 $this->SingleToArray[] = $this->addrFormat($t);
1595 if (count($this->to) > 0) {
1596 $result .= $this->addrAppend('To', $this->to);
1597 } elseif (count($this->cc) == 0) {
1598 $result .= $this->headerLine('To', 'undisclosed-recipients:;');
1603 $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
1605 // sendmail and mail() extract Cc from the header before sending
1606 if (count($this->cc) > 0) {
1607 $result .= $this->addrAppend('Cc', $this->cc);
1610 // sendmail and mail() extract Bcc from the header before sending
1611 if ((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) {
1612 $result .= $this->addrAppend('Bcc', $this->bcc);
1615 if (count($this->ReplyTo) > 0) {
1616 $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
1619 // mail() sets the subject itself
1620 if ($this->Mailer != 'mail') {
1621 $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
1624 if ($this->MessageID != '') {
1625 $this->lastMessageID = $this->MessageID;
1627 $this->lastMessageID = sprintf("<%s@%s>", $uniq_id, $this->ServerHostname());
1629 $result .= $this->HeaderLine('Message-ID', $this->lastMessageID);
1630 $result .= $this->headerLine('X-Priority', $this->Priority);
1631 if ($this->XMailer == '') {
1632 $result .= $this->headerLine(
1634 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer/)'
1637 $myXmailer = trim($this->XMailer);
1639 $result .= $this->headerLine('X-Mailer', $myXmailer);
1643 if ($this->ConfirmReadingTo != '') {
1644 $result .= $this->headerLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
1647 // Add custom headers
1648 for ($index = 0; $index < count($this->CustomHeader); $index++) {
1649 $result .= $this->headerLine(
1650 trim($this->CustomHeader[$index][0]),
1651 $this->encodeHeader(trim($this->CustomHeader[$index][1]))
1654 if (!$this->sign_key_file) {
1655 $result .= $this->headerLine('MIME-Version', '1.0');
1656 $result .= $this->getMailMIME();
1663 * Get the message MIME type headers.
1667 public function getMailMIME()
1670 switch ($this->message_type) {
1672 $result .= $this->headerLine('Content-Type', 'multipart/related;');
1673 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1676 case 'inline_attach':
1678 case 'alt_inline_attach':
1679 $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
1680 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1684 $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
1685 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1688 // Catches case 'plain': and case '':
1689 $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
1692 //RFC1341 part 5 says 7bit is assumed if not specified
1693 if ($this->Encoding != '7bit') {
1694 $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
1697 if ($this->Mailer != 'mail') {
1698 $result .= $this->LE;
1705 * Returns the whole MIME message.
1706 * Includes complete headers and body.
1707 * Only valid post PreSend().
1708 * @see PHPMailer::PreSend()
1712 public function getSentMIMEMessage()
1714 return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;
1719 * Assemble the message body.
1720 * Returns an empty string on failure.
1722 * @throws phpmailerException
1723 * @return string The assembled message body
1725 public function createBody()
1729 if ($this->sign_key_file) {
1730 $body .= $this->getMailMIME() . $this->LE;
1733 $this->setWordWrap();
1735 switch ($this->message_type) {
1737 $body .= $this->getBoundary($this->boundary[1], '', '', '');
1738 $body .= $this->encodeString($this->Body, $this->Encoding);
1739 $body .= $this->LE . $this->LE;
1740 $body .= $this->attachAll('inline', $this->boundary[1]);
1743 $body .= $this->getBoundary($this->boundary[1], '', '', '');
1744 $body .= $this->encodeString($this->Body, $this->Encoding);
1745 $body .= $this->LE . $this->LE;
1746 $body .= $this->attachAll('attachment', $this->boundary[1]);
1748 case 'inline_attach':
1749 $body .= $this->textLine('--' . $this->boundary[1]);
1750 $body .= $this->headerLine('Content-Type', 'multipart/related;');
1751 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1753 $body .= $this->getBoundary($this->boundary[2], '', '', '');
1754 $body .= $this->encodeString($this->Body, $this->Encoding);
1755 $body .= $this->LE . $this->LE;
1756 $body .= $this->attachAll('inline', $this->boundary[2]);
1758 $body .= $this->attachAll('attachment', $this->boundary[1]);
1761 $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', '');
1762 $body .= $this->encodeString($this->AltBody, $this->Encoding);
1763 $body .= $this->LE . $this->LE;
1764 $body .= $this->getBoundary($this->boundary[1], '', 'text/html', '');
1765 $body .= $this->encodeString($this->Body, $this->Encoding);
1766 $body .= $this->LE . $this->LE;
1767 if (!empty($this->Ical)) {
1768 $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
1769 $body .= $this->encodeString($this->Ical, $this->Encoding);
1770 $body .= $this->LE . $this->LE;
1772 $body .= $this->endBoundary($this->boundary[1]);
1775 $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', '');
1776 $body .= $this->encodeString($this->AltBody, $this->Encoding);
1777 $body .= $this->LE . $this->LE;
1778 $body .= $this->textLine('--' . $this->boundary[1]);
1779 $body .= $this->headerLine('Content-Type', 'multipart/related;');
1780 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1782 $body .= $this->getBoundary($this->boundary[2], '', 'text/html', '');
1783 $body .= $this->encodeString($this->Body, $this->Encoding);
1784 $body .= $this->LE . $this->LE;
1785 $body .= $this->attachAll('inline', $this->boundary[2]);
1787 $body .= $this->endBoundary($this->boundary[1]);
1790 $body .= $this->textLine('--' . $this->boundary[1]);
1791 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
1792 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1794 $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', '');
1795 $body .= $this->encodeString($this->AltBody, $this->Encoding);
1796 $body .= $this->LE . $this->LE;
1797 $body .= $this->getBoundary($this->boundary[2], '', 'text/html', '');
1798 $body .= $this->encodeString($this->Body, $this->Encoding);
1799 $body .= $this->LE . $this->LE;
1800 $body .= $this->endBoundary($this->boundary[2]);
1802 $body .= $this->attachAll('attachment', $this->boundary[1]);
1804 case 'alt_inline_attach':
1805 $body .= $this->textLine('--' . $this->boundary[1]);
1806 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
1807 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
1809 $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', '');
1810 $body .= $this->encodeString($this->AltBody, $this->Encoding);
1811 $body .= $this->LE . $this->LE;
1812 $body .= $this->textLine('--' . $this->boundary[2]);
1813 $body .= $this->headerLine('Content-Type', 'multipart/related;');
1814 $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
1816 $body .= $this->getBoundary($this->boundary[3], '', 'text/html', '');
1817 $body .= $this->encodeString($this->Body, $this->Encoding);
1818 $body .= $this->LE . $this->LE;
1819 $body .= $this->attachAll('inline', $this->boundary[3]);
1821 $body .= $this->endBoundary($this->boundary[2]);
1823 $body .= $this->attachAll('attachment', $this->boundary[1]);
1826 // catch case 'plain' and case ''
1827 $body .= $this->encodeString($this->Body, $this->Encoding);
1831 if ($this->isError()) {
1833 } elseif ($this->sign_key_file) {
1835 if (!defined('PKCS7_TEXT')) {
1836 throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
1838 $file = tempnam(sys_get_temp_dir(), 'mail');
1839 file_put_contents($file, $body); //TODO check this worked
1840 $signed = tempnam(sys_get_temp_dir(), 'signed');
1841 if (@openssl_pkcs7_sign(
1844 'file://' . realpath($this->sign_cert_file),
1845 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
1850 $body = file_get_contents($signed);
1855 throw new phpmailerException($this->lang('signing') . openssl_error_string());
1857 } catch (phpmailerException $e) {
1859 if ($this->exceptions) {
1868 * Return the start of a message boundary.
1870 * @param string $boundary
1871 * @param string $charSet
1872 * @param string $contentType
1873 * @param string $encoding
1876 protected function getBoundary($boundary, $charSet, $contentType, $encoding)
1879 if ($charSet == '') {
1880 $charSet = $this->CharSet;
1882 if ($contentType == '') {
1883 $contentType = $this->ContentType;
1885 if ($encoding == '') {
1886 $encoding = $this->Encoding;
1888 $result .= $this->textLine('--' . $boundary);
1889 $result .= sprintf("Content-Type: %s; charset=%s", $contentType, $charSet);
1890 $result .= $this->LE;
1891 $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
1892 $result .= $this->LE;
1898 * Return the end of a message boundary.
1900 * @param string $boundary
1903 protected function endBoundary($boundary)
1905 return $this->LE . '--' . $boundary . '--' . $this->LE;
1909 * Set the message type.
1910 * PHPMailer only supports some preset message types,
1911 * not arbitrary MIME structures.
1915 protected function setMessageType()
1917 $this->message_type = array();
1918 if ($this->alternativeExists()) {
1919 $this->message_type[] = "alt";
1921 if ($this->inlineImageExists()) {
1922 $this->message_type[] = "inline";
1924 if ($this->attachmentExists()) {
1925 $this->message_type[] = "attach";
1927 $this->message_type = implode("_", $this->message_type);
1928 if ($this->message_type == "") {
1929 $this->message_type = "plain";
1934 * Format a header line.
1936 * @param string $name
1937 * @param string $value
1940 public function headerLine($name, $value)
1942 return $name . ': ' . $value . $this->LE;
1946 * Return a formatted mail line.
1948 * @param string $value
1951 public function textLine($value)
1953 return $value . $this->LE;
1957 * Add an attachment from a path on the filesystem.
1958 * Returns false if the file could not be found or read.
1959 * @param string $path Path to the attachment.
1960 * @param string $name Overrides the attachment name.
1961 * @param string $encoding File encoding (see $Encoding).
1962 * @param string $type File extension (MIME) type.
1963 * @param string $disposition Disposition to use
1964 * @throws phpmailerException
1967 public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
1970 if (!@is_file($path)) {
1971 throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
1974 //If a MIME type is not specified, try to work it out from the file name
1976 $type = self::filenameToType($path);
1979 $filename = basename($path);
1984 $this->attachment[] = array(
1990 5 => false, // isStringAttachment
1995 } catch (phpmailerException $e) {
1996 $this->setError($e->getMessage());
1997 if ($this->exceptions) {
2000 $this->edebug($e->getMessage() . "\n");
2007 * Return the array of attachments.
2010 public function getAttachments()
2012 return $this->attachment;
2016 * Attach all file, string, and binary attachments to the message.
2017 * Returns an empty string on failure.
2019 * @param string $disposition_type
2020 * @param string $boundary
2023 protected function attachAll($disposition_type, $boundary)
2025 // Return text of body
2030 // Add all attachments
2031 foreach ($this->attachment as $attachment) {
2032 // Check if it is a valid disposition_filter
2033 if ($attachment[6] == $disposition_type) {
2034 // Check for string attachment
2037 $bString = $attachment[5];
2039 $string = $attachment[0];
2041 $path = $attachment[0];
2044 $inclhash = md5(serialize($attachment));
2045 if (in_array($inclhash, $incl)) {
2048 $incl[] = $inclhash;
2049 $name = $attachment[2];
2050 $encoding = $attachment[3];
2051 $type = $attachment[4];
2052 $disposition = $attachment[6];
2053 $cid = $attachment[7];
2054 if ($disposition == 'inline' && isset($cidUniq[$cid])) {
2057 $cidUniq[$cid] = true;
2059 $mime[] = sprintf("--%s%s", $boundary, $this->LE);
2061 "Content-Type: %s; name=\"%s\"%s",
2063 $this->encodeHeader($this->secureHeader($name)),
2066 $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
2068 if ($disposition == 'inline') {
2069 $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
2072 // If a filename contains any of these chars, it should be quoted,
2073 // but not otherwise: RFC2183 & RFC2045 5.1
2074 // Fixes a warning in IETF's msglint MIME checker
2075 // Allow for bypassing the Content-Disposition header totally
2076 if (!(empty($disposition))) {
2077 if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) {
2079 "Content-Disposition: %s; filename=\"%s\"%s",
2081 $this->encodeHeader($this->secureHeader($name)),
2082 $this->LE . $this->LE
2086 "Content-Disposition: %s; filename=%s%s",
2088 $this->encodeHeader($this->secureHeader($name)),
2089 $this->LE . $this->LE
2093 $mime[] = $this->LE;
2096 // Encode as string attachment
2098 $mime[] = $this->encodeString($string, $encoding);
2099 if ($this->isError()) {
2102 $mime[] = $this->LE . $this->LE;
2104 $mime[] = $this->encodeFile($path, $encoding);
2105 if ($this->isError()) {
2108 $mime[] = $this->LE . $this->LE;
2113 $mime[] = sprintf("--%s--%s", $boundary, $this->LE);
2115 return implode("", $mime);
2119 * Encode a file attachment in requested format.
2120 * Returns an empty string on failure.
2121 * @param string $path The full path to the file
2122 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2123 * @throws phpmailerException
2124 * @see EncodeFile(encodeFile
2128 protected function encodeFile($path, $encoding = 'base64')
2131 if (!is_readable($path)) {
2132 throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
2134 $magic_quotes = get_magic_quotes_runtime();
2135 if ($magic_quotes) {
2136 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2137 set_magic_quotes_runtime(0);
2139 ini_set('magic_quotes_runtime', 0);
2142 $file_buffer = file_get_contents($path);
2143 $file_buffer = $this->encodeString($file_buffer, $encoding);
2144 if ($magic_quotes) {
2145 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2146 set_magic_quotes_runtime($magic_quotes);
2148 ini_set('magic_quotes_runtime', $magic_quotes);
2151 return $file_buffer;
2152 } catch (Exception $e) {
2153 $this->setError($e->getMessage());
2159 * Encode a string in requested format.
2160 * Returns an empty string on failure.
2161 * @param string $str The text to encode
2162 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2166 public function encodeString($str, $encoding = 'base64')
2169 switch (strtolower($encoding)) {
2171 $encoded = chunk_split(base64_encode($str), 76, $this->LE);
2175 $encoded = $this->fixEOL($str);
2176 //Make sure it ends with a line break
2177 if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
2178 $encoded .= $this->LE;
2184 case 'quoted-printable':
2185 $encoded = $this->encodeQP($str);
2188 $this->setError($this->lang('encoding') . $encoding);
2195 * Encode a header string optimally.
2196 * Picks shortest of Q, B, quoted-printable or none.
2198 * @param string $str
2199 * @param string $position
2202 public function encodeHeader($str, $position = 'text')
2205 switch (strtolower($position)) {
2207 if (!preg_match('/[\200-\377]/', $str)) {
2208 // Can't use addslashes as we don't know what value has magic_quotes_sybase
2209 $encoded = addcslashes($str, "\0..\37\177\\\"");
2210 if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
2213 return ("\"$encoded\"");
2216 $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
2218 /** @noinspection PhpMissingBreakStatementInspection */
2220 $x = preg_match_all('/[()"]/', $str, $matches);
2221 // Intentional fall-through
2224 $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
2228 if ($x == 0) { //There are no chars that need encoding
2232 $maxlen = 75 - 7 - strlen($this->CharSet);
2233 // Try to select the encoding which should produce the shortest output
2234 if ($x > strlen($str) / 3) {
2235 //More than a third of the content will need encoding, so B encoding will be most efficient
2237 if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
2238 // Use a custom function which correctly encodes and wraps long
2239 // multibyte strings without breaking lines within a character
2240 $encoded = $this->base64EncodeWrapMB($str, "\n");
2242 $encoded = base64_encode($str);
2243 $maxlen -= $maxlen % 4;
2244 $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
2248 $encoded = $this->encodeQ($str, $position);
2249 $encoded = $this->wrapText($encoded, $maxlen, true);
2250 $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
2253 $encoded = preg_replace('/^(.*)$/m', " =?" . $this->CharSet . "?$encoding?\\1?=", $encoded);
2254 $encoded = trim(str_replace("\n", $this->LE, $encoded));
2260 * Check if a string contains multi-byte characters.
2262 * @param string $str multi-byte text to wrap encode
2265 public function hasMultiBytes($str)
2267 if (function_exists('mb_strlen')) {
2268 return (strlen($str) > mb_strlen($str, $this->CharSet));
2269 } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
2275 * Encode and wrap long multibyte strings for mail headers
2276 * without breaking lines within a character.
2277 * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php
2279 * @param string $str multi-byte text to wrap encode
2280 * @param string $lf string to use as linefeed/end-of-line
2283 public function base64EncodeWrapMB($str, $lf = null)
2285 $start = "=?" . $this->CharSet . "?B?";
2292 $mb_length = mb_strlen($str, $this->CharSet);
2293 // Each line must have length <= 75, including $start and $end
2294 $length = 75 - strlen($start) - strlen($end);
2295 // Average multi-byte ratio
2296 $ratio = $mb_length / strlen($str);
2297 // Base64 has a 4:3 ratio
2298 $avgLength = floor($length * $ratio * .75);
2300 for ($i = 0; $i < $mb_length; $i += $offset) {
2303 $offset = $avgLength - $lookBack;
2304 $chunk = mb_substr($str, $i, $offset, $this->CharSet);
2305 $chunk = base64_encode($chunk);
2307 } while (strlen($chunk) > $length);
2308 $encoded .= $chunk . $lf;
2311 // Chomp the last linefeed
2312 $encoded = substr($encoded, 0, -strlen($lf));
2317 * Encode a string in quoted-printable format.
2318 * According to RFC2045 section 6.7.
2320 * @param string $string The text to encode
2321 * @param integer $line_max Number of chars allowed on a line before wrapping
2323 * @link PHP version adapted from http://www.php.net/manual/en/function.quoted-printable-decode.php#89417
2325 public function encodeQP($string, $line_max = 76)
2327 if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3)
2328 return quoted_printable_encode($string);
2330 //Fall back to a pure PHP implementation
2331 $string = str_replace(
2332 array('%20', '%0D%0A.', '%0D%0A', '%'),
2333 array(' ', "\r\n=2E", "\r\n", '='),
2334 rawurlencode($string)
2336 $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
2341 * Backward compatibility wrapper for an old QP encoding function that was removed.
2342 * @see PHPMailer::encodeQP()
2344 * @param string $string
2345 * @param integer $line_max
2346 * @param bool $space_conv
2348 * @deprecated Use encodeQP instead.
2350 public function encodeQPphp(
2353 /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
2355 return $this->encodeQP($string, $line_max);
2359 * Encode a string using Q encoding.
2360 * @link http://tools.ietf.org/html/rfc2047
2361 * @param string $str the text to encode
2362 * @param string $position Where the text is going to be used, see the RFC for what that means
2366 public function encodeQ($str, $position = 'text')
2368 //There should not be any EOL in the string
2370 $encoded = str_replace(array("\r", "\n"), '', $str);
2371 switch (strtolower($position)) {
2373 //RFC 2047 section 5.3
2374 $pattern = '^A-Za-z0-9!*+\/ -';
2376 /** @noinspection PhpMissingBreakStatementInspection */
2378 //RFC 2047 section 5.2
2380 //intentional fall-through
2381 //for this reason we build the $pattern without including delimiters and []
2384 //RFC 2047 section 5.1
2385 //Replace every high ascii, control, =, ? and _ characters
2386 $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
2390 if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
2391 //If the string contains an '=', make sure it's the first thing we replace
2392 //so as to avoid double-encoding
2393 $s = array_search('=', $matches[0]);
2395 unset($matches[0][$s]);
2396 array_unshift($matches[0], '=');
2398 foreach (array_unique($matches[0]) as $char) {
2399 $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
2402 //Replace every spaces to _ (more readable than =20)
2403 return str_replace(' ', '_', $encoded);
2408 * Add a string or binary attachment (non-filesystem).
2409 * This method can be used to attach ascii or binary data,
2410 * such as a BLOB record from a database.
2411 * @param string $string String attachment data.
2412 * @param string $filename Name of the attachment.
2413 * @param string $encoding File encoding (see $Encoding).
2414 * @param string $type File extension (MIME) type.
2415 * @param string $disposition Disposition to use
2418 public function addStringAttachment(
2421 $encoding = 'base64',
2423 $disposition = 'attachment'
2425 //If a MIME type is not specified, try to work it out from the file name
2427 $type = self::filenameToType($filename);
2429 // Append to $attachment array
2430 $this->attachment[] = array(
2433 2 => basename($filename),
2436 5 => true, // isStringAttachment
2443 * Add an embedded (inline) attachment from a file.
2444 * This can include images, sounds, and just about any other document type.
2445 * These differ from 'regular' attachmants in that they are intended to be
2446 * displayed inline with the message, not just attached for download.
2447 * This is used in HTML messages that embed the images
2448 * the HTML refers to using the $cid value.
2449 * @param string $path Path to the attachment.
2450 * @param string $cid Content ID of the attachment; Use this to reference
2451 * the content when using an embedded image in HTML.
2452 * @param string $name Overrides the attachment name.
2453 * @param string $encoding File encoding (see $Encoding).
2454 * @param string $type File MIME type.
2455 * @param string $disposition Disposition to use
2456 * @return bool True on successfully adding an attachment
2458 public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
2460 if (!@is_file($path)) {
2461 $this->setError($this->lang('file_access') . $path);
2465 //If a MIME type is not specified, try to work it out from the file name
2467 $type = self::filenameToType($path);
2470 $filename = basename($path);
2475 // Append to $attachment array
2476 $this->attachment[] = array(
2482 5 => false, // isStringAttachment
2490 * Add an embedded stringified attachment.
2491 * This can include images, sounds, and just about any other document type.
2492 * Be sure to set the $type to an image type for images:
2493 * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
2494 * @param string $string The attachment binary data.
2495 * @param string $cid Content ID of the attachment; Use this to reference
2496 * the content when using an embedded image in HTML.
2497 * @param string $name
2498 * @param string $encoding File encoding (see $Encoding).
2499 * @param string $type MIME type.
2500 * @param string $disposition Disposition to use
2501 * @return bool True on successfully adding an attachment
2503 public function addStringEmbeddedImage(
2507 $encoding = 'base64',
2509 $disposition = 'inline'
2511 //If a MIME type is not specified, try to work it out from the name
2513 $type = self::filenameToType($name);
2516 // Append to $attachment array
2517 $this->attachment[] = array(
2523 5 => true, // isStringAttachment
2531 * Check if an inline attachment is present.
2535 public function inlineImageExists()
2537 foreach ($this->attachment as $attachment) {
2538 if ($attachment[6] == 'inline') {
2546 * Check if an attachment (non-inline) is present.
2549 public function attachmentExists()
2551 foreach ($this->attachment as $attachment) {
2552 if ($attachment[6] == 'attachment') {
2560 * Check if this message has an alternative body set.
2563 public function alternativeExists()
2565 return !empty($this->AltBody);
2569 * Clear all To recipients.
2572 public function clearAddresses()
2574 foreach ($this->to as $to) {
2575 unset($this->all_recipients[strtolower($to[0])]);
2577 $this->to = array();
2581 * Clear all CC recipients.
2584 public function clearCCs()
2586 foreach ($this->cc as $cc) {
2587 unset($this->all_recipients[strtolower($cc[0])]);
2589 $this->cc = array();
2593 * Clear all BCC recipients.
2596 public function clearBCCs()
2598 foreach ($this->bcc as $bcc) {
2599 unset($this->all_recipients[strtolower($bcc[0])]);
2601 $this->bcc = array();
2605 * Clear all ReplyTo recipients.
2608 public function clearReplyTos()
2610 $this->ReplyTo = array();
2614 * Clear all recipient types.
2617 public function clearAllRecipients()
2619 $this->to = array();
2620 $this->cc = array();
2621 $this->bcc = array();
2622 $this->all_recipients = array();
2626 * Clear all filesystem, string, and binary attachments.
2629 public function clearAttachments()
2631 $this->attachment = array();
2635 * Clear all custom headers.
2638 public function clearCustomHeaders()
2640 $this->CustomHeader = array();
2644 * Add an error message to the error container.
2646 * @param string $msg
2649 protected function setError($msg)
2651 $this->error_count++;
2652 if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
2653 $lasterror = $this->smtp->getError();
2654 if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {
2655 $msg .= '<p>' . $this->lang('smtp_error') . $lasterror['smtp_msg'] . "</p>\n";
2658 $this->ErrorInfo = $msg;
2662 * Return an RFC 822 formatted date.
2667 public static function rfcDate()
2669 //Set the time zone to whatever the default is to avoid 500 errors
2670 //Will default to UTC if it's not set properly in php.ini
2671 date_default_timezone_set(@date_default_timezone_get());
2672 return date('D, j M Y H:i:s O');
2676 * Get the server hostname.
2677 * Returns 'localhost.localdomain' if unknown.
2681 protected function serverHostname()
2683 if (!empty($this->Hostname)) {
2684 $result = $this->Hostname;
2685 } elseif (isset($_SERVER['SERVER_NAME'])) {
2686 $result = $_SERVER['SERVER_NAME'];
2688 $result = 'localhost.localdomain';
2695 * Get an error message in the current language.
2697 * @param string $key
2700 protected function lang($key)
2702 if (count($this->language) < 1) {
2703 $this->setLanguage('en'); // set the default language
2706 if (isset($this->language[$key])) {
2707 return $this->language[$key];
2709 return 'Language string failed to load: ' . $key;
2714 * Check if an error occurred.
2716 * @return bool True if an error did occur.
2718 public function isError()
2720 return ($this->error_count > 0);
2724 * Ensure consistent line endings in a string.
2725 * Changes every end of line from CRLF, CR or LF to $this->LE.
2727 * @param string $str String to fixEOL
2730 public function fixEOL($str)
2733 $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
2734 // Now convert LE as needed
2735 if ($this->LE !== "\n") {
2736 $nstr = str_replace("\n", $this->LE, $nstr);
2742 * Add a custom header.
2743 * $name value can be overloaded to contain
2744 * both header name and value (name:value)
2746 * @param string $name Custom header name
2747 * @param string $value Header value
2750 public function addCustomHeader($name, $value = null)
2752 if ($value === null) {
2753 // Value passed in as name:value
2754 $this->CustomHeader[] = explode(':', $name, 2);
2756 $this->CustomHeader[] = array($name, $value);
2761 * Create a message from an HTML string.
2762 * Automatically makes modifications for inline images and backgrounds
2763 * and creates a plain-text version by converting the HTML.
2764 * Overwrites any existing values in $this->Body and $this->AltBody
2766 * @param string $message HTML message string
2767 * @param string $basedir baseline directory for path
2768 * @param bool $advanced Whether to use the advanced HTML to text converter
2769 * @return string $message
2771 public function msgHTML($message, $basedir = '', $advanced = false)
2773 preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images);
2774 if (isset($images[2])) {
2775 foreach ($images[2] as $i => $url) {
2776 // do not change urls for absolute images (thanks to corvuscorax)
2777 if (!preg_match('#^[A-z]+://#', $url)) {
2778 $filename = basename($url);
2779 $directory = dirname($url);
2780 if ($directory == '.') {
2783 $cid = md5($url) . '@phpmailer.0'; //RFC2392 S 2
2784 if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
2787 if (strlen($directory) > 1 && substr($directory, -1) != '/') {
2790 if ($this->addEmbeddedImage(
2791 $basedir . $directory . $filename,
2795 self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION))
2798 $message = preg_replace(
2799 "/" . $images[1][$i] . "=[\"']" . preg_quote($url, '/') . "[\"']/Ui",
2800 $images[1][$i] . "=\"cid:" . $cid . "\"",
2807 $this->isHTML(true);
2808 if (empty($this->AltBody)) {
2809 $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n";
2811 //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
2812 $this->Body = $this->normalizeBreaks($message);
2813 $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
2818 * Convert an HTML string into plain text.
2819 * @param string $html The HTML text to convert
2820 * @param bool $advanced Should this use the more complex html2text converter or just a simple one?
2823 public function html2text($html, $advanced = false)
2826 require_once 'extras/class.html2text.php';
2827 $h = new html2text($html);
2828 return $h->get_text();
2830 return html_entity_decode(
2831 trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
2838 * Get the MIME type for a file extension.
2839 * @param string $ext File extension
2841 * @return string MIME type of file.
2844 public static function _mime_types($ext = '')
2847 'xl' => 'application/excel',
2848 'hqx' => 'application/mac-binhex40',
2849 'cpt' => 'application/mac-compactpro',
2850 'bin' => 'application/macbinary',
2851 'doc' => 'application/msword',
2852 'word' => 'application/msword',
2853 'class' => 'application/octet-stream',
2854 'dll' => 'application/octet-stream',
2855 'dms' => 'application/octet-stream',
2856 'exe' => 'application/octet-stream',
2857 'lha' => 'application/octet-stream',
2858 'lzh' => 'application/octet-stream',
2859 'psd' => 'application/octet-stream',
2860 'sea' => 'application/octet-stream',
2861 'so' => 'application/octet-stream',
2862 'oda' => 'application/oda',
2863 'pdf' => 'application/pdf',
2864 'ai' => 'application/postscript',
2865 'eps' => 'application/postscript',
2866 'ps' => 'application/postscript',
2867 'smi' => 'application/smil',
2868 'smil' => 'application/smil',
2869 'mif' => 'application/vnd.mif',
2870 'xls' => 'application/vnd.ms-excel',
2871 'ppt' => 'application/vnd.ms-powerpoint',
2872 'wbxml' => 'application/vnd.wap.wbxml',
2873 'wmlc' => 'application/vnd.wap.wmlc',
2874 'dcr' => 'application/x-director',
2875 'dir' => 'application/x-director',
2876 'dxr' => 'application/x-director',
2877 'dvi' => 'application/x-dvi',
2878 'gtar' => 'application/x-gtar',
2879 'php3' => 'application/x-httpd-php',
2880 'php4' => 'application/x-httpd-php',
2881 'php' => 'application/x-httpd-php',
2882 'phtml' => 'application/x-httpd-php',
2883 'phps' => 'application/x-httpd-php-source',
2884 'js' => 'application/x-javascript',
2885 'swf' => 'application/x-shockwave-flash',
2886 'sit' => 'application/x-stuffit',
2887 'tar' => 'application/x-tar',
2888 'tgz' => 'application/x-tar',
2889 'xht' => 'application/xhtml+xml',
2890 'xhtml' => 'application/xhtml+xml',
2891 'zip' => 'application/zip',
2892 'mid' => 'audio/midi',
2893 'midi' => 'audio/midi',
2894 'mp2' => 'audio/mpeg',
2895 'mp3' => 'audio/mpeg',
2896 'mpga' => 'audio/mpeg',
2897 'aif' => 'audio/x-aiff',
2898 'aifc' => 'audio/x-aiff',
2899 'aiff' => 'audio/x-aiff',
2900 'ram' => 'audio/x-pn-realaudio',
2901 'rm' => 'audio/x-pn-realaudio',
2902 'rpm' => 'audio/x-pn-realaudio-plugin',
2903 'ra' => 'audio/x-realaudio',
2904 'wav' => 'audio/x-wav',
2905 'bmp' => 'image/bmp',
2906 'gif' => 'image/gif',
2907 'jpeg' => 'image/jpeg',
2908 'jpe' => 'image/jpeg',
2909 'jpg' => 'image/jpeg',
2910 'png' => 'image/png',
2911 'tiff' => 'image/tiff',
2912 'tif' => 'image/tiff',
2913 'eml' => 'message/rfc822',
2914 'css' => 'text/css',
2915 'html' => 'text/html',
2916 'htm' => 'text/html',
2917 'shtml' => 'text/html',
2918 'log' => 'text/plain',
2919 'text' => 'text/plain',
2920 'txt' => 'text/plain',
2921 'rtx' => 'text/richtext',
2922 'rtf' => 'text/rtf',
2923 'xml' => 'text/xml',
2924 'xsl' => 'text/xml',
2925 'mpeg' => 'video/mpeg',
2926 'mpe' => 'video/mpeg',
2927 'mpg' => 'video/mpeg',
2928 'mov' => 'video/quicktime',
2929 'qt' => 'video/quicktime',
2930 'rv' => 'video/vnd.rn-realvideo',
2931 'avi' => 'video/x-msvideo',
2932 'movie' => 'video/x-sgi-movie'
2934 return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)]: 'application/octet-stream');
2938 * Map a file name to a MIME type.
2939 * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
2940 * @param string $filename A file name or full path, does not need to exist as a file
2944 public static function filenameToType($filename)
2946 //In case the path is a URL, strip any query string before getting extension
2947 $qpos = strpos($filename, '?');
2948 if ($qpos !== false) {
2949 $filename = substr($filename, 0, $qpos);
2951 $pathinfo = self::mb_pathinfo($filename);
2952 return self::_mime_types($pathinfo['extension']);
2956 * Multi-byte-safe pathinfo replacement.
2957 * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
2958 * Works similarly to the one in PHP >= 5.2.0
2959 * @link http://www.php.net/manual/en/function.pathinfo.php#107461
2960 * @param string $path A filename or path, does not need to exist as a file
2961 * @param integer|string $options Either a PATHINFO_* constant,
2962 * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
2963 * @return string|array
2966 public static function mb_pathinfo($path, $options = null)
2968 $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
2970 preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m);
2971 if (array_key_exists(1, $m)) {
2972 $ret['dirname'] = $m[1];
2974 if (array_key_exists(2, $m)) {
2975 $ret['basename'] = $m[2];
2977 if (array_key_exists(5, $m)) {
2978 $ret['extension'] = $m[5];
2980 if (array_key_exists(3, $m)) {
2981 $ret['filename'] = $m[3];
2984 case PATHINFO_DIRNAME:
2986 return $ret['dirname'];
2988 case PATHINFO_BASENAME:
2990 return $ret['basename'];
2992 case PATHINFO_EXTENSION:
2994 return $ret['extension'];
2996 case PATHINFO_FILENAME:
2998 return $ret['filename'];
3006 * Set or reset instance properties.
3009 * $page->set('X-Priority', '3');
3012 * @param string $name
3013 * @param mixed $value
3014 * NOTE: will not work with arrays, there are no arrays to set/reset
3015 * @throws phpmailerException
3017 * @todo Should this not be using __set() magic function?
3019 public function set($name, $value = '')
3022 if (isset($this->$name)) {
3023 $this->$name = $value;
3025 throw new phpmailerException($this->lang('variable_set') . $name, self::STOP_CRITICAL);
3027 } catch (Exception $e) {
3028 $this->setError($e->getMessage());
3029 if ($e->getCode() == self::STOP_CRITICAL) {
3037 * Strip newlines to prevent header injection.
3039 * @param string $str
3042 public function secureHeader($str)
3044 return trim(str_replace(array("\r", "\n"), '', $str));
3048 * Normalize line breaks in a string.
3049 * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
3050 * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
3051 * @param string $text
3052 * @param string $breaktype What kind of line break to use, defaults to CRLF
3057 public static function normalizeBreaks($text, $breaktype = "\r\n")
3059 return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
3064 * Set the private key file and password for S/MIME signing.
3066 * @param string $cert_filename
3067 * @param string $key_filename
3068 * @param string $key_pass Password for private key
3070 public function sign($cert_filename, $key_filename, $key_pass)
3072 $this->sign_cert_file = $cert_filename;
3073 $this->sign_key_file = $key_filename;
3074 $this->sign_key_pass = $key_pass;
3078 * Quoted-Printable-encode a DKIM header.
3080 * @param string $txt
3083 public function DKIM_QP($txt)
3086 for ($i = 0; $i < strlen($txt); $i++) {
3087 $ord = ord($txt[$i]);
3088 if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
3091 $line .= "=" . sprintf("%02X", $ord);
3098 * Generate a DKIM signature.
3100 * @param string $s Header
3101 * @throws phpmailerException
3104 public function DKIM_Sign($s)
3106 if (!defined('PKCS7_TEXT')) {
3107 if ($this->exceptions) {
3108 throw new phpmailerException($this->lang("signing") . ' OpenSSL extension missing.');
3112 $privKeyStr = file_get_contents($this->DKIM_private);
3113 if ($this->DKIM_passphrase != '') {
3114 $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
3116 $privKey = $privKeyStr;
3118 if (openssl_sign($s, $signature, $privKey)) {
3119 return base64_encode($signature);
3125 * Generate a DKIM canonicalization header.
3127 * @param string $s Header
3130 public function DKIM_HeaderC($s)
3132 $s = preg_replace("/\r\n\s+/", " ", $s);
3133 $lines = explode("\r\n", $s);
3134 foreach ($lines as $key => $line) {
3135 list($heading, $value) = explode(":", $line, 2);
3136 $heading = strtolower($heading);
3137 $value = preg_replace("/\s+/", " ", $value); // Compress useless spaces
3138 $lines[$key] = $heading . ":" . trim($value); // Don't forget to remove WSP around the value
3140 $s = implode("\r\n", $lines);
3145 * Generate a DKIM canonicalization body.
3147 * @param string $body Message Body
3150 public function DKIM_BodyC($body)
3155 // stabilize line endings
3156 $body = str_replace("\r\n", "\n", $body);
3157 $body = str_replace("\n", "\r\n", $body);
3158 // END stabilize line endings
3159 while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
3160 $body = substr($body, 0, strlen($body) - 2);
3166 * Create the DKIM header and body in a new message header.
3168 * @param string $headers_line Header lines
3169 * @param string $subject Subject
3170 * @param string $body Body
3173 public function DKIM_Add($headers_line, $subject, $body)
3175 $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms
3176 $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
3177 $DKIMquery = 'dns/txt'; // Query method
3178 $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
3179 $subject_header = "Subject: $subject";
3180 $headers = explode($this->LE, $headers_line);
3184 foreach ($headers as $header) {
3185 if (strpos($header, 'From:') === 0) {
3186 $from_header = $header;
3187 $current = 'from_header';
3188 } elseif (strpos($header, 'To:') === 0) {
3189 $to_header = $header;
3190 $current = 'to_header';
3192 if ($current && strpos($header, ' =?') === 0) {
3193 $current .= $header;
3199 $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
3200 $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
3201 $subject = str_replace(
3204 $this->DKIM_QP($subject_header)
3205 ); // Copied header fields (dkim-quoted-printable)
3206 $body = $this->DKIM_BodyC($body);
3207 $DKIMlen = strlen($body); // Length of body
3208 $DKIMb64 = base64_encode(pack("H*", sha1($body))); // Base64 of packed binary SHA-1 hash of body
3209 $ident = ($this->DKIM_identity == '') ? '' : " i=" . $this->DKIM_identity . ";";
3210 $dkimhdrs = "DKIM-Signature: v=1; a=" .
3211 $DKIMsignatureType . "; q=" .
3212 $DKIMquery . "; l=" .
3214 $this->DKIM_selector .
3216 "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n" .
3217 "\th=From:To:Subject;\r\n" .
3218 "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n" .
3221 "\t|$subject;\r\n" .
3222 "\tbh=" . $DKIMb64 . ";\r\n" .
3224 $toSign = $this->DKIM_HeaderC(
3225 $from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs
3227 $signed = $this->DKIM_Sign($toSign);
3228 return $dkimhdrs . $signed . "\r\n";
3232 * Perform a callback.
3233 * @param bool $isSent
3236 * @param string $bcc
3237 * @param string $subject
3238 * @param string $body
3239 * @param string $from
3241 protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from = null)
3243 if (!empty($this->action_function) && is_callable($this->action_function)) {
3244 $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
3245 call_user_func_array($this->action_function, $params);
3251 * PHPMailer exception handler
3252 * @package PHPMailer
3254 class phpmailerException extends Exception
3257 * Prettify error message output
3260 public function errorMessage()
3262 $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";