[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/externals/phpmailer/ -> class.phpmailer-lite.php (source)

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


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1