Gentics Portal.Node PHP API
 All Classes Namespaces Functions Variables Pages
class.phpmailer.php
1 <?php
2 /*~ class.phpmailer.php
3 .---------------------------------------------------------------------------.
4 | Software: PHPMailer - PHP email class |
5 | Version: 5.0.0 |
6 | Contact: via sourceforge.net support pages (also www.codeworxtech.com) |
7 | Info: http://phpmailer.sourceforge.net |
8 | Support: http://sourceforge.net/projects/phpmailer/ |
9 | ------------------------------------------------------------------------- |
10 | Admin: Andy Prevost (project admininistrator) |
11 | Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net |
12 | : Marcus Bointon (coolbru) coolbru@users.sourceforge.net |
13 | Founder: Brent R. Matzelle (original founder) |
14 | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. |
15 | Copyright (c) 2001-2003, Brent R. Matzelle |
16 | ------------------------------------------------------------------------- |
17 | License: Distributed under the Lesser General Public License (LGPL) |
18 | http://www.gnu.org/copyleft/lesser.html |
19 | This program is distributed in the hope that it will be useful - WITHOUT |
20 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
21 | FITNESS FOR A PARTICULAR PURPOSE. |
22 | ------------------------------------------------------------------------- |
23 | We offer a number of paid services (www.codeworxtech.com): |
24 | - Web Hosting on highly optimized fast and secure servers |
25 | - Technology Consulting |
26 | - Oursourcing (highly qualified programmers and graphic designers) |
27 '---------------------------------------------------------------------------'
28 */
29 
30 /**
31  * PHPMailer - PHP email transport class
32  * NOTE: Requires PHP version 5 or later
33  * @package PHPMailer
34  * @author Andy Prevost
35  * @author Marcus Bointon
36  * @copyright 2004 - 2009 Andy Prevost
37  * @version $Id: class.phpmailer.php 254 2009-04-02 18:52:18Z codeworxtech $
38  * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
39  */
40 
41 if (version_compare(PHP_VERSION, '5.0.0', '<') ) exit("Sorry, this version of PHPMailer will only run on PHP version 5 or greater!\n");
42 
43 class PHPMailer {
44 
45  /////////////////////////////////////////////////
46  // PROPERTIES, PUBLIC
47  /////////////////////////////////////////////////
48 
49  /**
50  * Email priority (1 = High, 3 = Normal, 5 = low).
51  * @var int
52  */
53  public $Priority = 3;
54 
55  /**
56  * Sets the CharSet of the message.
57  * @var string
58  */
59  public $CharSet = 'iso-8859-1';
60 
61  /**
62  * Sets the Content-type of the message.
63  * @var string
64  */
65  public $ContentType = 'text/plain';
66 
67  /**
68  * Sets the Encoding of the message. Options for this are
69  * "8bit", "7bit", "binary", "base64", and "quoted-printable".
70  * @var string
71  */
72  public $Encoding = '8bit';
73 
74  /**
75  * Holds the most recent mailer error message.
76  * @var string
77  */
78  public $ErrorInfo = '';
79 
80  /**
81  * Sets the From email address for the message.
82  * @var string
83  */
84  public $From = 'root@localhost';
85 
86  /**
87  * Sets the From name of the message.
88  * @var string
89  */
90  public $FromName = 'Root User';
91 
92  /**
93  * Sets the Sender email (Return-Path) of the message. If not empty,
94  * will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
95  * @var string
96  */
97  public $Sender = '';
98 
99  /**
100  * Sets the Subject of the message.
101  * @var string
102  */
103  public $Subject = '';
104 
105  /**
106  * Sets the Body of the message. This can be either an HTML or text body.
107  * If HTML then run IsHTML(true).
108  * @var string
109  */
110  public $Body = '';
111 
112  /**
113  * Sets the text-only body of the message. This automatically sets the
114  * email to multipart/alternative. This body can be read by mail
115  * clients that do not have HTML email capability such as mutt. Clients
116  * that can read HTML will view the normal Body.
117  * @var string
118  */
119  public $AltBody = '';
120 
121  /**
122  * Sets word wrapping on the body of the message to a given number of
123  * characters.
124  * @var int
125  */
126  public $WordWrap = 0;
127 
128  /**
129  * Method to send mail: ("mail", "sendmail", or "smtp").
130  * @var string
131  */
132  public $Mailer = 'mail';
133 
134  /**
135  * Sets the path of the sendmail program.
136  * @var string
137  */
138  public $Sendmail = '/usr/sbin/sendmail';
139 
140  /**
141  * Path to PHPMailer plugins. Useful if the SMTP class
142  * is in a different directory than the PHP include path.
143  * @var string
144  */
145  public $PluginDir = '';
146 
147  /**
148  * Sets the email address that a reading confirmation will be sent.
149  * @var string
150  */
151  public $ConfirmReadingTo = '';
152 
153  /**
154  * Sets the hostname to use in Message-Id and Received headers
155  * and as default HELO string. If empty, the value returned
156  * by SERVER_NAME is used or 'localhost.localdomain'.
157  * @var string
158  */
159  public $Hostname = '';
160 
161  /**
162  * Sets the message ID to be used in the Message-Id header.
163  * If empty, a unique id will be generated.
164  * @var string
165  */
166  public $MessageID = '';
167 
168  /////////////////////////////////////////////////
169  // PROPERTIES FOR SMTP
170  /////////////////////////////////////////////////
171 
172  /**
173  * Sets the SMTP hosts. All hosts must be separated by a
174  * semicolon. You can also specify a different port
175  * for each host by using this format: [hostname:port]
176  * (e.g. "smtp1.example.com:25;smtp2.example.com").
177  * Hosts will be tried in order.
178  * @var string
179  */
180  public $Host = 'localhost';
181 
182  /**
183  * Sets the default SMTP server port.
184  * @var int
185  */
186  public $Port = 25;
187 
188  /**
189  * Sets the SMTP HELO of the message (Default is $Hostname).
190  * @var string
191  */
192  public $Helo = '';
193 
194  /**
195  * Sets connection prefix.
196  * Options are "", "ssl" or "tls"
197  * @var string
198  */
199  public $SMTPSecure = '';
200 
201  /**
202  * Sets SMTP authentication. Utilizes the Username and Password variables.
203  * @var bool
204  */
205  public $SMTPAuth = false;
206 
207  /**
208  * Sets SMTP username.
209  * @var string
210  */
211  public $Username = '';
212 
213  /**
214  * Sets SMTP password.
215  * @var string
216  */
217  public $Password = '';
218 
219  /**
220  * Sets the SMTP server timeout in seconds.
221  * This function will not work with the win32 version.
222  * @var int
223  */
224  public $Timeout = 10;
225 
226  /**
227  * Sets SMTP class debugging on or off.
228  * @var bool
229  */
230  public $SMTPDebug = false;
231 
232  /**
233  * Prevents the SMTP connection from being closed after each mail
234  * sending. If this is set to true then to close the connection
235  * requires an explicit call to SmtpClose().
236  * @var bool
237  */
238  public $SMTPKeepAlive = false;
239 
240  /**
241  * Provides the ability to have the TO field process individual
242  * emails, instead of sending to entire TO addresses
243  * @var bool
244  */
245  public $SingleTo = false;
246 
247  /**
248  * Provides the ability to change the line ending
249  * @var string
250  */
251  public $LE = "\n";
252 
253  /////////////////////////////////////////////////
254  // PROPERTIES, PRIVATE AND PROTECTED
255  /////////////////////////////////////////////////
256 
257  private $smtp = NULL;
258  private $to = array();
259  private $cc = array();
260  private $bcc = array();
261  private $ReplyTo = array();
262  private $all_recipients = array();
263  private $attachment = array();
264  private $CustomHeader = array();
265  private $message_type = '';
266  private $boundary = array();
267  protected $language = array();
268  private $error_count = 0;
269  private $sign_cert_file = "";
270  private $sign_key_file = "";
271  private $sign_key_pass = "";
272  private $exceptions = false;
273 
274  /////////////////////////////////////////////////
275  // CONSTANTS
276  /////////////////////////////////////////////////
277 
278  const VERSION = '5.0.0';
279  const STOP_MESSAGE = 0; // message only, continue processing
280  const STOP_CONTINUE = 1; // message?, likely ok to continue processing
281  const STOP_CRITICAL = 2; // message, plus full stop, critical error reached
282 
283  /////////////////////////////////////////////////
284  // METHODS, VARIABLES
285  /////////////////////////////////////////////////
286 
287  /**
288  * Constructor
289  * @param boolean $exceptions Should we throw external exceptions?
290  */
291  public function __construct($exceptions = false) {
292  $this->exceptions = ($exceptions == true);
293  }
294 
295  /**
296  * Sets message type to HTML.
297  * @param bool $ishtml
298  * @return void
299  */
300  public function IsHTML($ishtml = true) {
301  if ($ishtml) {
302  $this->ContentType = 'text/html';
303  } else {
304  $this->ContentType = 'text/plain';
305  }
306  }
307 
308  /**
309  * Sets Mailer to send message using SMTP.
310  * @return void
311  */
312  public function IsSMTP() {
313  $this->Mailer = 'smtp';
314  }
315 
316  /**
317  * Sets Mailer to send message using PHP mail() function.
318  * @return void
319  */
320  public function IsMail() {
321  $this->Mailer = 'mail';
322  }
323 
324  /**
325  * Sets Mailer to send message using the $Sendmail program.
326  * @return void
327  */
328  public function IsSendmail() {
329  if (!stristr(ini_get('sendmail_path'), 'sendmail')) {
330  $this->Sendmail = '/var/qmail/bin/sendmail';
331  }
332  $this->Mailer = 'sendmail';
333  }
334 
335  /**
336  * Sets Mailer to send message using the qmail MTA.
337  * @return void
338  */
339  public function IsQmail() {
340  if (stristr(ini_get('sendmail_path'), 'qmail')) {
341  $this->Sendmail = '/var/qmail/bin/sendmail';
342  }
343  $this->Mailer = 'sendmail';
344  }
345 
346  /////////////////////////////////////////////////
347  // METHODS, RECIPIENTS
348  /////////////////////////////////////////////////
349 
350  /**
351  * Adds a "To" address.
352  * @param string $address
353  * @param string $name
354  * @return boolean true on success, false if address already used
355  */
356  public function AddAddress($address, $name = '') {
357  return $this->AddAnAddress('to', $address, $name);
358  }
359 
360  /**
361  * Adds a "Cc" address.
362  * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer.
363  * @param string $address
364  * @param string $name
365  * @return boolean true on success, false if address already used
366  */
367  public function AddCC($address, $name = '') {
368  return $this->AddAnAddress('cc', $address, $name);
369  }
370 
371  /**
372  * Adds a "Bcc" address.
373  * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer.
374  * @param string $address
375  * @param string $name
376  * @return boolean true on success, false if address already used
377  */
378  public function AddBCC($address, $name = '') {
379  return $this->AddAnAddress('bcc', $address, $name);
380  }
381 
382  /**
383  * Adds a "Reply-to" address.
384  * @param string $address
385  * @param string $name
386  * @return boolean
387  */
388  public function AddReplyTo($address, $name = '') {
389  return $this->AddAnAddress('ReplyTo', $address, $name);
390  }
391 
392  /**
393  * Adds an address to one of the recipient arrays
394  * Addresses that have been added already return false, but do not throw exceptions
395  * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo'
396  * @param string $address The email address to send to
397  * @param string $name
398  * @return boolean true on success, false if address already used or invalid in some way
399  * @access private
400  */
401  private function AddAnAddress($kind, $address, $name = '') {
402  if (!preg_match('/^(to|cc|bcc|ReplyTo)$/', $kind)) {
403  echo 'Invalid recipient array: ' . kind;
404  return false;
405  }
406  $address = trim($address);
407  $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
408  if (!self::ValidateAddress($address)) {
409  $this->SetError($this->Lang('invalid_address').': '. $address);
410  if ($this->exceptions) {
411  throw new phpmailerException($this->Lang('invalid_address').': '.$address);
412  }
413  echo $this->Lang('invalid_address').': '.$address;
414  return false;
415  }
416  if ($kind != 'ReplyTo') {
417  if (!isset($this->all_recipients[strtolower($address)])) {
418  array_push($this->$kind, array($address, $name));
419  $this->all_recipients[strtolower($address)] = true;
420  return true;
421  }
422  } else {
423  if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
424  $this->ReplyTo[strtolower($address)] = array($address, $name);
425  return true;
426  }
427  }
428  return false;
429  }
430 
431 /**
432  * Set the From and FromName properties
433  * @param string $address
434  * @param string $name
435  * @return boolean
436  */
437  public function SetFrom($address, $name = '') {
438  $address = trim($address);
439  $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
440  if (!self::ValidateAddress($address)) {
441  $this->SetError($this->Lang('invalid_address').': '. $address);
442  if ($this->exceptions) {
443  throw new phpmailerException($this->Lang('invalid_address').': '.$address);
444  }
445  echo $this->Lang('invalid_address').': '.$address;
446  return false;
447  }
448  $this->From = $address;
449  $this->FromName = $name;
450  return true;
451  }
452 
453  /**
454  * Check that a string looks roughly like an email address should
455  * Static so it can be used without instantiation
456  * Tries to use PHP built-in validator in the filter extension (from PHP 5.2), falls back to a reasonably competent regex validator
457  * Conforms approximately to RFC2822
458  * @link http://www.hexillion.com/samples/#Regex Original pattern found here
459  * @param string $address The email address to check
460  * @return boolean
461  * @static
462  * @access public
463  */
464  public static function ValidateAddress($address) {
465  if (function_exists('filter_var')) { //Introduced in PHP 5.2
466  if(filter_var($address, FILTER_VALIDATE_EMAIL) === FALSE) {
467  return false;
468  } else {
469  return true;
470  }
471  } else {
472  return preg_match('/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!\.)){0,61}[a-zA-Z0-9_-]?\.)+[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!$)){0,61}[a-zA-Z0-9_]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/', $address);
473  }
474  }
475 
476  /////////////////////////////////////////////////
477  // METHODS, MAIL SENDING
478  /////////////////////////////////////////////////
479 
480  /**
481  * Creates message and assigns Mailer. If the message is
482  * not sent successfully then it returns false. Use the ErrorInfo
483  * variable to view description of the error.
484  * @return bool
485  */
486  public function Send() {
487  try {
488  if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
489  throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL);
490  }
491 
492  // Set whether the message is multipart/alternative
493  if(!empty($this->AltBody)) {
494  $this->ContentType = 'multipart/alternative';
495  }
496 
497  $this->error_count = 0; // reset errors
498  $this->SetMessageType();
499  $header = $this->CreateHeader();
500  $body = $this->CreateBody();
501 
502  if (empty($this->Body)) {
503  throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL);
504  }
505 
506  // Choose the mailer and send through it
507  switch($this->Mailer) {
508  case 'sendmail':
509  return $this->SendmailSend($header, $body);
510  case 'smtp':
511  return $this->SmtpSend($header, $body);
512  case 'mail':
513  default:
514  return $this->MailSend($header, $body);
515  }
516 
517  } catch (phpmailerException $e) {
518  $this->SetError($e->getMessage());
519  if ($this->exceptions) {
520  throw $e;
521  }
522  echo $e->getMessage()."\n";
523  return false;
524  }
525  }
526 
527  /**
528  * Sends mail using the $Sendmail program.
529  * @param string $header The message headers
530  * @param string $body The message body
531  * @access protected
532  * @return bool
533  */
534  protected function SendmailSend($header, $body) {
535  if ($this->Sender != '') {
536  $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
537  } else {
538  $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail));
539  }
540  if(!@$mail = popen($sendmail, 'w')) {
541  throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
542  }
543  fputs($mail, $header);
544  fputs($mail, $body);
545  $result = pclose($mail);
546  if($result != 0) {
547  throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
548  }
549  return true;
550  }
551 
552  /**
553  * Sends mail using the PHP mail() function.
554  * @param string $header The message headers
555  * @param string $body The message body
556  * @access protected
557  * @return bool
558  */
559  protected function MailSend($header, $body) {
560  $toArr = array();
561  foreach($this->to as $t) {
562  $toArr[] = $this->AddrFormat($t);
563  }
564  $to = implode(', ', $toArr);
565 
566  $params = sprintf("-oi -f %s", $this->Sender);
567  if ($this->Sender != '' && strlen(ini_get('safe_mode'))< 1) {
568  $old_from = ini_get('sendmail_from');
569  ini_set('sendmail_from', $this->Sender);
570  if ($this->SingleTo === true && count($toArr) > 1) {
571  foreach ($toArr as $key => $val) {
572  $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
573  }
574  } else {
575  $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
576  }
577  } else {
578  if ($this->SingleTo === true && count($toArr) > 1) {
579  foreach ($toArr as $key => $val) {
580  $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
581  }
582  } else {
583  $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header);
584  }
585  }
586  if (isset($old_from)) {
587  ini_set('sendmail_from', $old_from);
588  }
589  if(!$rt) {
590  throw new phpmailerException($this->Lang('instantiate'), self::STOP_CRITICAL);
591  }
592  return true;
593  }
594 
595  /**
596  * Sends mail via SMTP using PhpSMTP
597  * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
598  * @param string $header The message headers
599  * @param string $body The message body
600  * @uses SMTP
601  * @access protected
602  * @return bool
603  */
604  protected function SmtpSend($header, $body) {
605  require_once $this->PluginDir . 'class.smtp.php';
606  $bad_rcpt = array();
607 
608  if(!$this->SmtpConnect()) {
609  throw new phpmailerException($this->Lang('smtp_connect_failed'), self::STOP_CRITICAL);
610  }
611  $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
612  if(!$this->smtp->Mail($smtp_from)) {
613  throw new phpmailerException($this->Lang('from_failed') . $smtp_from, self::STOP_CRITICAL);
614  }
615 
616  // Attempt to send attach all recipients
617  foreach($this->to as $to) {
618  if (!$this->smtp->Recipient($to[0])) {
619  $bad_rcpt[] = $to[0];
620  }
621  }
622  foreach($this->cc as $cc) {
623  if (!$this->smtp->Recipient($cc[0])) {
624  $bad_rcpt[] = $cc[0];
625  }
626  }
627  foreach($this->bcc as $bcc) {
628  if (!$this->smtp->Recipient($bcc[0])) {
629  $bad_rcpt[] = $bcc[0];
630  }
631  }
632  if (count($bad_rcpt) > 0 ) { //Create error message for any bad addresses
633  $badaddresses = implode(', ', $bad_rcpt);
634  throw new phpmailerException($this->Lang('recipients_failed') . $badaddresses);
635  }
636  if(!$this->smtp->Data($header . $body)) {
637  throw new phpmailerException($this->Lang('data_not_accepted'), self::STOP_CRITICAL);
638  }
639  if($this->SMTPKeepAlive == true) {
640  $this->smtp->Reset();
641  }
642  return true;
643  }
644 
645  /**
646  * Initiates a connection to an SMTP server.
647  * Returns false if the operation failed.
648  * @uses SMTP
649  * @access public
650  * @return bool
651  */
652  public function SmtpConnect() {
653  if(is_null($this->smtp)) {
654  $this->smtp = new SMTP();
655  }
656 
657  $this->smtp->do_debug = $this->SMTPDebug;
658  $hosts = explode(';', $this->Host);
659  $index = 0;
660  $connection = $this->smtp->Connected();
661 
662  // Retry while there is no connection
663  try {
664  while($index < count($hosts) && !$connection) {
665  $hostinfo = array();
666  if (preg_match('/^(.+):([0-9]+)$/', $hosts[$index], $hostinfo)) {
667  $host = $hostinfo[1];
668  $port = $hostinfo[2];
669  } else {
670  $host = $hosts[$index];
671  $port = $this->Port;
672  }
673 
674  $tls = ($this->SMTPSecure == 'tls');
675  $ssl = ($this->SMTPSecure == 'ssl');
676 
677  if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) {
678 
679  $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname());
680  $this->smtp->Hello($hello);
681 
682  if ($tls) {
683  if (!$this->smtp->StartTLS()) {
684  throw new phpmailerException($this->Lang('tls'));
685  }
686 
687  //We must resend HELO after tls negotiation
688  $this->smtp->Hello($hello);
689  }
690 
691  $connection = true;
692  if ($this->SMTPAuth) {
693  if (!$this->smtp->Authenticate($this->Username, $this->Password)) {
694  throw new phpmailerException($this->Lang('authenticate'));
695  }
696  }
697  }
698  $index++;
699  if (!$connection) {
700  throw new phpmailerException($this->Lang('connect_host'));
701  }
702  }
703  } catch (phpmailerException $e) {
704  $this->smtp->Reset();
705  throw $e;
706  }
707  return true;
708  }
709 
710  /**
711  * Closes the active SMTP session if one exists.
712  * @return void
713  */
714  public function SmtpClose() {
715  if(!is_null($this->smtp)) {
716  if($this->smtp->Connected()) {
717  $this->smtp->Quit();
718  $this->smtp->Close();
719  }
720  }
721  }
722 
723  /**
724  * Sets the language for all class error messages.
725  * Returns false if it cannot load the language file. The default language is English.
726  * @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br")
727  * @param string $lang_path Path to the language file directory
728  * @access public
729  */
730  function SetLanguage($langcode = 'en', $lang_path = 'language/') {
731  //Define full set of translatable strings
732  $PHPMAILER_LANG = array(
733  'provide_address' => 'You must provide at least one recipient email address.',
734  'mailer_not_supported' => ' mailer is not supported.',
735  'execute' => 'Could not execute: ',
736  'instantiate' => 'Could not instantiate mail function.',
737  'authenticate' => 'SMTP Error: Could not authenticate.',
738  'from_failed' => 'The following From address failed: ',
739  'recipients_failed' => 'SMTP Error: The following recipients failed: ',
740  'data_not_accepted' => 'SMTP Error: Data not accepted.',
741  'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
742  'file_access' => 'Could not access file: ',
743  'file_open' => 'File Error: Could not open file: ',
744  'encoding' => 'Unknown encoding: ',
745  'signing' => 'Signing Error: ',
746  'smtp_error' => 'SMTP server error: ',
747  'empty_message' => 'Message body empty',
748  'invalid_address' => 'Invalid address',
749  'variable_set' => 'Cannot set or reset variable: '
750  );
751  //Overwrite language-specific strings. This way we'll never have missing translations - no more "language string failed to load"!
752  $l = true;
753  if ($langcode != 'en') { //There is no English translation file
754  $l = @include $lang_path.'phpmailer.lang-'.$langcode.'.php';
755  }
756  $this->language = $PHPMAILER_LANG;
757  return ($l == true); //Returns false if language not found
758  }
759 
760  /**
761  * Return the current array of language strings
762  * @return array
763  */
764  public function GetTranslations() {
765  return $this->language;
766  }
767 
768  /////////////////////////////////////////////////
769  // METHODS, MESSAGE CREATION
770  /////////////////////////////////////////////////
771 
772  /**
773  * Creates recipient headers.
774  * @access public
775  * @return string
776  */
777  public function AddrAppend($type, $addr) {
778  $addr_str = $type . ': ';
779  $addresses = array();
780  foreach ($addr as $a) {
781  $addresses[] = $this->AddrFormat($a);
782  }
783  $addr_str .= implode(', ', $addresses);
784  $addr_str .= $this->LE;
785 
786  return $addr_str;
787  }
788 
789  /**
790  * Formats an address correctly.
791  * @access public
792  * @return string
793  */
794  public function AddrFormat($addr) {
795  if (empty($addr[1])) {
796  return $this->SecureHeader($addr[0]);
797  } else {
798  return $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">";
799  }
800  }
801 
802  /**
803  * Wraps message for use with mailers that do not
804  * automatically perform wrapping and for quoted-printable.
805  * Original written by philippe.
806  * @param string $message The message to wrap
807  * @param integer $length The line length to wrap to
808  * @param boolean $qp_mode Whether to run in Quoted-Printable mode
809  * @access public
810  * @return string
811  */
812  public function WrapText($message, $length, $qp_mode = false) {
813  $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
814  // If utf-8 encoding is used, we will need to make sure we don't
815  // split multibyte characters when we wrap
816  $is_utf8 = (strtolower($this->CharSet) == "utf-8");
817 
818  $message = $this->FixEOL($message);
819  if (substr($message, -1) == $this->LE) {
820  $message = substr($message, 0, -1);
821  }
822 
823  $line = explode($this->LE, $message);
824  $message = '';
825  for ($i=0 ;$i < count($line); $i++) {
826  $line_part = explode(' ', $line[$i]);
827  $buf = '';
828  for ($e = 0; $e<count($line_part); $e++) {
829  $word = $line_part[$e];
830  if ($qp_mode and (strlen($word) > $length)) {
831  $space_left = $length - strlen($buf) - 1;
832  if ($e != 0) {
833  if ($space_left > 20) {
834  $len = $space_left;
835  if ($is_utf8) {
836  $len = $this->UTF8CharBoundary($word, $len);
837  } elseif (substr($word, $len - 1, 1) == "=") {
838  $len--;
839  } elseif (substr($word, $len - 2, 1) == "=") {
840  $len -= 2;
841  }
842  $part = substr($word, 0, $len);
843  $word = substr($word, $len);
844  $buf .= ' ' . $part;
845  $message .= $buf . sprintf("=%s", $this->LE);
846  } else {
847  $message .= $buf . $soft_break;
848  }
849  $buf = '';
850  }
851  while (strlen($word) > 0) {
852  $len = $length;
853  if ($is_utf8) {
854  $len = $this->UTF8CharBoundary($word, $len);
855  } elseif (substr($word, $len - 1, 1) == "=") {
856  $len--;
857  } elseif (substr($word, $len - 2, 1) == "=") {
858  $len -= 2;
859  }
860  $part = substr($word, 0, $len);
861  $word = substr($word, $len);
862 
863  if (strlen($word) > 0) {
864  $message .= $part . sprintf("=%s", $this->LE);
865  } else {
866  $buf = $part;
867  }
868  }
869  } else {
870  $buf_o = $buf;
871  $buf .= ($e == 0) ? $word : (' ' . $word);
872 
873  if (strlen($buf) > $length and $buf_o != '') {
874  $message .= $buf_o . $soft_break;
875  $buf = $word;
876  }
877  }
878  }
879  $message .= $buf . $this->LE;
880  }
881 
882  return $message;
883  }
884 
885  /**
886  * Finds last character boundary prior to maxLength in a utf-8
887  * quoted (printable) encoded string.
888  * Original written by Colin Brown.
889  * @access public
890  * @param string $encodedText utf-8 QP text
891  * @param int $maxLength find last character boundary prior to this length
892  * @return int
893  */
894  public function UTF8CharBoundary($encodedText, $maxLength) {
895  $foundSplitPos = false;
896  $lookBack = 3;
897  while (!$foundSplitPos) {
898  $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
899  $encodedCharPos = strpos($lastChunk, "=");
900  if ($encodedCharPos !== false) {
901  // Found start of encoded character byte within $lookBack block.
902  // Check the encoded byte value (the 2 chars after the '=')
903  $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
904  $dec = hexdec($hex);
905  if ($dec < 128) { // Single byte character.
906  // If the encoded char was found at pos 0, it will fit
907  // otherwise reduce maxLength to start of the encoded char
908  $maxLength = ($encodedCharPos == 0) ? $maxLength :
909  $maxLength - ($lookBack - $encodedCharPos);
910  $foundSplitPos = true;
911  } elseif ($dec >= 192) { // First byte of a multi byte character
912  // Reduce maxLength to split at start of character
913  $maxLength = $maxLength - ($lookBack - $encodedCharPos);
914  $foundSplitPos = true;
915  } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
916  $lookBack += 3;
917  }
918  } else {
919  // No encoded character found
920  $foundSplitPos = true;
921  }
922  }
923  return $maxLength;
924  }
925 
926 
927  /**
928  * Set the body wrapping.
929  * @access public
930  * @return void
931  */
932  public function SetWordWrap() {
933  if($this->WordWrap < 1) {
934  return;
935  }
936 
937  switch($this->message_type) {
938  case 'alt':
939  case 'alt_attachments':
940  $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);
941  break;
942  default:
943  $this->Body = $this->WrapText($this->Body, $this->WordWrap);
944  break;
945  }
946  }
947 
948  /**
949  * Assembles message header.
950  * @access public
951  * @return string The assembled header
952  */
953  public function CreateHeader() {
954  $result = '';
955 
956  // Set the boundaries
957  $uniq_id = md5(uniqid(time()));
958  $this->boundary[1] = 'b1_' . $uniq_id;
959  $this->boundary[2] = 'b2_' . $uniq_id;
960 
961  $result .= $this->HeaderLine('Date', self::RFCDate());
962  if($this->Sender == '') {
963  $result .= $this->HeaderLine('Return-Path', trim($this->From));
964  } else {
965  $result .= $this->HeaderLine('Return-Path', trim($this->Sender));
966  }
967 
968  // To be created automatically by mail()
969  if($this->Mailer != 'mail') {
970  if(count($this->to) > 0) {
971  $result .= $this->AddrAppend('To', $this->to);
972  } elseif (count($this->cc) == 0) {
973  $result .= $this->HeaderLine('To', 'undisclosed-recipients:;');
974  }
975  }
976 
977  $from = array();
978  $from[0][0] = trim($this->From);
979  $from[0][1] = $this->FromName;
980  $result .= $this->AddrAppend('From', $from);
981 
982  // sendmail and mail() extract Cc from the header before sending
983  if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->cc) > 0)) {
984  $result .= $this->AddrAppend('Cc', $this->cc);
985  }
986 
987  // sendmail and mail() extract Bcc from the header before sending
988  if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) {
989  $result .= $this->AddrAppend('Bcc', $this->bcc);
990  }
991 
992  if(count($this->ReplyTo) > 0) {
993  $result .= $this->AddrAppend('Reply-to', $this->ReplyTo);
994  }
995 
996  // mail() sets the subject itself
997  if($this->Mailer != 'mail') {
998  $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject)));
999  }
1000 
1001  if($this->MessageID != '') {
1002  $result .= $this->HeaderLine('Message-ID',$this->MessageID);
1003  } else {
1004  $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE);
1005  }
1006  $result .= $this->HeaderLine('X-Priority', $this->Priority);
1007  $result .= $this->HeaderLine('X-Mailer', 'PHPMailer ' . self::VERSION . ' (phpmailer.codeworxtech.com)');
1008 
1009  if($this->ConfirmReadingTo != '') {
1010  $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
1011  }
1012 
1013  // Add custom headers
1014  for($index = 0; $index < count($this->CustomHeader); $index++) {
1015  $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1])));
1016  }
1017  if (!$this->sign_key_file) {
1018  $result .= $this->HeaderLine('MIME-Version', '1.0');
1019  $result .= $this->GetMailMIME();
1020  }
1021 
1022  return $result;
1023  }
1024 
1025  /**
1026  * Returns the message MIME.
1027  * @access public
1028  * @return string
1029  */
1030  public function GetMailMIME() {
1031  $result = '';
1032  switch($this->message_type) {
1033  case 'plain':
1034  $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);
1035  $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet);
1036  break;
1037  case 'attachments':
1038  case 'alt_attachments':
1039  if($this->InlineImageExists()){
1040  $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE);
1041  } else {
1042  $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;');
1043  $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
1044  }
1045  break;
1046  case 'alt':
1047  $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
1048  $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
1049  break;
1050  }
1051 
1052  if($this->Mailer != 'mail') {
1053  $result .= $this->LE.$this->LE;
1054  }
1055 
1056  return $result;
1057  }
1058 
1059  /**
1060  * Assembles the message body. Returns an empty string on failure.
1061  * @access public
1062  * @return string The assembled message body
1063  */
1064  public function CreateBody() {
1065  $body = '';
1066 
1067  if ($this->sign_key_file) {
1068  $body .= $this->GetMailMIME();
1069  }
1070 
1071  $this->SetWordWrap();
1072 
1073  switch($this->message_type) {
1074  case 'alt':
1075  $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');
1076  $body .= $this->EncodeString($this->AltBody, $this->Encoding);
1077  $body .= $this->LE.$this->LE;
1078  $body .= $this->GetBoundary($this->boundary[1], '', 'text/html', '');
1079  $body .= $this->EncodeString($this->Body, $this->Encoding);
1080  $body .= $this->LE.$this->LE;
1081  $body .= $this->EndBoundary($this->boundary[1]);
1082  break;
1083  case 'plain':
1084  $body .= $this->EncodeString($this->Body, $this->Encoding);
1085  break;
1086  case 'attachments':
1087  $body .= $this->GetBoundary($this->boundary[1], '', '', '');
1088  $body .= $this->EncodeString($this->Body, $this->Encoding);
1089  $body .= $this->LE;
1090  $body .= $this->AttachAll();
1091  break;
1092  case 'alt_attachments':
1093  $body .= sprintf("--%s%s", $this->boundary[1], $this->LE);
1094  $body .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE);
1095  $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body
1096  $body .= $this->EncodeString($this->AltBody, $this->Encoding);
1097  $body .= $this->LE.$this->LE;
1098  $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body
1099  $body .= $this->EncodeString($this->Body, $this->Encoding);
1100  $body .= $this->LE.$this->LE;
1101  $body .= $this->EndBoundary($this->boundary[2]);
1102  $body .= $this->AttachAll();
1103  break;
1104  }
1105 
1106  if ($this->IsError()) {
1107  $body = '';
1108  } elseif ($this->sign_key_file) {
1109  try {
1110  $file = tempnam('', 'mail');
1111  file_put_contents($file, $body); //TODO check this worked
1112  $signed = tempnam("", "signed");
1113  if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) {
1114  @unlink($file);
1115  @unlink($signed);
1116  $body = file_get_contents($signed);
1117  } else {
1118  @unlink($file);
1119  @unlink($signed);
1120  throw new phpmailerException($this->Lang("signing").openssl_error_string());
1121  }
1122  } catch (phpmailerException $e) {
1123  $body = '';
1124  if ($this->exceptions) {
1125  throw $e;
1126  }
1127  }
1128  }
1129 
1130  return $body;
1131  }
1132 
1133  /**
1134  * Returns the start of a message boundary.
1135  * @access private
1136  */
1137  private function GetBoundary($boundary, $charSet, $contentType, $encoding) {
1138  $result = '';
1139  if($charSet == '') {
1140  $charSet = $this->CharSet;
1141  }
1142  if($contentType == '') {
1143  $contentType = $this->ContentType;
1144  }
1145  if($encoding == '') {
1146  $encoding = $this->Encoding;
1147  }
1148  $result .= $this->TextLine('--' . $boundary);
1149  $result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet);
1150  $result .= $this->LE;
1151  $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding);
1152  $result .= $this->LE;
1153 
1154  return $result;
1155  }
1156 
1157  /**
1158  * Returns the end of a message boundary.
1159  * @access private
1160  */
1161  private function EndBoundary($boundary) {
1162  return $this->LE . '--' . $boundary . '--' . $this->LE;
1163  }
1164 
1165  /**
1166  * Sets the message type.
1167  * @access private
1168  * @return void
1169  */
1170  private function SetMessageType() {
1171  if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) {
1172  $this->message_type = 'plain';
1173  } else {
1174  if(count($this->attachment) > 0) {
1175  $this->message_type = 'attachments';
1176  }
1177  if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) {
1178  $this->message_type = 'alt';
1179  }
1180  if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) {
1181  $this->message_type = 'alt_attachments';
1182  }
1183  }
1184  }
1185 
1186  /**
1187  * Returns a formatted header line.
1188  * @access public
1189  * @return string
1190  */
1191  public function HeaderLine($name, $value) {
1192  return $name . ': ' . $value . $this->LE;
1193  }
1194 
1195  /**
1196  * Returns a formatted mail line.
1197  * @access public
1198  * @return string
1199  */
1200  public function TextLine($value) {
1201  return $value . $this->LE;
1202  }
1203 
1204  /////////////////////////////////////////////////
1205  // CLASS METHODS, ATTACHMENTS
1206  /////////////////////////////////////////////////
1207 
1208  /**
1209  * Adds an attachment from a path on the filesystem.
1210  * Returns false if the file could not be found
1211  * or accessed.
1212  * @param string $path Path to the attachment.
1213  * @param string $name Overrides the attachment name.
1214  * @param string $encoding File encoding (see $Encoding).
1215  * @param string $type File extension (MIME) type.
1216  * @return bool
1217  */
1218  public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
1219  try {
1220  if ( !@is_file($path) ) {
1221  throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE);
1222  }
1223  $filename = basename($path);
1224  if ( $name == '' ) {
1225  $name = $filename;
1226  }
1227 
1228  $this->attachment[] = array(
1229  0 => $path,
1230  1 => $filename,
1231  2 => $name,
1232  3 => $encoding,
1233  4 => $type,
1234  5 => false, // isStringAttachment
1235  6 => 'attachment',
1236  7 => 0
1237  );
1238 
1239  } catch (phpmailerException $e) {
1240  $this->SetError($e->getMessage());
1241  if ($this->exceptions) {
1242  throw $e;
1243  }
1244  echo $e->getMessage()."\n";
1245  if ( $e->getCode() == self::STOP_CRITICAL ) {
1246  return false;
1247  }
1248  }
1249  return true;
1250  }
1251 
1252  /**
1253  * Return the current array of attachments
1254  * @return array
1255  */
1256  public function GetAttachments() {
1257  return $this->attachment;
1258  }
1259 
1260  /**
1261  * Attaches all fs, string, and binary attachments to the message.
1262  * Returns an empty string on failure.
1263  * @access private
1264  * @return string
1265  */
1266  private function AttachAll() {
1267  // Return text of body
1268  $mime = array();
1269  $cidUniq = array();
1270  $incl = array();
1271 
1272  // Add all attachments
1273  foreach ($this->attachment as $attachment) {
1274  // Check for string attachment
1275  $bString = $attachment[5];
1276  if ($bString) {
1277  $string = $attachment[0];
1278  } else {
1279  $path = $attachment[0];
1280  }
1281 
1282  if (in_array($attachment[0], $incl)) { continue; }
1283  $filename = $attachment[1];
1284  $name = $attachment[2];
1285  $encoding = $attachment[3];
1286  $type = $attachment[4];
1287  $disposition = $attachment[6];
1288  $cid = $attachment[7];
1289  $incl[] = $attachment[0];
1290  if ( isset($cidUniq[$cid]) ) { continue; }
1291  $cidUniq[$cid] = true;
1292 
1293  $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);
1294  $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE);
1295  $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
1296 
1297  if($disposition == 'inline') {
1298  $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
1299  }
1300 
1301  $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE);
1302 
1303  // Encode as string attachment
1304  if($bString) {
1305  $mime[] = $this->EncodeString($string, $encoding);
1306  if($this->IsError()) {
1307  return '';
1308  }
1309  $mime[] = $this->LE.$this->LE;
1310  } else {
1311  $mime[] = $this->EncodeFile($path, $encoding);
1312  if($this->IsError()) {
1313  return '';
1314  }
1315  $mime[] = $this->LE.$this->LE;
1316  }
1317  }
1318 
1319  $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE);
1320 
1321  return join('', $mime);
1322  }
1323 
1324  /**
1325  * Encodes attachment in requested format.
1326  * Returns an empty string on failure.
1327  * @param string $path The full path to the file
1328  * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
1329  * @see EncodeFile()
1330  * @access private
1331  * @return string
1332  */
1333  private function EncodeFile($path, $encoding = 'base64') {
1334  try {
1335  if (!is_readable($path)) {
1336  throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE);
1337  }
1338  if (function_exists('get_magic_quotes')) {
1339  function get_magic_quotes() {
1340  return false;
1341  }
1342  }
1343  if (PHP_VERSION < 6) {
1344  $magic_quotes = get_magic_quotes_runtime();
1345  set_magic_quotes_runtime(0);
1346  }
1347  $file_buffer = file_get_contents($path);
1348  $file_buffer = $this->EncodeString($file_buffer, $encoding);
1349  if (PHP_VERSION < 6) { set_magic_quotes_runtime($magic_quotes); }
1350  return $file_buffer;
1351  } catch (Exception $e) {
1352  $this->SetError($e->getMessage());
1353  return '';
1354  }
1355  }
1356 
1357  /**
1358  * Encodes string to requested format.
1359  * Returns an empty string on failure.
1360  * @param string $str The text to encode
1361  * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
1362  * @access public
1363  * @return string
1364  */
1365  public function EncodeString ($str, $encoding = 'base64') {
1366  $encoded = '';
1367  switch(strtolower($encoding)) {
1368  case 'base64':
1369  $encoded = chunk_split(base64_encode($str), 76, $this->LE);
1370  break;
1371  case '7bit':
1372  case '8bit':
1373  $encoded = $this->FixEOL($str);
1374  //Make sure it ends with a line break
1375  if (substr($encoded, -(strlen($this->LE))) != $this->LE)
1376  $encoded .= $this->LE;
1377  break;
1378  case 'binary':
1379  $encoded = $str;
1380  break;
1381  case 'quoted-printable':
1382  $encoded = $this->EncodeQP($str);
1383  break;
1384  default:
1385  $this->SetError($this->Lang('encoding') . $encoding);
1386  break;
1387  }
1388  return $encoded;
1389  }
1390 
1391  /**
1392  * Encode a header string to best (shortest) of Q, B, quoted or none.
1393  * @access public
1394  * @return string
1395  */
1396  public function EncodeHeader($str, $position = 'text') {
1397  $x = 0;
1398 
1399  switch (strtolower($position)) {
1400  case 'phrase':
1401  if (!preg_match('/[\200-\377]/', $str)) {
1402  // Can't use addslashes as we don't know what value has magic_quotes_sybase
1403  $encoded = addcslashes($str, "\0..\37\177\\\"");
1404  if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
1405  return ($encoded);
1406  } else {
1407  return ("\"$encoded\"");
1408  }
1409  }
1410  $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
1411  break;
1412  case 'comment':
1413  $x = preg_match_all('/[()"]/', $str, $matches);
1414  // Fall-through
1415  case 'text':
1416  default:
1417  $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
1418  break;
1419  }
1420 
1421  if ($x == 0) {
1422  return ($str);
1423  }
1424 
1425  $maxlen = 75 - 7 - strlen($this->CharSet);
1426  // Try to select the encoding which should produce the shortest output
1427  if (strlen($str)/3 < $x) {
1428  $encoding = 'B';
1429  if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) {
1430  // Use a custom function which correctly encodes and wraps long
1431  // multibyte strings without breaking lines within a character
1432  $encoded = $this->Base64EncodeWrapMB($str);
1433  } else {
1434  $encoded = base64_encode($str);
1435  $maxlen -= $maxlen % 4;
1436  $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
1437  }
1438  } else {
1439  $encoding = 'Q';
1440  $encoded = $this->EncodeQ($str, $position);
1441  $encoded = $this->WrapText($encoded, $maxlen, true);
1442  $encoded = str_replace('='.$this->LE, "\n", trim($encoded));
1443  }
1444 
1445  $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);
1446  $encoded = trim(str_replace("\n", $this->LE, $encoded));
1447 
1448  return $encoded;
1449  }
1450 
1451  /**
1452  * Checks if a string contains multibyte characters.
1453  * @access public
1454  * @param string $str multi-byte text to wrap encode
1455  * @return bool
1456  */
1457  public function HasMultiBytes($str) {
1458  if (function_exists('mb_strlen')) {
1459  return (strlen($str) > mb_strlen($str, $this->CharSet));
1460  } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
1461  return false;
1462  }
1463  }
1464 
1465  /**
1466  * Correctly encodes and wraps long multibyte strings for mail headers
1467  * without breaking lines within a character.
1468  * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php
1469  * @access public
1470  * @param string $str multi-byte text to wrap encode
1471  * @return string
1472  */
1473  public function Base64EncodeWrapMB($str) {
1474  $start = "=?".$this->CharSet."?B?";
1475  $end = "?=";
1476  $encoded = "";
1477 
1478  $mb_length = mb_strlen($str, $this->CharSet);
1479  // Each line must have length <= 75, including $start and $end
1480  $length = 75 - strlen($start) - strlen($end);
1481  // Average multi-byte ratio
1482  $ratio = $mb_length / strlen($str);
1483  // Base64 has a 4:3 ratio
1484  $offset = $avgLength = floor($length * $ratio * .75);
1485 
1486  for ($i = 0; $i < $mb_length; $i += $offset) {
1487  $lookBack = 0;
1488 
1489  do {
1490  $offset = $avgLength - $lookBack;
1491  $chunk = mb_substr($str, $i, $offset, $this->CharSet);
1492  $chunk = base64_encode($chunk);
1493  $lookBack++;
1494  }
1495  while (strlen($chunk) > $length);
1496 
1497  $encoded .= $chunk . $this->LE;
1498  }
1499 
1500  // Chomp the last linefeed
1501  $encoded = substr($encoded, 0, -strlen($this->LE));
1502  return $encoded;
1503  }
1504 
1505  /**
1506  * Encode string to quoted-printable.
1507  * Only uses standard PHP, slow, but will always work
1508  * @access public
1509  * @param string $string the text to encode
1510  * @param integer $line_max Number of chars allowed on a line before wrapping
1511  * @return string
1512  */
1513  public function EncodeQPphp( $input = '', $line_max = 76, $space_conv = false) {
1514  $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
1515  $lines = preg_split('/(?:\r\n|\r|\n)/', $input);
1516  $eol = "\r\n";
1517  $escape = '=';
1518  $output = '';
1519  while( list(, $line) = each($lines) ) {
1520  $linlen = strlen($line);
1521  $newline = '';
1522  for($i = 0; $i < $linlen; $i++) {
1523  $c = substr( $line, $i, 1 );
1524  $dec = ord( $c );
1525  if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E
1526  $c = '=2E';
1527  }
1528  if ( $dec == 32 ) {
1529  if ( $i == ( $linlen - 1 ) ) { // convert space at eol only
1530  $c = '=20';
1531  } else if ( $space_conv ) {
1532  $c = '=20';
1533  }
1534  } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required
1535  $h2 = floor($dec/16);
1536  $h1 = floor($dec%16);
1537  $c = $escape.$hex[$h2].$hex[$h1];
1538  }
1539  if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted
1540  $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay
1541  $newline = '';
1542  // check if newline first character will be point or not
1543  if ( $dec == 46 ) {
1544  $c = '=2E';
1545  }
1546  }
1547  $newline .= $c;
1548  } // end of for
1549  $output .= $newline.$eol;
1550  } // end of while
1551  return $output;
1552  }
1553 
1554  /**
1555  * Encode string to RFC2045 (6.7) quoted-printable format
1556  * Uses a PHP5 stream filter to do the encoding about 64x faster than the old version
1557  * Also results in same content as you started with after decoding
1558  * @see EncodeQPphp()
1559  * @access public
1560  * @param string $string the text to encode
1561  * @param integer $line_max Number of chars allowed on a line before wrapping
1562  * @param boolean $space_conv Dummy param for compatibility with existing EncodeQP function
1563  * @return string
1564  * @author Marcus Bointon
1565  */
1566  public function EncodeQP($string, $line_max = 76, $space_conv = false) {
1567  if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3)
1568  return quoted_printable_encode($string);
1569  }
1570  $filters = stream_get_filters();
1571  if (!in_array('convert.*', $filters)) { //Got convert stream filter?
1572  return $this->EncodeQPphp($string, $line_max, $space_conv); //Fall back to old implementation
1573  }
1574  $fp = fopen('php://temp/', 'r+');
1575  $string = preg_replace('/\r\n?/', $this->LE, $string); //Normalise line breaks
1576  $params = array('line-length' => $line_max, 'line-break-chars' => $this->LE);
1577  $s = stream_filter_append($fp, 'convert.quoted-printable-encode', STREAM_FILTER_READ, $params);
1578  fputs($fp, $string);
1579  rewind($fp);
1580  $out = stream_get_contents($fp);
1581  stream_filter_remove($s);
1582  $out = preg_replace('/^\./m', '=2E', $out); //Encode . if it is first char on a line, workaround for bug in Exchange
1583  fclose($fp);
1584  return $out;
1585  }
1586 
1587  /**
1588  * Encode string to q encoding.
1589  * @link http://tools.ietf.org/html/rfc2047
1590  * @param string $str the text to encode
1591  * @param string $position Where the text is going to be used, see the RFC for what that means
1592  * @access public
1593  * @return string
1594  */
1595  public function EncodeQ ($str, $position = 'text') {
1596  // There should not be any EOL in the string
1597  $encoded = preg_replace('/[\r\n]*/', '', $str);
1598 
1599  switch (strtolower($position)) {
1600  case 'phrase':
1601  $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
1602  break;
1603  case 'comment':
1604  $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
1605  case 'text':
1606  default:
1607  // Replace every high ascii, control =, ? and _ characters
1608  //TODO using /e (equivalent to eval()) is probably not a good idea
1609  $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e',
1610  "'='.sprintf('%02X', ord('\\1'))", $encoded);
1611  break;
1612  }
1613 
1614  // Replace every spaces to _ (more readable than =20)
1615  $encoded = str_replace(' ', '_', $encoded);
1616 
1617  return $encoded;
1618  }
1619 
1620  /**
1621  * Adds a string or binary attachment (non-filesystem) to the list.
1622  * This method can be used to attach ascii or binary data,
1623  * such as a BLOB record from a database.
1624  * @param string $string String attachment data.
1625  * @param string $filename Name of the attachment.
1626  * @param string $encoding File encoding (see $Encoding).
1627  * @param string $type File extension (MIME) type.
1628  * @return void
1629  */
1630  public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') {
1631  // Append to $attachment array
1632  $this->attachment[] = array(
1633  0 => $string,
1634  1 => $filename,
1635  2 => $filename,
1636  3 => $encoding,
1637  4 => $type,
1638  5 => true, // isStringAttachment
1639  6 => 'attachment',
1640  7 => 0
1641  );
1642  }
1643 
1644  /**
1645  * Adds an embedded attachment. This can include images, sounds, and
1646  * just about any other document. Make sure to set the $type to an
1647  * image type. For JPEG images use "image/jpeg" and for GIF images
1648  * use "image/gif".
1649  * @param string $path Path to the attachment.
1650  * @param string $cid Content ID of the attachment. Use this to identify
1651  * the Id for accessing the image in an HTML form.
1652  * @param string $name Overrides the attachment name.
1653  * @param string $encoding File encoding (see $Encoding).
1654  * @param string $type File extension (MIME) type.
1655  * @return bool
1656  */
1657  public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
1658 
1659  if ( !@is_file($path) ) {
1660  $this->SetError($this->Lang('file_access') . $path);
1661  return false;
1662  }
1663 
1664  $filename = basename($path);
1665  if ( $name == '' ) {
1666  $name = $filename;
1667  }
1668 
1669  // Append to $attachment array
1670  $this->attachment[] = array(
1671  0 => $path,
1672  1 => $filename,
1673  2 => $name,
1674  3 => $encoding,
1675  4 => $type,
1676  5 => false, // isStringAttachment
1677  6 => 'inline',
1678  7 => $cid
1679  );
1680 
1681  return true;
1682  }
1683 
1684  /**
1685  * Returns true if an inline attachment is present.
1686  * @access public
1687  * @return bool
1688  */
1689  public function InlineImageExists() {
1690  foreach($this->attachment as $attachment) {
1691  if ($attachment[6] == 'inline') {
1692  return true;
1693  }
1694  }
1695  return false;
1696  }
1697 
1698  /////////////////////////////////////////////////
1699  // CLASS METHODS, MESSAGE RESET
1700  /////////////////////////////////////////////////
1701 
1702  /**
1703  * Clears all recipients assigned in the TO array. Returns void.
1704  * @return void
1705  */
1706  public function ClearAddresses() {
1707  foreach($this->to as $to) {
1708  unset($this->all_recipients[strtolower($to[0])]);
1709  }
1710  $this->to = array();
1711  }
1712 
1713  /**
1714  * Clears all recipients assigned in the CC array. Returns void.
1715  * @return void
1716  */
1717  public function ClearCCs() {
1718  foreach($this->cc as $cc) {
1719  unset($this->all_recipients[strtolower($cc[0])]);
1720  }
1721  $this->cc = array();
1722  }
1723 
1724  /**
1725  * Clears all recipients assigned in the BCC array. Returns void.
1726  * @return void
1727  */
1728  public function ClearBCCs() {
1729  foreach($this->bcc as $bcc) {
1730  unset($this->all_recipients[strtolower($bcc[0])]);
1731  }
1732  $this->bcc = array();
1733  }
1734 
1735  /**
1736  * Clears all recipients assigned in the ReplyTo array. Returns void.
1737  * @return void
1738  */
1739  public function ClearReplyTos() {
1740  $this->ReplyTo = array();
1741  }
1742 
1743  /**
1744  * Clears all recipients assigned in the TO, CC and BCC
1745  * array. Returns void.
1746  * @return void
1747  */
1748  public function ClearAllRecipients() {
1749  $this->to = array();
1750  $this->cc = array();
1751  $this->bcc = array();
1752  $this->all_recipients = array();
1753  }
1754 
1755  /**
1756  * Clears all previously set filesystem, string, and binary
1757  * attachments. Returns void.
1758  * @return void
1759  */
1760  public function ClearAttachments() {
1761  $this->attachment = array();
1762  }
1763 
1764  /**
1765  * Clears all custom headers. Returns void.
1766  * @return void
1767  */
1768  public function ClearCustomHeaders() {
1769  $this->CustomHeader = array();
1770  }
1771 
1772  /////////////////////////////////////////////////
1773  // CLASS METHODS, MISCELLANEOUS
1774  /////////////////////////////////////////////////
1775 
1776  /**
1777  * Adds the error message to the error container.
1778  * @access protected
1779  * @return void
1780  */
1781  protected function SetError($msg) {
1782  $this->error_count++;
1783  if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
1784  $lasterror = $this->smtp->getError();
1785  if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {
1786  $msg .= '<p>' . $this->Lang('smtp_error') . $lasterror['smtp_msg'] . "</p>\n";
1787  }
1788  }
1789  $this->ErrorInfo = $msg;
1790  }
1791 
1792  /**
1793  * Returns the proper RFC 822 formatted date.
1794  * @access public
1795  * @return string
1796  * @static
1797  */
1798  public static function RFCDate() {
1799  $tz = date('Z');
1800  $tzs = ($tz < 0) ? '-' : '+';
1801  $tz = abs($tz);
1802  $tz = (int)($tz/3600)*100 + ($tz%3600)/60;
1803  $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz);
1804 
1805  return $result;
1806  }
1807 
1808  /**
1809  * Returns the server hostname or 'localhost.localdomain' if unknown.
1810  * @access private
1811  * @return string
1812  */
1813  private function ServerHostname() {
1814  if (!empty($this->Hostname)) {
1815  $result = $this->Hostname;
1816  } elseif (isset($_SERVER['SERVER_NAME'])) {
1817  $result = $_SERVER['SERVER_NAME'];
1818  } else {
1819  $result = 'localhost.localdomain';
1820  }
1821 
1822  return $result;
1823  }
1824 
1825  /**
1826  * Returns a message in the appropriate language.
1827  * @access private
1828  * @return string
1829  */
1830  private function Lang($key) {
1831  if(count($this->language) < 1) {
1832  $this->SetLanguage('en'); // set the default language
1833  }
1834 
1835  if(isset($this->language[$key])) {
1836  return $this->language[$key];
1837  } else {
1838  return 'Language string failed to load: ' . $key;
1839  }
1840  }
1841 
1842  /**
1843  * Returns true if an error occurred.
1844  * @access public
1845  * @return bool
1846  */
1847  public function IsError() {
1848  return ($this->error_count > 0);
1849  }
1850 
1851  /**
1852  * Changes every end of line from CR or LF to CRLF.
1853  * @access private
1854  * @return string
1855  */
1856  private function FixEOL($str) {
1857  $str = str_replace("\r\n", "\n", $str);
1858  $str = str_replace("\r", "\n", $str);
1859  $str = str_replace("\n", $this->LE, $str);
1860  return $str;
1861  }
1862 
1863  /**
1864  * Adds a custom header.
1865  * @access public
1866  * @return void
1867  */
1868  public function AddCustomHeader($custom_header) {
1869  $this->CustomHeader[] = explode(':', $custom_header, 2);
1870  }
1871 
1872  /**
1873  * Evaluates the message and returns modifications for inline images and backgrounds
1874  * @access public
1875  * @return $message
1876  */
1877  public function MsgHTML($message, $basedir = '') {
1878  preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images);
1879  if(isset($images[2])) {
1880  foreach($images[2] as $i => $url) {
1881  // do not change urls for absolute images (thanks to corvuscorax)
1882  if (!preg_match('#^[A-z]+://#',$url)) {
1883  $filename = basename($url);
1884  $directory = dirname($url);
1885  ($directory == '.')?$directory='':'';
1886  $cid = 'cid:' . md5($filename);
1887  $ext = pathinfo($filename, PATHINFO_EXTENSION);
1888  $mimeType = self::_mime_types($ext);
1889  if ( strlen($basedir) > 1 && substr($basedir,-1) != '/') { $basedir .= '/'; }
1890  if ( strlen($directory) > 1 && substr($directory,-1) != '/') { $directory .= '/'; }
1891  if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64',$mimeType) ) {
1892  $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message);
1893  }
1894  }
1895  }
1896  }
1897  $this->IsHTML(true);
1898  $this->Body = $message;
1899  $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$message)));
1900  if (!empty($textMsg) && empty($this->AltBody)) {
1901  $this->AltBody = html_entity_decode($textMsg);
1902  }
1903  if (empty($this->AltBody)) {
1904  $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n";
1905  }
1906  }
1907 
1908  /**
1909  * Gets the MIME type of the embedded or inline image
1910  * @param string File extension
1911  * @access public
1912  * @return string MIME type of ext
1913  * @static
1914  */
1915  public static function _mime_types($ext = '') {
1916  $mimes = array(
1917  'hqx' => 'application/mac-binhex40',
1918  'cpt' => 'application/mac-compactpro',
1919  'doc' => 'application/msword',
1920  'bin' => 'application/macbinary',
1921  'dms' => 'application/octet-stream',
1922  'lha' => 'application/octet-stream',
1923  'lzh' => 'application/octet-stream',
1924  'exe' => 'application/octet-stream',
1925  'class' => 'application/octet-stream',
1926  'psd' => 'application/octet-stream',
1927  'so' => 'application/octet-stream',
1928  'sea' => 'application/octet-stream',
1929  'dll' => 'application/octet-stream',
1930  'oda' => 'application/oda',
1931  'pdf' => 'application/pdf',
1932  'ai' => 'application/postscript',
1933  'eps' => 'application/postscript',
1934  'ps' => 'application/postscript',
1935  'smi' => 'application/smil',
1936  'smil' => 'application/smil',
1937  'mif' => 'application/vnd.mif',
1938  'xls' => 'application/vnd.ms-excel',
1939  'ppt' => 'application/vnd.ms-powerpoint',
1940  'wbxml' => 'application/vnd.wap.wbxml',
1941  'wmlc' => 'application/vnd.wap.wmlc',
1942  'dcr' => 'application/x-director',
1943  'dir' => 'application/x-director',
1944  'dxr' => 'application/x-director',
1945  'dvi' => 'application/x-dvi',
1946  'gtar' => 'application/x-gtar',
1947  'php' => 'application/x-httpd-php',
1948  'php4' => 'application/x-httpd-php',
1949  'php3' => 'application/x-httpd-php',
1950  'phtml' => 'application/x-httpd-php',
1951  'phps' => 'application/x-httpd-php-source',
1952  'js' => 'application/x-javascript',
1953  'swf' => 'application/x-shockwave-flash',
1954  'sit' => 'application/x-stuffit',
1955  'tar' => 'application/x-tar',
1956  'tgz' => 'application/x-tar',
1957  'xhtml' => 'application/xhtml+xml',
1958  'xht' => 'application/xhtml+xml',
1959  'zip' => 'application/zip',
1960  'mid' => 'audio/midi',
1961  'midi' => 'audio/midi',
1962  'mpga' => 'audio/mpeg',
1963  'mp2' => 'audio/mpeg',
1964  'mp3' => 'audio/mpeg',
1965  'aif' => 'audio/x-aiff',
1966  'aiff' => 'audio/x-aiff',
1967  'aifc' => 'audio/x-aiff',
1968  'ram' => 'audio/x-pn-realaudio',
1969  'rm' => 'audio/x-pn-realaudio',
1970  'rpm' => 'audio/x-pn-realaudio-plugin',
1971  'ra' => 'audio/x-realaudio',
1972  'rv' => 'video/vnd.rn-realvideo',
1973  'wav' => 'audio/x-wav',
1974  'bmp' => 'image/bmp',
1975  'gif' => 'image/gif',
1976  'jpeg' => 'image/jpeg',
1977  'jpg' => 'image/jpeg',
1978  'jpe' => 'image/jpeg',
1979  'png' => 'image/png',
1980  'tiff' => 'image/tiff',
1981  'tif' => 'image/tiff',
1982  'css' => 'text/css',
1983  'html' => 'text/html',
1984  'htm' => 'text/html',
1985  'shtml' => 'text/html',
1986  'txt' => 'text/plain',
1987  'text' => 'text/plain',
1988  'log' => 'text/plain',
1989  'rtx' => 'text/richtext',
1990  'rtf' => 'text/rtf',
1991  'xml' => 'text/xml',
1992  'xsl' => 'text/xml',
1993  'mpeg' => 'video/mpeg',
1994  'mpg' => 'video/mpeg',
1995  'mpe' => 'video/mpeg',
1996  'qt' => 'video/quicktime',
1997  'mov' => 'video/quicktime',
1998  'avi' => 'video/x-msvideo',
1999  'movie' => 'video/x-sgi-movie',
2000  'doc' => 'application/msword',
2001  'word' => 'application/msword',
2002  'xl' => 'application/excel',
2003  'eml' => 'message/rfc822'
2004  );
2005  return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)];
2006  }
2007 
2008  /**
2009  * Set (or reset) Class Objects (variables)
2010  *
2011  * Usage Example:
2012  * $page->set('X-Priority', '3');
2013  *
2014  * @access public
2015  * @param string $name Parameter Name
2016  * @param mixed $value Parameter Value
2017  * NOTE: will not work with arrays, there are no arrays to set/reset
2018  * @todo Should this not be using __set() magic function?
2019  */
2020  public function set($name, $value = '') {
2021  try {
2022  if (isset($this->$name) ) {
2023  $this->$name = $value;
2024  } else {
2025  throw new phpmailerException($this->Lang('variable_set') . $name, self::STOP_CRITICAL);
2026  }
2027  } catch (Exception $e) {
2028  $this->SetError($e->getMessage());
2029  if ($e->getCode() == self::STOP_CRITICAL) {
2030  return false;
2031  }
2032  }
2033  return true;
2034  }
2035 
2036  /**
2037  * Strips newlines to prevent header injection.
2038  * @access public
2039  * @param string $str String
2040  * @return string
2041  */
2042  public function SecureHeader($str) {
2043  $str = str_replace("\r", '', $str);
2044  $str = str_replace("\n", '', $str);
2045  return trim($str);
2046  }
2047 
2048  /**
2049  * Set the private key file and password to sign the message.
2050  *
2051  * @access public
2052  * @param string $key_filename Parameter File Name
2053  * @param string $key_pass Password for private key
2054  */
2055  public function Sign($cert_filename, $key_filename, $key_pass) {
2056  $this->sign_cert_file = $cert_filename;
2057  $this->sign_key_file = $key_filename;
2058  $this->sign_key_pass = $key_pass;
2059  }
2060 }
2061 
2062 class phpmailerException extends Exception {
2063  public function errorMessage() {
2064  $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
2065  return $errorMsg;
2066  }
2067 }
2068 ?>