Make WordPress Core

Ticket #25560: 25560v2.diff

File 25560v2.diff, 269.1 KB (added by MattyRob, 11 years ago)
  • wp-includes/PHPMailer/PHPMailerAutoload.php

     
     1<?php
     2/**
     3 * PHPMailer SPL autoloader.
     4 * PHP Version 5.0.0
     5 * @package PHPMailer
     6 * @link https://github.com/PHPMailer/PHPMailer/
     7 * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk>
     8 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
     9 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
     10 * @author Brent R. Matzelle (original founder)
     11 * @copyright 2013 Marcus Bointon
     12 * @copyright 2010 - 2012 Jim Jagielski
     13 * @copyright 2004 - 2009 Andy Prevost
     14 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
     15 * @note This program is distributed in the hope that it will be useful - WITHOUT
     16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     17 * FITNESS FOR A PARTICULAR PURPOSE.
     18 */
     19
     20/**
     21 * PHPMailer SPL autoloader.
     22 * @param string $classname The name of the class to load
     23 */
     24function PHPMailerAutoload($classname)
     25{
     26    //Can't use __DIR__ as it's only in PHP 5.3+
     27    $filename = dirname(__FILE__).DIRECTORY_SEPARATOR.'class.'.strtolower($classname).'.php';
     28    if (is_readable($filename)) {
     29        require $filename;
     30    }
     31}
     32
     33spl_autoload_register('PHPMailerAutoload');
  • wp-includes/PHPMailer/class.phpmailer.php

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

    Property changes on: wp-includes/PHPMailer/class.phpmailer.php
    ___________________________________________________________________
    Added: svn:executable
    ## -0,0 +1 ##
    +*
    \ No newline at end of property
     
     1<?php
     2/**
     3 * PHPMailer RFC821 SMTP email transport class.
     4 * Version 5.2.7
     5 * PHP version 5.0.0
     6 * @category  PHP
     7 * @package   PHPMailer
     8 * @link      https://github.com/PHPMailer/PHPMailer/
     9 * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk>
     10 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
     11 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
     12 * @copyright 2013 Marcus Bointon
     13 * @copyright 2004 - 2008 Andy Prevost
     14 * @copyright 2010 - 2012 Jim Jagielski
     15 * @license   http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)
     16 */
     17
     18/**
     19 * PHPMailer RFC821 SMTP email transport class.
     20 *
     21 * Implements RFC 821 SMTP commands
     22 * and provides some utility methods for sending mail to an SMTP server.
     23 *
     24 * PHP Version 5.0.0
     25 *
     26 * @category PHP
     27 * @package  PHPMailer
     28 * @link     https://github.com/PHPMailer/PHPMailer/blob/master/class.smtp.php
     29 * @author   Chris Ryan <unknown@example.com>
     30 * @author   Marcus Bointon <phpmailer@synchromedia.co.uk>
     31 * @license  http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL)
     32 */
     33
     34class SMTP
     35{
     36    /**
     37     * The PHPMailer SMTP Version number.
     38     */
     39    const VERSION = '5.2.7';
     40
     41    /**
     42     * SMTP line break constant.
     43     */
     44    const CRLF = "\r\n";
     45
     46    /**
     47     * The SMTP port to use if one is not specified.
     48     */
     49    const DEFAULT_SMTP_PORT = 25;
     50
     51    /**
     52     * The PHPMailer SMTP Version number.
     53     * @type string
     54     * @deprecated This should be a constant
     55     * @see SMTP::VERSION
     56     */
     57    public $Version = '5.2.7';
     58
     59    /**
     60     * SMTP server port number.
     61     * @type int
     62     * @deprecated This is only ever ued as default value, so should be a constant
     63     * @see SMTP::DEFAULT_SMTP_PORT
     64     */
     65    public $SMTP_PORT = 25;
     66
     67    /**
     68     * SMTP reply line ending
     69     * @type string
     70     * @deprecated Use the class constant instead
     71     * @see SMTP::CRLF
     72     */
     73    public $CRLF = "\r\n";
     74
     75    /**
     76     * Debug output level.
     77     * Options: 0 for no output, 1 for commands, 2 for data and commands
     78     * @type int
     79     */
     80    public $do_debug = 0;
     81
     82    /**
     83     * The function/method to use for debugging output.
     84     * Options: 'echo', 'html' or 'error_log'
     85     * @type string
     86     */
     87    public $Debugoutput = 'echo';
     88
     89    /**
     90     * Whether to use VERP.
     91     * @type bool
     92     */
     93    public $do_verp = false;
     94
     95    /**
     96     * The SMTP timeout value for reads, in seconds.
     97     * @type int
     98     */
     99    public $Timeout = 15;
     100
     101    /**
     102     * The SMTP timelimit value for reads, in seconds.
     103     * @type int
     104     */
     105    public $Timelimit = 30;
     106
     107    /**
     108     * The socket for the server connection.
     109     * @type resource
     110     */
     111    protected $smtp_conn;
     112
     113    /**
     114     * Error message, if any, for the last call.
     115     * @type string
     116     */
     117    protected $error = '';
     118
     119    /**
     120     * The reply the server sent to us for HELO.
     121     * @type string
     122     */
     123    protected $helo_rply = '';
     124
     125    /**
     126     * The most recent reply received from the server.
     127     * @type string
     128     */
     129    protected $last_reply = '';
     130
     131    /**
     132     * Constructor.
     133     * @access public
     134     */
     135    public function __construct()
     136    {
     137        $this->smtp_conn = 0;
     138        $this->error = null;
     139        $this->helo_rply = null;
     140
     141        $this->do_debug = 0;
     142    }
     143
     144    /**
     145     * Output debugging info via a user-selected method.
     146     * @param string $str Debug string to output
     147     * @return void
     148     */
     149    protected function edebug($str)
     150    {
     151        switch ($this->Debugoutput) {
     152            case 'error_log':
     153                //Don't output, just log
     154                error_log($str);
     155                break;
     156            case 'html':
     157                //Cleans up output a bit for a better looking, HTML-safe output
     158                echo htmlentities(
     159                    preg_replace('/[\r\n]+/', '', $str),
     160                    ENT_QUOTES,
     161                    'UTF-8'
     162                )
     163                . "<br>\n";
     164                break;
     165            case 'echo':
     166            default:
     167                //Just echoes whatever was received
     168                echo $str;
     169        }
     170    }
     171
     172    /**
     173     * Connect to an SMTP server.
     174     * @param string $host    SMTP server IP or host name
     175     * @param int $port    The port number to connect to
     176     * @param int $timeout How long to wait for the connection to open
     177     * @param array $options An array of options for stream_context_create()
     178     * @access public
     179     * @return bool
     180     */
     181    public function connect($host, $port = null, $timeout = 30, $options = array())
     182    {
     183        // Clear errors to avoid confusion
     184        $this->error = null;
     185
     186        // Make sure we are __not__ connected
     187        if ($this->connected()) {
     188            // Already connected, generate error
     189            $this->error = array('error' => 'Already connected to a server');
     190            return false;
     191        }
     192
     193        if (empty($port)) {
     194            $port = self::DEFAULT_SMTP_PORT;
     195        }
     196
     197        // Connect to the SMTP server
     198        $errno = 0;
     199        $errstr = '';
     200        $socket_context = stream_context_create($options);
     201        //Suppress errors; connection failures are handled at a higher level
     202        $this->smtp_conn = @stream_socket_client(
     203            $host . ":" . $port,
     204            $errno,
     205            $errstr,
     206            $timeout,
     207            STREAM_CLIENT_CONNECT,
     208            $socket_context
     209        );
     210
     211        // Verify we connected properly
     212        if (empty($this->smtp_conn)) {
     213            $this->error = array(
     214                'error' => 'Failed to connect to server',
     215                'errno' => $errno,
     216                'errstr' => $errstr
     217            );
     218            if ($this->do_debug >= 1) {
     219                $this->edebug(
     220                    'SMTP -> ERROR: ' . $this->error['error']
     221                    . ": $errstr ($errno)"
     222                );
     223            }
     224            return false;
     225        }
     226
     227        // SMTP server can take longer to respond, give longer timeout for first read
     228        // Windows does not have support for this timeout function
     229        if (substr(PHP_OS, 0, 3) != 'WIN') {
     230            $max = ini_get('max_execution_time');
     231            if ($max != 0 && $timeout > $max) { // Don't bother if unlimited
     232                @set_time_limit($timeout);
     233            }
     234            stream_set_timeout($this->smtp_conn, $timeout, 0);
     235        }
     236
     237        // Get any announcement
     238        $announce = $this->get_lines();
     239
     240        if ($this->do_debug >= 2) {
     241            $this->edebug('SMTP -> FROM SERVER:' . $announce);
     242        }
     243
     244        return true;
     245    }
     246
     247    /**
     248     * Initiate a TLS (encrypted) session.
     249     * @access public
     250     * @return bool
     251     */
     252    public function startTLS()
     253    {
     254        if (!$this->sendCommand("STARTTLS", "STARTTLS", 220)) {
     255            return false;
     256        }
     257        // Begin encrypted connection
     258        if (!stream_socket_enable_crypto(
     259            $this->smtp_conn,
     260            true,
     261            STREAM_CRYPTO_METHOD_TLS_CLIENT
     262        )
     263        ) {
     264            return false;
     265        }
     266        return true;
     267    }
     268
     269    /**
     270     * Perform SMTP authentication.
     271     * Must be run after hello().
     272     * @see hello()
     273     * @param string $username    The user name
     274     * @param string $password    The password
     275     * @param string $authtype    The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5)
     276     * @param string $realm       The auth realm for NTLM
     277     * @param string $workstation The auth workstation for NTLM
     278     * @access public
     279     * @return bool True if successfully authenticated.
     280     */
     281    public function authenticate(
     282        $username,
     283        $password,
     284        $authtype = 'LOGIN',
     285        $realm = '',
     286        $workstation = ''
     287    ) {
     288        if (empty($authtype)) {
     289            $authtype = 'LOGIN';
     290        }
     291
     292        switch ($authtype) {
     293            case 'PLAIN':
     294                // Start authentication
     295                if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
     296                    return false;
     297                }
     298                // Send encoded username and password
     299                if (!$this->sendCommand(
     300                    'User & Password',
     301                    base64_encode("\0" . $username . "\0" . $password),
     302                    235
     303                )
     304                ) {
     305                    return false;
     306                }
     307                break;
     308            case 'LOGIN':
     309                // Start authentication
     310                if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
     311                    return false;
     312                }
     313                if (!$this->sendCommand("Username", base64_encode($username), 334)) {
     314                    return false;
     315                }
     316                if (!$this->sendCommand("Password", base64_encode($password), 235)) {
     317                    return false;
     318                }
     319                break;
     320            case 'NTLM':
     321                /*
     322                 * ntlm_sasl_client.php
     323                 * Bundled with Permission
     324                 *
     325                 * How to telnet in windows:
     326                 * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
     327                 * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
     328                 */
     329                require_once 'extras/ntlm_sasl_client.php';
     330                $temp = new stdClass();
     331                $ntlm_client = new ntlm_sasl_client_class;
     332                //Check that functions are available
     333                if (!$ntlm_client->Initialize($temp)) {
     334                    $this->error = array('error' => $temp->error);
     335                    if ($this->do_debug >= 1) {
     336                        $this->edebug(
     337                            'You need to enable some modules in your php.ini file: '
     338                            . $this->error['error']
     339                        );
     340                    }
     341                    return false;
     342                }
     343                //msg1
     344                $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1
     345
     346                if (!$this->sendCommand(
     347                    'AUTH NTLM',
     348                    'AUTH NTLM ' . base64_encode($msg1),
     349                    334
     350                )
     351                ) {
     352                    return false;
     353                }
     354
     355                //Though 0 based, there is a white space after the 3 digit number
     356                //msg2
     357                $challenge = substr($this->last_reply, 3);
     358                $challenge = base64_decode($challenge);
     359                $ntlm_res = $ntlm_client->NTLMResponse(
     360                    substr($challenge, 24, 8),
     361                    $password
     362                );
     363                //msg3
     364                $msg3 = $ntlm_client->TypeMsg3(
     365                    $ntlm_res,
     366                    $username,
     367                    $realm,
     368                    $workstation
     369                );
     370                // send encoded username
     371                return $this->sendCommand('Username', base64_encode($msg3), 235);
     372                break;
     373            case 'CRAM-MD5':
     374                // Start authentication
     375                if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
     376                    return false;
     377                }
     378                // Get the challenge
     379                $challenge = base64_decode(substr($this->last_reply, 4));
     380
     381                // Build the response
     382                $response = $username . ' ' . $this->hmac($challenge, $password);
     383
     384                // send encoded credentials
     385                return $this->sendCommand('Username', base64_encode($response), 235);
     386                break;
     387        }
     388        return true;
     389    }
     390
     391    /**
     392     * Calculate an MD5 HMAC hash.
     393     * Works like hash_hmac('md5', $data, $key)
     394     * in case that function is not available
     395     * @param string $data The data to hash
     396     * @param string $key  The key to hash with
     397     * @access protected
     398     * @return string
     399     */
     400    protected function hmac($data, $key)
     401    {
     402        if (function_exists('hash_hmac')) {
     403            return hash_hmac('md5', $data, $key);
     404        }
     405
     406        // The following borrowed from
     407        // http://php.net/manual/en/function.mhash.php#27225
     408
     409        // RFC 2104 HMAC implementation for php.
     410        // Creates an md5 HMAC.
     411        // Eliminates the need to install mhash to compute a HMAC
     412        // Hacked by Lance Rushing
     413
     414        $b = 64; // byte length for md5
     415        if (strlen($key) > $b) {
     416            $key = pack('H*', md5($key));
     417        }
     418        $key = str_pad($key, $b, chr(0x00));
     419        $ipad = str_pad('', $b, chr(0x36));
     420        $opad = str_pad('', $b, chr(0x5c));
     421        $k_ipad = $key ^ $ipad;
     422        $k_opad = $key ^ $opad;
     423
     424        return md5($k_opad . pack('H*', md5($k_ipad . $data)));
     425    }
     426
     427    /**
     428     * Check connection state.
     429     * @access public
     430     * @return bool True if connected.
     431     */
     432    public function connected()
     433    {
     434        if (!empty($this->smtp_conn)) {
     435            $sock_status = stream_get_meta_data($this->smtp_conn);
     436            if ($sock_status['eof']) {
     437                // the socket is valid but we are not connected
     438                if ($this->do_debug >= 1) {
     439                    $this->edebug(
     440                        'SMTP -> NOTICE: EOF caught while checking if connected'
     441                    );
     442                }
     443                $this->close();
     444                return false;
     445            }
     446            return true; // everything looks good
     447        }
     448        return false;
     449    }
     450
     451    /**
     452     * Close the socket and clean up the state of the class.
     453     * Don't use this function without first trying to use QUIT.
     454     * @see quit()
     455     * @access public
     456     * @return void
     457     */
     458    public function close()
     459    {
     460        $this->error = null; // so there is no confusion
     461        $this->helo_rply = null;
     462        if (!empty($this->smtp_conn)) {
     463            // close the connection and cleanup
     464            fclose($this->smtp_conn);
     465            $this->smtp_conn = 0;
     466        }
     467    }
     468
     469    /**
     470     * Send an SMTP DATA command.
     471     * Issues a data command and sends the msg_data to the server,
     472     * finializing the mail transaction. $msg_data is the message
     473     * that is to be send with the headers. Each header needs to be
     474     * on a single line followed by a <CRLF> with the message headers
     475     * and the message body being separated by and additional <CRLF>.
     476     * Implements rfc 821: DATA <CRLF>
     477     * @param string $msg_data Message data to send
     478     * @access public
     479     * @return bool
     480     */
     481    public function data($msg_data)
     482    {
     483        if (!$this->sendCommand('DATA', 'DATA', 354)) {
     484            return false;
     485        }
     486
     487        /* The server is ready to accept data!
     488         * according to rfc821 we should not send more than 1000
     489         * including the CRLF
     490         * characters on a single line so we will break the data up
     491         * into lines by \r and/or \n then if needed we will break
     492         * each of those into smaller lines to fit within the limit.
     493         * in addition we will be looking for lines that start with
     494         * a period '.' and append and additional period '.' to that
     495         * line. NOTE: this does not count towards limit.
     496         */
     497
     498        // Normalize the line breaks before exploding
     499        $msg_data = str_replace("\r\n", "\n", $msg_data);
     500        $msg_data = str_replace("\r", "\n", $msg_data);
     501        $lines = explode("\n", $msg_data);
     502
     503        /* We need to find a good way to determine if headers are
     504         * in the msg_data or if it is a straight msg body
     505         * currently I am assuming rfc822 definitions of msg headers
     506         * and if the first field of the first line (':' separated)
     507         * does not contain a space then it _should_ be a header
     508         * and we can process all lines before a blank "" line as
     509         * headers.
     510         */
     511
     512        $field = substr($lines[0], 0, strpos($lines[0], ':'));
     513        $in_headers = false;
     514        if (!empty($field) && !strstr($field, ' ')) {
     515            $in_headers = true;
     516        }
     517
     518        //RFC 2822 section 2.1.1 limit
     519        $max_line_length = 998;
     520
     521        foreach ($lines as $line) {
     522            $lines_out = null;
     523            if ($line == '' && $in_headers) {
     524                $in_headers = false;
     525            }
     526            // ok we need to break this line up into several smaller lines
     527            while (strlen($line) > $max_line_length) {
     528                $pos = strrpos(substr($line, 0, $max_line_length), ' ');
     529
     530                // Patch to fix DOS attack
     531                if (!$pos) {
     532                    $pos = $max_line_length - 1;
     533                    $lines_out[] = substr($line, 0, $pos);
     534                    $line = substr($line, $pos);
     535                } else {
     536                    $lines_out[] = substr($line, 0, $pos);
     537                    $line = substr($line, $pos + 1);
     538                }
     539
     540                /* If processing headers add a LWSP-char to the front of new line
     541                 * rfc822 on long msg headers
     542                 */
     543                if ($in_headers) {
     544                    $line = "\t" . $line;
     545                }
     546            }
     547            $lines_out[] = $line;
     548
     549            // send the lines to the server
     550            while (list(, $line_out) = @each($lines_out)) {
     551                if (strlen($line_out) > 0) {
     552                    if (substr($line_out, 0, 1) == '.') {
     553                        $line_out = '.' . $line_out;
     554                    }
     555                }
     556                $this->client_send($line_out . self::CRLF);
     557            }
     558        }
     559
     560        // Message data has been sent, complete the command
     561        return $this->sendCommand('DATA END', '.', 250);
     562    }
     563
     564    /**
     565     * Send an SMTP HELO or EHLO command.
     566     * Used to identify the sending server to the receiving server.
     567     * This makes sure that client and server are in a known state.
     568     * Implements from RFC 821: HELO <SP> <domain> <CRLF>
     569     * and RFC 2821 EHLO.
     570     * @param string $host The host name or IP to connect to
     571     * @access public
     572     * @return bool
     573     */
     574    public function hello($host = '')
     575    {
     576        // Try extended hello first (RFC 2821)
     577        if (!$this->sendHello('EHLO', $host)) {
     578            if (!$this->sendHello('HELO', $host)) {
     579                return false;
     580            }
     581        }
     582
     583        return true;
     584    }
     585
     586    /**
     587     * Send an SMTP HELO or EHLO command.
     588     * Low-level implementation used by hello()
     589     * @see hello()
     590     * @param string $hello The HELO string
     591     * @param string $host  The hostname to say we are
     592     * @access protected
     593     * @return bool
     594     */
     595    protected function sendHello($hello, $host)
     596    {
     597        $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
     598        $this->helo_rply = $this->last_reply;
     599        return $noerror;
     600    }
     601
     602    /**
     603     * Send an SMTP MAIL command.
     604     * Starts a mail transaction from the email address specified in
     605     * $from. Returns true if successful or false otherwise. If True
     606     * the mail transaction is started and then one or more recipient
     607     * commands may be called followed by a data command.
     608     * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
     609     * @param string $from Source address of this message
     610     * @access public
     611     * @return bool
     612     */
     613    public function mail($from)
     614    {
     615        $useVerp = ($this->do_verp ? ' XVERP' : '');
     616        return $this->sendCommand(
     617            'MAIL FROM',
     618            'MAIL FROM:<' . $from . '>' . $useVerp,
     619            250
     620        );
     621    }
     622
     623    /**
     624     * Send an SMTP QUIT command.
     625     * Closes the socket if there is no error or the $close_on_error argument is true.
     626     * Implements from rfc 821: QUIT <CRLF>
     627     * @param bool $close_on_error Should the connection close if an error occurs?
     628     * @access public
     629     * @return bool
     630     */
     631    public function quit($close_on_error = true)
     632    {
     633        $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
     634        $e = $this->error; //Save any error
     635        if ($noerror or $close_on_error) {
     636            $this->close();
     637            $this->error = $e; //Restore any error from the quit command
     638        }
     639        return $noerror;
     640    }
     641
     642    /**
     643     * Send an SMTP RCPT command.
     644     * Sets the TO argument to $to.
     645     * Returns true if the recipient was accepted false if it was rejected.
     646     * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
     647     * @param string $to The address the message is being sent to
     648     * @access public
     649     * @return bool
     650     */
     651    public function recipient($to)
     652    {
     653        return $this->sendCommand(
     654            'RCPT TO ',
     655            'RCPT TO:<' . $to . '>',
     656            array(250, 251)
     657        );
     658    }
     659
     660    /**
     661     * Send an SMTP RSET command.
     662     * Abort any transaction that is currently in progress.
     663     * Implements rfc 821: RSET <CRLF>
     664     * @access public
     665     * @return bool True on success.
     666     */
     667    public function reset()
     668    {
     669        return $this->sendCommand('RSET', 'RSET', 250);
     670    }
     671
     672    /**
     673     * Send a command to an SMTP server and check its return code.
     674     * @param string $command       The command name - not sent to the server
     675     * @param string $commandstring The actual command to send
     676     * @param int|array $expect     One or more expected integer success codes
     677     * @access protected
     678     * @return bool True on success.
     679     */
     680    protected function sendCommand($command, $commandstring, $expect)
     681    {
     682        if (!$this->connected()) {
     683            $this->error = array(
     684                "error" => "Called $command without being connected"
     685            );
     686            return false;
     687        }
     688        $this->client_send($commandstring . self::CRLF);
     689
     690        $reply = $this->get_lines();
     691        $code = substr($reply, 0, 3);
     692
     693        if ($this->do_debug >= 2) {
     694            $this->edebug('SMTP -> FROM SERVER:' . $reply);
     695        }
     696
     697        if (!in_array($code, (array)$expect)) {
     698            $this->last_reply = null;
     699            $this->error = array(
     700                "error" => "$command command failed",
     701                "smtp_code" => $code,
     702                "detail" => substr($reply, 4)
     703            );
     704            if ($this->do_debug >= 1) {
     705                $this->edebug(
     706                    'SMTP -> ERROR: ' . $this->error['error'] . ': ' . $reply
     707                );
     708            }
     709            return false;
     710        }
     711
     712        $this->last_reply = $reply;
     713        $this->error = null;
     714        return true;
     715    }
     716
     717    /**
     718     * Send an SMTP SAML command.
     719     * Starts a mail transaction from the email address specified in $from.
     720     * Returns true if successful or false otherwise. If True
     721     * the mail transaction is started and then one or more recipient
     722     * commands may be called followed by a data command. This command
     723     * will send the message to the users terminal if they are logged
     724     * in and send them an email.
     725     * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
     726     * @param string $from The address the message is from
     727     * @access public
     728     * @return bool
     729     */
     730    public function sendAndMail($from)
     731    {
     732        return $this->sendCommand("SAML", "SAML FROM:$from", 250);
     733    }
     734
     735    /**
     736     * Send an SMTP VRFY command.
     737     * @param string $name The name to verify
     738     * @access public
     739     * @return bool
     740     */
     741    public function verify($name)
     742    {
     743        return $this->sendCommand("VRFY", "VRFY $name", array(250, 251));
     744    }
     745
     746    /**
     747     * Send an SMTP NOOP command.
     748     * Used to keep keep-alives alive, doesn't actually do anything
     749     * @access public
     750     * @return bool
     751     */
     752    public function noop()
     753    {
     754        return $this->sendCommand("NOOP", "NOOP", 250);
     755    }
     756
     757    /**
     758     * Send an SMTP TURN command.
     759     * This is an optional command for SMTP that this class does not support.
     760     * This method is here to make the RFC821 Definition
     761     * complete for this class and __may__ be implemented in future
     762     * Implements from rfc 821: TURN <CRLF>
     763     * @access public
     764     * @return bool
     765     */
     766    public function turn()
     767    {
     768        $this->error = array(
     769            'error' => 'The SMTP TURN command is not implemented'
     770        );
     771        if ($this->do_debug >= 1) {
     772            $this->edebug('SMTP -> NOTICE: ' . $this->error['error']);
     773        }
     774        return false;
     775    }
     776
     777    /**
     778     * Send raw data to the server.
     779     * @param string $data The data to send
     780     * @access public
     781     * @return int|bool The number of bytes sent to the server or FALSE on error
     782     */
     783    public function client_send($data)
     784    {
     785        if ($this->do_debug >= 1) {
     786            $this->edebug("CLIENT -> SMTP: $data");
     787        }
     788        return fwrite($this->smtp_conn, $data);
     789    }
     790
     791    /**
     792     * Get the latest error.
     793     * @access public
     794     * @return array
     795     */
     796    public function getError()
     797    {
     798        return $this->error;
     799    }
     800
     801    /**
     802     * Get the last reply from the server.
     803     * @access public
     804     * @return string
     805     */
     806    public function getLastReply()
     807    {
     808        return $this->last_reply;
     809    }
     810
     811    /**
     812     * Read the SMTP server's response.
     813     * Either before eof or socket timeout occurs on the operation.
     814     * With SMTP we can tell if we have more lines to read if the
     815     * 4th character is '-' symbol. If it is a space then we don't
     816     * need to read anything else.
     817     * @access protected
     818     * @return string
     819     */
     820    protected function get_lines()
     821    {
     822        $data = '';
     823        $endtime = 0;
     824        // If the connection is bad, give up now
     825        if (!is_resource($this->smtp_conn)) {
     826            return $data;
     827        }
     828        stream_set_timeout($this->smtp_conn, $this->Timeout);
     829        if ($this->Timelimit > 0) {
     830            $endtime = time() + $this->Timelimit;
     831        }
     832        while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
     833            $str = @fgets($this->smtp_conn, 515);
     834            if ($this->do_debug >= 4) {
     835                $this->edebug("SMTP -> get_lines(): \$data was \"$data\"");
     836                $this->edebug("SMTP -> get_lines(): \$str is \"$str\"");
     837            }
     838            $data .= $str;
     839            if ($this->do_debug >= 4) {
     840                $this->edebug("SMTP -> get_lines(): \$data is \"$data\"");
     841            }
     842            // if 4th character is a space, we are done reading, break the loop
     843            if (substr($str, 3, 1) == ' ') {
     844                break;
     845            }
     846            // Timed-out? Log and break
     847            $info = stream_get_meta_data($this->smtp_conn);
     848            if ($info['timed_out']) {
     849                if ($this->do_debug >= 4) {
     850                    $this->edebug(
     851                        'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)'
     852                    );
     853                }
     854                break;
     855            }
     856            // Now check if reads took too long
     857            if ($endtime) {
     858                if (time() > $endtime) {
     859                    if ($this->do_debug >= 4) {
     860                        $this->edebug(
     861                            'SMTP -> get_lines(): timelimit reached ('
     862                            . $this->Timelimit . ' sec)'
     863                        );
     864                    }
     865                    break;
     866                }
     867            }
     868        }
     869        return $data;
     870    }
     871
     872    /**
     873     * Enable or disable VERP address generation.
     874     * @param bool $enabled
     875     */
     876    public function setVerp($enabled = false)
     877    {
     878        $this->do_verp = $enabled;
     879    }
     880
     881    /**
     882     * Get VERP address generation mode.
     883     * @return bool
     884     */
     885    public function getVerp()
     886    {
     887        return $this->do_verp;
     888    }
     889
     890    /**
     891     * Set debug output method.
     892     * @param string $method The function/method to use for debugging output.
     893     */
     894    public function setDebugOutput($method = 'echo')
     895    {
     896        $this->Debugoutput = $method;
     897    }
     898
     899    /**
     900     * Get debug output method.
     901     * @return string
     902     */
     903    public function getDebugOutput()
     904    {
     905        return $this->Debugoutput;
     906    }
     907
     908    /**
     909     * Set debug output level.
     910     * @param int $level
     911     */
     912    public function setDebugLevel($level = 0)
     913    {
     914        $this->do_debug = $level;
     915    }
     916
     917    /**
     918     * Get debug output level.
     919     * @return int
     920     */
     921    public function getDebugLevel()
     922    {
     923        return $this->do_debug;
     924    }
     925
     926    /**
     927     * Set SMTP timeout.
     928     * @param int $timeout
     929     */
     930    public function setTimeout($timeout = 0)
     931    {
     932        $this->Timeout = $timeout;
     933    }
     934
     935    /**
     936     * Get SMTP timeout.
     937     * @return int
     938     */
     939    public function getTimeout()
     940    {
     941        return $this->Timeout;
     942    }
     943}
  • wp-includes/PHPMailer/extras/EasyPeasyICS.php

    Property changes on: wp-includes/PHPMailer/class.smtp.php
    ___________________________________________________________________
    Added: svn:executable
    ## -0,0 +1 ##
    +*
    \ No newline at end of property
     
     1<?php
     2
     3/* ------------------------------------------------------------------------ */
     4/* EasyPeasyICS
     5/* ------------------------------------------------------------------------ */
     6/* Manuel Reinhard, manu@sprain.ch
     7/* Twitter: @sprain
     8/* Web: www.sprain.ch
     9/*
     10/* Built with inspiration by
     11/" http://stackoverflow.com/questions/1463480/how-can-i-use-php-to-dynamically-publish-an-ical-file-to-be-read-by-google-calend/1464355#1464355
     12/* ------------------------------------------------------------------------ */
     13/* History:
     14/* 2010/12/17 - Manuel Reinhard - when it all started
     15/* ------------------------------------------------------------------------ */ 
     16
     17class EasyPeasyICS {
     18
     19        protected $calendarName;
     20        protected $events = array();
     21       
     22
     23        /**
     24         * Constructor
     25         * @param string $calendarName
     26         */     
     27        public function __construct($calendarName=""){
     28                $this->calendarName = $calendarName;
     29        }//function
     30
     31
     32        /**
     33         * Add event to calendar
     34         * @param string $calendarName
     35         */     
     36        public function addEvent($start, $end, $summary="", $description="", $url=""){
     37                $this->events[] = array(
     38                        "start" => $start,
     39                        "end"   => $end,
     40                        "summary" => $summary,
     41                        "description" => $description,
     42                        "url" => $url
     43                );
     44        }//function
     45       
     46       
     47        public function render($output = true){
     48               
     49                //start Variable
     50                $ics = "";
     51       
     52                //Add header
     53                $ics .= "BEGIN:VCALENDAR
     54METHOD:PUBLISH
     55VERSION:2.0
     56X-WR-CALNAME:".$this->calendarName."
     57PRODID:-//hacksw/handcal//NONSGML v1.0//EN";
     58               
     59                //Add events
     60                foreach($this->events as $event){
     61                        $ics .= "
     62BEGIN:VEVENT
     63UID:". md5(uniqid(mt_rand(), true)) ."@EasyPeasyICS.php
     64DTSTAMP:" . gmdate('Ymd').'T'. gmdate('His') . "Z
     65DTSTART:".gmdate('Ymd', $event["start"])."T".gmdate('His', $event["start"])."Z
     66DTEND:".gmdate('Ymd', $event["end"])."T".gmdate('His', $event["end"])."Z
     67SUMMARY:".str_replace("\n", "\\n", $event['summary'])."
     68DESCRIPTION:".str_replace("\n", "\\n", $event['description'])."
     69URL;VALUE=URI:".$event['url']."
     70END:VEVENT";
     71                }//foreach
     72               
     73               
     74                //Footer
     75                $ics .= "
     76END:VCALENDAR";
     77
     78
     79                if ($output) {
     80                        //Output
     81                        header('Content-type: text/calendar; charset=utf-8');
     82                        header('Content-Disposition: inline; filename='.$this->calendarName.'.ics');
     83                        echo $ics;
     84                } else {
     85                        return $ics;
     86                }
     87
     88        }//function
     89
     90}//class
     91 No newline at end of file
  • wp-includes/PHPMailer/extras/class.html2text.php

    Property changes on: wp-includes/PHPMailer/extras/EasyPeasyICS.php
    ___________________________________________________________________
    Added: svn:executable
    ## -0,0 +1 ##
    +*
    \ No newline at end of property
     
     1<?php
     2/*************************************************************************
     3 *                                                                       *
     4 * Converts HTML to formatted plain text                                 *
     5 *                                                                       *
     6 * Portions Copyright (c) 2005-2007 Jon Abernathy <jon@chuggnutt.com>    *
     7 * This version from https://github.com/mtibben/html2text                *
     8 *                                                                       *
     9 * This script is free software; you can redistribute it and/or modify   *
     10 * it under the terms of the GNU General Public License as published by  *
     11 * the Free Software Foundation; either version 2 of the License, or     *
     12 * (at your option) any later version.                                   *
     13 *                                                                       *
     14 * The GNU General Public License can be found at                        *
     15 * http://www.gnu.org/copyleft/gpl.html.                                 *
     16 *                                                                       *
     17 * This script is distributed in the hope that it will be useful,        *
     18 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
     19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
     20 * GNU General Public License for more details.                          *
     21 *                                                                       *
     22 *************************************************************************/
     23
     24
     25class html2text
     26{
     27
     28    /**
     29     *  Contains the HTML content to convert.
     30     *
     31     *  @var string $html
     32     *  @access public
     33     */
     34    public $html;
     35
     36    /**
     37     *  Contains the converted, formatted text.
     38     *
     39     *  @var string $text
     40     *  @access public
     41     */
     42    public $text;
     43
     44    /**
     45     *  Maximum width of the formatted text, in columns.
     46     *
     47     *  Set this value to 0 (or less) to ignore word wrapping
     48     *  and not constrain text to a fixed-width column.
     49     *
     50     *  @var integer $width
     51     *  @access public
     52     */
     53    public $width = 70;
     54
     55    /**
     56     *  List of preg* regular expression patterns to search for,
     57     *  used in conjunction with $replace.
     58     *
     59     *  @var array $search
     60     *  @access public
     61     *  @see $replace
     62     */
     63    public $search = array(
     64        "/\r/",                                  // Non-legal carriage return
     65        "/[\n\t]+/",                             // Newlines and tabs
     66        '/<head[^>]*>.*?<\/head>/i',             // <head>
     67        '/<script[^>]*>.*?<\/script>/i',         // <script>s -- which strip_tags supposedly has problems with
     68        '/<style[^>]*>.*?<\/style>/i',           // <style>s -- which strip_tags supposedly has problems with
     69        '/<p[^>]*>/i',                           // <P>
     70        '/<br[^>]*>/i',                          // <br>
     71        '/<i[^>]*>(.*?)<\/i>/i',                 // <i>
     72        '/<em[^>]*>(.*?)<\/em>/i',               // <em>
     73        '/(<ul[^>]*>|<\/ul>)/i',                 // <ul> and </ul>
     74        '/(<ol[^>]*>|<\/ol>)/i',                 // <ol> and </ol>
     75        '/<li[^>]*>(.*?)<\/li>/i',               // <li> and </li>
     76        '/<li[^>]*>/i',                          // <li>
     77        '/<hr[^>]*>/i',                          // <hr>
     78        '/<div[^>]*>/i',                         // <div>
     79        '/(<table[^>]*>|<\/table>)/i',           // <table> and </table>
     80        '/(<tr[^>]*>|<\/tr>)/i',                 // <tr> and </tr>
     81        '/<td[^>]*>(.*?)<\/td>/i',               // <td> and </td>
     82        '/<span class="_html2text_ignore">.+?<\/span>/i'  // <span class="_html2text_ignore">...</span>
     83    );
     84
     85    /**
     86     *  List of pattern replacements corresponding to patterns searched.
     87     *
     88     *  @var array $replace
     89     *  @access public
     90     *  @see $search
     91     */
     92    public $replace = array(
     93        '',                                     // Non-legal carriage return
     94        ' ',                                    // Newlines and tabs
     95        '',                                     // <head>
     96        '',                                     // <script>s -- which strip_tags supposedly has problems with
     97        '',                                     // <style>s -- which strip_tags supposedly has problems with
     98        "\n\n",                                 // <P>
     99        "\n",                                   // <br>
     100        '_\\1_',                                // <i>
     101        '_\\1_',                                // <em>
     102        "\n\n",                                 // <ul> and </ul>
     103        "\n\n",                                 // <ol> and </ol>
     104        "\t* \\1\n",                            // <li> and </li>
     105        "\n\t* ",                               // <li>
     106        "\n-------------------------\n",        // <hr>
     107        "<div>\n",                              // <div>
     108        "\n\n",                                 // <table> and </table>
     109        "\n",                                   // <tr> and </tr>
     110        "\t\t\\1\n",                            // <td> and </td>
     111        ""                                      // <span class="_html2text_ignore">...</span>
     112    );
     113
     114    /**
     115     *  List of preg* regular expression patterns to search for,
     116     *  used in conjunction with $ent_replace.
     117     *
     118     *  @var array $ent_search
     119     *  @access public
     120     *  @see $ent_replace
     121     */
     122    public $ent_search = array(
     123        '/&(nbsp|#160);/i',                      // Non-breaking space
     124        '/&(quot|rdquo|ldquo|#8220|#8221|#147|#148);/i',
     125        // Double quotes
     126        '/&(apos|rsquo|lsquo|#8216|#8217);/i',   // Single quotes
     127        '/&gt;/i',                               // Greater-than
     128        '/&lt;/i',                               // Less-than
     129        '/&(copy|#169);/i',                      // Copyright
     130        '/&(trade|#8482|#153);/i',               // Trademark
     131        '/&(reg|#174);/i',                       // Registered
     132        '/&(mdash|#151|#8212);/i',               // mdash
     133        '/&(ndash|minus|#8211|#8722);/i',        // ndash
     134        '/&(bull|#149|#8226);/i',                // Bullet
     135        '/&(pound|#163);/i',                     // Pound sign
     136        '/&(euro|#8364);/i',                     // Euro sign
     137        '/&(amp|#38);/i',                        // Ampersand: see _converter()
     138        '/[ ]{2,}/',                             // Runs of spaces, post-handling
     139    );
     140
     141    /**
     142     *  List of pattern replacements corresponding to patterns searched.
     143     *
     144     *  @var array $ent_replace
     145     *  @access public
     146     *  @see $ent_search
     147     */
     148    public $ent_replace = array(
     149        ' ',                                    // Non-breaking space
     150        '"',                                    // Double quotes
     151        "'",                                    // Single quotes
     152        '>',
     153        '<',
     154        '(c)',
     155        '(tm)',
     156        '(R)',
     157        '--',
     158        '-',
     159        '*',
     160        '£',
     161        'EUR',                                  // Euro sign. € ?
     162        '|+|amp|+|',                            // Ampersand: see _converter()
     163        ' ',                                    // Runs of spaces, post-handling
     164    );
     165
     166    /**
     167     *  List of preg* regular expression patterns to search for
     168     *  and replace using callback function.
     169     *
     170     *  @var array $callback_search
     171     *  @access public
     172     */
     173    public $callback_search = array(
     174        '/<(a) [^>]*href=("|\')([^"\']+)\2([^>]*)>(.*?)<\/a>/i', // <a href="">
     175        '/<(h)[123456]( [^>]*)?>(.*?)<\/h[123456]>/i',         // h1 - h6
     176        '/<(b)( [^>]*)?>(.*?)<\/b>/i',                         // <b>
     177        '/<(strong)( [^>]*)?>(.*?)<\/strong>/i',               // <strong>
     178        '/<(th)( [^>]*)?>(.*?)<\/th>/i',                       // <th> and </th>
     179    );
     180
     181    /**
     182     *  List of preg* regular expression patterns to search for in PRE body,
     183     *  used in conjunction with $pre_replace.
     184     *
     185     *  @var array $pre_search
     186     *  @access public
     187     *  @see $pre_replace
     188     */
     189    public $pre_search = array(
     190        "/\n/",
     191        "/\t/",
     192        '/ /',
     193        '/<pre[^>]*>/',
     194        '/<\/pre>/'
     195    );
     196
     197    /**
     198     *  List of pattern replacements corresponding to patterns searched for PRE body.
     199     *
     200     *  @var array $pre_replace
     201     *  @access public
     202     *  @see $pre_search
     203     */
     204    public $pre_replace = array(
     205        '<br>',
     206        '&nbsp;&nbsp;&nbsp;&nbsp;',
     207        '&nbsp;',
     208        '',
     209        ''
     210    );
     211
     212    /**
     213     *  Contains a list of HTML tags to allow in the resulting text.
     214     *
     215     *  @var string $allowed_tags
     216     *  @access public
     217     *  @see set_allowed_tags()
     218     */
     219    public $allowed_tags = '';
     220
     221    /**
     222     *  Contains the base URL that relative links should resolve to.
     223     *
     224     *  @var string $url
     225     *  @access public
     226     */
     227    public $url;
     228
     229    /**
     230     *  Indicates whether content in the $html variable has been converted yet.
     231     *
     232     *  @var boolean $_converted
     233     *  @access private
     234     *  @see $html, $text
     235     */
     236    private $_converted = false;
     237
     238    /**
     239     *  Contains URL addresses from links to be rendered in plain text.
     240     *
     241     *  @var array $_link_list
     242     *  @access private
     243     *  @see _build_link_list()
     244     */
     245    private $_link_list = array();
     246
     247
     248    /**
     249     *  Various configuration options (able to be set in the constructor)
     250     *
     251     *  @var array $_options
     252     *  @access private
     253     */
     254    private $_options = array(
     255
     256        // 'none'
     257        // 'inline' (show links inline)
     258        // 'nextline' (show links on the next line)
     259        // 'table' (if a table of link URLs should be listed after the text.
     260        'do_links' => 'inline',
     261
     262        //  Maximum width of the formatted text, in columns.
     263        //  Set this value to 0 (or less) to ignore word wrapping
     264        //  and not constrain text to a fixed-width column.
     265        'width' => 70,
     266    );
     267
     268
     269    /**
     270     *  Constructor.
     271     *
     272     *  If the HTML source string (or file) is supplied, the class
     273     *  will instantiate with that source propagated, all that has
     274     *  to be done it to call get_text().
     275     *
     276     *  @param string $source HTML content
     277     *  @param boolean $from_file Indicates $source is a file to pull content from
     278     *  @param array $options Set configuration options
     279     *  @access public
     280     *  @return void
     281     */
     282    public function __construct( $source = '', $from_file = false, $options = array() )
     283    {
     284        $this->_options = array_merge($this->_options, $options);
     285
     286        if ( !empty($source) ) {
     287            $this->set_html($source, $from_file);
     288        }
     289
     290        $this->set_base_url();
     291    }
     292
     293    /**
     294     *  Loads source HTML into memory, either from $source string or a file.
     295     *
     296     *  @param string $source HTML content
     297     *  @param boolean $from_file Indicates $source is a file to pull content from
     298     *  @access public
     299     *  @return void
     300     */
     301    public function set_html( $source, $from_file = false )
     302    {
     303        if ( $from_file && file_exists($source) ) {
     304            $this->html = file_get_contents($source);
     305        }
     306        else
     307            $this->html = $source;
     308
     309        $this->_converted = false;
     310    }
     311
     312    /**
     313     *  Returns the text, converted from HTML.
     314     *
     315     *  @access public
     316     *  @return string
     317     */
     318    public function get_text()
     319    {
     320        if ( !$this->_converted ) {
     321            $this->_convert();
     322        }
     323
     324        return $this->text;
     325    }
     326
     327    /**
     328     *  Prints the text, converted from HTML.
     329     *
     330     *  @access public
     331     *  @return void
     332     */
     333    public function print_text()
     334    {
     335        print $this->get_text();
     336    }
     337
     338    /**
     339     *  Alias to print_text(), operates identically.
     340     *
     341     *  @access public
     342     *  @return void
     343     *  @see print_text()
     344     */
     345    public function p()
     346    {
     347        print $this->get_text();
     348    }
     349
     350    /**
     351     *  Sets the allowed HTML tags to pass through to the resulting text.
     352     *
     353     *  Tags should be in the form "<p>", with no corresponding closing tag.
     354     *
     355     *  @access public
     356     *  @return void
     357     */
     358    public function set_allowed_tags( $allowed_tags = '' )
     359    {
     360        if ( !empty($allowed_tags) ) {
     361            $this->allowed_tags = $allowed_tags;
     362        }
     363    }
     364
     365    /**
     366     *  Sets a base URL to handle relative links.
     367     *
     368     *  @access public
     369     *  @return void
     370     */
     371    public function set_base_url( $url = '' )
     372    {
     373        if ( empty($url) ) {
     374            if ( !empty($_SERVER['HTTP_HOST']) ) {
     375                $this->url = 'http://' . $_SERVER['HTTP_HOST'];
     376            } else {
     377                $this->url = '';
     378            }
     379        } else {
     380            // Strip any trailing slashes for consistency (relative
     381            // URLs may already start with a slash like "/file.html")
     382            if ( substr($url, -1) == '/' ) {
     383                $url = substr($url, 0, -1);
     384            }
     385            $this->url = $url;
     386        }
     387    }
     388
     389    /**
     390     *  Workhorse function that does actual conversion (calls _converter() method).
     391     *
     392     *  @access private
     393     *  @return void
     394     */
     395    private function _convert()
     396    {
     397        // Variables used for building the link list
     398        $this->_link_list = array();
     399
     400        $text = trim(stripslashes($this->html));
     401
     402        // Convert HTML to TXT
     403        $this->_converter($text);
     404
     405        // Add link list
     406        if (!empty($this->_link_list)) {
     407            $text .= "\n\nLinks:\n------\n";
     408            foreach ($this->_link_list as $idx => $url) {
     409                $text .= '[' . ($idx+1) . '] ' . $url . "\n";
     410            }
     411        }
     412
     413        $this->text = $text;
     414
     415        $this->_converted = true;
     416    }
     417
     418    /**
     419     *  Workhorse function that does actual conversion.
     420     *
     421     *  First performs custom tag replacement specified by $search and
     422     *  $replace arrays. Then strips any remaining HTML tags, reduces whitespace
     423     *  and newlines to a readable format, and word wraps the text to
     424     *  $this->_options['width'] characters.
     425     *
     426     *  @param string Reference to HTML content string
     427     *
     428     *  @access private
     429     *  @return void
     430     */
     431    private function _converter(&$text)
     432    {
     433        // Convert <BLOCKQUOTE> (before PRE!)
     434        $this->_convert_blockquotes($text);
     435
     436        // Convert <PRE>
     437        $this->_convert_pre($text);
     438
     439        // Run our defined tags search-and-replace
     440        $text = preg_replace($this->search, $this->replace, $text);
     441
     442        // Run our defined tags search-and-replace with callback
     443        $text = preg_replace_callback($this->callback_search, array($this, '_preg_callback'), $text);
     444
     445        // Strip any other HTML tags
     446        $text = strip_tags($text, $this->allowed_tags);
     447
     448        // Run our defined entities/characters search-and-replace
     449        $text = preg_replace($this->ent_search, $this->ent_replace, $text);
     450
     451        // Replace known html entities
     452        $text = html_entity_decode($text, ENT_QUOTES);
     453
     454        // Remove unknown/unhandled entities (this cannot be done in search-and-replace block)
     455        $text = preg_replace('/&([a-zA-Z0-9]{2,6}|#[0-9]{2,4});/', '', $text);
     456
     457        // Convert "|+|amp|+|" into "&", need to be done after handling of unknown entities
     458        // This properly handles situation of "&amp;quot;" in input string
     459        $text = str_replace('|+|amp|+|', '&', $text);
     460
     461        // Bring down number of empty lines to 2 max
     462        $text = preg_replace("/\n\s+\n/", "\n\n", $text);
     463        $text = preg_replace("/[\n]{3,}/", "\n\n", $text);
     464
     465        // remove leading empty lines (can be produced by eg. P tag on the beginning)
     466        $text = ltrim($text, "\n");
     467
     468        // Wrap the text to a readable format
     469        // for PHP versions >= 4.0.2. Default width is 75
     470        // If width is 0 or less, don't wrap the text.
     471        if ( $this->_options['width'] > 0 ) {
     472            $text = wordwrap($text, $this->_options['width']);
     473        }
     474    }
     475
     476    /**
     477     *  Helper function called by preg_replace() on link replacement.
     478     *
     479     *  Maintains an internal list of links to be displayed at the end of the
     480     *  text, with numeric indices to the original point in the text they
     481     *  appeared. Also makes an effort at identifying and handling absolute
     482     *  and relative links.
     483     *
     484     *  @param string $link URL of the link
     485     *  @param string $display Part of the text to associate number with
     486     *  @access private
     487     *  @return string
     488     */
     489    private function _build_link_list( $link, $display, $link_override = null)
     490    {
     491        $link_method = ($link_override) ? $link_override : $this->_options['do_links'];
     492        if ($link_method == 'none')
     493            return $display;
     494
     495
     496        // Ignored link types
     497        if (preg_match('!^(javascript:|mailto:|#)!i', $link)) {
     498            return $display;
     499        }
     500        if (preg_match('!^([a-z][a-z0-9.+-]+:)!i', $link)) {
     501            $url = $link;
     502        }
     503        else {
     504            $url = $this->url;
     505            if (substr($link, 0, 1) != '/') {
     506                $url .= '/';
     507            }
     508            $url .= "$link";
     509        }
     510
     511        if ($link_method == 'table')
     512        {
     513            if (($index = array_search($url, $this->_link_list)) === false) {
     514                $index = count($this->_link_list);
     515                $this->_link_list[] = $url;
     516            }
     517
     518            return $display . ' [' . ($index+1) . ']';
     519        }
     520        elseif ($link_method == 'nextline')
     521        {
     522            return $display . "\n[" . $url . ']';
     523        }
     524        else // link_method defaults to inline
     525        {
     526            return $display . ' [' . $url . ']';
     527        }
     528    }
     529
     530    /**
     531     *  Helper function for PRE body conversion.
     532     *
     533     *  @param string HTML content
     534     *  @access private
     535     */
     536    private function _convert_pre(&$text)
     537    {
     538        // get the content of PRE element
     539        while (preg_match('/<pre[^>]*>(.*)<\/pre>/ismU', $text, $matches)) {
     540            $this->pre_content = $matches[1];
     541
     542            // Run our defined tags search-and-replace with callback
     543            $this->pre_content = preg_replace_callback($this->callback_search,
     544                array($this, '_preg_callback'), $this->pre_content);
     545
     546            // convert the content
     547            $this->pre_content = sprintf('<div><br>%s<br></div>',
     548                preg_replace($this->pre_search, $this->pre_replace, $this->pre_content));
     549            // replace the content (use callback because content can contain $0 variable)
     550            $text = preg_replace_callback('/<pre[^>]*>.*<\/pre>/ismU',
     551                array($this, '_preg_pre_callback'), $text, 1);
     552
     553            // free memory
     554            $this->pre_content = '';
     555        }
     556    }
     557
     558    /**
     559     *  Helper function for BLOCKQUOTE body conversion.
     560     *
     561     *  @param string HTML content
     562     *  @access private
     563     */
     564    private function _convert_blockquotes(&$text)
     565    {
     566        if (preg_match_all('/<\/*blockquote[^>]*>/i', $text, $matches, PREG_OFFSET_CAPTURE)) {
     567            $level = 0;
     568            $diff = 0;
     569            $start = 0;
     570            $taglen = 0;
     571            foreach ($matches[0] as $m) {
     572                if ($m[0][0] == '<' && $m[0][1] == '/') {
     573                    $level--;
     574                    if ($level < 0) {
     575                        $level = 0; // malformed HTML: go to next blockquote
     576                    }
     577                    else if ($level > 0) {
     578                        // skip inner blockquote
     579                    }
     580                    else {
     581                        $end  = $m[1];
     582                        $len  = $end - $taglen - $start;
     583                        // Get blockquote content
     584                        $body = substr($text, $start + $taglen - $diff, $len);
     585
     586                        // Set text width
     587                        $p_width = $this->_options['width'];
     588                        if ($this->_options['width'] > 0) $this->_options['width'] -= 2;
     589                        // Convert blockquote content
     590                        $body = trim($body);
     591                        $this->_converter($body);
     592                        // Add citation markers and create PRE block
     593                        $body = preg_replace('/((^|\n)>*)/', '\\1> ', trim($body));
     594                        $body = '<pre>' . htmlspecialchars($body) . '</pre>';
     595                        // Re-set text width
     596                        $this->_options['width'] = $p_width;
     597                        // Replace content
     598                        $text = substr($text, 0, $start - $diff)
     599                            . $body . substr($text, $end + strlen($m[0]) - $diff);
     600
     601                        $diff = $len + $taglen + strlen($m[0]) - strlen($body);
     602                        unset($body);
     603                    }
     604                }
     605                else {
     606                    if ($level == 0) {
     607                        $start = $m[1];
     608                        $taglen = strlen($m[0]);
     609                    }
     610                    $level ++;
     611                }
     612            }
     613        }
     614    }
     615
     616    /**
     617     *  Callback function for preg_replace_callback use.
     618     *
     619     *  @param  array PREG matches
     620     *  @return string
     621     */
     622    private function _preg_callback($matches)
     623    {
     624        switch (strtolower($matches[1])) {
     625            case 'b':
     626            case 'strong':
     627                return $this->_toupper($matches[3]);
     628            case 'th':
     629                return $this->_toupper("\t\t". $matches[3] ."\n");
     630            case 'h':
     631                return $this->_toupper("\n\n". $matches[3] ."\n\n");
     632            case 'a':
     633                // override the link method
     634                $link_override = null;
     635                if (preg_match("/_html2text_link_(\w+)/", $matches[4], $link_override_match))
     636                {
     637                    $link_override = $link_override_match[1];
     638                }
     639                // Remove spaces in URL (#1487805)
     640                $url = str_replace(' ', '', $matches[3]);
     641                return $this->_build_link_list($url, $matches[5], $link_override);
     642        }
     643    }
     644
     645    /**
     646     *  Callback function for preg_replace_callback use in PRE content handler.
     647     *
     648     *  @param  array PREG matches
     649     *  @return string
     650     */
     651    private function _preg_pre_callback($matches)
     652    {
     653        return $this->pre_content;
     654    }
     655
     656    /**
     657     * Strtoupper function with HTML tags and entities handling.
     658     *
     659     * @param string $str Text to convert
     660     * @return string Converted text
     661     */
     662    private function _toupper($str)
     663    {
     664        // string can containg HTML tags
     665        $chunks = preg_split('/(<[^>]*>)/', $str, null, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
     666
     667        // convert toupper only the text between HTML tags
     668        foreach ($chunks as $idx => $chunk) {
     669            if ($chunk[0] != '<') {
     670                $chunks[$idx] = $this->_strtoupper($chunk);
     671            }
     672        }
     673
     674        return implode($chunks);
     675    }
     676
     677    /**
     678     * Strtoupper multibyte wrapper function with HTML entities handling.
     679     *
     680     * @param string $str Text to convert
     681     * @return string Converted text
     682     */
     683    private function _strtoupper($str)
     684    {
     685        $str = html_entity_decode($str, ENT_COMPAT);
     686
     687        if (function_exists('mb_strtoupper'))
     688            $str = mb_strtoupper($str);
     689        else
     690            $str = strtoupper($str);
     691
     692        $str = htmlspecialchars($str, ENT_COMPAT);
     693
     694        return $str;
     695    }
     696}
     697 No newline at end of file
  • wp-includes/PHPMailer/extras/htmlfilter.php

    Property changes on: wp-includes/PHPMailer/extras/class.html2text.php
    ___________________________________________________________________
    Added: svn:executable
    ## -0,0 +1 ##
    +*
    \ No newline at end of property
     
     1<?php
     2/**
     3 * htmlfilter.inc
     4 * ---------------
     5 * This set of functions allows you to filter html in order to remove
     6 * any malicious tags from it. Useful in cases when you need to filter
     7 * user input for any cross-site-scripting attempts.
     8 *
     9 * Copyright (C) 2002-2004 by Duke University
     10 *
     11 * This library is free software; you can redistribute it and/or
     12 * modify it under the terms of the GNU Lesser General Public
     13 * License as published by the Free Software Foundation; either
     14 * version 2.1 of the License, or (at your option) any later version.
     15 *
     16 * This library is distributed in the hope that it will be useful,
     17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     19 * Lesser General Public License for more details.
     20 *
     21 * You should have received a copy of the GNU Lesser General Public
     22 * License along with this library; if not, write to the Free Software
     23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 
     24 * 02110-1301  USA
     25 *
     26 * @Author      Konstantin Riabitsev <icon@linux.duke.edu>
     27 * @Version 1.1 ($Date: 2011-07-04 14:02:23 -0400 (Mon, 04 Jul 2011) $)
     28 */
     29
     30/**
     31 * @Author  Jim Jagielski <jim@jaguNET.com / jimjag@gmail.com>
     32 */
     33
     34/**
     35 * This function returns the final tag out of the tag name, an array
     36 * of attributes, and the type of the tag. This function is called by
     37 * tln_sanitize internally.
     38 *
     39 * @param  $tagname      the name of the tag.
     40 * @param  $attary       the array of attributes and their values
     41 * @param  $tagtype      The type of the tag (see in comments).
     42 * @return                       a string with the final tag representation.
     43 */
     44function tln_tagprint($tagname, $attary, $tagtype){
     45        $me = 'tln_tagprint';
     46        if ($tagtype == 2){
     47                $fulltag = '</' . $tagname . '>';
     48        } else {
     49                $fulltag = '<' . $tagname;
     50                if (is_array($attary) && sizeof($attary)){
     51                        $atts = Array();
     52                        while (list($attname, $attvalue) = each($attary)){
     53                                array_push($atts, "$attname=$attvalue");
     54                        }
     55                        $fulltag .= ' ' . join(' ', $atts);
     56                }
     57                if ($tagtype == 3){
     58                        $fulltag .= ' /';
     59                }
     60                $fulltag .= '>';
     61        }
     62        return $fulltag;
     63}
     64
     65/**
     66 * A small helper function to use with array_walk. Modifies a by-ref
     67 * value and makes it lowercase.
     68 *
     69 * @param  $val a value passed by-ref.
     70 * @return              void since it modifies a by-ref value.
     71 */
     72function tln_casenormalize(&$val){
     73        $val = strtolower($val);
     74}
     75
     76/**
     77 * This function skips any whitespace from the current position within
     78 * a string and to the next non-whitespace value.
     79 *
     80 * @param  $body   the string
     81 * @param  $offset the offset within the string where we should start
     82 *                                 looking for the next non-whitespace character.
     83 * @return                 the location within the $body where the next
     84 *                                 non-whitespace char is located.
     85 */
     86function tln_skipspace($body, $offset){
     87        $me = 'tln_skipspace';
     88        preg_match('/^(\s*)/s', substr($body, $offset), $matches);
     89        if (sizeof($matches[1])){
     90                $count = strlen($matches[1]);
     91                $offset += $count;
     92        }
     93        return $offset;
     94}
     95
     96/**
     97 * This function looks for the next character within a string.  It's
     98 * really just a glorified "strpos", except it catches the failures
     99 * nicely.
     100 *
     101 * @param  $body   The string to look for needle in.
     102 * @param  $offset Start looking from this position.
     103 * @param  $needle The character/string to look for.
     104 * @return                 location of the next occurrence of the needle, or
     105 *                                 strlen($body) if needle wasn't found.
     106 */
     107function tln_findnxstr($body, $offset, $needle){
     108        $me = 'tln_findnxstr';
     109        $pos = strpos($body, $needle, $offset);
     110        if ($pos === FALSE){
     111                $pos = strlen($body);
     112        }
     113        return $pos;
     114}
     115
     116/**
     117 * This function takes a PCRE-style regexp and tries to match it
     118 * within the string.
     119 *
     120 * @param  $body   The string to look for needle in.
     121 * @param  $offset Start looking from here.
     122 * @param  $reg    A PCRE-style regex to match.
     123 * @return                 Returns a false if no matches found, or an array
     124 *                                 with the following members:
     125 *                                 - integer with the location of the match within $body
     126 *                                 - string with whatever content between offset and the match
     127 *                                 - string with whatever it is we matched
     128 */
     129function tln_findnxreg($body, $offset, $reg){
     130        $me = 'tln_findnxreg';
     131        $matches = Array();
     132        $retarr = Array();
     133        $preg_rule = '%^(.*?)(' . $reg . ')%s';
     134        preg_match($preg_rule, substr($body, $offset), $matches);
     135        if (!isset($matches[0])){
     136                $retarr = false;
     137        } else {
     138                $retarr[0] = $offset + strlen($matches[1]);
     139                $retarr[1] = $matches[1];
     140                $retarr[2] = $matches[2];
     141        }
     142        return $retarr;
     143}
     144
     145/**
     146 * This function looks for the next tag.
     147 *
     148 * @param  $body   String where to look for the next tag.
     149 * @param  $offset Start looking from here.
     150 * @return                 false if no more tags exist in the body, or
     151 *                                 an array with the following members:
     152 *                                 - string with the name of the tag
     153 *                                 - array with attributes and their values
     154 *                                 - integer with tag type (1, 2, or 3)
     155 *                                 - integer where the tag starts (starting "<")
     156 *                                 - integer where the tag ends (ending ">")
     157 *                                 first three members will be false, if the tag is invalid.
     158 */
     159function tln_getnxtag($body, $offset){
     160        $me = 'tln_getnxtag';
     161        if ($offset > strlen($body)){
     162                return false;
     163        }
     164        $lt = tln_findnxstr($body, $offset, '<');
     165        if ($lt == strlen($body)){
     166                return false;
     167        }
     168        /**
     169         * We are here:
     170         * blah blah <tag attribute="value">
     171         * \---------^
     172         */
     173        $pos = tln_skipspace($body, $lt + 1);
     174        if ($pos >= strlen($body)){
     175                return Array(false, false, false, $lt, strlen($body));
     176        }
     177        /**
     178         * There are 3 kinds of tags:
     179         * 1. Opening tag, e.g.:
     180         *        <a href="blah">
     181         * 2. Closing tag, e.g.:
     182         *        </a>
     183         * 3. XHTML-style content-less tag, e.g.:
     184         *        <img src="blah"/>
     185         */
     186        $tagtype = false;
     187        switch (substr($body, $pos, 1)){
     188        case '/':
     189                $tagtype = 2;
     190                $pos++;
     191                break;
     192        case '!':
     193                /**
     194                 * A comment or an SGML declaration.
     195                 */
     196                if (substr($body, $pos+1, 2) == '--'){
     197                        $gt = strpos($body, '-->', $pos);
     198                        if ($gt === false){
     199                                $gt = strlen($body);
     200                        } else {
     201                                $gt += 2;
     202                        }
     203                        return Array(false, false, false, $lt, $gt);
     204                } else {
     205                        $gt = tln_findnxstr($body, $pos, '>');
     206                        return Array(false, false, false, $lt, $gt);
     207                }
     208                break;
     209        default:
     210                /**
     211                 * Assume tagtype 1 for now. If it's type 3, we'll switch values
     212                 * later.
     213                 */
     214                $tagtype = 1;
     215                break;
     216        }
     217       
     218        $tag_start = $pos;
     219        $tagname = '';
     220        /**
     221         * Look for next [\W-_], which will indicate the end of the tag name.
     222         */
     223        $regary = tln_findnxreg($body, $pos, '[^\w\-_]');
     224        if ($regary == false){
     225                return Array(false, false, false, $lt, strlen($body));
     226        }
     227        list($pos, $tagname, $match) = $regary;
     228        $tagname = strtolower($tagname);
     229       
     230        /**
     231         * $match can be either of these:
     232         * '>'  indicating the end of the tag entirely.
     233         * '\s' indicating the end of the tag name.
     234         * '/'  indicating that this is type-3 xhtml tag.
     235         *
     236         * Whatever else we find there indicates an invalid tag.
     237         */
     238        switch ($match){
     239        case '/':
     240                /**
     241                 * This is an xhtml-style tag with a closing / at the
     242                 * end, like so: <img src="blah"/>. Check if it's followed
     243                 * by the closing bracket. If not, then this tag is invalid
     244                 */
     245                if (substr($body, $pos, 2) == '/>'){
     246                        $pos++;
     247                        $tagtype = 3;
     248                } else {
     249                        $gt = tln_findnxstr($body, $pos, '>');
     250                        $retary = Array(false, false, false, $lt, $gt);
     251                        return $retary;
     252                }
     253        case '>':
     254                return Array($tagname, false, $tagtype, $lt, $pos);
     255                break;
     256        default:
     257                /**
     258                 * Check if it's whitespace
     259                 */
     260                if (preg_match('/\s/', $match)){
     261                } else {
     262                        /**
     263                         * This is an invalid tag! Look for the next closing ">".
     264                         */
     265                        $gt = tln_findnxstr($body, $lt, '>');
     266                        return Array(false, false, false, $lt, $gt);
     267                }
     268        }
     269       
     270        /**
     271         * At this point we're here:
     272         * <tagname      attribute='blah'>
     273         * \-------^
     274         *
     275         * At this point we loop in order to find all attributes.
     276         */
     277        $attname = '';
     278        $atttype = false;
     279        $attary = Array();
     280       
     281        while ($pos <= strlen($body)){
     282                $pos = tln_skipspace($body, $pos);
     283                if ($pos == strlen($body)){
     284                        /**
     285                         * Non-closed tag.
     286                         */
     287                        return Array(false, false, false, $lt, $pos);
     288                }
     289                /**
     290                 * See if we arrived at a ">" or "/>", which means that we reached
     291                 * the end of the tag.
     292                 */
     293                $matches = Array();
     294                preg_match('%^(\s*)(>|/>)%s', substr($body, $pos), $matches);
     295                if (isset($matches[0]) && $matches[0]){
     296                        /**
     297                         * Yep. So we did.
     298                         */
     299                        $pos += strlen($matches[1]);
     300                        if ($matches[2] == '/>'){
     301                                $tagtype = 3;
     302                                $pos++;
     303                        }
     304                        return Array($tagname, $attary, $tagtype, $lt, $pos);
     305                }
     306               
     307                /**
     308                 * There are several types of attributes, with optional
     309                 * [:space:] between members.
     310                 * Type 1:
     311                 *       attrname[:space:]=[:space:]'CDATA'
     312                 * Type 2:
     313                 *       attrname[:space:]=[:space:]"CDATA"
     314                 * Type 3:
     315                 *       attr[:space:]=[:space:]CDATA
     316                 * Type 4:
     317                 *       attrname
     318                 *
     319                 * We leave types 1 and 2 the same, type 3 we check for
     320                 * '"' and convert to "&quot" if needed, then wrap in
     321                 * double quotes. Type 4 we convert into:
     322                 * attrname="yes".
     323                 */
     324                $regary = tln_findnxreg($body, $pos, '[^\w\-_]');
     325                if ($regary == false){
     326                        /**
     327                         * Looks like body ended before the end of tag.
     328                         */
     329                        return Array(false, false, false, $lt, strlen($body));
     330                }
     331                list($pos, $attname, $match) = $regary;
     332                $attname = strtolower($attname);
     333                /**
     334                 * We arrived at the end of attribute name. Several things possible
     335                 * here:
     336                 * '>'  means the end of the tag and this is attribute type 4
     337                 * '/'  if followed by '>' means the same thing as above
     338                 * '\s' means a lot of things -- look what it's followed by.
     339                 *              anything else means the attribute is invalid.
     340                 */
     341                switch($match){
     342                case '/':
     343                        /**
     344                         * This is an xhtml-style tag with a closing / at the
     345                         * end, like so: <img src="blah"/>. Check if it's followed
     346                         * by the closing bracket. If not, then this tag is invalid
     347                         */
     348                        if (substr($body, $pos, 2) == '/>'){
     349                                $pos++;
     350                                $tagtype = 3;
     351                        } else {
     352                                $gt = tln_findnxstr($body, $pos, '>');
     353                                $retary = Array(false, false, false, $lt, $gt);
     354                                return $retary;
     355                        }
     356                case '>':
     357                        $attary{$attname} = '"yes"';
     358                        return Array($tagname, $attary, $tagtype, $lt, $pos);
     359                        break;
     360                default:
     361                        /**
     362                         * Skip whitespace and see what we arrive at.
     363                         */
     364                        $pos = tln_skipspace($body, $pos);
     365                        $char = substr($body, $pos, 1);
     366                        /**
     367                         * Two things are valid here:
     368                         * '=' means this is attribute type 1 2 or 3.
     369                         * \w means this was attribute type 4.
     370                         * anything else we ignore and re-loop. End of tag and
     371                         * invalid stuff will be caught by our checks at the beginning
     372                         * of the loop.
     373                         */
     374                        if ($char == '='){
     375                                $pos++;
     376                                $pos = tln_skipspace($body, $pos);
     377                                /**
     378                                 * Here are 3 possibilities:
     379                                 * "'"  attribute type 1
     380                                 * '"'  attribute type 2
     381                                 * everything else is the content of tag type 3
     382                                 */
     383                                $quot = substr($body, $pos, 1);
     384                                if ($quot == '\''){
     385                                        $regary = tln_findnxreg($body, $pos+1, '\'');
     386                                        if ($regary == false){
     387                                                return Array(false, false, false, $lt, strlen($body));
     388                                        }
     389                                        list($pos, $attval, $match) = $regary;
     390                                        $pos++;
     391                                        $attary{$attname} = '\'' . $attval . '\'';
     392                                } else if ($quot == '"'){
     393                                        $regary = tln_findnxreg($body, $pos+1, '\"');
     394                                        if ($regary == false){
     395                                                return Array(false, false, false, $lt, strlen($body));
     396                                        }
     397                                        list($pos, $attval, $match) = $regary;
     398                                        $pos++;
     399                                        $attary{$attname} = '"' . $attval . '"';
     400                                } else {
     401                                        /**
     402                                         * These are hateful. Look for \s, or >.
     403                                         */
     404                                        $regary = tln_findnxreg($body, $pos, '[\s>]');
     405                                        if ($regary == false){
     406                                                return Array(false, false, false, $lt, strlen($body));
     407                                        }
     408                                        list($pos, $attval, $match) = $regary;
     409                                        /**
     410                                         * If it's ">" it will be caught at the top.
     411                                         */
     412                                        $attval = preg_replace('/\"/s', '&quot;', $attval);
     413                                        $attary{$attname} = '"' . $attval . '"';
     414                                }
     415                        } else if (preg_match('|[\w/>]|', $char)) {
     416                                /**
     417                                 * That was attribute type 4.
     418                                 */
     419                                $attary{$attname} = '"yes"';
     420                        } else {
     421                                /**
     422                                 * An illegal character. Find next '>' and return.
     423                                 */
     424                                $gt = tln_findnxstr($body, $pos, '>');
     425                                return Array(false, false, false, $lt, $gt);
     426                        }
     427                }
     428        }
     429        /**
     430         * The fact that we got here indicates that the tag end was never
     431         * found. Return invalid tag indication so it gets stripped.
     432         */
     433        return Array(false, false, false, $lt, strlen($body));
     434}
     435
     436/**
     437 * Translates entities into literal values so they can be checked.
     438 *
     439 * @param $attvalue the by-ref value to check.
     440 * @param $regex        the regular expression to check against.
     441 * @param $hex          whether the entites are hexadecimal.
     442 * @return                      True or False depending on whether there were matches.
     443 */
     444function tln_deent(&$attvalue, $regex, $hex=false){
     445        $me = 'tln_deent';
     446        $ret_match = false;
     447        preg_match_all($regex, $attvalue, $matches);
     448        if (is_array($matches) && sizeof($matches[0]) > 0){
     449                $repl = Array();
     450                for ($i = 0; $i < sizeof($matches[0]); $i++){
     451                        $numval = $matches[1][$i];
     452                        if ($hex){
     453                                $numval = hexdec($numval);
     454                        }
     455                        $repl{$matches[0][$i]} = chr($numval);
     456                }
     457                $attvalue = strtr($attvalue, $repl);
     458                return true;
     459        } else {
     460                return false;
     461        }
     462}
     463
     464/**
     465 * This function checks attribute values for entity-encoded values
     466 * and returns them translated into 8-bit strings so we can run
     467 * checks on them.
     468 *
     469 * @param  $attvalue A string to run entity check against.
     470 * @return                       Nothing, modifies a reference value.
     471 */
     472function tln_defang(&$attvalue){
     473        $me = 'tln_defang';
     474        /**
     475         * Skip this if there aren't ampersands or backslashes.
     476         */
     477        if (strpos($attvalue, '&') === false
     478                && strpos($attvalue, '\\') === false){
     479                return;
     480        }
     481        $m = false;
     482        do {
     483                $m = false;
     484                $m = $m || tln_deent($attvalue, '/\&#0*(\d+);*/s');
     485                $m = $m || tln_deent($attvalue, '/\&#x0*((\d|[a-f])+);*/si', true);
     486                $m = $m || tln_deent($attvalue, '/\\\\(\d+)/s', true);
     487        } while ($m == true);
     488        $attvalue = stripslashes($attvalue);
     489}
     490
     491/**
     492 * Kill any tabs, newlines, or carriage returns. Our friends the
     493 * makers of the browser with 95% market value decided that it'd
     494 * be funny to make "java[tab]script" be just as good as "javascript".
     495 *
     496 * @param  attvalue      The attribute value before extraneous spaces removed.
     497 * @return attvalue      Nothing, modifies a reference value.
     498 */
     499function tln_unspace(&$attvalue){
     500        $me = 'tln_unspace';
     501        if (strcspn($attvalue, "\t\r\n\0 ") != strlen($attvalue)){
     502                $attvalue = str_replace(Array("\t", "\r", "\n", "\0", " "),
     503                                                                Array('',       '',       '',   '',       ''), $attvalue);
     504        }
     505}
     506
     507/**
     508 * This function runs various checks against the attributes.
     509 *
     510 * @param  $tagname                     String with the name of the tag.
     511 * @param  $attary                      Array with all tag attributes.
     512 * @param  $rm_attnames         See description for tln_sanitize
     513 * @param  $bad_attvals         See description for tln_sanitize
     514 * @param  $add_attr_to_tag See description for tln_sanitize
     515 * @return                                      Array with modified attributes.
     516 */
     517function tln_fixatts($tagname,
     518                                 $attary,
     519                                 $rm_attnames,
     520                                 $bad_attvals,
     521                                 $add_attr_to_tag
     522                                 ){
     523        $me = 'tln_fixatts';
     524        while (list($attname, $attvalue) = each($attary)){
     525                /**
     526                 * See if this attribute should be removed.
     527                 */
     528                foreach ($rm_attnames as $matchtag=>$matchattrs){
     529                        if (preg_match($matchtag, $tagname)){
     530                                foreach ($matchattrs as $matchattr){
     531                                        if (preg_match($matchattr, $attname)){
     532                                                unset($attary{$attname});
     533                                                continue;
     534                                        }
     535                                }
     536                        }
     537                }
     538                /**
     539                 * Remove any backslashes, entities, or extraneous whitespace.
     540                 */
     541                tln_defang($attvalue);
     542                tln_unspace($attvalue);
     543               
     544                /**
     545                 * Now let's run checks on the attvalues.
     546                 * I don't expect anyone to comprehend this. If you do,
     547                 * get in touch with me so I can drive to where you live and
     548                 * shake your hand personally. :)
     549                 */
     550                foreach ($bad_attvals as $matchtag=>$matchattrs){
     551                        if (preg_match($matchtag, $tagname)){
     552                                foreach ($matchattrs as $matchattr=>$valary){
     553                                        if (preg_match($matchattr, $attname)){
     554                                                /**
     555                                                 * There are two arrays in valary.
     556                                                 * First is matches.
     557                                                 * Second one is replacements
     558                                                 */
     559                                                list($valmatch, $valrepl) = $valary;
     560                                                $newvalue = preg_replace($valmatch,$valrepl,$attvalue);
     561                                                if ($newvalue != $attvalue){
     562                                                        $attary{$attname} = $newvalue;
     563                                                }
     564                                        }
     565                                }
     566                        }
     567                }
     568        }
     569        /**
     570         * See if we need to append any attributes to this tag.
     571         */
     572        foreach ($add_attr_to_tag as $matchtag=>$addattary){
     573                if (preg_match($matchtag, $tagname)){
     574                        $attary = array_merge($attary, $addattary);
     575                }
     576        }
     577        return $attary;
     578}
     579
     580/**
     581 *
     582 * @param $body                                 the string with HTML you wish to filter
     583 * @param $tag_list                             see description above
     584 * @param $rm_tags_with_content see description above
     585 * @param $self_closing_tags    see description above
     586 * @param $force_tag_closing    see description above
     587 * @param $rm_attnames                  see description above
     588 * @param $bad_attvals                  see description above
     589 * @param $add_attr_to_tag              see description above
     590 * @return                                              tln_sanitized html safe to show on your pages.
     591 */
     592function tln_sanitize($body,
     593                                  $tag_list,
     594                                  $rm_tags_with_content,
     595                                  $self_closing_tags,
     596                                  $force_tag_closing,
     597                                  $rm_attnames,
     598                                  $bad_attvals,
     599                                  $add_attr_to_tag
     600                                  )
     601{
     602        $me = 'tln_sanitize';
     603        /**
     604         * Normalize rm_tags and rm_tags_with_content.
     605         */
     606        $rm_tags = array_shift($tag_list);
     607        @array_walk($tag_list, 'tln_casenormalize');
     608        @array_walk($rm_tags_with_content, 'tln_casenormalize');
     609        @array_walk($self_closing_tags, 'tln_casenormalize');
     610        /**
     611         * See if tag_list is of tags to remove or tags to allow.
     612         * false  means remove these tags
     613         * true   means allow these tags
     614         */
     615        $curpos = 0;
     616        $open_tags = Array();
     617        $trusted = "<!-- begin tln_sanitized html -->\n";
     618        $skip_content = false;
     619        /**
     620         * Take care of netscape's stupid javascript entities like
     621         * &{alert('boo')};
     622         */
     623        $body = preg_replace('/&(\{.*?\};)/si', '&amp;\\1', $body);
     624        while (($curtag = tln_getnxtag($body, $curpos)) != FALSE){
     625                list($tagname, $attary, $tagtype, $lt, $gt) = $curtag;
     626                $free_content = substr($body, $curpos, $lt - $curpos);
     627                if ($skip_content == false){
     628                        $trusted .= $free_content;
     629                } else {
     630                }
     631                if ($tagname != FALSE){
     632                        if ($tagtype == 2){
     633                                if ($skip_content == $tagname){
     634                                        /**
     635                                         * Got to the end of tag we needed to remove.
     636                                         */
     637                                        $tagname = false;
     638                                        $skip_content = false;
     639                                } else {
     640                                        if ($skip_content == false){
     641                                                if (isset($open_tags{$tagname}) &&
     642                                                        $open_tags{$tagname} > 0){
     643