WordPress.org

Make WordPress Core

Ticket #25014: 25014.patch

File 25014.patch, 54.7 KB (added by c3mdigital, 7 years ago)

Upgrade phpmailer to latest version 5.2.6

  • wp-includes/class-phpmailer.php

     
    22/*~ class.phpmailer.php
    33.---------------------------------------------------------------------------.
    44|  Software: PHPMailer - PHP email class                                    |
    5 |   Version: 5.2.4                                                          |
    6 |      Site: https://code.google.com/a/apache-extras.org/p/phpmailer/       |
     5|   Version: 5.2.6                                                          |
     6|      Site: https://github.com/PHPMailer/PHPMailer/                        |
    77| ------------------------------------------------------------------------- |
    8 |     Admin: Jim Jagielski (project admininistrator)                        |
     8|    Admins: Marcus Bointon                                                 |
     9|    Admins: Jim Jagielski                                                  |
    910|   Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net |
    10 |          : Marcus Bointon (coolbru) coolbru@users.sourceforge.net         |
     11|          : Marcus Bointon (coolbru) phpmailer@synchromedia.co.uk          |
    1112|          : Jim Jagielski (jimjag) jimjag@gmail.com                        |
    1213|   Founder: Brent R. Matzelle (original founder)                           |
    1314| Copyright (c) 2010-2012, Jim Jagielski. All Rights Reserved.              |
     
    3435 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
    3536 */
    3637
    37 if (version_compare(PHP_VERSION, '5.0.0', '<') ) exit("Sorry, this version of PHPMailer will only run on PHP version 5 or greater!\n");
     38if (version_compare(PHP_VERSION, '5.0.0', '<') ) {
     39  exit("Sorry, PHPMailer will only run on PHP version 5 or greater!\n");
     40}
    3841
    3942/**
    4043 * PHP email creation and transport class
     
    9093  public $FromName          = 'Root User';
    9194
    9295  /**
    93    * Sets the Sender email (Return-Path) of the message.  If not empty,
    94    * will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
     96   * Sets the Sender email (Return-Path) of the message.
     97   * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
    9598   * @var string
    9699   */
    97100  public $Sender            = '';
     
    110113  public $Subject           = '';
    111114
    112115  /**
    113    * Sets the Body of the message.  This can be either an HTML or text body.
    114    * If HTML then run IsHTML(true).
     116   * An HTML or plain text message body.
     117   * If HTML then call IsHTML(true).
    115118   * @var string
    116119   */
    117120  public $Body              = '';
    118121
    119122  /**
    120    * Sets the text-only body of the message.  This automatically sets the
    121    * email to multipart/alternative.  This body can be read by mail
    122    * clients that do not have HTML email capability such as mutt. Clients
    123    * that can read HTML will view the normal Body.
     123   * The plain-text message body.
     124   * This body can be read by mail clients that do not have HTML email
     125   * capability such as mutt & Eudora.
     126   * Clients that can read HTML will view the normal Body.
    124127   * @var string
    125128   */
    126129  public $AltBody           = '';
    127130
    128131  /**
     132   * An iCal message part body
     133   * Only supported in simple alt or alt_inline message types
     134   * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
     135   * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
     136   * @link http://kigkonsult.se/iCalcreator/
     137   * @var string
     138   */
     139  public $Ical              = '';
     140
     141  /**
    129142   * Stores the complete compiled MIME message body.
    130143   * @var string
    131144   * @access protected
     
    143156   * Stores the extra header list which CreateHeader() doesn't fold in
    144157   * @var string
    145158   * @access protected
    146   */
     159   */
    147160  protected $mailHeader     = '';
    148161
    149162  /**
     
    171184   * @var boolean
    172185   */
    173186  public $UseSendmailOptions    = true;
    174  
     187
    175188  /**
    176189   * Path to PHPMailer plugins.  Useful if the SMTP class
    177190   * is in a different directory than the PHP include path.
     
    260273  public $Password      = '';
    261274
    262275  /**
    263    *  Sets SMTP auth type. Options are LOGIN | PLAIN | NTLM (default LOGIN)
     276   *  Sets SMTP auth type. Options are LOGIN | PLAIN | NTLM | CRAM-MD5 (default LOGIN)
    264277   *  @var string
    265278   */
    266279  public $AuthType      = '';
    267  
     280
    268281  /**
    269282   *  Sets SMTP realm.
    270283   *  @var string
     
    312325   */
    313326  public $SingleTo      = false;
    314327
    315    /**
     328  /**
     329   * Should we generate VERP addresses when sending via SMTP?
     330   * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
     331   * @var bool
     332   */
     333  public $do_verp      = false;
     334
     335  /**
    316336   * If SingleTo is true, this provides the array to hold the email addresses
    317337   * @var bool
    318338   */
    319339  public $SingleToArray = array();
    320340
    321  /**
     341  /**
     342   * Should we allow sending messages with empty body?
     343   * @var bool
     344   */
     345  public $AllowEmpty = false;
     346
     347    /**
    322348   * Provides the ability to change the generic line ending
    323349   * NOTE: The default remains '\n'. We force CRLF where we KNOW
    324350   *        it must be used via self::CRLF
     
    390416   * Sets the PHPMailer Version number
    391417   * @var string
    392418   */
    393   public $Version         = '5.2.4';
     419  public $Version         = '5.2.6';
    394420
    395421  /**
    396422   * What to use in the X-Mailer header
     
    491517  const STOP_CONTINUE = 1; // message?, likely ok to continue processing
    492518  const STOP_CRITICAL = 2; // message, plus full stop, critical error reached
    493519  const CRLF = "\r\n";     // SMTP RFC specified EOL
    494  
     520
    495521  /////////////////////////////////////////////////
    496522  // METHODS, VARIABLES
    497523  /////////////////////////////////////////////////
     
    522548   * Outputs debugging info via user-defined method
    523549   * @param string $str
    524550   */
    525   private function edebug($str) {
    526     if ($this->Debugoutput == "error_log") {
     551  protected function edebug($str) {
     552    switch ($this->Debugoutput) {
     553      case 'error_log':
    527554        error_log($str);
    528     } else {
     555        break;
     556      case 'html':
     557        //Cleans up output a bit for a better looking display that's HTML-safe
     558        echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, $this->CharSet)."<br>\n";
     559        break;
     560      case 'echo':
     561      default:
     562        //Just echoes exactly what was received
    529563        echo $str;
    530564    }
    531565  }
     
    539573  }
    540574
    541575  /**
     576   * Destructor
     577   */
     578  public function __destruct() {
     579      if ($this->Mailer == 'smtp') { //Close any open SMTP connection nicely
     580          $this->SmtpClose();
     581      }
     582  }
     583
     584  /**
    542585   * Sets message type to HTML.
    543586   * @param bool $ishtml
    544587   * @return void
     
    683726  return false;
    684727}
    685728
    686 /**
    687  * Set the From and FromName properties
    688  * @param string $address
    689  * @param string $name
    690  * @param int $auto Also set Reply-To and Sender
     729  /**
     730   * Set the From and FromName properties
     731   * @param string $address
     732   * @param string $name
     733   * @param boolean $auto Whether to also set the Sender address, defaults to true
    691734   * @throws phpmailerException
    692  * @return boolean
    693  */
    694   public function SetFrom($address, $name = '', $auto = 1) {
     735   * @return boolean
     736   */
     737  public function SetFrom($address, $name = '', $auto = true) {
    695738    $address = trim($address);
    696739    $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
    697740    if (!$this->ValidateAddress($address)) {
     
    707750    $this->From = $address;
    708751    $this->FromName = $name;
    709752    if ($auto) {
    710       if (empty($this->ReplyTo)) {
    711         $this->AddAnAddress('Reply-To', $address, $name);
    712       }
    713753      if (empty($this->Sender)) {
    714754        $this->Sender = $address;
    715755      }
     
    723763   * Conforms to RFC5322: Uses *correct* regex on which FILTER_VALIDATE_EMAIL is
    724764   * based; So why not use FILTER_VALIDATE_EMAIL? Because it was broken to
    725765   * not allow a@b type valid addresses :(
    726    * Some Versions of PHP break on the regex though, likely due to PCRE, so use
    727    * the older validation method for those users. (http://php.net/manual/en/pcre.installation.php)
    728766   * @link http://squiloople.com/2009/12/20/email-address-validation/
    729767   * @copyright regex Copyright Michael Rushton 2009-10 | http://squiloople.com/ | Feel free to use and redistribute this code. But please keep this copyright notice.
    730768   * @param string $address The email address to check
     
    733771   * @access public
    734772   */
    735773  public static function ValidateAddress($address) {
    736         if ((defined('PCRE_VERSION')) && (version_compare(PCRE_VERSION, '8.0') >= 0)) {
    737           return preg_match('/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)((?>(?>(?>((?>(?>(?>\x0D\x0A)?[     ])+|(?>[        ]*\x0D\x0A)?[   ]+)?)(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}|(?!(?:.*[a-f0-9][:\]]){7,})((?6)(?>:(?6)){0,5})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:|(?!(?:.*[a-f0-9]:){5,})(?8)?::(?>((?6)(?>:(?6)){0,3}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', $address);
    738         } elseif (function_exists('filter_var')) { //Introduced in PHP 5.2
    739         if(filter_var($address, FILTER_VALIDATE_EMAIL) === FALSE) {
    740           return false;
    741         } else {
    742           return true;
    743         }
    744     } else {
    745         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);
    746         }
     774      if (defined('PCRE_VERSION')) { //Check this instead of extension_loaded so it works when that function is disabled
     775          if (version_compare(PCRE_VERSION, '8.0') >= 0) {
     776              return (boolean)preg_match('/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', $address);
     777          } else {
     778              //Fall back to an older regex that doesn't need a recent PCRE
     779              return (boolean)preg_match('/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', $address);
     780          }
     781      } else {
     782          //No PCRE! Do something _very_ approximate!
     783          //Check the address is 3 chars or longer and contains an @ that's not the first or last char
     784          return (strlen($address) >= 3 and strpos($address, '@') >= 1 and strpos($address, '@') != strlen($address) - 1);
     785      }
    747786  }
    748787
    749788  /////////////////////////////////////////////////
     
    790829
    791830      $this->error_count = 0; // reset errors
    792831      $this->SetMessageType();
    793       //Refuse to send an empty message
    794       if (empty($this->Body)) {
     832      //Refuse to send an empty message unless we are specifically allowing it
     833      if (!$this->AllowEmpty and empty($this->Body)) {
    795834        throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL);
    796835      }
    797836
     
    807846          $this->mailHeader .= $this->HeaderLine("To", "undisclosed-recipients:;");
    808847        }
    809848        $this->mailHeader .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader(trim($this->Subject))));
    810         // if(count($this->cc) > 0) {
    811             // $this->mailHeader .= $this->AddrAppend("Cc", $this->cc);
    812         // }
    813849      }
    814850
    815851      // digitally sign with DKIM if enabled
    816852      if (!empty($this->DKIM_domain) && !empty($this->DKIM_private) && !empty($this->DKIM_selector) && !empty($this->DKIM_domain) && file_exists($this->DKIM_private)) {
    817         $header_dkim = $this->DKIM_Add($this->MIMEHeader, $this->EncodeHeader($this->SecureHeader($this->Subject)), $this->MIMEBody);
     853        $header_dkim = $this->DKIM_Add($this->MIMEHeader . $this->mailHeader, $this->EncodeHeader($this->SecureHeader($this->Subject)), $this->MIMEBody);
    818854        $this->MIMEHeader = str_replace("\r\n", "\n", $header_dkim) . $this->MIMEHeader;
    819855      }
    820856
     
    911947   * Sends mail using the PHP mail() function.
    912948   * @param string $header The message headers
    913949   * @param string $body The message body
    914      * @throws phpmailerException
     950   * @throws phpmailerException
    915951   * @access protected
    916952   * @return bool
    917953   */
     
    923959    $to = implode(', ', $toArr);
    924960
    925961    if (empty($this->Sender)) {
    926       $params = "-oi ";
     962      $params = " ";
    927963    } else {
    928       $params = sprintf("-oi -f%s", $this->Sender);
     964      $params = sprintf("-f%s", $this->Sender);
    929965    }
    930966    if ($this->Sender != '' and !ini_get('safe_mode')) {
    931967      $old_from = ini_get('sendmail_from');
     
    9651001   * @return bool
    9661002   */
    9671003  protected function SmtpSend($header, $body) {
    968     require_once $this->PluginDir . 'class-smtp.php';
     1004    require_once $this->PluginDir . 'class.smtp.php';
    9691005    $bad_rcpt = array();
    9701006
    9711007    if(!$this->SmtpConnect()) {
     
    9731009    }
    9741010    $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
    9751011    if(!$this->smtp->Mail($smtp_from)) {
    976       $this->SetError($this->Lang('from_failed') . $smtp_from . " : " . implode(",",$this->smtp->getError())) ;
     1012      $this->SetError($this->Lang('from_failed') . $smtp_from . ' : ' .implode(',', $this->smtp->getError()));
    9771013      throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
    9781014    }
    9791015
     
    10351071  /**
    10361072   * Initiates a connection to an SMTP server.
    10371073   * Returns false if the operation failed.
     1074   * @param array $options An array of options compatible with stream_context_create()
    10381075   * @uses SMTP
    10391076   * @access public
    10401077   * @throws phpmailerException
    10411078   * @return bool
    10421079   */
    1043   public function SmtpConnect() {
     1080  public function SmtpConnect($options = array()) {
    10441081    if(is_null($this->smtp)) {
    10451082      $this->smtp = new SMTP;
    10461083    }
    10471084
     1085    //Already connected?
     1086    if ($this->smtp->Connected()) {
     1087      return true;
     1088    }
     1089
    10481090    $this->smtp->Timeout = $this->Timeout;
    10491091    $this->smtp->do_debug = $this->SMTPDebug;
     1092    $this->smtp->Debugoutput = $this->Debugoutput;
     1093    $this->smtp->do_verp = $this->do_verp;
     1094    $index = 0;
     1095    $tls = ($this->SMTPSecure == 'tls');
     1096    $ssl = ($this->SMTPSecure == 'ssl');
    10501097    $hosts = explode(';', $this->Host);
    1051     $index = 0;
    1052     $connection = $this->smtp->Connected();
     1098    $lastexception = null;
    10531099
    1054     // Retry while there is no connection
    1055     try {
    1056       while($index < count($hosts) && !$connection) {
    1057         $hostinfo = array();
    1058         if (preg_match('/^(.+):([0-9]+)$/', $hosts[$index], $hostinfo)) {
    1059           $host = $hostinfo[1];
    1060           $port = $hostinfo[2];
    1061         } else {
    1062           $host = $hosts[$index];
    1063           $port = $this->Port;
    1064         }
    1065 
    1066         $tls = ($this->SMTPSecure == 'tls');
    1067         $ssl = ($this->SMTPSecure == 'ssl');
    1068 
    1069         if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) {
    1070 
    1071           $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname());
     1100    foreach ($hosts as $hostentry) {
     1101      $hostinfo = array();
     1102      $host = $hostentry;
     1103      $port = $this->Port;
     1104      if (preg_match('/^(.+):([0-9]+)$/', $hostentry, $hostinfo)) { //If $hostentry contains 'address:port', override default
     1105        $host = $hostinfo[1];
     1106        $port = $hostinfo[2];
     1107      }
     1108      if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout, $options)) {
     1109        try {
     1110          if ($this->Helo) {
     1111            $hello = $this->Helo;
     1112          } else {
     1113            $hello = $this->ServerHostname();
     1114          }
    10721115          $this->smtp->Hello($hello);
    10731116
    10741117          if ($tls) {
    10751118            if (!$this->smtp->StartTLS()) {
    10761119              throw new phpmailerException($this->Lang('connect_host'));
    10771120            }
    1078 
    10791121            //We must resend HELO after tls negotiation
    10801122            $this->smtp->Hello($hello);
    10811123          }
    1082 
    1083           $connection = true;
    10841124          if ($this->SMTPAuth) {
    1085             if (!$this->smtp->Authenticate($this->Username, $this->Password, $this->AuthType,
    1086                                            $this->Realm, $this->Workstation)) {
     1125            if (!$this->smtp->Authenticate($this->Username, $this->Password, $this->AuthType, $this->Realm, $this->Workstation)) {
    10871126              throw new phpmailerException($this->Lang('authenticate'));
    10881127            }
    10891128          }
     1129          return true;
     1130        } catch (phpmailerException $e) {
     1131          $lastexception = $e;
     1132          //We must have connected, but then failed TLS or Auth, so close connection nicely
     1133          $this->smtp->Quit();
    10901134        }
    1091         $index++;
    1092       if (!$connection) {
    1093         throw new phpmailerException($this->Lang('connect_host'));
    10941135      }
    1095       }
    1096     } catch (phpmailerException $e) {
    1097       $this->smtp->Reset();
    1098       if ($this->exceptions) {
    1099         throw $e;
    1100       }
    11011136    }
    1102     return true;
     1137    //If we get here, all connection attempts have failed, so close connection hard
     1138    $this->smtp->Close();
     1139    //As we've caught all exceptions, just report whatever the last one was
     1140    if ($this->exceptions and !is_null($lastexception)) {
     1141      throw $lastexception;
     1142    }
     1143    return false;
    11031144  }
    11041145
    11051146  /**
     
    11161157  }
    11171158
    11181159  /**
    1119   * Sets the language for all class error messages.
    1120   * Returns false if it cannot load the language file.  The default language is English.
    1121   * @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br")
    1122   * @param string $lang_path Path to the language file directory
     1160   * Sets the language for all class error messages.
     1161   * Returns false if it cannot load the language file.  The default language is English.
     1162   * @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br")
     1163   * @param string $lang_path Path to the language file directory
    11231164   * @return bool
    1124   * @access public
    1125   */
     1165   * @access public
     1166   */
    11261167  function SetLanguage($langcode = 'en', $lang_path = 'language/') {
    11271168    //Define full set of translatable strings
    11281169    $PHPMAILER_LANG = array(
     
    12511292            $buf = '';
    12521293          }
    12531294          while (strlen($word) > 0) {
     1295            if ($length <= 0) {
     1296                break;
     1297            }
    12541298            $len = $length;
    12551299            if ($is_utf8) {
    12561300              $len = $this->UTF8CharBoundary($word, $len);
     
    13701414    }
    13711415
    13721416    if ($this->ReturnPath) {
    1373       $result .= $this->HeaderLine('Return-Path', trim($this->ReturnPath));
     1417      $result .= $this->HeaderLine('Return-Path', '<'.trim($this->ReturnPath).'>');
    13741418    } elseif ($this->Sender == '') {
    1375       $result .= $this->HeaderLine('Return-Path', trim($this->From));
     1419      $result .= $this->HeaderLine('Return-Path', '<'.trim($this->From).'>');
    13761420    } else {
    1377       $result .= $this->HeaderLine('Return-Path', trim($this->Sender));
     1421      $result .= $this->HeaderLine('Return-Path', '<'.trim($this->Sender).'>');
    13781422    }
    13791423
    13801424    // To be created automatically by mail()
     
    14231467    }
    14241468    $result .= $this->HeaderLine('X-Priority', $this->Priority);
    14251469    if ($this->XMailer == '') {
    1426         $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (http://code.google.com/a/apache-extras.org/p/phpmailer/)');
     1470        $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (https://github.com/PHPMailer/PHPMailer/)');
    14271471    } else {
    14281472      $myXmailer = trim($this->XMailer);
    14291473      if ($myXmailer) {
     
    14571501    switch($this->message_type) {
    14581502      case 'inline':
    14591503        $result .= $this->HeaderLine('Content-Type', 'multipart/related;');
    1460         $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
     1504        $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1].'"');
    14611505        break;
    14621506      case 'attach':
    14631507      case 'inline_attach':
    14641508      case 'alt_attach':
    14651509      case 'alt_inline_attach':
    14661510        $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;');
    1467         $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
     1511        $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1].'"');
    14681512        break;
    14691513      case 'alt':
    14701514      case 'alt_inline':
    14711515        $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
    1472         $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
     1516        $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1].'"');
    14731517        break;
    14741518      default:
    14751519        // Catches case 'plain': and case '':
    1476         $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);
    14771520        $result .= $this->TextLine('Content-Type: '.$this->ContentType.'; charset='.$this->CharSet);
    14781521        break;
    14791522    }
     1523    //RFC1341 part 5 says 7bit is assumed if not specified
     1524    if ($this->Encoding != '7bit') {
     1525      $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);
     1526    }
    14801527
    14811528    if($this->Mailer != 'mail') {
    14821529      $result .= $this->LE;
     
    15151562        $body .= $this->GetBoundary($this->boundary[1], '', '', '');
    15161563        $body .= $this->EncodeString($this->Body, $this->Encoding);
    15171564        $body .= $this->LE.$this->LE;
    1518         $body .= $this->AttachAll("inline", $this->boundary[1]);
     1565        $body .= $this->AttachAll('inline', $this->boundary[1]);
    15191566        break;
    15201567      case 'attach':
    15211568        $body .= $this->GetBoundary($this->boundary[1], '', '', '');
    15221569        $body .= $this->EncodeString($this->Body, $this->Encoding);
    15231570        $body .= $this->LE.$this->LE;
    1524         $body .= $this->AttachAll("attachment", $this->boundary[1]);
     1571        $body .= $this->AttachAll('attachment', $this->boundary[1]);
    15251572        break;
    15261573      case 'inline_attach':
    1527         $body .= $this->TextLine("--" . $this->boundary[1]);
     1574        $body .= $this->TextLine('--' . $this->boundary[1]);
    15281575        $body .= $this->HeaderLine('Content-Type', 'multipart/related;');
    1529         $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"');
     1576        $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"');
    15301577        $body .= $this->LE;
    15311578        $body .= $this->GetBoundary($this->boundary[2], '', '', '');
    15321579        $body .= $this->EncodeString($this->Body, $this->Encoding);
    15331580        $body .= $this->LE.$this->LE;
    1534         $body .= $this->AttachAll("inline", $this->boundary[2]);
     1581        $body .= $this->AttachAll('inline', $this->boundary[2]);
    15351582        $body .= $this->LE;
    1536         $body .= $this->AttachAll("attachment", $this->boundary[1]);
     1583        $body .= $this->AttachAll('attachment', $this->boundary[1]);
    15371584        break;
    15381585      case 'alt':
    15391586        $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');
     
    15421589        $body .= $this->GetBoundary($this->boundary[1], '', 'text/html', '');
    15431590        $body .= $this->EncodeString($this->Body, $this->Encoding);
    15441591        $body .= $this->LE.$this->LE;
     1592        if(!empty($this->Ical)) {
     1593          $body .= $this->GetBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
     1594          $body .= $this->EncodeString($this->Ical, $this->Encoding);
     1595          $body .= $this->LE.$this->LE;
     1596        }
    15451597        $body .= $this->EndBoundary($this->boundary[1]);
    15461598        break;
    15471599      case 'alt_inline':
    15481600        $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');
    15491601        $body .= $this->EncodeString($this->AltBody, $this->Encoding);
    15501602        $body .= $this->LE.$this->LE;
    1551         $body .= $this->TextLine("--" . $this->boundary[1]);
     1603        $body .= $this->TextLine('--' . $this->boundary[1]);
    15521604        $body .= $this->HeaderLine('Content-Type', 'multipart/related;');
    1553         $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"');
     1605        $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"');
    15541606        $body .= $this->LE;
    15551607        $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', '');
    15561608        $body .= $this->EncodeString($this->Body, $this->Encoding);
    15571609        $body .= $this->LE.$this->LE;
    1558         $body .= $this->AttachAll("inline", $this->boundary[2]);
     1610        $body .= $this->AttachAll('inline', $this->boundary[2]);
    15591611        $body .= $this->LE;
    15601612        $body .= $this->EndBoundary($this->boundary[1]);
    15611613        break;
    15621614      case 'alt_attach':
    1563         $body .= $this->TextLine("--" . $this->boundary[1]);
     1615        $body .= $this->TextLine('--' . $this->boundary[1]);
    15641616        $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
    1565         $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"');
     1617        $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"');
    15661618        $body .= $this->LE;
    15671619        $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '');
    15681620        $body .= $this->EncodeString($this->AltBody, $this->Encoding);
     
    15721624        $body .= $this->LE.$this->LE;
    15731625        $body .= $this->EndBoundary($this->boundary[2]);
    15741626        $body .= $this->LE;
    1575         $body .= $this->AttachAll("attachment", $this->boundary[1]);
     1627        $body .= $this->AttachAll('attachment', $this->boundary[1]);
    15761628        break;
    15771629      case 'alt_inline_attach':
    1578         $body .= $this->TextLine("--" . $this->boundary[1]);
     1630        $body .= $this->TextLine('--' . $this->boundary[1]);
    15791631        $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
    1580         $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"');
     1632        $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2].'"');
    15811633        $body .= $this->LE;
    15821634        $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '');
    15831635        $body .= $this->EncodeString($this->AltBody, $this->Encoding);
    15841636        $body .= $this->LE.$this->LE;
    1585         $body .= $this->TextLine("--" . $this->boundary[2]);
     1637        $body .= $this->TextLine('--' . $this->boundary[2]);
    15861638        $body .= $this->HeaderLine('Content-Type', 'multipart/related;');
    1587         $body .= $this->TextLine("\tboundary=\"" . $this->boundary[3] . '"');
     1639        $body .= $this->TextLine("\tboundary=\"" . $this->boundary[3].'"');
    15881640        $body .= $this->LE;
    15891641        $body .= $this->GetBoundary($this->boundary[3], '', 'text/html', '');
    15901642        $body .= $this->EncodeString($this->Body, $this->Encoding);
    15911643        $body .= $this->LE.$this->LE;
    1592         $body .= $this->AttachAll("inline", $this->boundary[3]);
     1644        $body .= $this->AttachAll('inline', $this->boundary[3]);
    15931645        $body .= $this->LE;
    15941646        $body .= $this->EndBoundary($this->boundary[2]);
    15951647        $body .= $this->LE;
    1596         $body .= $this->AttachAll("attachment", $this->boundary[1]);
     1648        $body .= $this->AttachAll('attachment', $this->boundary[1]);
    15971649        break;
    15981650      default:
    15991651        // catch case 'plain' and case ''
     
    16051657      $body = '';
    16061658    } elseif ($this->sign_key_file) {
    16071659      try {
    1608         $file = tempnam('', 'mail');
     1660        if (!defined('PKCS7_TEXT')) {
     1661            throw new phpmailerException($this->Lang('signing').' OpenSSL extension missing.');
     1662        }
     1663        $file = tempnam(sys_get_temp_dir(), 'mail');
    16091664        file_put_contents($file, $body); //TODO check this worked
    1610         $signed = tempnam("", "signed");
    1611         if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) {
     1665        $signed = tempnam(sys_get_temp_dir(), 'signed');
     1666        if (@openssl_pkcs7_sign($file, $signed, 'file://'.realpath($this->sign_cert_file), array('file://'.realpath($this->sign_key_file), $this->sign_key_pass), null)) {
    16121667          @unlink($file);
    16131668          $body = file_get_contents($signed);
    16141669          @unlink($signed);
    16151670        } else {
    16161671          @unlink($file);
    16171672          @unlink($signed);
    1618           throw new phpmailerException($this->Lang("signing").openssl_error_string());
     1673          throw new phpmailerException($this->Lang('signing').openssl_error_string());
    16191674        }
    16201675      } catch (phpmailerException $e) {
    16211676        $body = '';
     
    16241679        }
    16251680      }
    16261681    }
    1627 
    16281682    return $body;
    16291683  }
    16301684
     
    16821736  }
    16831737
    16841738  /**
    1685    *  Returns a formatted header line.
     1739   * Returns a formatted header line.
    16861740   * @access public
    16871741   * @param string $name
    16881742   * @param string $value
     
    17141768   * @param string $name Overrides the attachment name.
    17151769   * @param string $encoding File encoding (see $Encoding).
    17161770   * @param string $type File extension (MIME) type.
     1771   * @param string $disposition Disposition to use
    17171772   * @throws phpmailerException
    17181773   * @return bool
    17191774   */
    1720   public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
     1775  public function AddAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment') {
    17211776    try {
    17221777      if ( !@is_file($path) ) {
    17231778        throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE);
    17241779      }
     1780
     1781      //If a MIME type is not specified, try to work it out from the file name
     1782      if ($type == '') {
     1783        $type = self::filenameToType($path);
     1784      }
     1785
    17251786      $filename = basename($path);
    17261787      if ( $name == '' ) {
    17271788        $name = $filename;
     
    17341795        3 => $encoding,
    17351796        4 => $type,
    17361797        5 => false,  // isStringAttachment
    1737         6 => 'attachment',
     1798        6 => $disposition,
    17381799        7 => 0
    17391800      );
    17401801
     
    17461807      if ($this->SMTPDebug) {
    17471808        $this->edebug($e->getMessage()."\n");
    17481809      }
    1749       if ( $e->getCode() == self::STOP_CRITICAL ) {
    1750         return false;
    1751       }
     1810      return false;
    17521811    }
    17531812    return true;
    17541813  }
     
    18091868          $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
    18101869        }
    18111870
    1812         $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE);
     1871        //If a filename contains any of these chars, it should be quoted, but not otherwise: RFC2183 & RFC2045 5.1
     1872        //Fixes a warning in IETF's msglint MIME checker
     1873        //
     1874        // Allow for bypassing the Content-Disposition header totally
     1875        if (!(empty($disposition))) {
     1876          if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) {
     1877            $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE);
     1878          } else {
     1879            $mime[] = sprintf("Content-Disposition: %s; filename=%s%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE);
     1880          }
     1881        } else {
     1882          $mime[] = $this->LE;
     1883        }
    18131884
    18141885        // Encode as string attachment
    18151886        if($bString) {
     
    18481919      if (!is_readable($path)) {
    18491920        throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE);
    18501921      }
    1851       //  if (!function_exists('get_magic_quotes')) {
    1852       //    function get_magic_quotes() {
    1853       //      return false;
    1854       //    }
    1855       //  }
    18561922      $magic_quotes = get_magic_quotes_runtime();
    18571923      if ($magic_quotes) {
    18581924        if (version_compare(PHP_VERSION, '5.3.0', '<')) {
    18591925          set_magic_quotes_runtime(0);
    18601926        } else {
    1861           ini_set('magic_quotes_runtime', 0); 
     1927          ini_set('magic_quotes_runtime', 0);
    18621928        }
    18631929      }
    18641930      $file_buffer  = file_get_contents($path);
     
    18671933        if (version_compare(PHP_VERSION, '5.3.0', '<')) {
    18681934          set_magic_quotes_runtime($magic_quotes);
    18691935        } else {
    1870           ini_set('magic_quotes_runtime', $magic_quotes); 
     1936          ini_set('magic_quotes_runtime', $magic_quotes);
    18711937        }
    18721938      }
    18731939      return $file_buffer;
     
    19432009        break;
    19442010    }
    19452011
    1946     if ($x == 0) {
     2012    if ($x == 0) { //There are no chars that need encoding
    19472013      return ($str);
    19482014    }
    19492015
    19502016    $maxlen = 75 - 7 - strlen($this->CharSet);
    19512017    // Try to select the encoding which should produce the shortest output
    1952     if (strlen($str)/3 < $x) {
     2018    if ($x > strlen($str)/3) { //More than a third of the content will need encoding, so B encoding will be most efficient
    19532019      $encoding = 'B';
    19542020      if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) {
    19552021        // Use a custom function which correctly encodes and wraps long
     
    20322098  }
    20332099
    20342100  /**
    2035   * Encode string to quoted-printable.
    2036   * Only uses standard PHP, slow, but will always work
    2037   * @access public
    2038    * @param string $input
    2039   * @param integer $line_max Number of chars allowed on a line before wrapping
    2040    * @param bool $space_conv
    2041    * @internal param string $string the text to encode
    2042   * @return string
    2043   */
    2044   public function EncodeQPphp( $input = '', $line_max = 76, $space_conv = false) {
    2045     $hex = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
    2046     $lines = preg_split('/(?:\r\n|\r|\n)/', $input);
    2047     $eol = "\r\n";
    2048     $escape = '=';
    2049     $output = '';
    2050     while( list(, $line) = each($lines) ) {
    2051       $linlen = strlen($line);
    2052       $newline = '';
    2053       for($i = 0; $i < $linlen; $i++) {
    2054         $c = substr( $line, $i, 1 );
    2055         $dec = ord( $c );
    2056         if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E
    2057           $c = '=2E';
    2058         }
    2059         if ( $dec == 32 ) {
    2060           if ( $i == ( $linlen - 1 ) ) { // convert space at eol only
    2061             $c = '=20';
    2062           } else if ( $space_conv ) {
    2063             $c = '=20';
    2064           }
    2065         } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required
    2066           $h2 = (integer)floor($dec/16);
    2067           $h1 = (integer)floor($dec%16);
    2068           $c = $escape.$hex[$h2].$hex[$h1];
    2069         }
    2070         if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted
    2071           $output .= $newline.$escape.$eol; //  soft line break; " =\r\n" is okay
    2072           $newline = '';
    2073           // check if newline first character will be point or not
    2074           if ( $dec == 46 ) {
    2075             $c = '=2E';
    2076           }
    2077         }
    2078         $newline .= $c;
    2079       } // end of for
    2080       $output .= $newline.$eol;
    2081     } // end of while
    2082     return $output;
    2083   }
    2084 
    2085   /**
    2086   * Encode string to RFC2045 (6.7) quoted-printable format
    2087   * Uses a PHP5 stream filter to do the encoding about 64x faster than the old version
    2088   * Also results in same content as you started with after decoding
    2089   * @see EncodeQPphp()
    2090   * @access public
    2091   * @param string $string the text to encode
    2092   * @param integer $line_max Number of chars allowed on a line before wrapping
    2093   * @param boolean $space_conv Dummy param for compatibility with existing EncodeQP function
    2094   * @return string
    2095   * @author Marcus Bointon
    2096   */
    2097   public function EncodeQP($string, $line_max = 76, $space_conv = false) {
     2101   * Encode string to RFC2045 (6.7) quoted-printable format
     2102   * @access public
     2103   * @param string $string The text to encode
     2104   * @param integer $line_max Number of chars allowed on a line before wrapping
     2105   * @return string
     2106   * @link PHP version adapted from http://www.php.net/manual/en/function.quoted-printable-decode.php#89417
     2107   */
     2108  public function EncodeQP($string, $line_max = 76) {
    20982109    if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3)
    20992110      return quoted_printable_encode($string);
    21002111    }
    2101     $filters = stream_get_filters();
    2102     if (!in_array('convert.*', $filters)) { //Got convert stream filter?
    2103       return $this->EncodeQPphp($string, $line_max, $space_conv); //Fall back to old implementation
    2104     }
    2105     $fp = fopen('php://temp/', 'r+');
    2106     $string = preg_replace('/\r\n?/', $this->LE, $string); //Normalise line breaks
    2107     $params = array('line-length' => $line_max, 'line-break-chars' => $this->LE);
    2108     $s = stream_filter_append($fp, 'convert.quoted-printable-encode', STREAM_FILTER_READ, $params);
    2109     fputs($fp, $string);
    2110     rewind($fp);
    2111     $out = stream_get_contents($fp);
    2112     stream_filter_remove($s);
    2113     $out = preg_replace('/^\./m', '=2E', $out); //Encode . if it is first char on a line, workaround for bug in Exchange
    2114     fclose($fp);
    2115     return $out;
     2112    //Fall back to a pure PHP implementation
     2113    $string = str_replace(array('%20', '%0D%0A.', '%0D%0A', '%'), array(' ', "\r\n=2E", "\r\n", '='), rawurlencode($string));
     2114    $string = preg_replace('/[^\r\n]{'.($line_max - 3).'}[^=\r\n]{2}/', "$0=\r\n", $string);
     2115    return $string;
    21162116  }
    21172117
    21182118  /**
     2119   * Wrapper to preserve BC for old QP encoding function that was removed
     2120   * @see EncodeQP()
     2121   * @access public
     2122   * @param string $string
     2123   * @param integer $line_max
     2124   * @param bool $space_conv
     2125   * @return string
     2126   */
     2127  public function EncodeQPphp($string, $line_max = 76, $space_conv = false) {
     2128    return $this->EncodeQP($string, $line_max);
     2129  }
     2130
     2131  /**
    21192132   * Encode string to q encoding.
    21202133   * @link http://tools.ietf.org/html/rfc2047
    21212134   * @param string $str the text to encode
     
    21252138   */
    21262139  public function EncodeQ($str, $position = 'text') {
    21272140    //There should not be any EOL in the string
    2128         $pattern="";
     2141    $pattern = '';
    21292142    $encoded = str_replace(array("\r", "\n"), '', $str);
    21302143    switch (strtolower($position)) {
    21312144      case 'phrase':
     
    21342147
    21352148      case 'comment':
    21362149        $pattern = '\(\)"';
    2137         //note that we dont break here!
    2138         //for this reason we build the $pattern withoud including delimiters and []
     2150        //note that we don't break here!
     2151        //for this reason we build the $pattern without including delimiters and []
    21392152
    21402153      case 'text':
    21412154      default:
     
    21442157        $pattern = '\075\000-\011\013\014\016-\037\077\137\177-\377' . $pattern;
    21452158        break;
    21462159    }
    2147    
     2160
    21482161    if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
    21492162      foreach (array_unique($matches[0]) as $char) {
    21502163        $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
    21512164      }
    21522165    }
    2153    
     2166
    21542167    //Replace every spaces to _ (more readable than =20)
    21552168    return str_replace(' ', '_', $encoded);
    21562169}
     
    21642177   * @param string $filename Name of the attachment.
    21652178   * @param string $encoding File encoding (see $Encoding).
    21662179   * @param string $type File extension (MIME) type.
     2180   * @param string $disposition Disposition to use
    21672181   * @return void
    21682182   */
    2169   public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') {
     2183  public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = '', $disposition = 'attachment') {
     2184    //If a MIME type is not specified, try to work it out from the file name
     2185    if ($type == '') {
     2186      $type = self::filenameToType($filename);
     2187    }
    21702188    // Append to $attachment array
    21712189    $this->attachment[] = array(
    21722190      0 => $string,
     
    21752193      3 => $encoding,
    21762194      4 => $type,
    21772195      5 => true,  // isStringAttachment
    2178       6 => 'attachment',
     2196      6 => $disposition,
    21792197      7 => 0
    21802198    );
    21812199  }
    21822200
    21832201  /**
    2184    * Adds an embedded attachment.  This can include images, sounds, and
    2185    * just about any other document.  Make sure to set the $type to an
    2186    * image type.  For JPEG images use "image/jpeg" and for GIF images
    2187    * use "image/gif".
     2202   * Add an embedded attachment from a file.
     2203   * This can include images, sounds, and just about any other document type.
    21882204   * @param string $path Path to the attachment.
    2189    * @param string $cid Content ID of the attachment.  Use this to identify
    2190    *        the Id for accessing the image in an HTML form.
     2205   * @param string $cid Content ID of the attachment; Use this to reference
     2206   *        the content when using an embedded image in HTML.
    21912207   * @param string $name Overrides the attachment name.
    21922208   * @param string $encoding File encoding (see $Encoding).
    2193    * @param string $type File extension (MIME) type.
    2194    * @return bool
     2209   * @param string $type File MIME type.
     2210   * @param string $disposition Disposition to use
     2211   * @return bool True on successfully adding an attachment
    21952212   */
    2196   public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
    2197 
     2213  public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline') {
    21982214    if ( !@is_file($path) ) {
    21992215      $this->SetError($this->Lang('file_access') . $path);
    22002216      return false;
    22012217    }
    22022218
     2219    //If a MIME type is not specified, try to work it out from the file name
     2220    if ($type == '') {
     2221      $type = self::filenameToType($path);
     2222    }
     2223
    22032224    $filename = basename($path);
    22042225    if ( $name == '' ) {
    22052226      $name = $filename;
     
    22132234      3 => $encoding,
    22142235      4 => $type,
    22152236      5 => false,  // isStringAttachment
    2216       6 => 'inline',
     2237      6 => $disposition,
    22172238      7 => $cid
    22182239    );
    2219 
    22202240    return true;
    22212241  }
    22222242
     2243
    22232244  /**
    2224    * Adds an embedded stringified attachment.  This can include images, sounds, and
    2225    * just about any other document.  Make sure to set the $type to an
    2226    * image type.  For JPEG images use "image/jpeg" and for GIF images
    2227    * use "image/gif".
    2228    * @param string $string The attachment.
    2229    * @param string $cid Content ID of the attachment.  Use this to identify
    2230    *        the Id for accessing the image in an HTML form.
    2231    * @param string $name Overrides the attachment name.
     2245   * Add an embedded stringified attachment.
     2246   * This can include images, sounds, and just about any other document type.
     2247   * Be sure to set the $type to an image type for images:
     2248   * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
     2249   * @param string $string The attachment binary data.
     2250   * @param string $cid Content ID of the attachment; Use this to reference
     2251   *        the content when using an embedded image in HTML.
     2252   * @param string $name
    22322253   * @param string $encoding File encoding (see $Encoding).
    2233    * @param string $type File extension (MIME) type.
    2234    * @return bool
     2254   * @param string $type MIME type.
     2255   * @param string $disposition Disposition to use
     2256   * @return bool True on successfully adding an attachment
    22352257   */
    2236   public function AddStringEmbeddedImage($string, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
     2258  public function AddStringEmbeddedImage($string, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline') {
     2259    //If a MIME type is not specified, try to work it out from the name
     2260    if ($type == '') {
     2261      $type = self::filenameToType($name);
     2262    }
     2263
    22372264    // Append to $attachment array
    22382265    $this->attachment[] = array(
    22392266      0 => $string,
     
    22422269      3 => $encoding,
    22432270      4 => $type,
    22442271      5 => true,  // isStringAttachment
    2245       6 => 'inline',
     2272      6 => $disposition,
    22462273      7 => $cid
    22472274    );
     2275    return true;
    22482276  }
    22492277
    22502278  /**
     
    23842412   * @static
    23852413   */
    23862414  public static function RFCDate() {
    2387     $tz = date('Z');
    2388     $tzs = ($tz < 0) ? '-' : '+';
    2389     $tz = abs($tz);
    2390     $tz = (int)($tz/3600)*100 + ($tz%3600)/60;
    2391     $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz);
    2392 
    2393     return $result;
     2415    //Set the time zone to whatever the default is to avoid 500 errors
     2416    //Will default to UTC if it's not set properly in php.ini
     2417    date_default_timezone_set(@date_default_timezone_get());
     2418    return date('D, j M Y H:i:s O');
    23942419  }
    23952420
    23962421  /**
     
    24712496  }
    24722497
    24732498  /**
    2474    * Evaluates the message and returns modifications for inline images and backgrounds
     2499   * Creates a message from an HTML string, making modifications for inline images and backgrounds
     2500   * and creates a plain-text version by converting the HTML
     2501   * Overwrites any existing values in $this->Body and $this->AltBody
    24752502   * @access public
    2476    * @param string $message Text to be HTML modified
     2503   * @param string $message HTML message string
    24772504   * @param string $basedir baseline directory for path
     2505   * @param bool $advanced Whether to use the advanced HTML to text converter
    24782506   * @return string $message
    24792507   */
    2480   public function MsgHTML($message, $basedir = '') {
     2508  public function MsgHTML($message, $basedir = '', $advanced = false) {
    24812509    preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images);
    2482     if(isset($images[2])) {
    2483       foreach($images[2] as $i => $url) {
     2510    if (isset($images[2])) {
     2511      foreach ($images[2] as $i => $url) {
    24842512        // do not change urls for absolute images (thanks to corvuscorax)
    24852513        if (!preg_match('#^[A-z]+://#', $url)) {
    24862514          $filename = basename($url);
     
    24882516          if ($directory == '.') {
    24892517            $directory = '';
    24902518          }
    2491           $cid = 'cid:' . md5($url);
    2492           $ext = pathinfo($filename, PATHINFO_EXTENSION);
    2493           $mimeType  = self::_mime_types($ext);
    2494           if ( strlen($basedir) > 1 && substr($basedir, -1) != '/') { $basedir .= '/'; }
    2495           if ( strlen($directory) > 1 && substr($directory, -1) != '/') { $directory .= '/'; }
    2496           if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($url), $filename, 'base64', $mimeType) ) {
    2497             $message = preg_replace("/".$images[1][$i]."=[\"']".preg_quote($url, '/')."[\"']/Ui", $images[1][$i]."=\"".$cid."\"", $message);
     2519          $cid = md5($url).'@phpmailer.0'; //RFC2392 S 2
     2520          if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
     2521            $basedir .= '/';
    24982522          }
     2523          if (strlen($directory) > 1 && substr($directory, -1) != '/') {
     2524            $directory .= '/';
     2525          }
     2526          if ($this->AddEmbeddedImage($basedir.$directory.$filename, $cid, $filename, 'base64', self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION)))) {
     2527            $message = preg_replace("/".$images[1][$i]."=[\"']".preg_quote($url, '/')."[\"']/Ui", $images[1][$i]."=\"cid:".$cid."\"", $message);
     2528          }
    24992529        }
    25002530      }
    25012531    }
    25022532    $this->IsHTML(true);
    2503     $this->Body = $message;
    25042533    if (empty($this->AltBody)) {
    2505         $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s', '', $message)));
    2506         if (!empty($textMsg)) {
    2507             $this->AltBody = html_entity_decode($textMsg, ENT_QUOTES, $this->CharSet);
    2508         }
    2509     }
    2510     if (empty($this->AltBody)) {
    25112534      $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n";
    25122535    }
    2513     return $message;
     2536    //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
     2537    $this->Body = $this->NormalizeBreaks($message);
     2538    $this->AltBody = $this->NormalizeBreaks($this->html2text($message, $advanced));
     2539    return $this->Body;
    25142540  }
    25152541
     2542    /**
     2543     * Convert an HTML string into a plain text version
     2544     * @param string $html The HTML text to convert
     2545     * @param bool $advanced Should this use the more complex html2text converter or just a simple one?
     2546     * @return string
     2547     */
     2548  public function html2text($html, $advanced = false) {
     2549    if ($advanced) {
     2550      require_once 'extras/class.html2text.php';
     2551      $h = new html2text($html);
     2552      return $h->get_text();
     2553    }
     2554    return html_entity_decode(trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), ENT_QUOTES, $this->CharSet);
     2555  }
     2556
    25162557  /**
    25172558   * Gets the MIME type of the embedded or inline image
    25182559   * @param string $ext File extension
     
    26132654  }
    26142655
    26152656  /**
    2616   * Set (or reset) Class Objects (variables)
    2617   *
    2618   * Usage Example:
    2619   * $page->set('X-Priority', '3');
    2620   *
    2621   * @access public
    2622   * @param string $name Parameter Name
    2623   * @param mixed $value Parameter Value
    2624   * NOTE: will not work with arrays, there are no arrays to set/reset
     2657   * Try to map a file name to a MIME type, default to application/octet-stream
     2658   * @param string $filename A file name or full path, does not need to exist as a file
     2659   * @return string
     2660   * @static
     2661   */
     2662  public static function filenameToType($filename) {
     2663    //In case the path is a URL, strip any query string before getting extension
     2664    $qpos = strpos($filename, '?');
     2665    if ($qpos !== false) {
     2666      $filename = substr($filename, 0, $qpos);
     2667    }
     2668    $pathinfo = self::mb_pathinfo($filename);
     2669    return self::_mime_types($pathinfo['extension']);
     2670  }
     2671
     2672  /**
     2673   * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
     2674   * Works similarly to the one in PHP >= 5.2.0
     2675   * @link http://www.php.net/manual/en/function.pathinfo.php#107461
     2676   * @param string $path A filename or path, does not need to exist as a file
     2677   * @param integer|string $options Either a PATHINFO_* constant, or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
     2678   * @return string|array
     2679   * @static
     2680   */
     2681  public static function mb_pathinfo($path, $options = null) {
     2682    $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
     2683    $m = array();
     2684    preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m);
     2685    if(array_key_exists(1, $m)) {
     2686      $ret['dirname'] = $m[1];
     2687    }
     2688    if(array_key_exists(2, $m)) {
     2689      $ret['basename'] = $m[2];
     2690    }
     2691    if(array_key_exists(5, $m)) {
     2692      $ret['extension'] = $m[5];
     2693    }
     2694    if(array_key_exists(3, $m)) {
     2695      $ret['filename'] = $m[3];
     2696    }
     2697    switch($options) {
     2698      case PATHINFO_DIRNAME:
     2699      case 'dirname':
     2700        return $ret['dirname'];
     2701        break;
     2702      case PATHINFO_BASENAME:
     2703      case 'basename':
     2704        return $ret['basename'];
     2705        break;
     2706      case PATHINFO_EXTENSION:
     2707      case 'extension':
     2708        return $ret['extension'];
     2709        break;
     2710      case PATHINFO_FILENAME:
     2711      case 'filename':
     2712        return $ret['filename'];
     2713        break;
     2714      default:
     2715        return $ret;
     2716    }
     2717  }
     2718
     2719  /**
     2720   * Set (or reset) Class Objects (variables)
     2721   *
     2722   * Usage Example:
     2723   * $page->set('X-Priority', '3');
     2724   *
     2725   * @access public
     2726   * @param string $name
     2727   * @param mixed $value
     2728   * NOTE: will not work with arrays, there are no arrays to set/reset
    26252729   * @throws phpmailerException
    26262730   * @return bool
    2627   * @todo Should this not be using __set() magic function?
    2628   */
     2731   * @todo Should this not be using __set() magic function?
     2732   */
    26292733  public function set($name, $value = '') {
    26302734    try {
    26312735      if (isset($this->$name) ) {
     
    26452749  /**
    26462750   * Strips newlines to prevent header injection.
    26472751   * @access public
    2648    * @param string $str String
     2752   * @param string $str
    26492753   * @return string
    26502754   */
    26512755  public function SecureHeader($str) {
     
    26532757  }
    26542758
    26552759  /**
     2760   * Normalize UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format
     2761   * Defaults to CRLF (for message bodies) and preserves consecutive breaks
     2762   * @param string $text
     2763   * @param string $breaktype What kind of line break to use, defaults to CRLF
     2764   * @return string
     2765   * @access public
     2766   * @static
     2767   */
     2768  public static function NormalizeBreaks($text, $breaktype = "\r\n") {
     2769    return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
     2770  }
     2771
     2772
     2773        /**
    26562774   * Set the private key file and password to sign the message.
    26572775   *
    26582776   * @access public
    2659    * @param $cert_filename
    2660    * @param string $key_filename Parameter File Name
     2777   * @param string $cert_filename
     2778   * @param string $key_filename
    26612779   * @param string $key_pass Password for private key
    26622780   */
    26632781  public function Sign($cert_filename, $key_filename, $key_pass) {
     
    26912809   *
    26922810   * @access public
    26932811   * @param string $s Header
     2812   * @throws phpmailerException
    26942813   * @return string
    26952814   */
    26962815  public function DKIM_Sign($s) {
     2816    if (!defined('PKCS7_TEXT')) {
     2817        if ($this->exceptions) {
     2818            throw new phpmailerException($this->Lang("signing").' OpenSSL extension missing.');
     2819        }
     2820        return '';
     2821    }
    26972822    $privKeyStr = file_get_contents($this->DKIM_private);
    26982823    if ($this->DKIM_passphrase != '') {
    26992824      $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
     
    27612886    $DKIMtime             = time() ; // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
    27622887    $subject_header       = "Subject: $subject";
    27632888    $headers              = explode($this->LE, $headers_line);
    2764         $from_header          = "";
    2765         $to_header            = "";
     2889    $from_header          = '';
     2890    $to_header            = '';
     2891    $current = '';
    27662892    foreach($headers as $header) {
    27672893      if (strpos($header, 'From:') === 0) {
    27682894        $from_header = $header;
     2895        $current = 'from_header';
    27692896      } elseif (strpos($header, 'To:') === 0) {
    27702897        $to_header = $header;
     2898        $current = 'to_header';
     2899      } else {
     2900        if($current && strpos($header, ' =?') === 0){
     2901          $current .= $header;
     2902        } else {
     2903          $current = '';
     2904        }
    27712905      }
    27722906    }
    27732907    $from     = str_replace('|', '=7C', $this->DKIM_QP($from_header));
     
    27882922                "\tb=";
    27892923    $toSign   = $this->DKIM_HeaderC($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs);
    27902924    $signed   = $this->DKIM_Sign($toSign);
    2791     return "X-PHPMAILER-DKIM: code.google.com/a/apache-extras.org/p/phpmailer/\r\n".$dkimhdrs.$signed."\r\n";
     2925    return $dkimhdrs.$signed."\r\n";
    27922926  }
    27932927
    27942928  /**
     
    28012935   * @param string $body
    28022936   * @param string $from
    28032937   */
    2804   protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from=null) {
     2938  protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from = null) {
    28052939    if (!empty($this->action_function) && is_callable($this->action_function)) {
    28062940      $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
    28072941      call_user_func_array($this->action_function, $params);
     
    28222956    $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
    28232957    return $errorMsg;
    28242958  }
    2825 }
    2826 ?>
     2959}
     2960 No newline at end of file