[ Index ]

PHP Cross Reference of BackPress

title

Body

[close]

/includes/ -> class.mailer.php (source)

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


Generated: Sat Nov 23 01:00:54 2024 Cross-referenced by PHPXref 0.7.1