weekly release 3.1dev
[moodle.git] / lib / phpmailer / class.phpmailer.php
CommitLineData
c1ccd3dd 1<?php
c1ccd3dd 2/**
29bf99fd 3 * PHPMailer - PHP email creation and transport class.
ccff58da 4 * PHP Version 5
706be214 5 * @package PHPMailer
ccff58da
MG
6 * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
7 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
29bf99fd
PS
8 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
9 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
10 * @author Brent R. Matzelle (original founder)
ccff58da 11 * @copyright 2012 - 2014 Marcus Bointon
0747daba 12 * @copyright 2010 - 2012 Jim Jagielski
ed546836 13 * @copyright 2004 - 2009 Andy Prevost
ed546836 14 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
29bf99fd
PS
15 * @note This program is distributed in the hope that it will be useful - WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE.
c1ccd3dd 18 */
c1ccd3dd 19
ed546836 20/**
29bf99fd 21 * PHPMailer - PHP email creation and transport class.
29bf99fd 22 * @package PHPMailer
ccff58da 23 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
29bf99fd
PS
24 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
25 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
26 * @author Brent R. Matzelle (original founder)
ed546836 27 */
29bf99fd
PS
28class PHPMailer
29{
30 /**
31 * The PHPMailer Version number.
32 * @type string
33 */
ec9e2d04 34 public $Version = '5.2.13';
29bf99fd
PS
35
36 /**
37 * Email priority.
ec9e2d04
CB
38 * Options: null (default), 1 = High, 3 = Normal, 5 = low.
39 * When null, the header is not set at all.
ccff58da 40 * @type integer
29bf99fd 41 */
ec9e2d04 42 public $Priority = null;
29bf99fd
PS
43
44 /**
45 * The character set of the message.
46 * @type string
47 */
48 public $CharSet = 'iso-8859-1';
49
50 /**
51 * The MIME Content-type of the message.
52 * @type string
53 */
54 public $ContentType = 'text/plain';
55
56 /**
57 * The message encoding.
58 * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
59 * @type string
60 */
61 public $Encoding = '8bit';
62
63 /**
64 * Holds the most recent mailer error message.
65 * @type string
66 */
67 public $ErrorInfo = '';
68
69 /**
70 * The From email address for the message.
71 * @type string
72 */
73 public $From = 'root@localhost';
74
75 /**
76 * The From name of the message.
77 * @type string
78 */
79 public $FromName = 'Root User';
80
81 /**
82 * The Sender email (Return-Path) of the message.
83 * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
84 * @type string
85 */
86 public $Sender = '';
87
88 /**
89 * The Return-Path of the message.
90 * If empty, it will be set to either From or Sender.
91 * @type string
ccff58da
MG
92 * @deprecated Email senders should never set a return-path header;
93 * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
94 * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
29bf99fd
PS
95 */
96 public $ReturnPath = '';
97
98 /**
99 * The Subject of the message.
100 * @type string
101 */
102 public $Subject = '';
103
104 /**
105 * An HTML or plain text message body.
106 * If HTML then call isHTML(true).
107 * @type string
108 */
109 public $Body = '';
110
111 /**
112 * The plain-text message body.
113 * This body can be read by mail clients that do not have HTML email
114 * capability such as mutt & Eudora.
115 * Clients that can read HTML will view the normal Body.
116 * @type string
117 */
118 public $AltBody = '';
119
120 /**
121 * An iCal message part body.
122 * Only supported in simple alt or alt_inline message types
123 * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
124 * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
125 * @link http://kigkonsult.se/iCalcreator/
126 * @type string
127 */
128 public $Ical = '';
129
130 /**
131 * The complete compiled MIME message body.
132 * @access protected
133 * @type string
134 */
135 protected $MIMEBody = '';
136
137 /**
138 * The complete compiled MIME message headers.
139 * @type string
140 * @access protected
141 */
142 protected $MIMEHeader = '';
143
144 /**
145 * Extra headers that createHeader() doesn't fold in.
146 * @type string
147 * @access protected
148 */
149 protected $mailHeader = '';
150
151 /**
152 * Word-wrap the message body to this number of chars.
ec9e2d04 153 * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
ccff58da 154 * @type integer
29bf99fd
PS
155 */
156 public $WordWrap = 0;
157
158 /**
159 * Which method to use to send mail.
160 * Options: "mail", "sendmail", or "smtp".
161 * @type string
162 */
163 public $Mailer = 'mail';
164
165 /**
166 * The path to the sendmail program.
167 * @type string
168 */
169 public $Sendmail = '/usr/sbin/sendmail';
170
171 /**
172 * Whether mail() uses a fully sendmail-compatible MTA.
173 * One which supports sendmail's "-oi -f" options.
ccff58da 174 * @type boolean
29bf99fd
PS
175 */
176 public $UseSendmailOptions = true;
177
178 /**
179 * Path to PHPMailer plugins.
180 * Useful if the SMTP class is not in the PHP include path.
181 * @type string
182 * @deprecated Should not be needed now there is an autoloader.
183 */
184 public $PluginDir = '';
185
186 /**
187 * The email address that a reading confirmation should be sent to.
188 * @type string
189 */
190 public $ConfirmReadingTo = '';
191
192 /**
193 * The hostname to use in Message-Id and Received headers
194 * and as default HELO string.
195 * If empty, the value returned
196 * by SERVER_NAME is used or 'localhost.localdomain'.
197 * @type string
198 */
199 public $Hostname = '';
200
201 /**
202 * An ID to be used in the Message-Id header.
203 * If empty, a unique id will be generated.
204 * @type string
205 */
206 public $MessageID = '';
207
208 /**
209 * The message Date to be used in the Date header.
210 * If empty, the current date will be added.
211 * @type string
212 */
213 public $MessageDate = '';
214
215 /**
216 * SMTP hosts.
217 * Either a single hostname or multiple semicolon-delimited hostnames.
218 * You can also specify a different port
219 * for each host by using this format: [hostname:port]
220 * (e.g. "smtp1.example.com:25;smtp2.example.com").
ccff58da
MG
221 * You can also specify encryption type, for example:
222 * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
29bf99fd
PS
223 * Hosts will be tried in order.
224 * @type string
225 */
226 public $Host = 'localhost';
227
228 /**
229 * The default SMTP server port.
ccff58da
MG
230 * @type integer
231 * @TODO Why is this needed when the SMTP class takes care of it?
29bf99fd
PS
232 */
233 public $Port = 25;
234
235 /**
236 * The SMTP HELO of the message.
237 * Default is $Hostname.
238 * @type string
239 * @see PHPMailer::$Hostname
240 */
241 public $Helo = '';
242
243 /**
ec9e2d04
CB
244 * What kind of encryption to use on the SMTP connection.
245 * Options: '', 'ssl' or 'tls'
29bf99fd
PS
246 * @type string
247 */
248 public $SMTPSecure = '';
249
ec9e2d04
CB
250 /**
251 * Whether to enable TLS encryption automatically if a server supports it,
252 * even if `SMTPSecure` is not set to 'tls'.
253 * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
254 * @type boolean
255 */
256 public $SMTPAutoTLS = true;
257
29bf99fd
PS
258 /**
259 * Whether to use SMTP authentication.
260 * Uses the Username and Password properties.
ccff58da 261 * @type boolean
29bf99fd
PS
262 * @see PHPMailer::$Username
263 * @see PHPMailer::$Password
264 */
265 public $SMTPAuth = false;
266
ec9e2d04
CB
267 /**
268 * Options array passed to stream_context_create when connecting via SMTP.
269 * @type array
270 */
271 public $SMTPOptions = array();
272
29bf99fd
PS
273 /**
274 * SMTP username.
275 * @type string
276 */
277 public $Username = '';
278
279 /**
280 * SMTP password.
281 * @type string
282 */
283 public $Password = '';
284
285 /**
286 * SMTP auth type.
287 * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
288 * @type string
289 */
290 public $AuthType = '';
291
292 /**
293 * SMTP realm.
294 * Used for NTLM auth
295 * @type string
296 */
297 public $Realm = '';
298
299 /**
300 * SMTP workstation.
301 * Used for NTLM auth
302 * @type string
303 */
304 public $Workstation = '';
305
306 /**
307 * The SMTP server timeout in seconds.
ec9e2d04 308 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
ccff58da 309 * @type integer
29bf99fd 310 */
ec9e2d04 311 public $Timeout = 300;
29bf99fd
PS
312
313 /**
314 * SMTP class debug output mode.
ccff58da
MG
315 * Debug output level.
316 * Options:
317 * * `0` No output
318 * * `1` Commands
319 * * `2` Data and commands
320 * * `3` As 2 plus connection status
321 * * `4` Low-level data output
322 * @type integer
29bf99fd
PS
323 * @see SMTP::$do_debug
324 */
325 public $SMTPDebug = 0;
326
327 /**
ccff58da
MG
328 * How to handle debug output.
329 * Options:
330 * * `echo` Output plain-text as-is, appropriate for CLI
331 * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
332 * * `error_log` Output to error log as configured in php.ini
333 *
334 * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
335 * <code>
336 * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
337 * </code>
338 * @type string|callable
29bf99fd
PS
339 * @see SMTP::$Debugoutput
340 */
ccff58da 341 public $Debugoutput = 'echo';
29bf99fd
PS
342
343 /**
344 * Whether to keep SMTP connection open after each message.
345 * If this is set to true then to close the connection
346 * requires an explicit call to smtpClose().
ccff58da 347 * @type boolean
29bf99fd
PS
348 */
349 public $SMTPKeepAlive = false;
350
351 /**
352 * Whether to split multiple to addresses into multiple messages
353 * or send them all in one message.
ccff58da 354 * @type boolean
29bf99fd
PS
355 */
356 public $SingleTo = false;
357
358 /**
359 * Storage for addresses when SingleTo is enabled.
360 * @type array
ccff58da 361 * @TODO This should really not be public
29bf99fd
PS
362 */
363 public $SingleToArray = array();
364
365 /**
366 * Whether to generate VERP addresses on send.
367 * Only applicable when sending via SMTP.
368 * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
ccff58da
MG
369 * @link http://www.postfix.org/VERP_README.html Postfix VERP info
370 * @type boolean
29bf99fd
PS
371 */
372 public $do_verp = false;
373
374 /**
375 * Whether to allow sending messages with an empty body.
ccff58da 376 * @type boolean
29bf99fd
PS
377 */
378 public $AllowEmpty = false;
379
380 /**
381 * The default line ending.
382 * @note The default remains "\n". We force CRLF where we know
383 * it must be used via self::CRLF.
384 * @type string
385 */
386 public $LE = "\n";
387
388 /**
389 * DKIM selector.
390 * @type string
391 */
392 public $DKIM_selector = '';
393
394 /**
395 * DKIM Identity.
396 * Usually the email address used as the source of the email
397 * @type string
398 */
399 public $DKIM_identity = '';
400
401 /**
402 * DKIM passphrase.
403 * Used if your key is encrypted.
404 * @type string
405 */
406 public $DKIM_passphrase = '';
407
408 /**
409 * DKIM signing domain name.
410 * @example 'example.com'
411 * @type string
412 */
413 public $DKIM_domain = '';
414
415 /**
416 * DKIM private key file path.
417 * @type string
418 */
419 public $DKIM_private = '';
420
421 /**
422 * Callback Action function name.
423 *
424 * The function that handles the result of the send email action.
425 * It is called out by send() for each email sent.
426 *
ccff58da 427 * Value can be any php callable: http://www.php.net/is_callable
29bf99fd
PS
428 *
429 * Parameters:
ccff58da 430 * boolean $result result of the send action
29bf99fd
PS
431 * string $to email address of the recipient
432 * string $cc cc email addresses
433 * string $bcc bcc email addresses
434 * string $subject the subject
435 * string $body the email body
436 * string $from email address of sender
29bf99fd
PS
437 * @type string
438 */
439 public $action_function = '';
440
441 /**
ec9e2d04
CB
442 * What to put in the X-Mailer header.
443 * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
29bf99fd
PS
444 * @type string
445 */
446 public $XMailer = '';
ec9e2d04
CB
447
448 /**
449 * Only For XOAUTH - Google
450 * Options: An empty string for PHPMailer default, Enter the email used to get access token
451 * @type string
452 */
453// public $UserEmail = '';
454// public $RefreshToken = '';
455// public $ClientId = '';
456// public $ClientSecret = '';
457
458
29bf99fd
PS
459 /**
460 * An instance of the SMTP sender class.
461 * @type SMTP
462 * @access protected
463 */
464 protected $smtp = null;
465
466 /**
467 * The array of 'to' addresses.
468 * @type array
469 * @access protected
470 */
471 protected $to = array();
472
473 /**
474 * The array of 'cc' addresses.
475 * @type array
476 * @access protected
477 */
478 protected $cc = array();
479
480 /**
481 * The array of 'bcc' addresses.
482 * @type array
483 * @access protected
484 */
485 protected $bcc = array();
486
487 /**
488 * The array of reply-to names and addresses.
489 * @type array
490 * @access protected
491 */
492 protected $ReplyTo = array();
493
494 /**
495 * An array of all kinds of addresses.
ec9e2d04 496 * Includes all of $to, $cc, $bcc
29bf99fd
PS
497 * @type array
498 * @access protected
499 */
500 protected $all_recipients = array();
501
502 /**
503 * The array of attachments.
504 * @type array
505 * @access protected
506 */
507 protected $attachment = array();
508
509 /**
510 * The array of custom headers.
511 * @type array
512 * @access protected
513 */
514 protected $CustomHeader = array();
515
516 /**
517 * The most recent Message-ID (including angular brackets).
518 * @type string
519 * @access protected
520 */
521 protected $lastMessageID = '';
522
523 /**
524 * The message's MIME type.
525 * @type string
526 * @access protected
527 */
528 protected $message_type = '';
529
530 /**
531 * The array of MIME boundary strings.
532 * @type array
533 * @access protected
534 */
535 protected $boundary = array();
536
537 /**
538 * The array of available languages.
539 * @type array
540 * @access protected
541 */
542 protected $language = array();
543
544 /**
545 * The number of errors encountered.
546 * @type integer
547 * @access protected
548 */
549 protected $error_count = 0;
550
551 /**
552 * The S/MIME certificate file path.
553 * @type string
554 * @access protected
555 */
556 protected $sign_cert_file = '';
557
558 /**
559 * The S/MIME key file path.
560 * @type string
561 * @access protected
562 */
563 protected $sign_key_file = '';
564
ec9e2d04
CB
565 /**
566 * The optional S/MIME extra certificates ("CA Chain") file path.
567 * @type string
568 * @access protected
569 */
570 protected $sign_extracerts_file = '';
571
29bf99fd
PS
572 /**
573 * The S/MIME password for the key.
574 * Used only if the key is encrypted.
575 * @type string
576 * @access protected
577 */
578 protected $sign_key_pass = '';
579
580 /**
581 * Whether to throw exceptions for errors.
ccff58da 582 * @type boolean
29bf99fd
PS
583 * @access protected
584 */
585 protected $exceptions = false;
586
ec9e2d04
CB
587 /**
588 * Unique ID used for message ID and boundaries.
589 * @type string
590 * @access protected
591 */
592 protected $uniqueid = '';
593
29bf99fd 594 /**
ccff58da 595 * Error severity: message only, continue processing.
29bf99fd
PS
596 */
597 const STOP_MESSAGE = 0;
598
599 /**
ccff58da 600 * Error severity: message, likely ok to continue processing.
29bf99fd
PS
601 */
602 const STOP_CONTINUE = 1;
603
604 /**
ccff58da 605 * Error severity: message, plus full stop, critical error reached.
29bf99fd
PS
606 */
607 const STOP_CRITICAL = 2;
608
609 /**
ccff58da 610 * SMTP RFC standard line ending.
29bf99fd
PS
611 */
612 const CRLF = "\r\n";
613
ec9e2d04
CB
614 /**
615 * The maximum line length allowed by RFC 2822 section 2.1.1
616 * @type integer
617 */
618 const MAX_LINE_LENGTH = 998;
619
29bf99fd 620 /**
ccff58da
MG
621 * Constructor.
622 * @param boolean $exceptions Should we throw external exceptions?
29bf99fd
PS
623 */
624 public function __construct($exceptions = false)
625 {
ec9e2d04 626 $this->exceptions = (boolean)$exceptions;
29bf99fd
PS
627 }
628
629 /**
630 * Destructor.
631 */
632 public function __destruct()
633 {
ec9e2d04
CB
634 //Close any open SMTP connection nicely
635 if ($this->Mailer == 'smtp') {
29bf99fd
PS
636 $this->smtpClose();
637 }
638 }
639
640 /**
641 * Call mail() in a safe_mode-aware fashion.
642 * Also, unless sendmail_path points to sendmail (or something that
643 * claims to be sendmail), don't pass params (not a perfect fix,
644 * but it will do)
645 * @param string $to To
646 * @param string $subject Subject
647 * @param string $body Message Body
648 * @param string $header Additional Header(s)
649 * @param string $params Params
650 * @access private
ccff58da 651 * @return boolean
29bf99fd
PS
652 */
653 private function mailPassthru($to, $subject, $body, $header, $params)
654 {
ccff58da
MG
655 //Check overloading of mail function to avoid double-encoding
656 if (ini_get('mbstring.func_overload') & 1) {
657 $subject = $this->secureHeader($subject);
658 } else {
659 $subject = $this->encodeHeader($this->secureHeader($subject));
660 }
29bf99fd 661 if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
ccff58da 662 $result = @mail($to, $subject, $body, $header);
29bf99fd 663 } else {
ccff58da 664 $result = @mail($to, $subject, $body, $header, $params);
29bf99fd 665 }
ccff58da 666 return $result;
29bf99fd
PS
667 }
668
669 /**
670 * Output debugging info via user-defined method.
ccff58da 671 * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
29bf99fd
PS
672 * @see PHPMailer::$Debugoutput
673 * @see PHPMailer::$SMTPDebug
674 * @param string $str
675 */
676 protected function edebug($str)
677 {
ccff58da
MG
678 if ($this->SMTPDebug <= 0) {
679 return;
680 }
ec9e2d04
CB
681 //Avoid clash with built-in function names
682 if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
ccff58da 683 call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
29bf99fd
PS
684 return;
685 }
686 switch ($this->Debugoutput) {
687 case 'error_log':
ccff58da 688 //Don't output, just log
29bf99fd
PS
689 error_log($str);
690 break;
691 case 'html':
ccff58da
MG
692 //Cleans up output a bit for a better looking, HTML-safe output
693 echo htmlentities(
694 preg_replace('/[\r\n]+/', '', $str),
695 ENT_QUOTES,
696 'UTF-8'
697 )
698 . "<br>\n";
29bf99fd
PS
699 break;
700 case 'echo':
701 default:
ccff58da
MG
702 //Normalize line breaks
703 $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
704 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
705 "\n",
706 "\n \t ",
707 trim($str)
708 ) . "\n";
29bf99fd
PS
709 }
710 }
711
712 /**
713 * Sets message type to HTML or plain.
ccff58da 714 * @param boolean $isHtml True for HTML mode.
29bf99fd
PS
715 * @return void
716 */
ccff58da 717 public function isHTML($isHtml = true)
29bf99fd 718 {
ccff58da 719 if ($isHtml) {
29bf99fd
PS
720 $this->ContentType = 'text/html';
721 } else {
722 $this->ContentType = 'text/plain';
723 }
724 }
725
726 /**
727 * Send messages using SMTP.
728 * @return void
729 */
730 public function isSMTP()
731 {
732 $this->Mailer = 'smtp';
733 }
734
735 /**
736 * Send messages using PHP's mail() function.
737 * @return void
738 */
739 public function isMail()
740 {
741 $this->Mailer = 'mail';
742 }
743
744 /**
745 * Send messages using $Sendmail.
746 * @return void
747 */
748 public function isSendmail()
749 {
ccff58da
MG
750 $ini_sendmail_path = ini_get('sendmail_path');
751
752 if (!stristr($ini_sendmail_path, 'sendmail')) {
753 $this->Sendmail = '/usr/sbin/sendmail';
754 } else {
755 $this->Sendmail = $ini_sendmail_path;
29bf99fd
PS
756 }
757 $this->Mailer = 'sendmail';
758 }
759
760 /**
761 * Send messages using qmail.
762 * @return void
763 */
764 public function isQmail()
765 {
ccff58da
MG
766 $ini_sendmail_path = ini_get('sendmail_path');
767
768 if (!stristr($ini_sendmail_path, 'qmail')) {
769 $this->Sendmail = '/var/qmail/bin/qmail-inject';
770 } else {
771 $this->Sendmail = $ini_sendmail_path;
29bf99fd 772 }
ccff58da 773 $this->Mailer = 'qmail';
29bf99fd
PS
774 }
775
776 /**
777 * Add a "To" address.
778 * @param string $address
779 * @param string $name
ccff58da 780 * @return boolean true on success, false if address already used
29bf99fd
PS
781 */
782 public function addAddress($address, $name = '')
783 {
784 return $this->addAnAddress('to', $address, $name);
785 }
786
787 /**
788 * Add a "CC" address.
789 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
790 * @param string $address
791 * @param string $name
ccff58da 792 * @return boolean true on success, false if address already used
29bf99fd
PS
793 */
794 public function addCC($address, $name = '')
795 {
796 return $this->addAnAddress('cc', $address, $name);
797 }
798
799 /**
800 * Add a "BCC" address.
801 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
802 * @param string $address
803 * @param string $name
ccff58da 804 * @return boolean true on success, false if address already used
29bf99fd
PS
805 */
806 public function addBCC($address, $name = '')
807 {
808 return $this->addAnAddress('bcc', $address, $name);
809 }
810
811 /**
812 * Add a "Reply-to" address.
813 * @param string $address
814 * @param string $name
ccff58da 815 * @return boolean
29bf99fd
PS
816 */
817 public function addReplyTo($address, $name = '')
818 {
819 return $this->addAnAddress('Reply-To', $address, $name);
820 }
821
822 /**
823 * Add an address to one of the recipient arrays.
824 * Addresses that have been added already return false, but do not throw exceptions
825 * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo'
826 * @param string $address The email address to send to
827 * @param string $name
828 * @throws phpmailerException
ccff58da 829 * @return boolean true on success, false if address already used or invalid in some way
29bf99fd
PS
830 * @access protected
831 */
832 protected function addAnAddress($kind, $address, $name = '')
833 {
834 if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) {
835 $this->setError($this->lang('Invalid recipient array') . ': ' . $kind);
ccff58da 836 $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);
29bf99fd
PS
837 if ($this->exceptions) {
838 throw new phpmailerException('Invalid recipient array: ' . $kind);
839 }
29bf99fd
PS
840 return false;
841 }
842 $address = trim($address);
843 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
844 if (!$this->validateAddress($address)) {
845 $this->setError($this->lang('invalid_address') . ': ' . $address);
ccff58da 846 $this->edebug($this->lang('invalid_address') . ': ' . $address);
29bf99fd
PS
847 if ($this->exceptions) {
848 throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
849 }
29bf99fd
PS
850 return false;
851 }
852 if ($kind != 'Reply-To') {
853 if (!isset($this->all_recipients[strtolower($address)])) {
854 array_push($this->$kind, array($address, $name));
855 $this->all_recipients[strtolower($address)] = true;
856 return true;
857 }
858 } else {
859 if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
860 $this->ReplyTo[strtolower($address)] = array($address, $name);
861 return true;
862 }
863 }
ed546836 864 return false;
29bf99fd
PS
865 }
866
ec9e2d04
CB
867 /**
868 * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
869 * of the form "display name <address>" into an array of name/address pairs.
870 * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
871 * Note that quotes in the name part are removed.
872 * @param string $addrstr The address list string
873 * @param bool $useimap Whether to use the IMAP extension to parse the list
874 * @return array
875 * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
876 */
877 public function parseAddresses($addrstr, $useimap = true)
878 {
879 $addresses = array();
880 if ($useimap and function_exists('imap_rfc822_parse_adrlist')) {
881 //Use this built-in parser if it's available
882 $list = imap_rfc822_parse_adrlist($addrstr, '');
883 foreach ($list as $address) {
884 if ($address->host != '.SYNTAX-ERROR.') {
885 if ($this->validateAddress($address->mailbox . '@' . $address->host)) {
886 $addresses[] = array(
887 'name' => (property_exists($address, 'personal') ? $address->personal : ''),
888 'address' => $address->mailbox . '@' . $address->host
889 );
890 }
891 }
892 }
893 } else {
894 //Use this simpler parser
895 $list = explode(',', $addrstr);
896 foreach ($list as $address) {
897 $address = trim($address);
898 //Is there a separate name part?
899 if (strpos($address, '<') === false) {
900 //No separate name, just use the whole thing
901 if ($this->validateAddress($address)) {
902 $addresses[] = array(
903 'name' => '',
904 'address' => $address
905 );
906 }
907 } else {
908 list($name, $email) = explode('<', $address);
909 $email = trim(str_replace('>', '', $email));
910 if ($this->validateAddress($email)) {
911 $addresses[] = array(
912 'name' => trim(str_replace(array('"', "'"), '', $name)),
913 'address' => $email
914 );
915 }
916 }
917 }
918 }
919 return $addresses;
920 }
921
29bf99fd
PS
922 /**
923 * Set the From and FromName properties.
924 * @param string $address
925 * @param string $name
ccff58da 926 * @param boolean $auto Whether to also set the Sender address, defaults to true
29bf99fd 927 * @throws phpmailerException
ccff58da 928 * @return boolean
29bf99fd
PS
929 */
930 public function setFrom($address, $name = '', $auto = true)
931 {
932 $address = trim($address);
933 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
934 if (!$this->validateAddress($address)) {
935 $this->setError($this->lang('invalid_address') . ': ' . $address);
ccff58da 936 $this->edebug($this->lang('invalid_address') . ': ' . $address);
29bf99fd
PS
937 if ($this->exceptions) {
938 throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
939 }
29bf99fd
PS
940 return false;
941 }
942 $this->From = $address;
943 $this->FromName = $name;
944 if ($auto) {
945 if (empty($this->Sender)) {
946 $this->Sender = $address;
947 }
948 }
ed546836 949 return true;
29bf99fd
PS
950 }
951
952 /**
953 * Return the Message-ID header of the last email.
954 * Technically this is the value from the last time the headers were created,
955 * but it's also the message ID of the last sent message except in
956 * pathological cases.
957 * @return string
958 */
959 public function getLastMessageID()
960 {
961 return $this->lastMessageID;
962 }
963
964 /**
965 * Check that a string looks like an email address.
966 * @param string $address The email address to check
967 * @param string $patternselect A selector for the validation pattern to use :
ccff58da
MG
968 * * `auto` Pick strictest one automatically;
969 * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
970 * * `pcre` Use old PCRE implementation;
971 * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; same as pcre8 but does not allow 'dotless' domains;
972 * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
973 * * `noregex` Don't use a regex: super fast, really dumb.
974 * @return boolean
29bf99fd
PS
975 * @static
976 * @access public
977 */
978 public static function validateAddress($address, $patternselect = 'auto')
979 {
ccff58da
MG
980 if (!$patternselect or $patternselect == 'auto') {
981 //Check this constant first so it works when extension_loaded() is disabled by safe mode
982 //Constant was added in PHP 5.2.4
983 if (defined('PCRE_VERSION')) {
984 //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
985 if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
29bf99fd
PS
986 $patternselect = 'pcre8';
987 } else {
988 $patternselect = 'pcre';
989 }
ccff58da
MG
990 } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
991 //Fall back to older PCRE
992 $patternselect = 'pcre';
29bf99fd
PS
993 } else {
994 //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
995 if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
996 $patternselect = 'php';
997 } else {
998 $patternselect = 'noregex';
999 }
1000 }
1001 }
1002 switch ($patternselect) {
1003 case 'pcre8':
1004 /**
ccff58da 1005 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
29bf99fd
PS
1006 * @link http://squiloople.com/2009/12/20/email-address-validation/
1007 * @copyright 2009-2010 Michael Rushton
1008 * Feel free to use and redistribute this code. But please keep this copyright notice.
1009 */
ccff58da 1010 return (boolean)preg_match(
29bf99fd
PS
1011 '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
1012 '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
1013 '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
1014 '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
1015 '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
1016 '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
1017 '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
1018 '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1019 '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
1020 $address
1021 );
29bf99fd
PS
1022 case 'pcre':
1023 //An older regex that doesn't need a recent PCRE
ccff58da 1024 return (boolean)preg_match(
29bf99fd
PS
1025 '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
1026 '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
1027 '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
1028 '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
1029 '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
1030 '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
1031 '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
1032 '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
1033 '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
1034 '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
1035 $address
1036 );
ccff58da
MG
1037 case 'html5':
1038 /**
1039 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
1040 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
1041 */
1042 return (boolean)preg_match(
1043 '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
1044 '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
1045 $address
1046 );
29bf99fd
PS
1047 case 'noregex':
1048 //No PCRE! Do something _very_ approximate!
1049 //Check the address is 3 chars or longer and contains an @ that's not the first or last char
1050 return (strlen($address) >= 3
1051 and strpos($address, '@') >= 1
1052 and strpos($address, '@') != strlen($address) - 1);
ccff58da
MG
1053 case 'php':
1054 default:
1055 return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
29bf99fd
PS
1056 }
1057 }
1058
1059 /**
1060 * Create a message and send it.
1061 * Uses the sending method specified by $Mailer.
29bf99fd 1062 * @throws phpmailerException
ccff58da 1063 * @return boolean false on error - See the ErrorInfo property for details of the error.
29bf99fd
PS
1064 */
1065 public function send()
1066 {
1067 try {
1068 if (!$this->preSend()) {
1069 return false;
1070 }
1071 return $this->postSend();
ccff58da 1072 } catch (phpmailerException $exc) {
29bf99fd 1073 $this->mailHeader = '';
ccff58da 1074 $this->setError($exc->getMessage());
29bf99fd 1075 if ($this->exceptions) {
ccff58da 1076 throw $exc;
29bf99fd
PS
1077 }
1078 return false;
1079 }
1080 }
1081
1082 /**
1083 * Prepare a message for sending.
1084 * @throws phpmailerException
ccff58da 1085 * @return boolean
29bf99fd
PS
1086 */
1087 public function preSend()
1088 {
1089 try {
ccff58da 1090 $this->mailHeader = '';
29bf99fd
PS
1091 if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
1092 throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
1093 }
1094
1095 // Set whether the message is multipart/alternative
1096 if (!empty($this->AltBody)) {
1097 $this->ContentType = 'multipart/alternative';
1098 }
1099
ec9e2d04 1100 $this->error_count = 0; // Reset errors
29bf99fd
PS
1101 $this->setMessageType();
1102 // Refuse to send an empty message unless we are specifically allowing it
1103 if (!$this->AllowEmpty and empty($this->Body)) {
1104 throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
1105 }
1106
ec9e2d04
CB
1107 // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
1108 $this->MIMEHeader = '';
29bf99fd 1109 $this->MIMEBody = $this->createBody();
ec9e2d04
CB
1110 // createBody may have added some headers, so retain them
1111 $tempheaders = $this->MIMEHeader;
1112 $this->MIMEHeader = $this->createHeader();
1113 $this->MIMEHeader .= $tempheaders;
29bf99fd
PS
1114
1115 // To capture the complete message when using mail(), create
1116 // an extra header list which createHeader() doesn't fold in
1117 if ($this->Mailer == 'mail') {
1118 if (count($this->to) > 0) {
ccff58da 1119 $this->mailHeader .= $this->addrAppend('To', $this->to);
29bf99fd 1120 } else {
ccff58da 1121 $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
29bf99fd
PS
1122 }
1123 $this->mailHeader .= $this->headerLine(
1124 'Subject',
1125 $this->encodeHeader($this->secureHeader(trim($this->Subject)))
1126 );
1127 }
1128
1129 // Sign with DKIM if enabled
1130 if (!empty($this->DKIM_domain)
1131 && !empty($this->DKIM_private)
1132 && !empty($this->DKIM_selector)
29bf99fd
PS
1133 && file_exists($this->DKIM_private)) {
1134 $header_dkim = $this->DKIM_Add(
1135 $this->MIMEHeader . $this->mailHeader,
1136 $this->encodeHeader($this->secureHeader($this->Subject)),
1137 $this->MIMEBody
1138 );
1139 $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
1140 str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
1141 }
1142 return true;
ccff58da
MG
1143 } catch (phpmailerException $exc) {
1144 $this->setError($exc->getMessage());
29bf99fd 1145 if ($this->exceptions) {
ccff58da 1146 throw $exc;
29bf99fd
PS
1147 }
1148 return false;
1149 }
1150 }
1151
1152 /**
1153 * Actually send a message.
1154 * Send the email via the selected mechanism
1155 * @throws phpmailerException
ccff58da 1156 * @return boolean
29bf99fd
PS
1157 */
1158 public function postSend()
1159 {
1160 try {
1161 // Choose the mailer and send through it
1162 switch ($this->Mailer) {
1163 case 'sendmail':
ccff58da 1164 case 'qmail':
29bf99fd
PS
1165 return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1166 case 'smtp':
1167 return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1168 case 'mail':
1169 return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1170 default:
ccff58da
MG
1171 $sendMethod = $this->Mailer.'Send';
1172 if (method_exists($this, $sendMethod)) {
1173 return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
1174 }
1175
29bf99fd
PS
1176 return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1177 }
ccff58da
MG
1178 } catch (phpmailerException $exc) {
1179 $this->setError($exc->getMessage());
1180 $this->edebug($exc->getMessage());
29bf99fd 1181 if ($this->exceptions) {
ccff58da 1182 throw $exc;
29bf99fd 1183 }
29bf99fd
PS
1184 }
1185 return false;
1186 }
1187
1188 /**
1189 * Send mail using the $Sendmail program.
1190 * @param string $header The message headers
1191 * @param string $body The message body
1192 * @see PHPMailer::$Sendmail
1193 * @throws phpmailerException
1194 * @access protected
ccff58da 1195 * @return boolean
29bf99fd
PS
1196 */
1197 protected function sendmailSend($header, $body)
1198 {
1199 if ($this->Sender != '') {
ccff58da
MG
1200 if ($this->Mailer == 'qmail') {
1201 $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1202 } else {
1203 $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
1204 }
0747daba 1205 } else {
ccff58da
MG
1206 if ($this->Mailer == 'qmail') {
1207 $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
1208 } else {
1209 $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
1210 }
29bf99fd 1211 }
ec9e2d04 1212 if ($this->SingleTo) {
ccff58da 1213 foreach ($this->SingleToArray as $toAddr) {
29bf99fd
PS
1214 if (!@$mail = popen($sendmail, 'w')) {
1215 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1216 }
ccff58da 1217 fputs($mail, 'To: ' . $toAddr . "\n");
29bf99fd
PS
1218 fputs($mail, $header);
1219 fputs($mail, $body);
1220 $result = pclose($mail);
ccff58da
MG
1221 $this->doCallback(
1222 ($result == 0),
1223 array($toAddr),
1224 $this->cc,
1225 $this->bcc,
1226 $this->Subject,
1227 $body,
1228 $this->From
1229 );
29bf99fd
PS
1230 if ($result != 0) {
1231 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1232 }
1233 }
ed546836 1234 } else {
29bf99fd
PS
1235 if (!@$mail = popen($sendmail, 'w')) {
1236 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1237 }
1238 fputs($mail, $header);
1239 fputs($mail, $body);
1240 $result = pclose($mail);
ccff58da 1241 $this->doCallback(($result == 0), $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
29bf99fd
PS
1242 if ($result != 0) {
1243 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
1244 }
c1ccd3dd 1245 }
29bf99fd
PS
1246 return true;
1247 }
c1ccd3dd 1248
29bf99fd
PS
1249 /**
1250 * Send mail using the PHP mail() function.
1251 * @param string $header The message headers
1252 * @param string $body The message body
1253 * @link http://www.php.net/manual/en/book.mail.php
1254 * @throws phpmailerException
1255 * @access protected
ccff58da 1256 * @return boolean
29bf99fd
PS
1257 */
1258 protected function mailSend($header, $body)
1259 {
1260 $toArr = array();
ccff58da
MG
1261 foreach ($this->to as $toaddr) {
1262 $toArr[] = $this->addrFormat($toaddr);
29bf99fd
PS
1263 }
1264 $to = implode(', ', $toArr);
c1ccd3dd 1265
29bf99fd 1266 if (empty($this->Sender)) {
ccff58da 1267 $params = ' ';
29bf99fd 1268 } else {
ccff58da 1269 $params = sprintf('-f%s', $this->Sender);
29bf99fd
PS
1270 }
1271 if ($this->Sender != '' and !ini_get('safe_mode')) {
1272 $old_from = ini_get('sendmail_from');
1273 ini_set('sendmail_from', $this->Sender);
1274 }
ccff58da 1275 $result = false;
ec9e2d04 1276 if ($this->SingleTo && count($toArr) > 1) {
ccff58da
MG
1277 foreach ($toArr as $toAddr) {
1278 $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
1279 $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
29bf99fd
PS
1280 }
1281 } else {
ccff58da
MG
1282 $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1283 $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
29bf99fd
PS
1284 }
1285 if (isset($old_from)) {
1286 ini_set('sendmail_from', $old_from);
1287 }
ccff58da 1288 if (!$result) {
29bf99fd
PS
1289 throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1290 }
1291 return true;
1292 }
ed546836 1293
29bf99fd
PS
1294 /**
1295 * Get an instance to use for SMTP operations.
1296 * Override this function to load your own SMTP implementation
1297 * @return SMTP
1298 */
1299 public function getSMTPInstance()
1300 {
1301 if (!is_object($this->smtp)) {
1302 $this->smtp = new SMTP;
1303 }
1304 return $this->smtp;
1305 }
1306
1307 /**
1308 * Send mail via SMTP.
1309 * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
1310 * Uses the PHPMailerSMTP class by default.
1311 * @see PHPMailer::getSMTPInstance() to use a different class.
1312 * @param string $header The message headers
1313 * @param string $body The message body
1314 * @throws phpmailerException
1315 * @uses SMTP
1316 * @access protected
ccff58da 1317 * @return boolean
29bf99fd
PS
1318 */
1319 protected function smtpSend($header, $body)
1320 {
1321 $bad_rcpt = array();
ec9e2d04 1322 if (!$this->smtpConnect($this->SMTPOptions)) {
29bf99fd
PS
1323 throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1324 }
ec9e2d04
CB
1325 if ('' == $this->Sender) {
1326 $smtp_from = $this->From;
1327 } else {
1328 $smtp_from = $this->Sender;
1329 }
29bf99fd
PS
1330 if (!$this->smtp->mail($smtp_from)) {
1331 $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
1332 throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1333 }
ed546836 1334
ccff58da 1335 // Attempt to send to all recipients
ec9e2d04
CB
1336 foreach (array($this->to, $this->cc, $this->bcc) as $togroup) {
1337 foreach ($togroup as $to) {
1338 if (!$this->smtp->recipient($to[0])) {
1339 $error = $this->smtp->getError();
1340 $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']);
1341 $isSent = false;
1342 } else {
1343 $isSent = true;
1344 }
1345 $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
29bf99fd 1346 }
29bf99fd 1347 }
c1ccd3dd 1348
ccff58da
MG
1349 // Only send the DATA command if we have viable recipients
1350 if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
29bf99fd
PS
1351 throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1352 }
ec9e2d04 1353 if ($this->SMTPKeepAlive) {
29bf99fd
PS
1354 $this->smtp->reset();
1355 } else {
1356 $this->smtp->quit();
1357 $this->smtp->close();
1358 }
ec9e2d04
CB
1359 //Create error message for any bad addresses
1360 if (count($bad_rcpt) > 0) {
1361 $errstr = '';
1362 foreach ($bad_rcpt as $bad) {
1363 $errstr .= $bad['to'] . ': ' . $bad['error'];
1364 }
ccff58da 1365 throw new phpmailerException(
ec9e2d04 1366 $this->lang('recipients_failed') . $errstr,
ccff58da
MG
1367 self::STOP_CONTINUE
1368 );
1369 }
29bf99fd
PS
1370 return true;
1371 }
c1ccd3dd 1372
29bf99fd
PS
1373 /**
1374 * Initiate a connection to an SMTP server.
1375 * Returns false if the operation failed.
1376 * @param array $options An array of options compatible with stream_context_create()
1377 * @uses SMTP
1378 * @access public
1379 * @throws phpmailerException
ccff58da 1380 * @return boolean
29bf99fd
PS
1381 */
1382 public function smtpConnect($options = array())
1383 {
1384 if (is_null($this->smtp)) {
1385 $this->smtp = $this->getSMTPInstance();
1386 }
ec9e2d04 1387
ccff58da 1388 // Already connected?
29bf99fd
PS
1389 if ($this->smtp->connected()) {
1390 return true;
1391 }
1392
1393 $this->smtp->setTimeout($this->Timeout);
1394 $this->smtp->setDebugLevel($this->SMTPDebug);
1395 $this->smtp->setDebugOutput($this->Debugoutput);
1396 $this->smtp->setVerp($this->do_verp);
29bf99fd
PS
1397 $hosts = explode(';', $this->Host);
1398 $lastexception = null;
1399
1400 foreach ($hosts as $hostentry) {
1401 $hostinfo = array();
ccff58da
MG
1402 if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
1403 // Not a valid host entry
1404 continue;
1405 }
1406 // $hostinfo[2]: optional ssl or tls prefix
1407 // $hostinfo[3]: the hostname
1408 // $hostinfo[4]: optional port number
1409 // The host string prefix can temporarily override the current setting for SMTPSecure
1410 // If it's not specified, the default value is used
1411 $prefix = '';
ec9e2d04 1412 $secure = $this->SMTPSecure;
ccff58da 1413 $tls = ($this->SMTPSecure == 'tls');
ec9e2d04 1414 if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
ccff58da 1415 $prefix = 'ssl://';
ec9e2d04
CB
1416 $tls = false; // Can't have SSL and TLS at the same time
1417 $secure = 'ssl';
ccff58da
MG
1418 } elseif ($hostinfo[2] == 'tls') {
1419 $tls = true;
1420 // tls doesn't use a prefix
ec9e2d04
CB
1421 $secure = 'tls';
1422 }
1423 //Do we need the OpenSSL extension?
1424 $sslext = defined('OPENSSL_ALGO_SHA1');
1425 if ('tls' === $secure or 'ssl' === $secure) {
1426 //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
1427 if (!$sslext) {
1428 throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);
1429 }
ccff58da
MG
1430 }
1431 $host = $hostinfo[3];
29bf99fd 1432 $port = $this->Port;
ccff58da
MG
1433 $tport = (integer)$hostinfo[4];
1434 if ($tport > 0 and $tport < 65536) {
1435 $port = $tport;
ed546836 1436 }
ccff58da 1437 if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
29bf99fd
PS
1438 try {
1439 if ($this->Helo) {
1440 $hello = $this->Helo;
1441 } else {
1442 $hello = $this->serverHostname();
1443 }
1444 $this->smtp->hello($hello);
ec9e2d04
CB
1445 //Automatically enable TLS encryption if:
1446 // * it's not disabled
1447 // * we have openssl extension
1448 // * we are not already using SSL
1449 // * the server offers STARTTLS
1450 if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
1451 $tls = true;
1452 }
29bf99fd
PS
1453 if ($tls) {
1454 if (!$this->smtp->startTLS()) {
1455 throw new phpmailerException($this->lang('connect_host'));
1456 }
ccff58da 1457 // We must resend HELO after tls negotiation
29bf99fd
PS
1458 $this->smtp->hello($hello);
1459 }
1460 if ($this->SMTPAuth) {
1461 if (!$this->smtp->authenticate(
ec9e2d04
CB
1462 $this->Username,
1463 $this->Password,
1464 $this->AuthType,
1465 $this->Realm,
29bf99fd
PS
1466 $this->Workstation
1467 )
1468 ) {
1469 throw new phpmailerException($this->lang('authenticate'));
1470 }
1471 }
1472 return true;
ccff58da
MG
1473 } catch (phpmailerException $exc) {
1474 $lastexception = $exc;
ec9e2d04 1475 $this->edebug($exc->getMessage());
ccff58da 1476 // We must have connected, but then failed TLS or Auth, so close connection nicely
29bf99fd
PS
1477 $this->smtp->quit();
1478 }
1479 }
1480 }
ccff58da 1481 // If we get here, all connection attempts have failed, so close connection hard
29bf99fd 1482 $this->smtp->close();
ccff58da 1483 // As we've caught all exceptions, just report whatever the last one was
29bf99fd
PS
1484 if ($this->exceptions and !is_null($lastexception)) {
1485 throw $lastexception;
1486 }
1487 return false;
1488 }
1489
1490 /**
1491 * Close the active SMTP session if one exists.
1492 * @return void
1493 */
1494 public function smtpClose()
1495 {
1496 if ($this->smtp !== null) {
1497 if ($this->smtp->connected()) {
1498 $this->smtp->quit();
1499 $this->smtp->close();
706be214 1500 }
29bf99fd
PS
1501 }
1502 }
1503
1504 /**
1505 * Set the language for error messages.
1506 * Returns false if it cannot load the language file.
1507 * The default language is English.
1508 * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
1509 * @param string $lang_path Path to the language file directory, with trailing separator (slash)
ccff58da 1510 * @return boolean
29bf99fd
PS
1511 * @access public
1512 */
ccff58da 1513 public function setLanguage($langcode = 'en', $lang_path = '')
29bf99fd 1514 {
ccff58da 1515 // Define full set of translatable strings in English
29bf99fd
PS
1516 $PHPMAILER_LANG = array(
1517 'authenticate' => 'SMTP Error: Could not authenticate.',
1518 'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1519 'data_not_accepted' => 'SMTP Error: data not accepted.',
1520 'empty_message' => 'Message body empty',
1521 'encoding' => 'Unknown encoding: ',
1522 'execute' => 'Could not execute: ',
1523 'file_access' => 'Could not access file: ',
1524 'file_open' => 'File Error: Could not open file: ',
1525 'from_failed' => 'The following From address failed: ',
1526 'instantiate' => 'Could not instantiate mail function.',
1527 'invalid_address' => 'Invalid address',
1528 'mailer_not_supported' => ' mailer is not supported.',
1529 'provide_address' => 'You must provide at least one recipient email address.',
1530 'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1531 'signing' => 'Signing Error: ',
1532 'smtp_connect_failed' => 'SMTP connect() failed.',
1533 'smtp_error' => 'SMTP server error: ',
ec9e2d04
CB
1534 'variable_set' => 'Cannot set or reset variable: ',
1535 'extension_missing' => 'Extension missing: '
29bf99fd 1536 );
ccff58da
MG
1537 if (empty($lang_path)) {
1538 // Calculate an absolute path so it can work if CWD is not here
1539 $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
1540 }
1541 $foundlang = true;
1542 $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
ec9e2d04
CB
1543 // There is no English translation file
1544 if ($langcode != 'en') {
ccff58da
MG
1545 // Make sure language file path is readable
1546 if (!is_readable($lang_file)) {
1547 $foundlang = false;
1548 } else {
1549 // Overwrite language-specific strings.
ec9e2d04 1550 // This way we'll never have missing translation keys.
ccff58da
MG
1551 $foundlang = include $lang_file;
1552 }
29bf99fd
PS
1553 }
1554 $this->language = $PHPMAILER_LANG;
ec9e2d04 1555 return (boolean)$foundlang; // Returns false if language not found
29bf99fd
PS
1556 }
1557
1558 /**
1559 * Get the array of strings for the current language.
1560 * @return array
1561 */
1562 public function getTranslations()
1563 {
1564 return $this->language;
1565 }
1566
1567 /**
1568 * Create recipient headers.
1569 * @access public
1570 * @param string $type
1571 * @param array $addr An array of recipient,
1572 * where each recipient is a 2-element indexed array with element 0 containing an address
1573 * and element 1 containing a name, like:
1574 * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
1575 * @return string
1576 */
1577 public function addrAppend($type, $addr)
1578 {
1579 $addresses = array();
ccff58da
MG
1580 foreach ($addr as $address) {
1581 $addresses[] = $this->addrFormat($address);
29bf99fd
PS
1582 }
1583 return $type . ': ' . implode(', ', $addresses) . $this->LE;
1584 }
1585
1586 /**
1587 * Format an address for use in a message header.
1588 * @access public
1589 * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
1590 * like array('joe@example.com', 'Joe User')
1591 * @return string
1592 */
1593 public function addrFormat($addr)
1594 {
1595 if (empty($addr[1])) { // No name provided
1596 return $this->secureHeader($addr[0]);
1597 } else {
ccff58da 1598 return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
29bf99fd 1599 $addr[0]
ccff58da 1600 ) . '>';
29bf99fd
PS
1601 }
1602 }
1603
1604 /**
1605 * Word-wrap message.
1606 * For use with mailers that do not automatically perform wrapping
1607 * and for quoted-printable encoded messages.
1608 * Original written by philippe.
1609 * @param string $message The message to wrap
1610 * @param integer $length The line length to wrap to
ccff58da 1611 * @param boolean $qp_mode Whether to run in Quoted-Printable mode
29bf99fd
PS
1612 * @access public
1613 * @return string
1614 */
1615 public function wrapText($message, $length, $qp_mode = false)
1616 {
ec9e2d04
CB
1617 if ($qp_mode) {
1618 $soft_break = sprintf(' =%s', $this->LE);
1619 } else {
1620 $soft_break = $this->LE;
1621 }
29bf99fd
PS
1622 // If utf-8 encoding is used, we will need to make sure we don't
1623 // split multibyte characters when we wrap
ccff58da 1624 $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
29bf99fd
PS
1625 $lelen = strlen($this->LE);
1626 $crlflen = strlen(self::CRLF);
1627
1628 $message = $this->fixEOL($message);
ec9e2d04 1629 //Remove a trailing line break
29bf99fd
PS
1630 if (substr($message, -$lelen) == $this->LE) {
1631 $message = substr($message, 0, -$lelen);
1632 }
1633
ec9e2d04
CB
1634 //Split message into lines
1635 $lines = explode($this->LE, $message);
1636 //Message will be rebuilt in here
29bf99fd 1637 $message = '';
ec9e2d04
CB
1638 foreach ($lines as $line) {
1639 $words = explode(' ', $line);
ed546836 1640 $buf = '';
ec9e2d04
CB
1641 $firstword = true;
1642 foreach ($words as $word) {
29bf99fd
PS
1643 if ($qp_mode and (strlen($word) > $length)) {
1644 $space_left = $length - strlen($buf) - $crlflen;
ec9e2d04 1645 if (!$firstword) {
29bf99fd
PS
1646 if ($space_left > 20) {
1647 $len = $space_left;
1648 if ($is_utf8) {
1649 $len = $this->utf8CharBoundary($word, $len);
ccff58da 1650 } elseif (substr($word, $len - 1, 1) == '=') {
29bf99fd 1651 $len--;
ccff58da 1652 } elseif (substr($word, $len - 2, 1) == '=') {
29bf99fd
PS
1653 $len -= 2;
1654 }
1655 $part = substr($word, 0, $len);
1656 $word = substr($word, $len);
1657 $buf .= ' ' . $part;
ccff58da 1658 $message .= $buf . sprintf('=%s', self::CRLF);
29bf99fd
PS
1659 } else {
1660 $message .= $buf . $soft_break;
1661 }
1662 $buf = '';
1663 }
1664 while (strlen($word) > 0) {
1665 if ($length <= 0) {
1666 break;
1667 }
1668 $len = $length;
1669 if ($is_utf8) {
1670 $len = $this->utf8CharBoundary($word, $len);
ccff58da 1671 } elseif (substr($word, $len - 1, 1) == '=') {
29bf99fd 1672 $len--;
ccff58da 1673 } elseif (substr($word, $len - 2, 1) == '=') {
29bf99fd
PS
1674 $len -= 2;
1675 }
1676 $part = substr($word, 0, $len);
1677 $word = substr($word, $len);
1678
1679 if (strlen($word) > 0) {
ccff58da 1680 $message .= $part . sprintf('=%s', self::CRLF);
29bf99fd
PS
1681 } else {
1682 $buf = $part;
1683 }
1684 }
1685 } else {
1686 $buf_o = $buf;
ec9e2d04
CB
1687 if (!$firstword) {
1688 $buf .= ' ';
1689 }
1690 $buf .= $word;
29bf99fd
PS
1691
1692 if (strlen($buf) > $length and $buf_o != '') {
1693 $message .= $buf_o . $soft_break;
1694 $buf = $word;
1695 }
1696 }
ec9e2d04 1697 $firstword = false;
346d4eab 1698 }
29bf99fd
PS
1699 $message .= $buf . self::CRLF;
1700 }
ed546836 1701
29bf99fd
PS
1702 return $message;
1703 }
1704
1705 /**
1706 * Find the last character boundary prior to $maxLength in a utf-8
ec9e2d04 1707 * quoted-printable encoded string.
29bf99fd
PS
1708 * Original written by Colin Brown.
1709 * @access public
1710 * @param string $encodedText utf-8 QP text
ec9e2d04 1711 * @param integer $maxLength Find the last character boundary prior to this length
ccff58da 1712 * @return integer
29bf99fd
PS
1713 */
1714 public function utf8CharBoundary($encodedText, $maxLength)
1715 {
1716 $foundSplitPos = false;
1717 $lookBack = 3;
1718 while (!$foundSplitPos) {
1719 $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
ccff58da 1720 $encodedCharPos = strpos($lastChunk, '=');
ec9e2d04 1721 if (false !== $encodedCharPos) {
29bf99fd
PS
1722 // Found start of encoded character byte within $lookBack block.
1723 // Check the encoded byte value (the 2 chars after the '=')
1724 $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
1725 $dec = hexdec($hex);
ec9e2d04
CB
1726 if ($dec < 128) {
1727 // Single byte character.
29bf99fd
PS
1728 // If the encoded char was found at pos 0, it will fit
1729 // otherwise reduce maxLength to start of the encoded char
ec9e2d04
CB
1730 if ($encodedCharPos > 0) {
1731 $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1732 }
29bf99fd 1733 $foundSplitPos = true;
ec9e2d04
CB
1734 } elseif ($dec >= 192) {
1735 // First byte of a multi byte character
29bf99fd
PS
1736 // Reduce maxLength to split at start of character
1737 $maxLength = $maxLength - ($lookBack - $encodedCharPos);
1738 $foundSplitPos = true;
ec9e2d04
CB
1739 } elseif ($dec < 192) {
1740 // Middle byte of a multi byte character, look further back
29bf99fd
PS
1741 $lookBack += 3;
1742 }
ed546836 1743 } else {
29bf99fd
PS
1744 // No encoded character found
1745 $foundSplitPos = true;
706be214 1746 }
29bf99fd
PS
1747 }
1748 return $maxLength;
1749 }
1750
29bf99fd 1751 /**
ec9e2d04
CB
1752 * Apply word wrapping to the message body.
1753 * Wraps the message body to the number of chars set in the WordWrap property.
1754 * You should only do this to plain-text bodies as wrapping HTML tags may break them.
1755 * This is called automatically by createBody(), so you don't need to call it yourself.
29bf99fd
PS
1756 * @access public
1757 * @return void
1758 */
1759 public function setWordWrap()
1760 {
1761 if ($this->WordWrap < 1) {
1762 return;
1763 }
1764
1765 switch ($this->message_type) {
1766 case 'alt':
1767 case 'alt_inline':
1768 case 'alt_attach':
1769 case 'alt_inline_attach':
1770 $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
1771 break;
1772 default:
1773 $this->Body = $this->wrapText($this->Body, $this->WordWrap);
1774 break;
1775 }
1776 }
1777
1778 /**
1779 * Assemble message headers.
1780 * @access public
1781 * @return string The assembled headers
1782 */
1783 public function createHeader()
1784 {
1785 $result = '';
1786
29bf99fd 1787 if ($this->MessageDate == '') {
ccff58da 1788 $this->MessageDate = self::rfcDate();
c1ccd3dd 1789 }
ccff58da 1790 $result .= $this->headerLine('Date', $this->MessageDate);
29bf99fd 1791
c1ccd3dd 1792
29bf99fd 1793 // To be created automatically by mail()
ec9e2d04 1794 if ($this->SingleTo) {
ccff58da
MG
1795 if ($this->Mailer != 'mail') {
1796 foreach ($this->to as $toaddr) {
1797 $this->SingleToArray[] = $this->addrFormat($toaddr);
29bf99fd 1798 }
ccff58da
MG
1799 }
1800 } else {
1801 if (count($this->to) > 0) {
1802 if ($this->Mailer != 'mail') {
29bf99fd 1803 $result .= $this->addrAppend('To', $this->to);
29bf99fd 1804 }
ccff58da
MG
1805 } elseif (count($this->cc) == 0) {
1806 $result .= $this->headerLine('To', 'undisclosed-recipients:;');
29bf99fd
PS
1807 }
1808 }
0747daba 1809
29bf99fd 1810 $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
0747daba 1811
29bf99fd
PS
1812 // sendmail and mail() extract Cc from the header before sending
1813 if (count($this->cc) > 0) {
1814 $result .= $this->addrAppend('Cc', $this->cc);
0747daba 1815 }
c1ccd3dd 1816
29bf99fd 1817 // sendmail and mail() extract Bcc from the header before sending
ccff58da
MG
1818 if ((
1819 $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
1820 )
1821 and count($this->bcc) > 0
1822 ) {
29bf99fd
PS
1823 $result .= $this->addrAppend('Bcc', $this->bcc);
1824 }
c1ccd3dd 1825
29bf99fd
PS
1826 if (count($this->ReplyTo) > 0) {
1827 $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
1828 }
1829
1830 // mail() sets the subject itself
1831 if ($this->Mailer != 'mail') {
1832 $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
1833 }
1834
1835 if ($this->MessageID != '') {
1836 $this->lastMessageID = $this->MessageID;
0747daba 1837 } else {
ec9e2d04
CB
1838 $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->ServerHostname());
1839 }
1840 $result .= $this->headerLine('Message-ID', $this->lastMessageID);
1841 if (!is_null($this->Priority)) {
1842 $result .= $this->headerLine('X-Priority', $this->Priority);
29bf99fd 1843 }
29bf99fd
PS
1844 if ($this->XMailer == '') {
1845 $result .= $this->headerLine(
1846 'X-Mailer',
1847 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer/)'
1848 );
0747daba 1849 } else {
29bf99fd
PS
1850 $myXmailer = trim($this->XMailer);
1851 if ($myXmailer) {
1852 $result .= $this->headerLine('X-Mailer', $myXmailer);
1853 }
1854 }
1855
1856 if ($this->ConfirmReadingTo != '') {
1857 $result .= $this->headerLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
1858 }
1859
1860 // Add custom headers
ec9e2d04 1861 foreach ($this->CustomHeader as $header) {
29bf99fd 1862 $result .= $this->headerLine(
ec9e2d04
CB
1863 trim($header[0]),
1864 $this->encodeHeader(trim($header[1]))
29bf99fd
PS
1865 );
1866 }
1867 if (!$this->sign_key_file) {
1868 $result .= $this->headerLine('MIME-Version', '1.0');
1869 $result .= $this->getMailMIME();
1870 }
1871
1872 return $result;
1873 }
1874
1875 /**
1876 * Get the message MIME type headers.
1877 * @access public
1878 * @return string
1879 */
1880 public function getMailMIME()
1881 {
1882 $result = '';
ccff58da 1883 $ismultipart = true;
29bf99fd
PS
1884 switch ($this->message_type) {
1885 case 'inline':
1886 $result .= $this->headerLine('Content-Type', 'multipart/related;');
1887 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1888 break;
1889 case 'attach':
1890 case 'inline_attach':
1891 case 'alt_attach':
1892 case 'alt_inline_attach':
1893 $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
1894 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1895 break;
1896 case 'alt':
1897 case 'alt_inline':
1898 $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
1899 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
1900 break;
1901 default:
1902 // Catches case 'plain': and case '':
1903 $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
ccff58da 1904 $ismultipart = false;
29bf99fd
PS
1905 break;
1906 }
ccff58da 1907 // RFC1341 part 5 says 7bit is assumed if not specified
29bf99fd 1908 if ($this->Encoding != '7bit') {
ccff58da
MG
1909 // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
1910 if ($ismultipart) {
1911 if ($this->Encoding == '8bit') {
1912 $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
1913 }
1914 // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
1915 } else {
1916 $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
1917 }
29bf99fd
PS
1918 }
1919
1920 if ($this->Mailer != 'mail') {
1921 $result .= $this->LE;
1922 }
1923
1924 return $result;
1925 }
1926
1927 /**
1928 * Returns the whole MIME message.
1929 * Includes complete headers and body.
ccff58da
MG
1930 * Only valid post preSend().
1931 * @see PHPMailer::preSend()
29bf99fd
PS
1932 * @access public
1933 * @return string
1934 */
1935 public function getSentMIMEMessage()
1936 {
1937 return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;
1938 }
1939
29bf99fd
PS
1940 /**
1941 * Assemble the message body.
1942 * Returns an empty string on failure.
1943 * @access public
1944 * @throws phpmailerException
1945 * @return string The assembled message body
1946 */
1947 public function createBody()
1948 {
1949 $body = '';
ec9e2d04
CB
1950 //Create unique IDs and preset boundaries
1951 $this->uniqueid = md5(uniqid(time()));
1952 $this->boundary[1] = 'b1_' . $this->uniqueid;
1953 $this->boundary[2] = 'b2_' . $this->uniqueid;
1954 $this->boundary[3] = 'b3_' . $this->uniqueid;
29bf99fd
PS
1955
1956 if ($this->sign_key_file) {
1957 $body .= $this->getMailMIME() . $this->LE;
1958 }
1959
1960 $this->setWordWrap();
1961
ccff58da
MG
1962 $bodyEncoding = $this->Encoding;
1963 $bodyCharSet = $this->CharSet;
ec9e2d04 1964 //Can we do a 7-bit downgrade?
ccff58da
MG
1965 if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
1966 $bodyEncoding = '7bit';
1967 $bodyCharSet = 'us-ascii';
1968 }
ec9e2d04
CB
1969 //If lines are too long, and we're not already using an encoding that will shorten them,
1970 //change to quoted-printable transfer encoding
1971 if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) {
1972 $this->Encoding = 'quoted-printable';
1973 $bodyEncoding = 'quoted-printable';
1974 }
1975
ccff58da
MG
1976 $altBodyEncoding = $this->Encoding;
1977 $altBodyCharSet = $this->CharSet;
ec9e2d04 1978 //Can we do a 7-bit downgrade?
ccff58da
MG
1979 if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
1980 $altBodyEncoding = '7bit';
1981 $altBodyCharSet = 'us-ascii';
1982 }
ec9e2d04
CB
1983 //If lines are too long, change to quoted-printable transfer encoding
1984 if (self::hasLineLongerThanMax($this->AltBody)) {
1985 $altBodyEncoding = 'quoted-printable';
1986 }
1987 //Use this as a preamble in all multipart message types
1988 $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE;
29bf99fd
PS
1989 switch ($this->message_type) {
1990 case 'inline':
ec9e2d04 1991 $body .= $mimepre;
ccff58da
MG
1992 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
1993 $body .= $this->encodeString($this->Body, $bodyEncoding);
29bf99fd
PS
1994 $body .= $this->LE . $this->LE;
1995 $body .= $this->attachAll('inline', $this->boundary[1]);
1996 break;
1997 case 'attach':
ec9e2d04 1998 $body .= $mimepre;
ccff58da
MG
1999 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2000 $body .= $this->encodeString($this->Body, $bodyEncoding);
29bf99fd
PS
2001 $body .= $this->LE . $this->LE;
2002 $body .= $this->attachAll('attachment', $this->boundary[1]);
2003 break;
2004 case 'inline_attach':
ec9e2d04 2005 $body .= $mimepre;
29bf99fd
PS
2006 $body .= $this->textLine('--' . $this->boundary[1]);
2007 $body .= $this->headerLine('Content-Type', 'multipart/related;');
2008 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2009 $body .= $this->LE;
ccff58da
MG
2010 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
2011 $body .= $this->encodeString($this->Body, $bodyEncoding);
29bf99fd
PS
2012 $body .= $this->LE . $this->LE;
2013 $body .= $this->attachAll('inline', $this->boundary[2]);
2014 $body .= $this->LE;
2015 $body .= $this->attachAll('attachment', $this->boundary[1]);
2016 break;
2017 case 'alt':
ec9e2d04 2018 $body .= $mimepre;
ccff58da
MG
2019 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2020 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
29bf99fd 2021 $body .= $this->LE . $this->LE;
ccff58da
MG
2022 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
2023 $body .= $this->encodeString($this->Body, $bodyEncoding);
29bf99fd
PS
2024 $body .= $this->LE . $this->LE;
2025 if (!empty($this->Ical)) {
2026 $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
2027 $body .= $this->encodeString($this->Ical, $this->Encoding);
2028 $body .= $this->LE . $this->LE;
2029 }
2030 $body .= $this->endBoundary($this->boundary[1]);
2031 break;
2032 case 'alt_inline':
ec9e2d04 2033 $body .= $mimepre;
ccff58da
MG
2034 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2035 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
29bf99fd
PS
2036 $body .= $this->LE . $this->LE;
2037 $body .= $this->textLine('--' . $this->boundary[1]);
2038 $body .= $this->headerLine('Content-Type', 'multipart/related;');
2039 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2040 $body .= $this->LE;
ccff58da
MG
2041 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2042 $body .= $this->encodeString($this->Body, $bodyEncoding);
29bf99fd
PS
2043 $body .= $this->LE . $this->LE;
2044 $body .= $this->attachAll('inline', $this->boundary[2]);
2045 $body .= $this->LE;
2046 $body .= $this->endBoundary($this->boundary[1]);
2047 break;
2048 case 'alt_attach':
ec9e2d04 2049 $body .= $mimepre;
29bf99fd
PS
2050 $body .= $this->textLine('--' . $this->boundary[1]);
2051 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2052 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2053 $body .= $this->LE;
ccff58da
MG
2054 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2055 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
29bf99fd 2056 $body .= $this->LE . $this->LE;
ccff58da
MG
2057 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2058 $body .= $this->encodeString($this->Body, $bodyEncoding);
29bf99fd
PS
2059 $body .= $this->LE . $this->LE;
2060 $body .= $this->endBoundary($this->boundary[2]);
2061 $body .= $this->LE;
2062 $body .= $this->attachAll('attachment', $this->boundary[1]);
2063 break;
2064 case 'alt_inline_attach':
ec9e2d04 2065 $body .= $mimepre;
29bf99fd
PS
2066 $body .= $this->textLine('--' . $this->boundary[1]);
2067 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2068 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
2069 $body .= $this->LE;
ccff58da
MG
2070 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2071 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
29bf99fd
PS
2072 $body .= $this->LE . $this->LE;
2073 $body .= $this->textLine('--' . $this->boundary[2]);
2074 $body .= $this->headerLine('Content-Type', 'multipart/related;');
2075 $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
2076 $body .= $this->LE;
ccff58da
MG
2077 $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
2078 $body .= $this->encodeString($this->Body, $bodyEncoding);
29bf99fd
PS
2079 $body .= $this->LE . $this->LE;
2080 $body .= $this->attachAll('inline', $this->boundary[3]);
2081 $body .= $this->LE;
2082 $body .= $this->endBoundary($this->boundary[2]);
2083 $body .= $this->LE;
2084 $body .= $this->attachAll('attachment', $this->boundary[1]);
2085 break;
2086 default:
2087 // catch case 'plain' and case ''
ccff58da 2088 $body .= $this->encodeString($this->Body, $bodyEncoding);
29bf99fd
PS
2089 break;
2090 }
2091
2092 if ($this->isError()) {
2093 $body = '';
2094 } elseif ($this->sign_key_file) {
2095 try {
2096 if (!defined('PKCS7_TEXT')) {
ec9e2d04 2097 throw new phpmailerException($this->lang('extension_missing') . 'openssl');
29bf99fd 2098 }
ccff58da 2099 // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
29bf99fd 2100 $file = tempnam(sys_get_temp_dir(), 'mail');
ec9e2d04
CB
2101 if (false === file_put_contents($file, $body)) {
2102 throw new phpmailerException($this->lang('signing') . ' Could not write temp file');
2103 }
29bf99fd 2104 $signed = tempnam(sys_get_temp_dir(), 'signed');
ec9e2d04
CB
2105 //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
2106 if (empty($this->sign_extracerts_file)) {
2107 $sign = @openssl_pkcs7_sign(
2108 $file,
2109 $signed,
2110 'file://' . realpath($this->sign_cert_file),
2111 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2112 null
2113 );
2114 } else {
2115 $sign = @openssl_pkcs7_sign(
2116 $file,
2117 $signed,
2118 'file://' . realpath($this->sign_cert_file),
2119 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
2120 null,
2121 PKCS7_DETACHED,
2122 $this->sign_extracerts_file
2123 );
2124 }
2125 if ($sign) {
29bf99fd
PS
2126 @unlink($file);
2127 $body = file_get_contents($signed);
2128 @unlink($signed);
ec9e2d04
CB
2129 //The message returned by openssl contains both headers and body, so need to split them up
2130 $parts = explode("\n\n", $body, 2);
2131 $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE;
2132 $body = $parts[1];
29bf99fd
PS
2133 } else {
2134 @unlink($file);
2135 @unlink($signed);
2136 throw new phpmailerException($this->lang('signing') . openssl_error_string());
2137 }
ccff58da 2138 } catch (phpmailerException $exc) {
29bf99fd
PS
2139 $body = '';
2140 if ($this->exceptions) {
ccff58da 2141 throw $exc;
29bf99fd
PS
2142 }
2143 }
2144 }
2145 return $body;
2146 }
2147
2148 /**
2149 * Return the start of a message boundary.
2150 * @access protected
2151 * @param string $boundary
2152 * @param string $charSet
2153 * @param string $contentType
2154 * @param string $encoding
2155 * @return string
2156 */
2157 protected function getBoundary($boundary, $charSet, $contentType, $encoding)
2158 {
2159 $result = '';
2160 if ($charSet == '') {
2161 $charSet = $this->CharSet;
2162 }
2163 if ($contentType == '') {
2164 $contentType = $this->ContentType;
2165 }
2166 if ($encoding == '') {
2167 $encoding = $this->Encoding;
2168 }
2169 $result .= $this->textLine('--' . $boundary);
ccff58da 2170 $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
29bf99fd 2171 $result .= $this->LE;
ccff58da
MG
2172 // RFC1341 part 5 says 7bit is assumed if not specified
2173 if ($encoding != '7bit') {
2174 $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
2175 }
29bf99fd
PS
2176 $result .= $this->LE;
2177
2178 return $result;
2179 }
2180
2181 /**
2182 * Return the end of a message boundary.
2183 * @access protected
2184 * @param string $boundary
2185 * @return string
2186 */
2187 protected function endBoundary($boundary)
2188 {
2189 return $this->LE . '--' . $boundary . '--' . $this->LE;
2190 }
2191
2192 /**
2193 * Set the message type.
2194 * PHPMailer only supports some preset message types,
2195 * not arbitrary MIME structures.
2196 * @access protected
2197 * @return void
2198 */
2199 protected function setMessageType()
2200 {
ccff58da 2201 $type = array();
29bf99fd 2202 if ($this->alternativeExists()) {
ccff58da 2203 $type[] = 'alt';
29bf99fd
PS
2204 }
2205 if ($this->inlineImageExists()) {
ccff58da 2206 $type[] = 'inline';
29bf99fd
PS
2207 }
2208 if ($this->attachmentExists()) {
ccff58da 2209 $type[] = 'attach';
29bf99fd 2210 }
ccff58da
MG
2211 $this->message_type = implode('_', $type);
2212 if ($this->message_type == '') {
2213 $this->message_type = 'plain';
29bf99fd
PS
2214 }
2215 }
2216
2217 /**
2218 * Format a header line.
2219 * @access public
2220 * @param string $name
2221 * @param string $value
2222 * @return string
2223 */
2224 public function headerLine($name, $value)
2225 {
2226 return $name . ': ' . $value . $this->LE;
2227 }
2228
2229 /**
2230 * Return a formatted mail line.
2231 * @access public
2232 * @param string $value
2233 * @return string
2234 */
2235 public function textLine($value)
2236 {
2237 return $value . $this->LE;
2238 }
2239
2240 /**
2241 * Add an attachment from a path on the filesystem.
2242 * Returns false if the file could not be found or read.
2243 * @param string $path Path to the attachment.
2244 * @param string $name Overrides the attachment name.
2245 * @param string $encoding File encoding (see $Encoding).
2246 * @param string $type File extension (MIME) type.
2247 * @param string $disposition Disposition to use
2248 * @throws phpmailerException
ccff58da 2249 * @return boolean
29bf99fd
PS
2250 */
2251 public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2252 {
2253 try {
2254 if (!@is_file($path)) {
2255 throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
2256 }
2257
ccff58da 2258 // If a MIME type is not specified, try to work it out from the file name
29bf99fd
PS
2259 if ($type == '') {
2260 $type = self::filenameToType($path);
2261 }
2262
2263 $filename = basename($path);
2264 if ($name == '') {
2265 $name = $filename;
2266 }
2267
2268 $this->attachment[] = array(
2269 0 => $path,
2270 1 => $filename,
2271 2 => $name,
2272 3 => $encoding,
2273 4 => $type,
2274 5 => false, // isStringAttachment
2275 6 => $disposition,
2276 7 => 0
2277 );
2278
ccff58da
MG
2279 } catch (phpmailerException $exc) {
2280 $this->setError($exc->getMessage());
2281 $this->edebug($exc->getMessage());
29bf99fd 2282 if ($this->exceptions) {
ccff58da 2283 throw $exc;
29bf99fd 2284 }
29bf99fd
PS
2285 return false;
2286 }
2287 return true;
2288 }
2289
2290 /**
2291 * Return the array of attachments.
2292 * @return array
2293 */
2294 public function getAttachments()
2295 {
2296 return $this->attachment;
2297 }
2298
2299 /**
2300 * Attach all file, string, and binary attachments to the message.
2301 * Returns an empty string on failure.
2302 * @access protected
2303 * @param string $disposition_type
2304 * @param string $boundary
2305 * @return string
2306 */
2307 protected function attachAll($disposition_type, $boundary)
2308 {
2309 // Return text of body
2310 $mime = array();
2311 $cidUniq = array();
2312 $incl = array();
2313
2314 // Add all attachments
2315 foreach ($this->attachment as $attachment) {
2316 // Check if it is a valid disposition_filter
2317 if ($attachment[6] == $disposition_type) {
2318 // Check for string attachment
2319 $string = '';
2320 $path = '';
2321 $bString = $attachment[5];
2322 if ($bString) {
2323 $string = $attachment[0];
2324 } else {
2325 $path = $attachment[0];
2326 }
2327
2328 $inclhash = md5(serialize($attachment));
2329 if (in_array($inclhash, $incl)) {
2330 continue;
2331 }
2332 $incl[] = $inclhash;
2333 $name = $attachment[2];
2334 $encoding = $attachment[3];
2335 $type = $attachment[4];
2336 $disposition = $attachment[6];
2337 $cid = $attachment[7];
2338 if ($disposition == 'inline' && isset($cidUniq[$cid])) {
2339 continue;
2340 }
2341 $cidUniq[$cid] = true;
2342
ccff58da 2343 $mime[] = sprintf('--%s%s', $boundary, $this->LE);
ec9e2d04
CB
2344 //Only include a filename property if we have one
2345 if (!empty($name)) {
2346 $mime[] = sprintf(
2347 'Content-Type: %s; name="%s"%s',
2348 $type,
2349 $this->encodeHeader($this->secureHeader($name)),
2350 $this->LE
2351 );
2352 } else {
2353 $mime[] = sprintf(
2354 'Content-Type: %s%s',
2355 $type,
2356 $this->LE
2357 );
2358 }
ccff58da
MG
2359 // RFC1341 part 5 says 7bit is assumed if not specified
2360 if ($encoding != '7bit') {
2361 $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
2362 }
29bf99fd
PS
2363
2364 if ($disposition == 'inline') {
ccff58da 2365 $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
29bf99fd
PS
2366 }
2367
2368 // If a filename contains any of these chars, it should be quoted,
2369 // but not otherwise: RFC2183 & RFC2045 5.1
2370 // Fixes a warning in IETF's msglint MIME checker
2371 // Allow for bypassing the Content-Disposition header totally
2372 if (!(empty($disposition))) {
ccff58da
MG
2373 $encoded_name = $this->encodeHeader($this->secureHeader($name));
2374 if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
29bf99fd 2375 $mime[] = sprintf(
ccff58da 2376 'Content-Disposition: %s; filename="%s"%s',
29bf99fd 2377 $disposition,
ccff58da 2378 $encoded_name,
29bf99fd
PS
2379 $this->LE . $this->LE
2380 );
2381 } else {
ec9e2d04
CB
2382 if (!empty($encoded_name)) {
2383 $mime[] = sprintf(
2384 'Content-Disposition: %s; filename=%s%s',
2385 $disposition,
2386 $encoded_name,
2387 $this->LE . $this->LE
2388 );
2389 } else {
2390 $mime[] = sprintf(
2391 'Content-Disposition: %s%s',
2392 $disposition,
2393 $this->LE . $this->LE
2394 );
2395 }
29bf99fd
PS
2396 }
2397 } else {
2398 $mime[] = $this->LE;
2399 }
2400
2401 // Encode as string attachment
2402 if ($bString) {
2403 $mime[] = $this->encodeString($string, $encoding);
2404 if ($this->isError()) {
2405 return '';
2406 }
2407 $mime[] = $this->LE . $this->LE;
2408 } else {
2409 $mime[] = $this->encodeFile($path, $encoding);
2410 if ($this->isError()) {
2411 return '';
2412 }
2413 $mime[] = $this->LE . $this->LE;
2414 }
2415 }
2416 }
2417
ccff58da 2418 $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
29bf99fd 2419
ccff58da 2420 return implode('', $mime);
29bf99fd
PS
2421 }
2422
2423 /**
2424 * Encode a file attachment in requested format.
2425 * Returns an empty string on failure.
2426 * @param string $path The full path to the file
2427 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2428 * @throws phpmailerException
2429 * @see EncodeFile(encodeFile
2430 * @access protected
2431 * @return string
2432 */
2433 protected function encodeFile($path, $encoding = 'base64')
2434 {
2435 try {
2436 if (!is_readable($path)) {
2437 throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
2438 }
2439 $magic_quotes = get_magic_quotes_runtime();
2440 if ($magic_quotes) {
2441 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
ccff58da 2442 set_magic_quotes_runtime(false);
29bf99fd 2443 } else {
ccff58da
MG
2444 //Doesn't exist in PHP 5.4, but we don't need to check because
2445 //get_magic_quotes_runtime always returns false in 5.4+
2446 //so it will never get here
ec9e2d04 2447 ini_set('magic_quotes_runtime', false);
29bf99fd
PS
2448 }
2449 }
2450 $file_buffer = file_get_contents($path);
2451 $file_buffer = $this->encodeString($file_buffer, $encoding);
2452 if ($magic_quotes) {
2453 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2454 set_magic_quotes_runtime($magic_quotes);
2455 } else {
ec9e2d04 2456 ini_set('magic_quotes_runtime', $magic_quotes);
29bf99fd
PS
2457 }
2458 }
2459 return $file_buffer;
ccff58da
MG
2460 } catch (Exception $exc) {
2461 $this->setError($exc->getMessage());
29bf99fd
PS
2462 return '';
2463 }
2464 }
2465
2466 /**
2467 * Encode a string in requested format.
2468 * Returns an empty string on failure.
2469 * @param string $str The text to encode
2470 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2471 * @access public
2472 * @return string
2473 */
2474 public function encodeString($str, $encoding = 'base64')
2475 {
2476 $encoded = '';
2477 switch (strtolower($encoding)) {
2478 case 'base64':
2479 $encoded = chunk_split(base64_encode($str), 76, $this->LE);
2480 break;
2481 case '7bit':
2482 case '8bit':
2483 $encoded = $this->fixEOL($str);
ccff58da 2484 // Make sure it ends with a line break
29bf99fd
PS
2485 if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
2486 $encoded .= $this->LE;
2487 }
2488 break;
2489 case 'binary':
2490 $encoded = $str;
2491 break;
2492 case 'quoted-printable':
2493 $encoded = $this->encodeQP($str);
2494 break;
2495 default:
2496 $this->setError($this->lang('encoding') . $encoding);
2497 break;
2498 }
2499 return $encoded;
2500 }
2501
2502 /**
2503 * Encode a header string optimally.
2504 * Picks shortest of Q, B, quoted-printable or none.
2505 * @access public
2506 * @param string $str
2507 * @param string $position
2508 * @return string
2509 */
2510 public function encodeHeader($str, $position = 'text')
2511 {
ccff58da 2512 $matchcount = 0;
29bf99fd
PS
2513 switch (strtolower($position)) {
2514 case 'phrase':
2515 if (!preg_match('/[\200-\377]/', $str)) {
ccff58da 2516 // Can't use addslashes as we don't know the value of magic_quotes_sybase
29bf99fd
PS
2517 $encoded = addcslashes($str, "\0..\37\177\\\"");
2518 if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
2519 return ($encoded);
2520 } else {
2521 return ("\"$encoded\"");
2522 }
2523 }
ccff58da 2524 $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
29bf99fd
PS
2525 break;
2526 /** @noinspection PhpMissingBreakStatementInspection */
2527 case 'comment':
ccff58da 2528 $matchcount = preg_match_all('/[()"]/', $str, $matches);
29bf99fd
PS
2529 // Intentional fall-through
2530 case 'text':
2531 default:
ccff58da 2532 $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
29bf99fd
PS
2533 break;
2534 }
2535
ec9e2d04
CB
2536 //There are no chars that need encoding
2537 if ($matchcount == 0) {
29bf99fd
PS
2538 return ($str);
2539 }
2540
2541 $maxlen = 75 - 7 - strlen($this->CharSet);
2542 // Try to select the encoding which should produce the shortest output
ccff58da
MG
2543 if ($matchcount > strlen($str) / 3) {
2544 // More than a third of the content will need encoding, so B encoding will be most efficient
29bf99fd
PS
2545 $encoding = 'B';
2546 if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
2547 // Use a custom function which correctly encodes and wraps long
2548 // multibyte strings without breaking lines within a character
2549 $encoded = $this->base64EncodeWrapMB($str, "\n");
2550 } else {
2551 $encoded = base64_encode($str);
2552 $maxlen -= $maxlen % 4;
2553 $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
2554 }
0747daba 2555 } else {
29bf99fd
PS
2556 $encoding = 'Q';
2557 $encoded = $this->encodeQ($str, $position);
2558 $encoded = $this->wrapText($encoded, $maxlen, true);
2559 $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
2560 }
2561
ccff58da 2562 $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
29bf99fd
PS
2563 $encoded = trim(str_replace("\n", $this->LE, $encoded));
2564
2565 return $encoded;
2566 }
2567
2568 /**
2569 * Check if a string contains multi-byte characters.
2570 * @access public
2571 * @param string $str multi-byte text to wrap encode
ccff58da 2572 * @return boolean
29bf99fd
PS
2573 */
2574 public function hasMultiBytes($str)
2575 {
2576 if (function_exists('mb_strlen')) {
2577 return (strlen($str) > mb_strlen($str, $this->CharSet));
2578 } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
2579 return false;
2580 }
2581 }
2582
ccff58da
MG
2583 /**
2584 * Does a string contain any 8-bit chars (in any charset)?
2585 * @param string $text
2586 * @return boolean
2587 */
2588 public function has8bitChars($text)
2589 {
2590 return (boolean)preg_match('/[\x80-\xFF]/', $text);
2591 }
2592
29bf99fd
PS
2593 /**
2594 * Encode and wrap long multibyte strings for mail headers
2595 * without breaking lines within a character.
ccff58da
MG
2596 * Adapted from a function by paravoid
2597 * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
29bf99fd
PS
2598 * @access public
2599 * @param string $str multi-byte text to wrap encode
ccff58da 2600 * @param string $linebreak string to use as linefeed/end-of-line
29bf99fd
PS
2601 * @return string
2602 */
ccff58da 2603 public function base64EncodeWrapMB($str, $linebreak = null)
29bf99fd 2604 {
ccff58da
MG
2605 $start = '=?' . $this->CharSet . '?B?';
2606 $end = '?=';
2607 $encoded = '';
2608 if ($linebreak === null) {
2609 $linebreak = $this->LE;
29bf99fd
PS
2610 }
2611
2612 $mb_length = mb_strlen($str, $this->CharSet);
2613 // Each line must have length <= 75, including $start and $end
2614 $length = 75 - strlen($start) - strlen($end);
2615 // Average multi-byte ratio
2616 $ratio = $mb_length / strlen($str);
2617 // Base64 has a 4:3 ratio
2618 $avgLength = floor($length * $ratio * .75);
2619
2620 for ($i = 0; $i < $mb_length; $i += $offset) {
2621 $lookBack = 0;
2622 do {
2623 $offset = $avgLength - $lookBack;
2624 $chunk = mb_substr($str, $i, $offset, $this->CharSet);
2625 $chunk = base64_encode($chunk);
2626 $lookBack++;
2627 } while (strlen($chunk) > $length);
ccff58da 2628 $encoded .= $chunk . $linebreak;
29bf99fd
PS
2629 }
2630
2631 // Chomp the last linefeed
ccff58da 2632 $encoded = substr($encoded, 0, -strlen($linebreak));
29bf99fd
PS
2633 return $encoded;
2634 }
2635
2636 /**
2637 * Encode a string in quoted-printable format.
2638 * According to RFC2045 section 6.7.
2639 * @access public
2640 * @param string $string The text to encode
2641 * @param integer $line_max Number of chars allowed on a line before wrapping
2642 * @return string
ccff58da 2643 * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
29bf99fd
PS
2644 */
2645 public function encodeQP($string, $line_max = 76)
2646 {
ec9e2d04
CB
2647 // Use native function if it's available (>= PHP5.3)
2648 if (function_exists('quoted_printable_encode')) {
2649 return quoted_printable_encode($string);
29bf99fd 2650 }
ccff58da 2651 // Fall back to a pure PHP implementation
29bf99fd
PS
2652 $string = str_replace(
2653 array('%20', '%0D%0A.', '%0D%0A', '%'),
2654 array(' ', "\r\n=2E", "\r\n", '='),
2655 rawurlencode($string)
2656 );
ec9e2d04 2657 return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
29bf99fd
PS
2658 }
2659
2660 /**
2661 * Backward compatibility wrapper for an old QP encoding function that was removed.
2662 * @see PHPMailer::encodeQP()
2663 * @access public
2664 * @param string $string
2665 * @param integer $line_max
ccff58da 2666 * @param boolean $space_conv
29bf99fd
PS
2667 * @return string
2668 * @deprecated Use encodeQP instead.
2669 */
2670 public function encodeQPphp(
2671 $string,
2672 $line_max = 76,
2673 /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
2674 ) {
2675 return $this->encodeQP($string, $line_max);
2676 }
2677
2678 /**
2679 * Encode a string using Q encoding.
2680 * @link http://tools.ietf.org/html/rfc2047
2681 * @param string $str the text to encode
2682 * @param string $position Where the text is going to be used, see the RFC for what that means
2683 * @access public
2684 * @return string
2685 */
2686 public function encodeQ($str, $position = 'text')
2687 {
ccff58da 2688 // There should not be any EOL in the string
29bf99fd
PS
2689 $pattern = '';
2690 $encoded = str_replace(array("\r", "\n"), '', $str);
2691 switch (strtolower($position)) {
2692 case 'phrase':
ccff58da 2693 // RFC 2047 section 5.3
29bf99fd
PS
2694 $pattern = '^A-Za-z0-9!*+\/ -';
2695 break;
2696 /** @noinspection PhpMissingBreakStatementInspection */
2697 case 'comment':
ccff58da 2698 // RFC 2047 section 5.2
29bf99fd 2699 $pattern = '\(\)"';
ccff58da
MG
2700 // intentional fall-through
2701 // for this reason we build the $pattern without including delimiters and []
29bf99fd
PS
2702 case 'text':
2703 default:
ccff58da
MG
2704 // RFC 2047 section 5.1
2705 // Replace every high ascii, control, =, ? and _ characters
29bf99fd
PS
2706 $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
2707 break;
2708 }
2709 $matches = array();
2710 if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
ccff58da
MG
2711 // If the string contains an '=', make sure it's the first thing we replace
2712 // so as to avoid double-encoding
2713 $eqkey = array_search('=', $matches[0]);
ec9e2d04 2714 if (false !== $eqkey) {
ccff58da 2715 unset($matches[0][$eqkey]);
29bf99fd
PS
2716 array_unshift($matches[0], '=');
2717 }
2718 foreach (array_unique($matches[0]) as $char) {
2719 $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
2720 }
2721 }
ccff58da 2722 // Replace every spaces to _ (more readable than =20)
29bf99fd
PS
2723 return str_replace(' ', '_', $encoded);
2724 }
2725
2726
2727 /**
2728 * Add a string or binary attachment (non-filesystem).
2729 * This method can be used to attach ascii or binary data,
2730 * such as a BLOB record from a database.
2731 * @param string $string String attachment data.
2732 * @param string $filename Name of the attachment.
2733 * @param string $encoding File encoding (see $Encoding).
2734 * @param string $type File extension (MIME) type.
2735 * @param string $disposition Disposition to use
2736 * @return void
2737 */
2738 public function addStringAttachment(
2739 $string,
2740 $filename,
2741 $encoding = 'base64',
2742 $type = '',
2743 $disposition = 'attachment'
2744 ) {
ccff58da 2745 // If a MIME type is not specified, try to work it out from the file name
29bf99fd
PS
2746 if ($type == '') {
2747 $type = self::filenameToType($filename);
2748 }
2749 // Append to $attachment array
2750 $this->attachment[] = array(
2751 0 => $string,
2752 1 => $filename,
2753 2 => basename($filename),
2754 3 => $encoding,
2755 4 => $type,
2756 5 => true, // isStringAttachment
2757 6 => $disposition,
2758 7 => 0
2759 );
2760 }
2761
2762 /**
2763 * Add an embedded (inline) attachment from a file.
2764 * This can include images, sounds, and just about any other document type.
ec9e2d04 2765 * These differ from 'regular' attachments in that they are intended to be
29bf99fd
PS
2766 * displayed inline with the message, not just attached for download.
2767 * This is used in HTML messages that embed the images
2768 * the HTML refers to using the $cid value.
2769 * @param string $path Path to the attachment.
2770 * @param string $cid Content ID of the attachment; Use this to reference
2771 * the content when using an embedded image in HTML.
2772 * @param string $name Overrides the attachment name.
2773 * @param string $encoding File encoding (see $Encoding).
2774 * @param string $type File MIME type.
2775 * @param string $disposition Disposition to use
ccff58da 2776 * @return boolean True on successfully adding an attachment
29bf99fd
PS
2777 */
2778 public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
2779 {
2780 if (!@is_file($path)) {
2781 $this->setError($this->lang('file_access') . $path);
2782 return false;
2783 }
2784
ccff58da 2785 // If a MIME type is not specified, try to work it out from the file name
29bf99fd
PS
2786 if ($type == '') {
2787 $type = self::filenameToType($path);
2788 }
2789
2790 $filename = basename($path);
2791 if ($name == '') {
2792 $name = $filename;
2793 }
2794
2795 // Append to $attachment array
2796 $this->attachment[] = array(
2797 0 => $path,
2798 1 => $filename,
2799 2 => $name,
2800 3 => $encoding,
2801 4 => $type,
2802 5 => false, // isStringAttachment
2803 6 => $disposition,
2804 7 => $cid
2805 );
0b821adf 2806 return true;
2807 }
ed546836 2808
29bf99fd
PS
2809 /**
2810 * Add an embedded stringified attachment.
2811 * This can include images, sounds, and just about any other document type.
2812 * Be sure to set the $type to an image type for images:
2813 * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
2814 * @param string $string The attachment binary data.
2815 * @param string $cid Content ID of the attachment; Use this to reference
2816 * the content when using an embedded image in HTML.
2817 * @param string $name
2818 * @param string $encoding File encoding (see $Encoding).
2819 * @param string $type MIME type.
2820 * @param string $disposition Disposition to use
ccff58da 2821 * @return boolean True on successfully adding an attachment
29bf99fd
PS
2822 */
2823 public function addStringEmbeddedImage(
2824 $string,
2825 $cid,
2826 $name = '',
2827 $encoding = 'base64',
2828 $type = '',
2829 $disposition = 'inline'
2830 ) {
ccff58da 2831 // If a MIME type is not specified, try to work it out from the name
ec9e2d04 2832 if ($type == '' and !empty($name)) {
29bf99fd
PS
2833 $type = self::filenameToType($name);
2834 }
2835
2836 // Append to $attachment array
2837 $this->attachment[] = array(
2838 0 => $string,
2839 1 => $name,
2840 2 => $name,
2841 3 => $encoding,
2842 4 => $type,
2843 5 => true, // isStringAttachment
2844 6 => $disposition,
2845 7 => $cid
2846 );
0747daba 2847 return true;
29bf99fd
PS
2848 }
2849
2850 /**
2851 * Check if an inline attachment is present.
2852 * @access public
ccff58da 2853 * @return boolean
29bf99fd
PS
2854 */
2855 public function inlineImageExists()
2856 {
2857 foreach ($this->attachment as $attachment) {
2858 if ($attachment[6] == 'inline') {
2859 return true;
2860 }
2861 }
ed546836 2862 return false;
29bf99fd
PS
2863 }
2864
2865 /**
2866 * Check if an attachment (non-inline) is present.
ccff58da 2867 * @return boolean
29bf99fd
PS
2868 */
2869 public function attachmentExists()
2870 {
2871 foreach ($this->attachment as $attachment) {
2872 if ($attachment[6] == 'attachment') {
2873 return true;
2874 }
2875 }
2876 return false;
2877 }
2878
2879 /**
2880 * Check if this message has an alternative body set.
ccff58da 2881 * @return boolean
29bf99fd
PS
2882 */
2883 public function alternativeExists()
2884 {
2885 return !empty($this->AltBody);
2886 }
2887
2888 /**
2889 * Clear all To recipients.
2890 * @return void
2891 */
2892 public function clearAddresses()
2893 {
2894 foreach ($this->to as $to) {
2895 unset($this->all_recipients[strtolower($to[0])]);
2896 }
2897 $this->to = array();
2898 }
2899
2900 /**
2901 * Clear all CC recipients.
2902 * @return void
2903 */
2904 public function clearCCs()
2905 {
2906 foreach ($this->cc as $cc) {
2907 unset($this->all_recipients[strtolower($cc[0])]);
2908 }
2909 $this->cc = array();
2910 }
2911
2912 /**
2913 * Clear all BCC recipients.
2914 * @return void
2915 */
2916 public function clearBCCs()
2917 {
2918 foreach ($this->bcc as $bcc) {
2919 unset($this->all_recipients[strtolower($bcc[0])]);
2920 }
2921 $this->bcc = array();
2922 }
2923
2924 /**
2925 * Clear all ReplyTo recipients.
2926 * @return void
2927 */
2928 public function clearReplyTos()
2929 {
2930 $this->ReplyTo = array();
2931 }
2932
2933 /**
2934 * Clear all recipient types.
2935 * @return void
2936 */
2937 public function clearAllRecipients()
2938 {
2939 $this->to = array();
2940 $this->cc = array();
2941 $this->bcc = array();
2942 $this->all_recipients = array();
2943 }
2944
2945 /**
2946 * Clear all filesystem, string, and binary attachments.
2947 * @return void
2948 */
2949 public function clearAttachments()
2950 {
2951 $this->attachment = array();
2952 }
2953
2954 /**
2955 * Clear all custom headers.
2956 * @return void
2957 */
2958 public function clearCustomHeaders()
2959 {
2960 $this->CustomHeader = array();
2961 }
2962
2963 /**
2964 * Add an error message to the error container.
2965 * @access protected
2966 * @param string $msg
2967 * @return void
2968 */
2969 protected function setError($msg)
2970 {
2971 $this->error_count++;
2972 if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
2973 $lasterror = $this->smtp->getError();
ec9e2d04
CB
2974 if (!empty($lasterror['error'])) {
2975 $msg .= $this->lang('smtp_error') . $lasterror['error'];
2976 if (!empty($lasterror['detail'])) {
2977 $msg .= ' Detail: '. $lasterror['detail'];
2978 }
2979 if (!empty($lasterror['smtp_code'])) {
2980 $msg .= ' SMTP code: ' . $lasterror['smtp_code'];
2981 }
2982 if (!empty($lasterror['smtp_code_ex'])) {
2983 $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
2984 }
29bf99fd
PS
2985 }
2986 }
2987 $this->ErrorInfo = $msg;
2988 }
2989
2990 /**
2991 * Return an RFC 822 formatted date.
2992 * @access public
2993 * @return string
2994 * @static
2995 */
2996 public static function rfcDate()
2997 {
ccff58da
MG
2998 // Set the time zone to whatever the default is to avoid 500 errors
2999 // Will default to UTC if it's not set properly in php.ini
29bf99fd
PS
3000 date_default_timezone_set(@date_default_timezone_get());
3001 return date('D, j M Y H:i:s O');
3002 }
3003
3004 /**
3005 * Get the server hostname.
3006 * Returns 'localhost.localdomain' if unknown.
3007 * @access protected
3008 * @return string
3009 */
3010 protected function serverHostname()
3011 {
ccff58da 3012 $result = 'localhost.localdomain';
29bf99fd
PS
3013 if (!empty($this->Hostname)) {
3014 $result = $this->Hostname;
ccff58da 3015 } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
29bf99fd 3016 $result = $_SERVER['SERVER_NAME'];
ccff58da
MG
3017 } elseif (function_exists('gethostname') && gethostname() !== false) {
3018 $result = gethostname();
3019 } elseif (php_uname('n') !== false) {
3020 $result = php_uname('n');
29bf99fd 3021 }
29bf99fd
PS
3022 return $result;
3023 }
3024
3025 /**
3026 * Get an error message in the current language.
3027 * @access protected
3028 * @param string $key
3029 * @return string
3030 */
3031 protected function lang($key)
3032 {
3033 if (count($this->language) < 1) {
3034 $this->setLanguage('en'); // set the default language
3035 }
3036
ec9e2d04
CB
3037 if (array_key_exists($key, $this->language)) {
3038 if ($key == 'smtp_connect_failed') {
3039 //Include a link to troubleshooting docs on SMTP connection failure
3040 //this is by far the biggest cause of support questions
3041 //but it's usually not PHPMailer's fault.
3042 return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
3043 }
29bf99fd
PS
3044 return $this->language[$key];
3045 } else {
ec9e2d04
CB
3046 //Return the key as a fallback
3047 return $key;
29bf99fd
PS
3048 }
3049 }
3050
3051 /**
3052 * Check if an error occurred.
3053 * @access public
ccff58da 3054 * @return boolean True if an error did occur.
29bf99fd
PS
3055 */
3056 public function isError()
3057 {
3058 return ($this->error_count > 0);
3059 }
3060
3061 /**
3062 * Ensure consistent line endings in a string.
3063 * Changes every end of line from CRLF, CR or LF to $this->LE.
3064 * @access public
3065 * @param string $str String to fixEOL
3066 * @return string
3067 */
3068 public function fixEOL($str)
3069 {
3070 // Normalise to \n
3071 $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
3072 // Now convert LE as needed
3073 if ($this->LE !== "\n") {
3074 $nstr = str_replace("\n", $this->LE, $nstr);
3075 }
3076 return $nstr;
3077 }
3078
3079 /**
3080 * Add a custom header.
3081 * $name value can be overloaded to contain
3082 * both header name and value (name:value)
3083 * @access public
3084 * @param string $name Custom header name
3085 * @param string $value Header value
3086 * @return void
3087 */
3088 public function addCustomHeader($name, $value = null)
3089 {
3090 if ($value === null) {
3091 // Value passed in as name:value
3092 $this->CustomHeader[] = explode(':', $name, 2);
3093 } else {
3094 $this->CustomHeader[] = array($name, $value);
3095 }
3096 }
3097
ec9e2d04
CB
3098 /**
3099 * Returns all custom headers
3100 *
3101 * @return array
3102 */
3103 public function getCustomHeaders()
3104 {
3105 return $this->CustomHeader;
3106 }
3107
29bf99fd
PS
3108 /**
3109 * Create a message from an HTML string.
3110 * Automatically makes modifications for inline images and backgrounds
3111 * and creates a plain-text version by converting the HTML.
3112 * Overwrites any existing values in $this->Body and $this->AltBody
3113 * @access public
3114 * @param string $message HTML message string
3115 * @param string $basedir baseline directory for path
ec9e2d04
CB
3116 * @param boolean|callable $advanced Whether to use the internal HTML to text converter
3117 * or your own custom converter @see html2text()
29bf99fd
PS
3118 * @return string $message
3119 */
3120 public function msgHTML($message, $basedir = '', $advanced = false)
3121 {
ccff58da 3122 preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
29bf99fd 3123 if (isset($images[2])) {
ccff58da
MG
3124 foreach ($images[2] as $imgindex => $url) {
3125 // Convert data URIs into embedded images
3126 if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
3127 $data = substr($url, strpos($url, ','));
3128 if ($match[2]) {
3129 $data = base64_decode($data);
3130 } else {
3131 $data = rawurldecode($data);
3132 }
3133 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
ec9e2d04
CB
3134 if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) {
3135 $message = str_replace(
3136 $images[0][$imgindex],
ccff58da
MG
3137 $images[1][$imgindex] . '="cid:' . $cid . '"',
3138 $message
3139 );
3140 }
ec9e2d04 3141 } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[A-z]+://#', $url)) {
ccff58da 3142 // Do not change urls for absolute images (thanks to corvuscorax)
ec9e2d04 3143 // Do not change urls that are already inline images
29bf99fd
PS
3144 $filename = basename($url);
3145 $directory = dirname($url);
3146 if ($directory == '.') {
3147 $directory = '';
3148 }
ccff58da 3149 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
29bf99fd
PS
3150 if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
3151 $basedir .= '/';
3152 }
3153 if (strlen($directory) > 1 && substr($directory, -1) != '/') {
3154 $directory .= '/';
3155 }
3156 if ($this->addEmbeddedImage(
3157 $basedir . $directory . $filename,
3158 $cid,
3159 $filename,
3160 'base64',
ec9e2d04 3161 self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
29bf99fd
PS
3162 )
3163 ) {
3164 $message = preg_replace(
ccff58da
MG
3165 '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
3166 $images[1][$imgindex] . '="cid:' . $cid . '"',
29bf99fd
PS
3167 $message
3168 );
3169 }
3170 }
3171 }
3172 }
3173 $this->isHTML(true);
ccff58da 3174 // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
29bf99fd
PS
3175 $this->Body = $this->normalizeBreaks($message);
3176 $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
ccff58da
MG
3177 if (empty($this->AltBody)) {
3178 $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
3179 self::CRLF . self::CRLF;
3180 }
29bf99fd
PS
3181 return $this->Body;
3182 }
3183
3184 /**
3185 * Convert an HTML string into plain text.
ec9e2d04
CB
3186 * This is used by msgHTML().
3187 * Note - older versions of this function used a bundled advanced converter
3188 * which was been removed for license reasons in #232
3189 * Example usage:
3190 * <code>
3191 * // Use default conversion
3192 * $plain = $mail->html2text($html);
3193 * // Use your own custom converter
3194 * $plain = $mail->html2text($html, function($html) {
3195 * $converter = new MyHtml2text($html);
3196 * return $converter->get_text();
3197 * });
3198 * </code>
29bf99fd 3199 * @param string $html The HTML text to convert
ec9e2d04
CB
3200 * @param boolean|callable $advanced Any boolean value to use the internal converter,
3201 * or provide your own callable for custom conversion.
29bf99fd
PS
3202 * @return string
3203 */
3204 public function html2text($html, $advanced = false)
3205 {
ec9e2d04
CB
3206 if (is_callable($advanced)) {
3207 return call_user_func($advanced, $html);
29bf99fd
PS
3208 }
3209 return html_entity_decode(
3210 trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
3211 ENT_QUOTES,
3212 $this->CharSet
3213 );
3214 }
3215
3216 /**
3217 * Get the MIME type for a file extension.
3218 * @param string $ext File extension
3219 * @access public
3220 * @return string MIME type of file.
3221 * @static
3222 */
3223 public static function _mime_types($ext = '')
3224 {
3225 $mimes = array(
ec9e2d04
CB
3226 'xl' => 'application/excel',
3227 'js' => 'application/javascript',
3228 'hqx' => 'application/mac-binhex40',
3229 'cpt' => 'application/mac-compactpro',
3230 'bin' => 'application/macbinary',
3231 'doc' => 'application/msword',
3232 'word' => 'application/msword',
3233 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
3234 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
3235 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
3236 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
3237 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
3238 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
3239 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
3240 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
3241 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
3242 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
29bf99fd 3243 'class' => 'application/octet-stream',
ec9e2d04
CB
3244 'dll' => 'application/octet-stream',
3245 'dms' => 'application/octet-stream',
3246 'exe' => 'application/octet-stream',
3247 'lha' => 'application/octet-stream',
3248 'lzh' => 'application/octet-stream',
3249 'psd' => 'application/octet-stream',
3250 'sea' => 'application/octet-stream',
3251 'so' => 'application/octet-stream',
3252 'oda' => 'application/oda',
3253 'pdf' => 'application/pdf',
3254 'ai' => 'application/postscript',
3255 'eps' => 'application/postscript',
3256 'ps' => 'application/postscript',
3257 'smi' => 'application/smil',
3258 'smil' => 'application/smil',
3259 'mif' => 'application/vnd.mif',
3260 'xls' => 'application/vnd.ms-excel',
3261 'ppt' => 'application/vnd.ms-powerpoint',
29bf99fd 3262 'wbxml' => 'application/vnd.wap.wbxml',
ec9e2d04
CB
3263 'wmlc' => 'application/vnd.wap.wmlc',
3264 'dcr' => 'application/x-director',
3265 'dir' => 'application/x-director',
3266 'dxr' => 'application/x-director',
3267 'dvi' => 'application/x-dvi',
3268 'gtar' => 'application/x-gtar',
3269 'php3' => 'application/x-httpd-php',
3270 'php4' => 'application/x-httpd-php',
3271 'php' => 'application/x-httpd-php',
29bf99fd 3272 'phtml' => 'application/x-httpd-php',
ec9e2d04
CB
3273 'phps' => 'application/x-httpd-php-source',
3274 'swf' => 'application/x-shockwave-flash',
3275 'sit' => 'application/x-stuffit',
3276 'tar' => 'application/x-tar',
3277 'tgz' => 'application/x-tar',
3278 'xht' => 'application/xhtml+xml',
29bf99fd 3279 'xhtml' => 'application/xhtml+xml',
ec9e2d04
CB
3280 'zip' => 'application/zip',
3281 'mid' => 'audio/midi',
3282 'midi' => 'audio/midi',
3283 'mp2' => 'audio/mpeg',
3284 'mp3' => 'audio/mpeg',
3285 'mpga' => 'audio/mpeg',
3286 'aif' => 'audio/x-aiff',
3287 'aifc' => 'audio/x-aiff',
3288 'aiff' => 'audio/x-aiff',
3289 'ram' => 'audio/x-pn-realaudio',
3290 'rm' => 'audio/x-pn-realaudio',
3291 'rpm' => 'audio/x-pn-realaudio-plugin',
3292 'ra' => 'audio/x-realaudio',
3293 'wav' => 'audio/x-wav',
3294 'bmp' => 'image/bmp',
3295 'gif' => 'image/gif',
3296 'jpeg' => 'image/jpeg',
3297 'jpe' => 'image/jpeg',
3298 'jpg' => 'image/jpeg',
3299 'png' => 'image/png',
3300 'tiff' => 'image/tiff',
3301 'tif' => 'image/tiff',
3302 'eml' => 'message/rfc822',
3303 'css' => 'text/css',
3304 'html' => 'text/html',
3305 'htm' => 'text/html',
29bf99fd 3306 'shtml' => 'text/html',
ec9e2d04
CB
3307 'log' => 'text/plain',
3308 'text' => 'text/plain',
3309 'txt' => 'text/plain',
3310 'rtx' => 'text/richtext',
3311 'rtf' => 'text/rtf',
3312 'vcf' => 'text/vcard',
ccff58da 3313 'vcard' => 'text/vcard',
ec9e2d04
CB
3314 'xml' => 'text/xml',
3315 'xsl' => 'text/xml',
3316 'mpeg' => 'video/mpeg',
3317 'mpe' => 'video/mpeg',
3318 'mpg' => 'video/mpeg',
3319 'mov' => 'video/quicktime',
3320 'qt' => 'video/quicktime',
3321 'rv' => 'video/vnd.rn-realvideo',
3322 'avi' => 'video/x-msvideo',
29bf99fd
PS
3323 'movie' => 'video/x-sgi-movie'
3324 );
ec9e2d04
CB
3325 if (array_key_exists(strtolower($ext), $mimes)) {
3326 return $mimes[strtolower($ext)];
3327 }
3328 return 'application/octet-stream';
29bf99fd
PS
3329 }
3330
3331 /**
3332 * Map a file name to a MIME type.
3333 * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
3334 * @param string $filename A file name or full path, does not need to exist as a file
3335 * @return string
3336 * @static
3337 */
3338 public static function filenameToType($filename)
3339 {
ccff58da 3340 // In case the path is a URL, strip any query string before getting extension
29bf99fd 3341 $qpos = strpos($filename, '?');
ec9e2d04 3342 if (false !== $qpos) {
29bf99fd
PS
3343 $filename = substr($filename, 0, $qpos);
3344 }
3345 $pathinfo = self::mb_pathinfo($filename);
3346 return self::_mime_types($pathinfo['extension']);
3347 }
3348
3349 /**
3350 * Multi-byte-safe pathinfo replacement.
3351 * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
3352 * Works similarly to the one in PHP >= 5.2.0
3353 * @link http://www.php.net/manual/en/function.pathinfo.php#107461
3354 * @param string $path A filename or path, does not need to exist as a file
3355 * @param integer|string $options Either a PATHINFO_* constant,
3356 * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
3357 * @return string|array
3358 * @static
3359 */
3360 public static function mb_pathinfo($path, $options = null)
3361 {
3362 $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
ccff58da
MG
3363 $pathinfo = array();
3364 if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
3365 if (array_key_exists(1, $pathinfo)) {
3366 $ret['dirname'] = $pathinfo[1];
3367 }
3368 if (array_key_exists(2, $pathinfo)) {
3369 $ret['basename'] = $pathinfo[2];
3370 }
3371 if (array_key_exists(5, $pathinfo)) {
3372 $ret['extension'] = $pathinfo[5];
3373 }
3374 if (array_key_exists(3, $pathinfo)) {
3375 $ret['filename'] = $pathinfo[3];
3376 }
29bf99fd
PS
3377 }
3378 switch ($options) {
3379 case PATHINFO_DIRNAME:
3380 case 'dirname':
3381 return $ret['dirname'];
29bf99fd
PS
3382 case PATHINFO_BASENAME:
3383 case 'basename':
3384 return $ret['basename'];
29bf99fd
PS
3385 case PATHINFO_EXTENSION:
3386 case 'extension':
3387 return $ret['extension'];
29bf99fd
PS
3388 case PATHINFO_FILENAME:
3389 case 'filename':
3390 return $ret['filename'];
29bf99fd
PS
3391 default:
3392 return $ret;
3393 }
3394 }
3395
3396 /**