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