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