WordPress.org

Make WordPress Core

Ticket #3862: phpmailer.diff

File phpmailer.diff, 84.8 KB (added by ryan, 11 years ago)

class-phpmailer.php, class-smtp.php, new wp_mail()

  • wp-includes/class-phpmailer.php

     
     1<?php
     2////////////////////////////////////////////////////
     3// PHPMailer - PHP email class
     4//
     5// Class for sending email using either
     6// sendmail, PHP mail(), or SMTP.  Methods are
     7// based upon the standard AspEmail(tm) classes.
     8//
     9// Copyright (C) 2001 - 2003  Brent R. Matzelle
     10//
     11// License: LGPL, see LICENSE
     12////////////////////////////////////////////////////
     13
     14/**
     15 * PHPMailer - PHP email transport class
     16 * @package PHPMailer
     17 * @author Brent R. Matzelle
     18 * @copyright 2001 - 2003 Brent R. Matzelle
     19 */
     20class PHPMailer
     21{
     22    /////////////////////////////////////////////////
     23    // PUBLIC VARIABLES
     24    /////////////////////////////////////////////////
     25
     26    /**
     27     * Email priority (1 = High, 3 = Normal, 5 = low).
     28     * @var int
     29     */
     30    var $Priority          = 3;
     31
     32    /**
     33     * Sets the CharSet of the message.
     34     * @var string
     35     */
     36    var $CharSet           = "UTF-8";
     37
     38    /**
     39     * Sets the Content-type of the message.
     40     * @var string
     41     */
     42    var $ContentType        = "text/plain";
     43
     44    /**
     45     * Sets the Encoding of the message. Options for this are "8bit",
     46     * "7bit", "binary", "base64", and "quoted-printable".
     47     * @var string
     48     */
     49    var $Encoding          = "8bit";
     50
     51    /**
     52     * Holds the most recent mailer error message.
     53     * @var string
     54     */
     55    var $ErrorInfo         = "";
     56
     57    /**
     58     * Sets the From email address for the message.
     59     * @var string
     60     */
     61    var $From               = "support@wordpress.com";
     62
     63    /**
     64     * Sets the From name of the message.
     65     * @var string
     66     */
     67    var $FromName           = "Support";
     68
     69    /**
     70     * Sets the Sender email (Return-Path) of the message.  If not empty,
     71     * will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
     72     * @var string
     73     */
     74    var $Sender            = "";
     75
     76    /**
     77     * Sets the Subject of the message.
     78     * @var string
     79     */
     80    var $Subject           = "";
     81
     82    /**
     83     * Sets the Body of the message.  This can be either an HTML or text body.
     84     * If HTML then run IsHTML(true).
     85     * @var string
     86     */
     87    var $Body               = "";
     88
     89    /**
     90     * Sets the text-only body of the message.  This automatically sets the
     91     * email to multipart/alternative.  This body can be read by mail
     92     * clients that do not have HTML email capability such as mutt. Clients
     93     * that can read HTML will view the normal Body.
     94     * @var string
     95     */
     96    var $AltBody           = "";
     97
     98    /**
     99     * Sets word wrapping on the body of the message to a given number of
     100     * characters.
     101     * @var int
     102     */
     103    var $WordWrap          = 0;
     104
     105    /**
     106     * Method to send mail: ("mail", "sendmail", or "smtp").
     107     * @var string
     108     */
     109    var $Mailer            = "mail";
     110
     111    /**
     112     * Sets the path of the sendmail program.
     113     * @var string
     114     */
     115    var $Sendmail          = "/usr/sbin/sendmail";
     116   
     117    /**
     118     * Path to PHPMailer plugins.  This is now only useful if the SMTP class
     119     * is in a different directory than the PHP include path. 
     120     * @var string
     121     */
     122    var $PluginDir         = "";
     123
     124    /**
     125     *  Holds PHPMailer version.
     126     *  @var string
     127     */
     128    var $Version           = "1.73";
     129
     130    /**
     131     * Sets the email address that a reading confirmation will be sent.
     132     * @var string
     133     */
     134    var $ConfirmReadingTo  = "";
     135
     136    /**
     137     *  Sets the hostname to use in Message-Id and Received headers
     138     *  and as default HELO string. If empty, the value returned
     139     *  by SERVER_NAME is used or 'localhost.localdomain'.
     140     *  @var string
     141     */
     142    var $Hostname          = "";
     143
     144    /////////////////////////////////////////////////
     145    // SMTP VARIABLES
     146    /////////////////////////////////////////////////
     147
     148    /**
     149     *  Sets the SMTP hosts.  All hosts must be separated by a
     150     *  semicolon.  You can also specify a different port
     151     *  for each host by using this format: [hostname:port]
     152     *  (e.g. "smtp1.example.com:25;smtp2.example.com").
     153     *  Hosts will be tried in order.
     154     *  @var string
     155     */
     156    var $Host        = "localhost";
     157
     158    /**
     159     *  Sets the default SMTP server port.
     160     *  @var int
     161     */
     162    var $Port        = 25;
     163
     164    /**
     165     *  Sets the SMTP HELO of the message (Default is $Hostname).
     166     *  @var string
     167     */
     168    var $Helo        = "";
     169
     170    /**
     171     *  Sets SMTP authentication. Utilizes the Username and Password variables.
     172     *  @var bool
     173     */
     174    var $SMTPAuth     = false;
     175
     176    /**
     177     *  Sets SMTP username.
     178     *  @var string
     179     */
     180    var $Username     = "";
     181
     182    /**
     183     *  Sets SMTP password.
     184     *  @var string
     185     */
     186    var $Password     = "";
     187
     188    /**
     189     *  Sets the SMTP server timeout in seconds. This function will not
     190     *  work with the win32 version.
     191     *  @var int
     192     */
     193    var $Timeout      = 10;
     194
     195    /**
     196     *  Sets SMTP class debugging on or off.
     197     *  @var bool
     198     */
     199    var $SMTPDebug    = false;
     200
     201    /**
     202     * Prevents the SMTP connection from being closed after each mail
     203     * sending.  If this is set to true then to close the connection
     204     * requires an explicit call to SmtpClose().
     205     * @var bool
     206     */
     207    var $SMTPKeepAlive = false;
     208
     209    /**#@+
     210     * @access private
     211     */
     212    var $smtp            = NULL;
     213    var $to              = array();
     214    var $cc              = array();
     215    var $bcc             = array();
     216    var $ReplyTo         = array();
     217    var $attachment      = array();
     218    var $CustomHeader    = array();
     219    var $message_type    = "";
     220    var $boundary        = array();
     221    var $language        = array();
     222    var $error_count     = 0;
     223    var $LE              = "\n";
     224    /**#@-*/
     225   
     226    /////////////////////////////////////////////////
     227    // VARIABLE METHODS
     228    /////////////////////////////////////////////////
     229
     230    /**
     231     * Sets message type to HTML. 
     232     * @param bool $bool
     233     * @return void
     234     */
     235    function IsHTML($bool) {
     236        if($bool == true)
     237            $this->ContentType = "text/html";
     238        else
     239            $this->ContentType = "text/plain";
     240    }
     241
     242    /**
     243     * Sets Mailer to send message using SMTP.
     244     * @return void
     245     */
     246    function IsSMTP() {
     247        $this->Mailer = "smtp";
     248    }
     249
     250    /**
     251     * Sets Mailer to send message using PHP mail() function.
     252     * @return void
     253     */
     254    function IsMail() {
     255        $this->Mailer = "mail";
     256    }
     257
     258    /**
     259     * Sets Mailer to send message using the $Sendmail program.
     260     * @return void
     261     */
     262    function IsSendmail() {
     263        $this->Mailer = "sendmail";
     264    }
     265
     266    /**
     267     * Sets Mailer to send message using the qmail MTA.
     268     * @return void
     269     */
     270    function IsQmail() {
     271        $this->Sendmail = "/var/qmail/bin/sendmail";
     272        $this->Mailer = "sendmail";
     273    }
     274
     275
     276    /////////////////////////////////////////////////
     277    // RECIPIENT METHODS
     278    /////////////////////////////////////////////////
     279
     280    /**
     281     * Adds a "To" address. 
     282     * @param string $address
     283     * @param string $name
     284     * @return void
     285     */
     286    function AddAddress($address, $name = "") {
     287        $cur = count($this->to);
     288        $this->to[$cur][0] = trim($address);
     289        $this->to[$cur][1] = $name;
     290    }
     291
     292    /**
     293     * Adds a "Cc" address. Note: this function works
     294     * with the SMTP mailer on win32, not with the "mail"
     295     * mailer. 
     296     * @param string $address
     297     * @param string $name
     298     * @return void
     299    */
     300    function AddCC($address, $name = "") {
     301        $cur = count($this->cc);
     302        $this->cc[$cur][0] = trim($address);
     303        $this->cc[$cur][1] = $name;
     304    }
     305
     306    /**
     307     * Adds a "Bcc" address. Note: this function works
     308     * with the SMTP mailer on win32, not with the "mail"
     309     * mailer. 
     310     * @param string $address
     311     * @param string $name
     312     * @return void
     313     */
     314    function AddBCC($address, $name = "") {
     315        $cur = count($this->bcc);
     316        $this->bcc[$cur][0] = trim($address);
     317        $this->bcc[$cur][1] = $name;
     318    }
     319
     320    /**
     321     * Adds a "Reply-to" address. 
     322     * @param string $address
     323     * @param string $name
     324     * @return void
     325     */
     326    function AddReplyTo($address, $name = "") {
     327        $cur = count($this->ReplyTo);
     328        $this->ReplyTo[$cur][0] = trim($address);
     329        $this->ReplyTo[$cur][1] = $name;
     330    }
     331
     332
     333    /////////////////////////////////////////////////
     334    // MAIL SENDING METHODS
     335    /////////////////////////////////////////////////
     336
     337    /**
     338     * Creates message and assigns Mailer. If the message is
     339     * not sent successfully then it returns false.  Use the ErrorInfo
     340     * variable to view description of the error. 
     341     * @return bool
     342     */
     343    function Send() {
     344        $header = "";
     345        $body = "";
     346        $result = true;
     347
     348        if((count($this->to) + count($this->cc) + count($this->bcc)) < 1)
     349        {
     350            $this->SetError($this->Lang("provide_address"));
     351            return false;
     352        }
     353
     354        // Set whether the message is multipart/alternative
     355        if(!empty($this->AltBody))
     356            $this->ContentType = "multipart/alternative";
     357
     358        $this->error_count = 0; // reset errors
     359        $this->SetMessageType();
     360        $header .= $this->CreateHeader();
     361        $body = $this->CreateBody();
     362
     363        if($body == "") { return false; }
     364
     365        // Choose the mailer
     366        switch($this->Mailer)
     367        {
     368            case "sendmail":
     369                $result = $this->SendmailSend($header, $body);
     370                break;
     371            case "mail":
     372                $result = $this->MailSend($header, $body);
     373                break;
     374            case "smtp":
     375                $result = $this->SmtpSend($header, $body);
     376                break;
     377            default:
     378            $this->SetError($this->Mailer . $this->Lang("mailer_not_supported"));
     379                $result = false;
     380                break;
     381        }
     382
     383        return $result;
     384    }
     385   
     386    /**
     387     * Sends mail using the $Sendmail program. 
     388     * @access private
     389     * @return bool
     390     */
     391    function SendmailSend($header, $body) {
     392        if ($this->Sender != "")
     393            $sendmail = sprintf("%s -oi -f %s -t", $this->Sendmail, $this->Sender);
     394        else
     395            $sendmail = sprintf("%s -oi -t", $this->Sendmail);
     396
     397        if(!@$mail = popen($sendmail, "w"))
     398        {
     399            $this->SetError($this->Lang("execute") . $this->Sendmail);
     400            return false;
     401        }
     402
     403        fputs($mail, $header);
     404        fputs($mail, $body);
     405       
     406        $result = pclose($mail) >> 8 & 0xFF;
     407        if($result != 0)
     408        {
     409            $this->SetError($this->Lang("execute") . $this->Sendmail);
     410            return false;
     411        }
     412
     413        return true;
     414    }
     415
     416    /**
     417     * Sends mail using the PHP mail() function. 
     418     * @access private
     419     * @return bool
     420     */
     421    function MailSend($header, $body) {
     422        $to = "";
     423        for($i = 0; $i < count($this->to); $i++)
     424        {
     425            if($i != 0) { $to .= ", "; }
     426            $to .= $this->to[$i][0];
     427        }
     428
     429        if ($this->Sender != "" && strlen(ini_get("safe_mode"))< 1)
     430        {
     431            $old_from = ini_get("sendmail_from");
     432            ini_set("sendmail_from", $this->Sender);
     433            $params = sprintf("-oi -f %s", $this->Sender);
     434            $rt = @mail($to, $this->EncodeHeader($this->Subject), $body,
     435                        $header, $params);
     436        }
     437        else
     438            $rt = @mail($to, $this->EncodeHeader($this->Subject), $body, $header);
     439
     440        if (isset($old_from))
     441            ini_set("sendmail_from", $old_from);
     442
     443        if(!$rt)
     444        {
     445            $this->SetError($this->Lang("instantiate"));
     446            return false;
     447        }
     448
     449        return true;
     450    }
     451
     452    /**
     453     * Sends mail via SMTP using PhpSMTP (Author:
     454     * Chris Ryan).  Returns bool.  Returns false if there is a
     455     * bad MAIL FROM, RCPT, or DATA input.
     456     * @access private
     457     * @return bool
     458     */
     459    function SmtpSend($header, $body) {
     460        include_once($this->PluginDir . "class.smtp.php");
     461        $error = "";
     462        $bad_rcpt = array();
     463
     464        if(!$this->SmtpConnect())
     465            return false;
     466
     467        $smtp_from = ($this->Sender == "") ? $this->From : $this->Sender;
     468        if(!$this->smtp->Mail($smtp_from))
     469        {
     470            $error = $this->Lang("from_failed") . $smtp_from;
     471            $this->SetError($error);
     472            $this->smtp->Reset();
     473            return false;
     474        }
     475
     476        // Attempt to send attach all recipients
     477        for($i = 0; $i < count($this->to); $i++)
     478        {
     479            if(!$this->smtp->Recipient($this->to[$i][0]))
     480                $bad_rcpt[] = $this->to[$i][0];
     481        }
     482        for($i = 0; $i < count($this->cc); $i++)
     483        {
     484            if(!$this->smtp->Recipient($this->cc[$i][0]))
     485                $bad_rcpt[] = $this->cc[$i][0];
     486        }
     487        for($i = 0; $i < count($this->bcc); $i++)
     488        {
     489            if(!$this->smtp->Recipient($this->bcc[$i][0]))
     490                $bad_rcpt[] = $this->bcc[$i][0];
     491        }
     492
     493        if(count($bad_rcpt) > 0) // Create error message
     494        {
     495            for($i = 0; $i < count($bad_rcpt); $i++)
     496            {
     497                if($i != 0) { $error .= ", "; }
     498                $error .= $bad_rcpt[$i];
     499            }
     500            $error = $this->Lang("recipients_failed") . $error;
     501            $this->SetError($error);
     502            $this->smtp->Reset();
     503            return false;
     504        }
     505
     506        if(!$this->smtp->Data($header . $body))
     507        {
     508            $this->SetError($this->Lang("data_not_accepted"));
     509            $this->smtp->Reset();
     510            return false;
     511        }
     512        if($this->SMTPKeepAlive == true)
     513            $this->smtp->Reset();
     514        else
     515            $this->SmtpClose();
     516
     517        return true;
     518    }
     519
     520    /**
     521     * Initiates a connection to an SMTP server.  Returns false if the
     522     * operation failed.
     523     * @access private
     524     * @return bool
     525     */
     526    function SmtpConnect() {
     527        if($this->smtp == NULL) { $this->smtp = new SMTP(); }
     528
     529        $this->smtp->do_debug = $this->SMTPDebug;
     530        $hosts = explode(";", $this->Host);
     531        $index = 0;
     532        $connection = ($this->smtp->Connected());
     533
     534        // Retry while there is no connection
     535        while($index < count($hosts) && $connection == false)
     536        {
     537            if(strstr($hosts[$index], ":"))
     538                list($host, $port) = explode(":", $hosts[$index]);
     539            else
     540            {
     541                $host = $hosts[$index];
     542                $port = $this->Port;
     543            }
     544
     545            if($this->smtp->Connect($host, $port, $this->Timeout))
     546            {
     547                if ($this->Helo != '')
     548                    $this->smtp->Hello($this->Helo);
     549                else
     550                    $this->smtp->Hello($this->ServerHostname());
     551       
     552                if($this->SMTPAuth)
     553                {
     554                    if(!$this->smtp->Authenticate($this->Username,
     555                                                  $this->Password))
     556                    {
     557                        $this->SetError($this->Lang("authenticate"));
     558                        $this->smtp->Reset();
     559                        $connection = false;
     560                    }
     561                }
     562                $connection = true;
     563            }
     564            $index++;
     565        }
     566        if(!$connection)
     567            $this->SetError($this->Lang("connect_host"));
     568
     569        return $connection;
     570    }
     571
     572    /**
     573     * Closes the active SMTP session if one exists.
     574     * @return void
     575     */
     576    function SmtpClose() {
     577        if($this->smtp != NULL)
     578        {
     579            if($this->smtp->Connected())
     580            {
     581                $this->smtp->Quit();
     582                $this->smtp->Close();
     583            }
     584        }
     585    }
     586
     587    /**
     588     * Sets the language for all class error messages.  Returns false
     589     * if it cannot load the language file.  The default language type
     590     * is English.
     591     * @param string $lang_type Type of language (e.g. Portuguese: "br")
     592     * @param string $lang_path Path to the language file directory
     593     * @access public
     594     * @return bool
     595     */
     596    function SetLanguage($lang_type, $lang_path = "language/") {
     597        if(file_exists($lang_path.'phpmailer.lang-'.$lang_type.'.php'))
     598            include($lang_path.'phpmailer.lang-'.$lang_type.'.php');
     599        else if(file_exists($lang_path.'phpmailer.lang-en.php'))
     600            include($lang_path.'phpmailer.lang-en.php');
     601        else
     602        {
     603            $this->SetError("Could not load language file");
     604            return false;
     605        }
     606        $this->language = $PHPMAILER_LANG;
     607   
     608        return true;
     609    }
     610
     611    /////////////////////////////////////////////////
     612    // MESSAGE CREATION METHODS
     613    /////////////////////////////////////////////////
     614
     615    /**
     616     * Creates recipient headers. 
     617     * @access private
     618     * @return string
     619     */
     620    function AddrAppend($type, $addr) {
     621        $addr_str = $type . ": ";
     622        $addr_str .= $this->AddrFormat($addr[0]);
     623        if(count($addr) > 1)
     624        {
     625            for($i = 1; $i < count($addr); $i++)
     626                $addr_str .= ", " . $this->AddrFormat($addr[$i]);
     627        }
     628        $addr_str .= $this->LE;
     629
     630        return $addr_str;
     631    }
     632   
     633    /**
     634     * Formats an address correctly.
     635     * @access private
     636     * @return string
     637     */
     638    function AddrFormat($addr) {
     639        if(empty($addr[1]))
     640            $formatted = $addr[0];
     641        else
     642        {
     643            $formatted = $this->EncodeHeader($addr[1], 'phrase') . " <" .
     644                         $addr[0] . ">";
     645        }
     646
     647        return $formatted;
     648    }
     649
     650    /**
     651     * Wraps message for use with mailers that do not
     652     * automatically perform wrapping and for quoted-printable.
     653     * Original written by philippe. 
     654     * @access private
     655     * @return string
     656     */
     657    function WrapText($message, $length, $qp_mode = false) {
     658        $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
     659
     660        $message = $this->FixEOL($message);
     661        if (substr($message, -1) == $this->LE)
     662            $message = substr($message, 0, -1);
     663
     664        $line = explode($this->LE, $message);
     665        $message = "";
     666        for ($i=0 ;$i < count($line); $i++)
     667        {
     668          $line_part = explode(" ", $line[$i]);
     669          $buf = "";
     670          for ($e = 0; $e<count($line_part); $e++)
     671          {
     672              $word = $line_part[$e];
     673              if ($qp_mode and (strlen($word) > $length))
     674              {
     675                $space_left = $length - strlen($buf) - 1;
     676                if ($e != 0)
     677                {
     678                    if ($space_left > 20)
     679                    {
     680                        $len = $space_left;
     681                        if (substr($word, $len - 1, 1) == "=")
     682                          $len--;
     683                        elseif (substr($word, $len - 2, 1) == "=")
     684                          $len -= 2;
     685                        $part = substr($word, 0, $len);
     686                        $word = substr($word, $len);
     687                        $buf .= " " . $part;
     688                        $message .= $buf . sprintf("=%s", $this->LE);
     689                    }
     690                    else
     691                    {
     692                        $message .= $buf . $soft_break;
     693                    }
     694                    $buf = "";
     695                }
     696                while (strlen($word) > 0)
     697                {
     698                    $len = $length;
     699                    if (substr($word, $len - 1, 1) == "=")
     700                        $len--;
     701                    elseif (substr($word, $len - 2, 1) == "=")
     702                        $len -= 2;
     703                    $part = substr($word, 0, $len);
     704                    $word = substr($word, $len);
     705
     706                    if (strlen($word) > 0)
     707                        $message .= $part . sprintf("=%s", $this->LE);
     708                    else
     709                        $buf = $part;
     710                }
     711              }
     712              else
     713              {
     714                $buf_o = $buf;
     715                $buf .= ($e == 0) ? $word : (" " . $word);
     716
     717                if (strlen($buf) > $length and $buf_o != "")
     718                {
     719                    $message .= $buf_o . $soft_break;
     720                    $buf = $word;
     721                }
     722              }
     723          }
     724          $message .= $buf . $this->LE;
     725        }
     726
     727        return $message;
     728    }
     729   
     730    /**
     731     * Set the body wrapping.
     732     * @access private
     733     * @return void
     734     */
     735    function SetWordWrap() {
     736        if($this->WordWrap < 1)
     737            return;
     738           
     739        switch($this->message_type)
     740        {
     741           case "alt":
     742              // fall through
     743           case "alt_attachments":
     744              $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);
     745              break;
     746           default:
     747              $this->Body = $this->WrapText($this->Body, $this->WordWrap);
     748              break;
     749        }
     750    }
     751
     752    /**
     753     * Assembles message header. 
     754     * @access private
     755     * @return string
     756     */
     757    function CreateHeader() {
     758        $result = "";
     759       
     760        // Set the boundaries
     761        $uniq_id = md5(uniqid(time()));
     762        $this->boundary[1] = "b1_" . $uniq_id;
     763        $this->boundary[2] = "b2_" . $uniq_id;
     764
     765        $result .= $this->HeaderLine("Date", $this->RFCDate());
     766        if($this->Sender == "")
     767            $result .= $this->HeaderLine("Return-Path", trim($this->From));
     768        else
     769            $result .= $this->HeaderLine("Return-Path", trim($this->Sender));
     770       
     771        // To be created automatically by mail()
     772        if($this->Mailer != "mail")
     773        {
     774            if(count($this->to) > 0)
     775                $result .= $this->AddrAppend("To", $this->to);
     776            else if (count($this->cc) == 0)
     777                $result .= $this->HeaderLine("To", "undisclosed-recipients:;");
     778            if(count($this->cc) > 0)
     779                $result .= $this->AddrAppend("Cc", $this->cc);
     780        }
     781
     782        $from = array();
     783        $from[0][0] = trim($this->From);
     784        $from[0][1] = $this->FromName;
     785        $result .= $this->AddrAppend("From", $from);
     786
     787        // sendmail and mail() extract Bcc from the header before sending
     788        if((($this->Mailer == "sendmail") || ($this->Mailer == "mail")) && (count($this->bcc) > 0))
     789            $result .= $this->AddrAppend("Bcc", $this->bcc);
     790
     791        if(count($this->ReplyTo) > 0)
     792            $result .= $this->AddrAppend("Reply-to", $this->ReplyTo);
     793
     794        // mail() sets the subject itself
     795        if($this->Mailer != "mail")
     796            $result .= $this->HeaderLine("Subject", $this->EncodeHeader(trim($this->Subject)));
     797
     798        $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE);
     799        $result .= $this->HeaderLine("X-Priority", $this->Priority);
     800       
     801        if($this->ConfirmReadingTo != "")
     802        {
     803            $result .= $this->HeaderLine("Disposition-Notification-To",
     804                       "<" . trim($this->ConfirmReadingTo) . ">");
     805        }
     806
     807        // Add custom headers
     808        for($index = 0; $index < count($this->CustomHeader); $index++)
     809        {
     810            $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]),
     811                       $this->EncodeHeader(trim($this->CustomHeader[$index][1])));
     812        }
     813        $result .= $this->HeaderLine("MIME-Version", "1.0");
     814
     815        switch($this->message_type)
     816        {
     817            case "plain":
     818                $result .= $this->HeaderLine("Content-Transfer-Encoding", $this->Encoding);
     819                $result .= sprintf("Content-Type: %s; charset=\"%s\"",
     820                                    $this->ContentType, $this->CharSet);
     821                break;
     822            case "attachments":
     823                // fall through
     824            case "alt_attachments":
     825                if($this->InlineImageExists())
     826                {
     827                    $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s",
     828                                    "multipart/related", $this->LE, $this->LE,
     829                                    $this->boundary[1], $this->LE);
     830                }
     831                else
     832                {
     833                    $result .= $this->HeaderLine("Content-Type", "multipart/mixed;");
     834                    $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
     835                }
     836                break;
     837            case "alt":
     838                $result .= $this->HeaderLine("Content-Type", "multipart/alternative;");
     839                $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
     840                break;
     841        }
     842
     843        if($this->Mailer != "mail")
     844            $result .= $this->LE.$this->LE;
     845
     846        return $result;
     847    }
     848
     849    /**
     850     * Assembles the message body.  Returns an empty string on failure.
     851     * @access private
     852     * @return string
     853     */
     854    function CreateBody() {
     855        $result = "";
     856
     857        $this->SetWordWrap();
     858
     859        switch($this->message_type)
     860        {
     861            case "alt":
     862                $result .= $this->GetBoundary($this->boundary[1], "",
     863                                              "text/plain", "");
     864                $result .= $this->EncodeString($this->AltBody, $this->Encoding);
     865                $result .= $this->LE.$this->LE;
     866                $result .= $this->GetBoundary($this->boundary[1], "",
     867                                              "text/html", "");
     868               
     869                $result .= $this->EncodeString($this->Body, $this->Encoding);
     870                $result .= $this->LE.$this->LE;
     871   
     872                $result .= $this->EndBoundary($this->boundary[1]);
     873                break;
     874            case "plain":
     875                $result .= $this->EncodeString($this->Body, $this->Encoding);
     876                break;
     877            case "attachments":
     878                $result .= $this->GetBoundary($this->boundary[1], "", "", "");
     879                $result .= $this->EncodeString($this->Body, $this->Encoding);
     880                $result .= $this->LE;
     881     
     882                $result .= $this->AttachAll();
     883                break;
     884            case "alt_attachments":
     885                $result .= sprintf("--%s%s", $this->boundary[1], $this->LE);
     886                $result .= sprintf("Content-Type: %s;%s" .
     887                                   "\tboundary=\"%s\"%s",
     888                                   "multipart/alternative", $this->LE,
     889                                   $this->boundary[2], $this->LE.$this->LE);
     890   
     891                // Create text body
     892                $result .= $this->GetBoundary($this->boundary[2], "",
     893                                              "text/plain", "") . $this->LE;
     894
     895                $result .= $this->EncodeString($this->AltBody, $this->Encoding);
     896                $result .= $this->LE.$this->LE;
     897   
     898                // Create the HTML body
     899                $result .= $this->GetBoundary($this->boundary[2], "",
     900                                              "text/html", "") . $this->LE;
     901   
     902                $result .= $this->EncodeString($this->Body, $this->Encoding);
     903                $result .= $this->LE.$this->LE;
     904
     905                $result .= $this->EndBoundary($this->boundary[2]);
     906               
     907                $result .= $this->AttachAll();
     908                break;
     909        }
     910        if($this->IsError())
     911            $result = "";
     912
     913        return $result;
     914    }
     915
     916    /**
     917     * Returns the start of a message boundary.
     918     * @access private
     919     */
     920    function GetBoundary($boundary, $charSet, $contentType, $encoding) {
     921        $result = "";
     922        if($charSet == "") { $charSet = $this->CharSet; }
     923        if($contentType == "") { $contentType = $this->ContentType; }
     924        if($encoding == "") { $encoding = $this->Encoding; }
     925
     926        $result .= $this->TextLine("--" . $boundary);
     927        $result .= sprintf("Content-Type: %s; charset = \"%s\"",
     928                            $contentType, $charSet);
     929        $result .= $this->LE;
     930        $result .= $this->HeaderLine("Content-Transfer-Encoding", $encoding);
     931        $result .= $this->LE;
     932       
     933        return $result;
     934    }
     935   
     936    /**
     937     * Returns the end of a message boundary.
     938     * @access private
     939     */
     940    function EndBoundary($boundary) {
     941        return $this->LE . "--" . $boundary . "--" . $this->LE;
     942    }
     943   
     944    /**
     945     * Sets the message type.
     946     * @access private
     947     * @return void
     948     */
     949    function SetMessageType() {
     950        if(count($this->attachment) < 1 && strlen($this->AltBody) < 1)
     951            $this->message_type = "plain";
     952        else
     953        {
     954            if(count($this->attachment) > 0)
     955                $this->message_type = "attachments";
     956            if(strlen($this->AltBody) > 0 && count($this->attachment) < 1)
     957                $this->message_type = "alt";
     958            if(strlen($this->AltBody) > 0 && count($this->attachment) > 0)
     959                $this->message_type = "alt_attachments";
     960        }
     961    }
     962
     963    /**
     964     * Returns a formatted header line.
     965     * @access private
     966     * @return string
     967     */
     968    function HeaderLine($name, $value) {
     969        return $name . ": " . $value . $this->LE;
     970    }
     971
     972    /**
     973     * Returns a formatted mail line.
     974     * @access private
     975     * @return string
     976     */
     977    function TextLine($value) {
     978        return $value . $this->LE;
     979    }
     980
     981    /////////////////////////////////////////////////
     982    // ATTACHMENT METHODS
     983    /////////////////////////////////////////////////
     984
     985    /**
     986     * Adds an attachment from a path on the filesystem.
     987     * Returns false if the file could not be found
     988     * or accessed.
     989     * @param string $path Path to the attachment.
     990     * @param string $name Overrides the attachment name.
     991     * @param string $encoding File encoding (see $Encoding).
     992     * @param string $type File extension (MIME) type.
     993     * @return bool
     994     */
     995    function AddAttachment($path, $name = "", $encoding = "base64",
     996                           $type = "application/octet-stream") {
     997        if(!@is_file($path))
     998        {
     999            $this->SetError($this->Lang("file_access") . $path);
     1000            return false;
     1001        }
     1002
     1003        $filename = basename($path);
     1004        if($name == "")
     1005            $name = $filename;
     1006
     1007        $cur = count($this->attachment);
     1008        $this->attachment[$cur][0] = $path;
     1009        $this->attachment[$cur][1] = $filename;
     1010        $this->attachment[$cur][2] = $name;
     1011        $this->attachment[$cur][3] = $encoding;
     1012        $this->attachment[$cur][4] = $type;
     1013        $this->attachment[$cur][5] = false; // isStringAttachment
     1014        $this->attachment[$cur][6] = "attachment";
     1015        $this->attachment[$cur][7] = 0;
     1016
     1017        return true;
     1018    }
     1019
     1020    /**
     1021     * Attaches all fs, string, and binary attachments to the message.
     1022     * Returns an empty string on failure.
     1023     * @access private
     1024     * @return string
     1025     */
     1026    function AttachAll() {
     1027        // Return text of body
     1028        $mime = array();
     1029
     1030        // Add all attachments
     1031        for($i = 0; $i < count($this->attachment); $i++)
     1032        {
     1033            // Check for string attachment
     1034            $bString = $this->attachment[$i][5];
     1035            if ($bString)
     1036                $string = $this->attachment[$i][0];
     1037            else
     1038                $path = $this->attachment[$i][0];
     1039
     1040            $filename    = $this->attachment[$i][1];
     1041            $name        = $this->attachment[$i][2];
     1042            $encoding    = $this->attachment[$i][3];
     1043            $type        = $this->attachment[$i][4];
     1044            $disposition = $this->attachment[$i][6];
     1045            $cid         = $this->attachment[$i][7];
     1046           
     1047            $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);
     1048            $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $name, $this->LE);
     1049            $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
     1050
     1051            if($disposition == "inline")
     1052                $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
     1053
     1054            $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s",
     1055                              $disposition, $name, $this->LE.$this->LE);
     1056
     1057            // Encode as string attachment
     1058            if($bString)
     1059            {
     1060                $mime[] = $this->EncodeString($string, $encoding);
     1061                if($this->IsError()) { return ""; }
     1062                $mime[] = $this->LE.$this->LE;
     1063            }
     1064            else
     1065            {
     1066                $mime[] = $this->EncodeFile($path, $encoding);               
     1067                if($this->IsError()) { return ""; }
     1068                $mime[] = $this->LE.$this->LE;
     1069            }
     1070        }
     1071
     1072        $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE);
     1073
     1074        return join("", $mime);
     1075    }
     1076   
     1077    /**
     1078     * Encodes attachment in requested format.  Returns an
     1079     * empty string on failure.
     1080     * @access private
     1081     * @return string
     1082     */
     1083    function EncodeFile ($path, $encoding = "base64") {
     1084        if(!@$fd = fopen($path, "rb"))
     1085        {
     1086            $this->SetError($this->Lang("file_open") . $path);
     1087            return "";
     1088        }
     1089        $magic_quotes = get_magic_quotes_runtime();
     1090        set_magic_quotes_runtime(0);
     1091        $file_buffer = fread($fd, filesize($path));
     1092        $file_buffer = $this->EncodeString($file_buffer, $encoding);
     1093        fclose($fd);
     1094        set_magic_quotes_runtime($magic_quotes);
     1095
     1096        return $file_buffer;
     1097    }
     1098
     1099    /**
     1100     * Encodes string to requested format. Returns an
     1101     * empty string on failure.
     1102     * @access private
     1103     * @return string
     1104     */
     1105    function EncodeString ($str, $encoding = "base64") {
     1106        $encoded = "";
     1107        switch(strtolower($encoding)) {
     1108          case "base64":
     1109              // chunk_split is found in PHP >= 3.0.6
     1110              $encoded = chunk_split(base64_encode($str), 76, $this->LE);
     1111              break;
     1112          case "7bit":
     1113          case "8bit":
     1114              $encoded = $this->FixEOL($str);
     1115              if (substr($encoded, -(strlen($this->LE))) != $this->LE)
     1116                $encoded .= $this->LE;
     1117              break;
     1118          case "binary":
     1119              $encoded = $str;
     1120              break;
     1121          case "quoted-printable":
     1122              $encoded = $this->EncodeQP($str);
     1123              break;
     1124          default:
     1125              $this->SetError($this->Lang("encoding") . $encoding);
     1126              break;
     1127        }
     1128        return $encoded;
     1129    }
     1130
     1131    /**
     1132     * Encode a header string to best of Q, B, quoted or none. 
     1133     * @access private
     1134     * @return string
     1135     */
     1136    function EncodeHeader ($str, $position = 'text') {
     1137      $x = 0;
     1138     
     1139      switch (strtolower($position)) {
     1140        case 'phrase':
     1141          if (!preg_match('/[\200-\377]/', $str)) {
     1142            // Can't use addslashes as we don't know what value has magic_quotes_sybase.
     1143            $encoded = addcslashes($str, "\0..\37\177\\\"");
     1144
     1145            if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str))
     1146              return ($encoded);
     1147            else
     1148              return ("\"$encoded\"");
     1149          }
     1150          $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
     1151          break;
     1152        case 'comment':
     1153          $x = preg_match_all('/[()"]/', $str, $matches);
     1154          // Fall-through
     1155        case 'text':
     1156        default:
     1157          $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
     1158          break;
     1159      }
     1160
     1161      if ($x == 0)
     1162        return ($str);
     1163
     1164      $maxlen = 75 - 7 - strlen($this->CharSet);
     1165      // Try to select the encoding which should produce the shortest output
     1166      if (strlen($str)/3 < $x) {
     1167        $encoding = 'B';
     1168        $encoded = base64_encode($str);
     1169        $maxlen -= $maxlen % 4;
     1170        $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
     1171      } else {
     1172        $encoding = 'Q';
     1173        $encoded = $this->EncodeQ($str, $position);
     1174        $encoded = $this->WrapText($encoded, $maxlen, true);
     1175        $encoded = str_replace("=".$this->LE, "\n", trim($encoded));
     1176      }
     1177
     1178      $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);
     1179      $encoded = trim(str_replace("\n", $this->LE, $encoded));
     1180     
     1181      return $encoded;
     1182    }
     1183   
     1184    /**
     1185     * Encode string to quoted-printable. 
     1186     * @access private
     1187     * @return string
     1188     */
     1189    function EncodeQP ($str) {
     1190        $encoded = $this->FixEOL($str);
     1191        if (substr($encoded, -(strlen($this->LE))) != $this->LE)
     1192            $encoded .= $this->LE;
     1193
     1194        // Replace every high ascii, control and = characters
     1195        $encoded = preg_replace('/([\000-\010\013\014\016-\037\075\177-\377])/e',
     1196                  "'='.sprintf('%02X', ord('\\1'))", $encoded);
     1197        // Replace every spaces and tabs when it's the last character on a line
     1198        $encoded = preg_replace("/([\011\040])".$this->LE."/e",
     1199                  "'='.sprintf('%02X', ord('\\1')).'".$this->LE."'", $encoded);
     1200
     1201        // Maximum line length of 76 characters before CRLF (74 + space + '=')
     1202        $encoded = $this->WrapText($encoded, 74, true);
     1203
     1204        return $encoded;
     1205    }
     1206
     1207    /**
     1208     * Encode string to q encoding. 
     1209     * @access private
     1210     * @return string
     1211     */
     1212    function EncodeQ ($str, $position = "text") {
     1213        // There should not be any EOL in the string
     1214        $encoded = preg_replace("[\r\n]", "", $str);
     1215
     1216        switch (strtolower($position)) {
     1217          case "phrase":
     1218            $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
     1219            break;
     1220          case "comment":
     1221            $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
     1222          case "text":
     1223          default:
     1224            // Replace every high ascii, control =, ? and _ characters
     1225            $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e',
     1226                  "'='.sprintf('%02X', ord('\\1'))", $encoded);
     1227            break;
     1228        }
     1229       
     1230        // Replace every spaces to _ (more readable than =20)
     1231        $encoded = str_replace(" ", "_", $encoded);
     1232
     1233        return $encoded;
     1234    }
     1235
     1236    /**
     1237     * Adds a string or binary attachment (non-filesystem) to the list.
     1238     * This method can be used to attach ascii or binary data,
     1239     * such as a BLOB record from a database.
     1240     * @param string $string String attachment data.
     1241     * @param string $filename Name of the attachment.
     1242     * @param string $encoding File encoding (see $Encoding).
     1243     * @param string $type File extension (MIME) type.
     1244     * @return void
     1245     */
     1246    function AddStringAttachment($string, $filename, $encoding = "base64",
     1247                                 $type = "application/octet-stream") {
     1248        // Append to $attachment array
     1249        $cur = count($this->attachment);
     1250        $this->attachment[$cur][0] = $string;
     1251        $this->attachment[$cur][1] = $filename;
     1252        $this->attachment[$cur][2] = $filename;
     1253        $this->attachment[$cur][3] = $encoding;
     1254        $this->attachment[$cur][4] = $type;
     1255        $this->attachment[$cur][5] = true; // isString
     1256        $this->attachment[$cur][6] = "attachment";
     1257        $this->attachment[$cur][7] = 0;
     1258    }
     1259   
     1260    /**
     1261     * Adds an embedded attachment.  This can include images, sounds, and
     1262     * just about any other document.  Make sure to set the $type to an
     1263     * image type.  For JPEG images use "image/jpeg" and for GIF images
     1264     * use "image/gif".
     1265     * @param string $path Path to the attachment.
     1266     * @param string $cid Content ID of the attachment.  Use this to identify
     1267     *        the Id for accessing the image in an HTML form.
     1268     * @param string $name Overrides the attachment name.
     1269     * @param string $encoding File encoding (see $Encoding).
     1270     * @param string $type File extension (MIME) type. 
     1271     * @return bool
     1272     */
     1273    function AddEmbeddedImage($path, $cid, $name = "", $encoding = "base64",
     1274                              $type = "application/octet-stream") {
     1275   
     1276        if(!@is_file($path))
     1277        {
     1278            $this->SetError($this->Lang("file_access") . $path);
     1279            return false;
     1280        }
     1281
     1282        $filename = basename($path);
     1283        if($name == "")
     1284            $name = $filename;
     1285
     1286        // Append to $attachment array
     1287        $cur = count($this->attachment);
     1288        $this->attachment[$cur][0] = $path;
     1289        $this->attachment[$cur][1] = $filename;
     1290        $this->attachment[$cur][2] = $name;
     1291        $this->attachment[$cur][3] = $encoding;
     1292        $this->attachment[$cur][4] = $type;
     1293        $this->attachment[$cur][5] = false; // isStringAttachment
     1294        $this->attachment[$cur][6] = "inline";
     1295        $this->attachment[$cur][7] = $cid;
     1296   
     1297        return true;
     1298    }
     1299   
     1300    /**
     1301     * Returns true if an inline attachment is present.
     1302     * @access private
     1303     * @return bool
     1304     */
     1305    function InlineImageExists() {
     1306        $result = false;
     1307        for($i = 0; $i < count($this->attachment); $i++)
     1308        {
     1309            if($this->attachment[$i][6] == "inline")
     1310            {
     1311                $result = true;
     1312                break;
     1313            }
     1314        }
     1315       
     1316        return $result;
     1317    }
     1318
     1319    /////////////////////////////////////////////////
     1320    // MESSAGE RESET METHODS
     1321    /////////////////////////////////////////////////
     1322
     1323    /**
     1324     * Clears all recipients assigned in the TO array.  Returns void.
     1325     * @return void
     1326     */
     1327    function ClearAddresses() {
     1328        $this->to = array();
     1329    }
     1330
     1331    /**
     1332     * Clears all recipients assigned in the CC array.  Returns void.
     1333     * @return void
     1334     */
     1335    function ClearCCs() {
     1336        $this->cc = array();
     1337    }
     1338
     1339    /**
     1340     * Clears all recipients assigned in the BCC array.  Returns void.
     1341     * @return void
     1342     */
     1343    function ClearBCCs() {
     1344        $this->bcc = array();
     1345    }
     1346
     1347    /**
     1348     * Clears all recipients assigned in the ReplyTo array.  Returns void.
     1349     * @return void
     1350     */
     1351    function ClearReplyTos() {
     1352        $this->ReplyTo = array();
     1353    }
     1354
     1355    /**
     1356     * Clears all recipients assigned in the TO, CC and BCC
     1357     * array.  Returns void.
     1358     * @return void
     1359     */
     1360    function ClearAllRecipients() {
     1361        $this->to = array();
     1362        $this->cc = array();
     1363        $this->bcc = array();
     1364    }
     1365
     1366    /**
     1367     * Clears all previously set filesystem, string, and binary
     1368     * attachments.  Returns void.
     1369     * @return void
     1370     */
     1371    function ClearAttachments() {
     1372        $this->attachment = array();
     1373    }
     1374
     1375    /**
     1376     * Clears all custom headers.  Returns void.
     1377     * @return void
     1378     */
     1379    function ClearCustomHeaders() {
     1380        $this->CustomHeader = array();
     1381    }
     1382
     1383
     1384    /////////////////////////////////////////////////
     1385    // MISCELLANEOUS METHODS
     1386    /////////////////////////////////////////////////
     1387
     1388    /**
     1389     * Adds the error message to the error container.
     1390     * Returns void.
     1391     * @access private
     1392     * @return void
     1393     */
     1394    function SetError($msg) {
     1395        $this->error_count++;
     1396        $this->ErrorInfo = $msg;
     1397    }
     1398
     1399    /**
     1400     * Returns the proper RFC 822 formatted date.
     1401     * @access private
     1402     * @return string
     1403     */
     1404    function RFCDate() {
     1405        $tz = date("Z");
     1406        $tzs = ($tz < 0) ? "-" : "+";
     1407        $tz = abs($tz);
     1408        $tz = ($tz/3600)*100 + ($tz%3600)/60;
     1409        $result = sprintf("%s %s%04d", date("D, j M Y H:i:s"), $tzs, $tz);
     1410
     1411        return $result;
     1412    }
     1413   
     1414    /**
     1415     * Returns the appropriate server variable.  Should work with both
     1416     * PHP 4.1.0+ as well as older versions.  Returns an empty string
     1417     * if nothing is found.
     1418     * @access private
     1419     * @return mixed
     1420     */
     1421    function ServerVar($varName) {
     1422        global $HTTP_SERVER_VARS;
     1423        global $HTTP_ENV_VARS;
     1424
     1425        if(!isset($_SERVER))
     1426        {
     1427            $_SERVER = $HTTP_SERVER_VARS;
     1428            if(!isset($_SERVER["REMOTE_ADDR"]))
     1429                $_SERVER = $HTTP_ENV_VARS; // must be Apache
     1430        }
     1431       
     1432        if(isset($_SERVER[$varName]))
     1433            return $_SERVER[$varName];
     1434        else
     1435            return "";
     1436    }
     1437
     1438    /**
     1439     * Returns the server hostname or 'localhost.localdomain' if unknown.
     1440     * @access private
     1441     * @return string
     1442     */
     1443    function ServerHostname() {
     1444        if ($this->Hostname != "")
     1445            $result = $this->Hostname;
     1446        elseif ($this->ServerVar('SERVER_NAME') != "")
     1447            $result = $this->ServerVar('SERVER_NAME');
     1448        else
     1449            $result = "localhost.localdomain";
     1450
     1451        return $result;
     1452    }
     1453
     1454    /**
     1455     * Returns a message in the appropriate language.
     1456     * @access private
     1457     * @return string
     1458     */
     1459    function Lang($key) {
     1460        if(count($this->language) < 1)
     1461            $this->SetLanguage("en"); // set the default language
     1462   
     1463        if(isset($this->language[$key]))
     1464            return $this->language[$key];
     1465        else
     1466            return "Language string failed to load: " . $key;
     1467    }
     1468   
     1469    /**
     1470     * Returns true if an error occurred.
     1471     * @return bool
     1472     */
     1473    function IsError() {
     1474        return ($this->error_count > 0);
     1475    }
     1476
     1477    /**
     1478     * Changes every end of line from CR or LF to CRLF. 
     1479     * @access private
     1480     * @return string
     1481     */
     1482    function FixEOL($str) {
     1483        $str = str_replace("\r\n", "\n", $str);
     1484        $str = str_replace("\r", "\n", $str);
     1485        $str = str_replace("\n", $this->LE, $str);
     1486        return $str;
     1487    }
     1488
     1489    /**
     1490     * Adds a custom header.
     1491     * @return void
     1492     */
     1493    function AddCustomHeader($custom_header) {
     1494        $this->CustomHeader[] = explode(":", $custom_header, 2);
     1495    }
     1496}
     1497
     1498?>
  • wp-includes/pluggable.php

     
    156156
    157157if ( !function_exists('wp_mail') ) :
    158158function wp_mail($to, $subject, $message, $headers = '') {
    159         if( $headers == '' ) {
     159        global $phpmailer;
     160        error_log("mailing $subject\n", 0);
     161        if ( !is_object( $phpmailer ) ) {
     162                require_once(ABSPATH . WPINC . '/class-phpmailer.php');
     163                require_once(ABSPATH . WPINC . '/class-smtp.php');
     164                $phpmailer = new PHPMailer();
     165        }
     166               
     167        $mail = compact('to', 'subject', 'message', 'headers');
     168        $mail = apply_filters('wp_mail', $mail);
     169        extract($mail);
     170
     171        if ( $headers == '' ) {
    160172                $headers = "MIME-Version: 1.0\n" .
    161173                        "From: wordpress@" . preg_replace('#^www\.#', '', strtolower($_SERVER['SERVER_NAME'])) . "\n" .
    162174                        "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"\n";
    163175        }
    164176
    165         return @mail($to, $subject, $message, $headers);
     177        $phpmailer->ClearAddresses();
     178        $phpmailer->ClearCCs();
     179        $phpmailer->ClearBCCs();
     180        $phpmailer->ClearReplyTos();
     181        $phpmailer->ClearAllRecipients();
     182        $phpmailer->ClearCustomHeaders();
     183
     184        $phpmailer->FromName = "WordPress";
     185        $phpmailer->AddAddress("$to", "");
     186        $phpmailer->Subject = $subject;
     187        $phpmailer->Body    = $message;
     188        $phpmailer->IsHTML(false);
     189        $phpmailer->IsMail(); // set mailer to use php mail()
     190
     191        do_action_ref_array('phpmailer_init', array(&$phpmailer));
     192
     193        $mailheaders = (array) explode( "\n", $headers );
     194        foreach ( $mailheaders as $line ) {
     195                $header = explode( ":", $line );
     196                switch ( trim( $header[0] ) ) {
     197                        case "From":
     198                                $from = trim( str_replace( '"', '', $header[1] ) );
     199                                if ( strpos( $from, '<' ) ) {
     200                                        $phpmailer->FromName = str_replace( '"', '', substr( $header[1], 0, strpos( $header[1], '<' ) - 1 ) );
     201                                        $from = trim( substr( $from, strpos( $from, '<' ) + 1 ) );
     202                                        $from = str_replace( '>', '', $from );
     203                                } else {
     204                                        $phpmailer->FromName = $from;
     205                                }
     206                                $phpmailer->From = trim( $from );
     207                                break;
     208                        default:
     209                                if ( $line != '' && $header[0] != 'MIME-Version' && $header[0] != 'Content-Type' )
     210                                        $phpmailer->AddCustomHeader( $line );
     211                                break;
     212                }
     213        }
     214
     215        $result = @$phpmailer->Send();
     216        error_log("mailing result $result\n", 0);
     217        return $result;
    166218}
    167219endif;
    168220
  • wp-includes/class-smtp.php

     
     1<?php
     2////////////////////////////////////////////////////
     3// SMTP - PHP SMTP class
     4//
     5// Version 1.02
     6//
     7// Define an SMTP class that can be used to connect
     8// and communicate with any SMTP server. It implements
     9// all the SMTP functions defined in RFC821 except TURN.
     10//
     11// Author: Chris Ryan
     12//
     13// License: LGPL, see LICENSE
     14////////////////////////////////////////////////////
     15
     16/**
     17 * SMTP is rfc 821 compliant and implements all the rfc 821 SMTP
     18 * commands except TURN which will always return a not implemented
     19 * error. SMTP also provides some utility methods for sending mail
     20 * to an SMTP server.
     21 * @package PHPMailer
     22 * @author Chris Ryan
     23 */
     24class SMTP
     25{
     26    /**
     27     *  SMTP server port
     28     *  @var int
     29     */
     30    var $SMTP_PORT = 25;
     31   
     32    /**
     33     *  SMTP reply line ending
     34     *  @var string
     35     */
     36    var $CRLF = "\r\n";
     37   
     38    /**
     39     *  Sets whether debugging is turned on
     40     *  @var bool
     41     */
     42    var $do_debug;       # the level of debug to perform
     43
     44    /**#@+
     45     * @access private
     46     */
     47    var $smtp_conn;      # the socket to the server
     48    var $error;          # error if any on the last call
     49    var $helo_rply;      # the reply the server sent to us for HELO
     50    /**#@-*/
     51
     52    /**
     53     * Initialize the class so that the data is in a known state.
     54     * @access public
     55     * @return void
     56     */
     57    function SMTP() {
     58        $this->smtp_conn = 0;
     59        $this->error = null;
     60        $this->helo_rply = null;
     61
     62        $this->do_debug = 0;
     63    }
     64
     65    /*************************************************************
     66     *                    CONNECTION FUNCTIONS                  *
     67     ***********************************************************/
     68
     69    /**
     70     * Connect to the server specified on the port specified.
     71     * If the port is not specified use the default SMTP_PORT.
     72     * If tval is specified then a connection will try and be
     73     * established with the server for that number of seconds.
     74     * If tval is not specified the default is 30 seconds to
     75     * try on the connection.
     76     *
     77     * SMTP CODE SUCCESS: 220
     78     * SMTP CODE FAILURE: 421
     79     * @access public
     80     * @return bool
     81     */
     82    function Connect($host,$port=0,$tval=30) {
     83        # set the error val to null so there is no confusion
     84        $this->error = null;
     85
     86        # make sure we are __not__ connected
     87        if($this->connected()) {
     88            # ok we are connected! what should we do?
     89            # for now we will just give an error saying we
     90            # are already connected
     91            $this->error =
     92                array("error" => "Already connected to a server");
     93            return false;
     94        }
     95
     96        if(empty($port)) {
     97            $port = $this->SMTP_PORT;
     98        }
     99
     100        #connect to the smtp server
     101        $this->smtp_conn = fsockopen($host,    # the host of the server
     102                                     $port,    # the port to use
     103                                     $errno,   # error number if any
     104                                     $errstr,  # error message if any
     105                                     $tval);   # give up after ? secs
     106        # verify we connected properly
     107        if(empty($this->smtp_conn)) {
     108            $this->error = array("error" => "Failed to connect to server",
     109                                 "errno" => $errno,
     110                                 "errstr" => $errstr);
     111            if($this->do_debug >= 1) {
     112                echo "SMTP -> ERROR: " . $this->error["error"] .
     113                         ": $errstr ($errno)" . $this->CRLF;
     114            }
     115            return false;
     116        }
     117
     118        # sometimes the SMTP server takes a little longer to respond
     119        # so we will give it a longer timeout for the first read
     120        // Windows still does not have support for this timeout function
     121        if(substr(PHP_OS, 0, 3) != "WIN")
     122           socket_set_timeout($this->smtp_conn, $tval, 0);
     123
     124        # get any announcement stuff
     125        $announce = $this->get_lines();
     126
     127        # set the timeout  of any socket functions at 1/10 of a second
     128        //if(function_exists("socket_set_timeout"))
     129        //   socket_set_timeout($this->smtp_conn, 0, 100000);
     130
     131        if($this->do_debug >= 2) {
     132            echo "SMTP -> FROM SERVER:" . $this->CRLF . $announce;
     133        }
     134
     135        return true;
     136    }
     137
     138    /**
     139     * Performs SMTP authentication.  Must be run after running the
     140     * Hello() method.  Returns true if successfully authenticated.
     141     * @access public
     142     * @return bool
     143     */
     144    function Authenticate($username, $password) {
     145        // Start authentication
     146        fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF);
     147
     148        $rply = $this->get_lines();
     149        $code = substr($rply,0,3);
     150
     151        if($code != 334) {
     152            $this->error =
     153                array("error" => "AUTH not accepted from server",
     154                      "smtp_code" => $code,
     155                      "smtp_msg" => substr($rply,4));
     156            if($this->do_debug >= 1) {
     157                echo "SMTP -> ERROR: " . $this->error["error"] .
     158                         ": " . $rply . $this->CRLF;
     159            }
     160            return false;
     161        }
     162
     163        // Send encoded username
     164        fputs($this->smtp_conn, base64_encode($username) . $this->CRLF);
     165
     166        $rply = $this->get_lines();
     167        $code = substr($rply,0,3);
     168
     169        if($code != 334) {
     170            $this->error =
     171                array("error" => "Username not accepted from server",
     172                      "smtp_code" => $code,
     173                      "smtp_msg" => substr($rply,4));
     174            if($this->do_debug >= 1) {
     175                echo "SMTP -> ERROR: " . $this->error["error"] .
     176                         ": " . $rply . $this->CRLF;
     177            }
     178            return false;
     179        }
     180
     181        // Send encoded password
     182        fputs($this->smtp_conn, base64_encode($password) . $this->CRLF);
     183
     184        $rply = $this->get_lines();
     185        $code = substr($rply,0,3);
     186
     187        if($code != 235) {
     188            $this->error =
     189                array("error" => "Password not accepted from server",
     190                      "smtp_code" => $code,
     191                      "smtp_msg" => substr($rply,4));
     192            if($this->do_debug >= 1) {
     193                echo "SMTP -> ERROR: " . $this->error["error"] .
     194                         ": " . $rply . $this->CRLF;
     195            }
     196            return false;
     197        }
     198
     199        return true;
     200    }
     201
     202    /**
     203     * Returns true if connected to a server otherwise false
     204     * @access private
     205     * @return bool
     206     */
     207    function Connected() {
     208        if(!empty($this->smtp_conn)) {
     209            $sock_status = socket_get_status($this->smtp_conn);
     210            if($sock_status["eof"]) {
     211                # hmm this is an odd situation... the socket is
     212                # valid but we aren't connected anymore
     213                if($this->do_debug >= 1) {
     214                    echo "SMTP -> NOTICE:" . $this->CRLF .
     215                         "EOF caught while checking if connected";
     216                }
     217                $this->Close();
     218                return false;
     219            }
     220            return true; # everything looks good
     221        }
     222        return false;
     223    }
     224
     225    /**
     226     * Closes the socket and cleans up the state of the class.
     227     * It is not considered good to use this function without
     228     * first trying to use QUIT.
     229     * @access public
     230     * @return void
     231     */
     232    function Close() {
     233        $this->error = null; # so there is no confusion
     234        $this->helo_rply = null;
     235        if(!empty($this->smtp_conn)) {
     236            # close the connection and cleanup
     237            fclose($this->smtp_conn);
     238            $this->smtp_conn = 0;
     239        }
     240    }
     241
     242
     243    /***************************************************************
     244     *                        SMTP COMMANDS                       *
     245     *************************************************************/
     246
     247    /**
     248     * Issues a data command and sends the msg_data to the server
     249     * finializing the mail transaction. $msg_data is the message
     250     * that is to be send with the headers. Each header needs to be
     251     * on a single line followed by a <CRLF> with the message headers
     252     * and the message body being seperated by and additional <CRLF>.
     253     *
     254     * Implements rfc 821: DATA <CRLF>
     255     *
     256     * SMTP CODE INTERMEDIATE: 354
     257     *     [data]
     258     *     <CRLF>.<CRLF>
     259     *     SMTP CODE SUCCESS: 250
     260     *     SMTP CODE FAILURE: 552,554,451,452
     261     * SMTP CODE FAILURE: 451,554
     262     * SMTP CODE ERROR  : 500,501,503,421
     263     * @access public
     264     * @return bool
     265     */
     266    function Data($msg_data) {
     267        $this->error = null; # so no confusion is caused
     268
     269        if(!$this->connected()) {
     270            $this->error = array(
     271                    "error" => "Called Data() without being connected");
     272            return false;
     273        }
     274
     275        fputs($this->smtp_conn,"DATA" . $this->CRLF);
     276
     277        $rply = $this->get_lines();
     278        $code = substr($rply,0,3);
     279
     280        if($this->do_debug >= 2) {
     281            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
     282        }
     283
     284        if($code != 354) {
     285            $this->error =
     286                array("error" => "DATA command not accepted from server",
     287                      "smtp_code" => $code,
     288                      "smtp_msg" => substr($rply,4));
     289            if($this->do_debug >= 1) {
     290                echo "SMTP -> ERROR: " . $this->error["error"] .
     291                         ": " . $rply . $this->CRLF;
     292            }
     293            return false;
     294        }
     295
     296        # the server is ready to accept data!
     297        # according to rfc 821 we should not send more than 1000
     298        # including the CRLF
     299        # characters on a single line so we will break the data up
     300        # into lines by \r and/or \n then if needed we will break
     301        # each of those into smaller lines to fit within the limit.
     302        # in addition we will be looking for lines that start with
     303        # a period '.' and append and additional period '.' to that
     304        # line. NOTE: this does not count towards are limit.
     305
     306        # normalize the line breaks so we know the explode works
     307        $msg_data = str_replace("\r\n","\n",$msg_data);
     308        $msg_data = str_replace("\r","\n",$msg_data);
     309        $lines = explode("\n",$msg_data);
     310
     311        # we need to find a good way to determine is headers are
     312        # in the msg_data or if it is a straight msg body
     313        # currently I'm assuming rfc 822 definitions of msg headers
     314        # and if the first field of the first line (':' sperated)
     315        # does not contain a space then it _should_ be a header
     316        # and we can process all lines before a blank "" line as
     317        # headers.
     318        $field = substr($lines[0],0,strpos($lines[0],":"));
     319        $in_headers = false;
     320        if(!empty($field) && !strstr($field," ")) {
     321            $in_headers = true;
     322        }
     323
     324        $max_line_length = 998; # used below; set here for ease in change
     325
     326        while(list(,$line) = @each($lines)) {
     327            $lines_out = null;
     328            if($line == "" && $in_headers) {
     329                $in_headers = false;
     330            }
     331            # ok we need to break this line up into several
     332            # smaller lines
     333            while(strlen($line) > $max_line_length) {
     334                $pos = strrpos(substr($line,0,$max_line_length)," ");
     335
     336                # Patch to fix DOS attack
     337                if(!$pos) {
     338                    $pos = $max_line_length - 1;
     339                }
     340
     341                $lines_out[] = substr($line,0,$pos);
     342                $line = substr($line,$pos + 1);
     343                # if we are processing headers we need to
     344                # add a LWSP-char to the front of the new line
     345                # rfc 822 on long msg headers
     346                if($in_headers) {
     347                    $line = "\t" . $line;
     348                }
     349            }
     350            $lines_out[] = $line;
     351
     352            # now send the lines to the server
     353            while(list(,$line_out) = @each($lines_out)) {
     354                if(strlen($line_out) > 0)
     355                {
     356                    if(substr($line_out, 0, 1) == ".") {
     357                        $line_out = "." . $line_out;
     358                    }
     359                }
     360                fputs($this->smtp_conn,$line_out . $this->CRLF);
     361            }
     362        }
     363
     364        # ok all the message data has been sent so lets get this
     365        # over with aleady
     366        fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF);
     367
     368        $rply = $this->get_lines();
     369        $code = substr($rply,0,3);
     370
     371        if($this->do_debug >= 2) {
     372            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
     373        }
     374
     375        if($code != 250) {
     376            $this->error =
     377                array("error" => "DATA not accepted from server",
     378                      "smtp_code" => $code,
     379                      "smtp_msg" => substr($rply,4));
     380            if($this->do_debug >= 1) {
     381                echo "SMTP -> ERROR: " . $this->error["error"] .
     382                         ": " . $rply . $this->CRLF;
     383            }
     384            return false;
     385        }
     386        return true;
     387    }
     388
     389    /**
     390     * Expand takes the name and asks the server to list all the
     391     * people who are members of the _list_. Expand will return
     392     * back and array of the result or false if an error occurs.
     393     * Each value in the array returned has the format of:
     394     *     [ <full-name> <sp> ] <path>
     395     * The definition of <path> is defined in rfc 821
     396     *
     397     * Implements rfc 821: EXPN <SP> <string> <CRLF>
     398     *
     399     * SMTP CODE SUCCESS: 250
     400     * SMTP CODE FAILURE: 550
     401     * SMTP CODE ERROR  : 500,501,502,504,421
     402     * @access public
     403     * @return string array
     404     */
     405    function Expand($name) {
     406        $this->error = null; # so no confusion is caused
     407
     408        if(!$this->connected()) {
     409            $this->error = array(
     410                    "error" => "Called Expand() without being connected");
     411            return false;
     412        }
     413
     414        fputs($this->smtp_conn,"EXPN " . $name . $this->CRLF);
     415
     416        $rply = $this->get_lines();
     417        $code = substr($rply,0,3);
     418
     419        if($this->do_debug >= 2) {
     420            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
     421        }
     422
     423        if($code != 250) {
     424            $this->error =
     425                array("error" => "EXPN not accepted from server",
     426                      "smtp_code" => $code,
     427                      "smtp_msg" => substr($rply,4));
     428            if($this->do_debug >= 1) {
     429                echo "SMTP -> ERROR: " . $this->error["error"] .
     430                         ": " . $rply . $this->CRLF;
     431            }
     432            return false;
     433        }
     434
     435        # parse the reply and place in our array to return to user
     436        $entries = explode($this->CRLF,$rply);
     437        while(list(,$l) = @each($entries)) {
     438            $list[] = substr($l,4);
     439        }
     440
     441        return $list;
     442    }
     443
     444    /**
     445     * Sends the HELO command to the smtp server.
     446     * This makes sure that we and the server are in
     447     * the same known state.
     448     *
     449     * Implements from rfc 821: HELO <SP> <domain> <CRLF>
     450     *
     451     * SMTP CODE SUCCESS: 250
     452     * SMTP CODE ERROR  : 500, 501, 504, 421
     453     * @access public
     454     * @return bool
     455     */
     456    function Hello($host="") {
     457        $this->error = null; # so no confusion is caused
     458
     459        if(!$this->connected()) {
     460            $this->error = array(
     461                    "error" => "Called Hello() without being connected");
     462            return false;
     463        }
     464
     465        # if a hostname for the HELO wasn't specified determine
     466        # a suitable one to send
     467        if(empty($host)) {
     468            # we need to determine some sort of appopiate default
     469            # to send to the server
     470            $host = "localhost";
     471        }
     472
     473        // Send extended hello first (RFC 2821)
     474        if(!$this->SendHello("EHLO", $host))
     475        {
     476            if(!$this->SendHello("HELO", $host))
     477                return false;
     478        }
     479
     480        return true;
     481    }
     482
     483    /**
     484     * Sends a HELO/EHLO command.
     485     * @access private
     486     * @return bool
     487     */
     488    function SendHello($hello, $host) {
     489        fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF);
     490
     491        $rply = $this->get_lines();
     492        $code = substr($rply,0,3);
     493
     494        if($this->do_debug >= 2) {
     495            echo "SMTP -> FROM SERVER: " . $this->CRLF . $rply;
     496        }
     497
     498        if($code != 250) {
     499            $this->error =
     500                array("error" => $hello . " not accepted from server",
     501                      "smtp_code" => $code,
     502                      "smtp_msg" => substr($rply,4));
     503            if($this->do_debug >= 1) {
     504                echo "SMTP -> ERROR: " . $this->error["error"] .
     505                         ": " . $rply . $this->CRLF;
     506            }
     507            return false;
     508        }
     509
     510        $this->helo_rply = $rply;
     511       
     512        return true;
     513    }
     514
     515    /**
     516     * Gets help information on the keyword specified. If the keyword
     517     * is not specified then returns generic help, ussually contianing
     518     * A list of keywords that help is available on. This function
     519     * returns the results back to the user. It is up to the user to
     520     * handle the returned data. If an error occurs then false is
     521     * returned with $this->error set appropiately.
     522     *
     523     * Implements rfc 821: HELP [ <SP> <string> ] <CRLF>
     524     *
     525     * SMTP CODE SUCCESS: 211,214
     526     * SMTP CODE ERROR  : 500,501,502,504,421
     527     * @access public
     528     * @return string
     529     */
     530    function Help($keyword="") {
     531        $this->error = null; # to avoid confusion
     532
     533        if(!$this->connected()) {
     534            $this->error = array(
     535                    "error" => "Called Help() without being connected");
     536            return false;
     537        }
     538
     539        $extra = "";
     540        if(!empty($keyword)) {
     541            $extra = " " . $keyword;
     542        }
     543
     544        fputs($this->smtp_conn,"HELP" . $extra . $this->CRLF);
     545
     546        $rply = $this->get_lines();
     547        $code = substr($rply,0,3);
     548
     549        if($this->do_debug >= 2) {
     550            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
     551        }
     552
     553        if($code != 211 && $code != 214) {
     554            $this->error =
     555                array("error" => "HELP not accepted from server",
     556                      "smtp_code" => $code,
     557                      "smtp_msg" => substr($rply,4));
     558            if($this->do_debug >= 1) {
     559                echo "SMTP -> ERROR: " . $this->error["error"] .
     560                         ": " . $rply . $this->CRLF;
     561            }
     562            return false;
     563        }
     564
     565        return $rply;
     566    }
     567
     568    /**
     569     * Starts a mail transaction from the email address specified in
     570     * $from. Returns true if successful or false otherwise. If True
     571     * the mail transaction is started and then one or more Recipient
     572     * commands may be called followed by a Data command.
     573     *
     574     * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
     575     *
     576     * SMTP CODE SUCCESS: 250
     577     * SMTP CODE SUCCESS: 552,451,452
     578     * SMTP CODE SUCCESS: 500,501,421
     579     * @access public
     580     * @return bool
     581     */
     582    function Mail($from) {
     583        $this->error = null; # so no confusion is caused
     584
     585        if(!$this->connected()) {
     586            $this->error = array(
     587                    "error" => "Called Mail() without being connected");
     588            return false;
     589        }
     590
     591        fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $this->CRLF);
     592
     593        $rply = $this->get_lines();
     594        $code = substr($rply,0,3);
     595
     596        if($this->do_debug >= 2) {
     597            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
     598        }
     599
     600        if($code != 250) {
     601            $this->error =
     602                array("error" => "MAIL not accepted from server",
     603                      "smtp_code" => $code,
     604                      "smtp_msg" => substr($rply,4));
     605            if($this->do_debug >= 1) {
     606                echo "SMTP -> ERROR: " . $this->error["error"] .
     607                         ": " . $rply . $this->CRLF;
     608            }
     609            return false;
     610        }
     611        return true;
     612    }
     613
     614    /**
     615     * Sends the command NOOP to the SMTP server.
     616     *
     617     * Implements from rfc 821: NOOP <CRLF>
     618     *
     619     * SMTP CODE SUCCESS: 250
     620     * SMTP CODE ERROR  : 500, 421
     621     * @access public
     622     * @return bool
     623     */
     624    function Noop() {
     625        $this->error = null; # so no confusion is caused
     626
     627        if(!$this->connected()) {
     628            $this->error = array(
     629                    "error" => "Called Noop() without being connected");
     630            return false;
     631        }
     632
     633        fputs($this->smtp_conn,"NOOP" . $this->CRLF);
     634
     635        $rply = $this->get_lines();
     636        $code = substr($rply,0,3);
     637
     638        if($this->do_debug >= 2) {
     639            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
     640        }
     641
     642        if($code != 250) {
     643            $this->error =
     644                array("error" => "NOOP not accepted from server",
     645                      "smtp_code" => $code,
     646                      "smtp_msg" => substr($rply,4));
     647            if($this->do_debug >= 1) {
     648                echo "SMTP -> ERROR: " . $this->error["error"] .
     649                         ": " . $rply . $this->CRLF;
     650            }
     651            return false;
     652        }
     653        return true;
     654    }
     655
     656    /**
     657     * Sends the quit command to the server and then closes the socket
     658     * if there is no error or the $close_on_error argument is true.
     659     *
     660     * Implements from rfc 821: QUIT <CRLF>
     661     *
     662     * SMTP CODE SUCCESS: 221
     663     * SMTP CODE ERROR  : 500
     664     * @access public
     665     * @return bool
     666     */
     667    function Quit($close_on_error=true) {
     668        $this->error = null; # so there is no confusion
     669
     670        if(!$this->connected()) {
     671            $this->error = array(
     672                    "error" => "Called Quit() without being connected");
     673            return false;
     674        }
     675
     676        # send the quit command to the server
     677        fputs($this->smtp_conn,"quit" . $this->CRLF);
     678
     679        # get any good-bye messages
     680        $byemsg = $this->get_lines();
     681
     682        if($this->do_debug >= 2) {
     683            echo "SMTP -> FROM SERVER:" . $this->CRLF . $byemsg;
     684        }
     685
     686        $rval = true;
     687        $e = null;
     688
     689        $code = substr($byemsg,0,3);
     690        if($code != 221) {
     691            # use e as a tmp var cause Close will overwrite $this->error
     692            $e = array("error" => "SMTP server rejected quit command",
     693                       "smtp_code" => $code,
     694                       "smtp_rply" => substr($byemsg,4));
     695            $rval = false;
     696            if($this->do_debug >= 1) {
     697                echo "SMTP -> ERROR: " . $e["error"] . ": " .
     698                         $byemsg . $this->CRLF;
     699            }
     700        }
     701
     702        if(empty($e) || $close_on_error) {
     703            $this->Close();
     704        }
     705
     706        return $rval;
     707    }
     708
     709    /**
     710     * Sends the command RCPT to the SMTP server with the TO: argument of $to.
     711     * Returns true if the recipient was accepted false if it was rejected.
     712     *
     713     * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
     714     *
     715     * SMTP CODE SUCCESS: 250,251
     716     * SMTP CODE FAILURE: 550,551,552,553,450,451,452
     717     * SMTP CODE ERROR  : 500,501,503,421
     718     * @access public
     719     * @return bool
     720     */
     721    function Recipient($to) {
     722        $this->error = null; # so no confusion is caused
     723
     724        if(!$this->connected()) {
     725            $this->error = array(
     726                    "error" => "Called Recipient() without being connected");
     727            return false;
     728        }
     729
     730        fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF);
     731
     732        $rply = $this->get_lines();
     733        $code = substr($rply,0,3);
     734
     735        if($this->do_debug >= 2) {
     736            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
     737        }
     738
     739        if($code != 250 && $code != 251) {
     740            $this->error =
     741                array("error" => "RCPT not accepted from server",
     742                      "smtp_code" => $code,
     743                      "smtp_msg" => substr($rply,4));
     744            if($this->do_debug >= 1) {
     745                echo "SMTP -> ERROR: " . $this->error["error"] .
     746                         ": " . $rply . $this->CRLF;
     747            }
     748            return false;
     749        }
     750        return true;
     751    }
     752
     753    /**
     754     * Sends the RSET command to abort and transaction that is
     755     * currently in progress. Returns true if successful false
     756     * otherwise.
     757     *
     758     * Implements rfc 821: RSET <CRLF>
     759     *
     760     * SMTP CODE SUCCESS: 250
     761     * SMTP CODE ERROR  : 500,501,504,421
     762     * @access public
     763     * @return bool
     764     */
     765    function Reset() {
     766        $this->error = null; # so no confusion is caused
     767
     768        if(!$this->connected()) {
     769            $this->error = array(
     770                    "error" => "Called Reset() without being connected");
     771            return false;
     772        }
     773
     774        fputs($this->smtp_conn,"RSET" . $this->CRLF);
     775
     776        $rply = $this->get_lines();
     777        $code = substr($rply,0,3);
     778
     779        if($this->do_debug >= 2) {
     780            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
     781        }
     782
     783        if($code != 250) {
     784            $this->error =
     785                array("error" => "RSET failed",
     786                      "smtp_code" => $code,
     787                      "smtp_msg" => substr($rply,4));
     788            if($this->do_debug >= 1) {
     789                echo "SMTP -> ERROR: " . $this->error["error"] .
     790                         ": " . $rply . $this->CRLF;
     791            }
     792            return false;
     793        }
     794
     795        return true;
     796    }
     797
     798    /**
     799     * Starts a mail transaction from the email address specified in
     800     * $from. Returns true if successful or false otherwise. If True
     801     * the mail transaction is started and then one or more Recipient
     802     * commands may be called followed by a Data command. This command
     803     * will send the message to the users terminal if they are logged
     804     * in.
     805     *
     806     * Implements rfc 821: SEND <SP> FROM:<reverse-path> <CRLF>
     807     *
     808     * SMTP CODE SUCCESS: 250
     809     * SMTP CODE SUCCESS: 552,451,452
     810     * SMTP CODE SUCCESS: 500,501,502,421
     811     * @access public
     812     * @return bool
     813     */
     814    function Send($from) {
     815        $this->error = null; # so no confusion is caused
     816
     817        if(!$this->connected()) {
     818            $this->error = array(
     819                    "error" => "Called Send() without being connected");
     820            return false;
     821        }
     822
     823        fputs($this->smtp_conn,"SEND FROM:" . $from . $this->CRLF);
     824
     825        $rply = $this->get_lines();
     826        $code = substr($rply,0,3);
     827
     828        if($this->do_debug >= 2) {
     829            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
     830        }
     831
     832        if($code != 250) {
     833            $this->error =
     834                array("error" => "SEND not accepted from server",
     835                      "smtp_code" => $code,
     836                      "smtp_msg" => substr($rply,4));
     837            if($this->do_debug >= 1) {
     838                echo "SMTP -> ERROR: " . $this->error["error"] .
     839                         ": " . $rply . $this->CRLF;
     840            }
     841            return false;
     842        }
     843        return true;
     844    }
     845
     846    /**
     847     * Starts a mail transaction from the email address specified in
     848     * $from. Returns true if successful or false otherwise. If True
     849     * the mail transaction is started and then one or more Recipient
     850     * commands may be called followed by a Data command. This command
     851     * will send the message to the users terminal if they are logged
     852     * in and send them an email.
     853     *
     854     * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
     855     *
     856     * SMTP CODE SUCCESS: 250
     857     * SMTP CODE SUCCESS: 552,451,452
     858     * SMTP CODE SUCCESS: 500,501,502,421
     859     * @access public
     860     * @return bool
     861     */
     862    function SendAndMail($from) {
     863        $this->error = null; # so no confusion is caused
     864
     865        if(!$this->connected()) {
     866            $this->error = array(
     867                "error" => "Called SendAndMail() without being connected");
     868            return false;
     869        }
     870
     871        fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF);
     872
     873        $rply = $this->get_lines();
     874        $code = substr($rply,0,3);
     875
     876        if($this->do_debug >= 2) {
     877            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
     878        }
     879
     880        if($code != 250) {
     881            $this->error =
     882                array("error" => "SAML not accepted from server",
     883                      "smtp_code" => $code,
     884                      "smtp_msg" => substr($rply,4));
     885            if($this->do_debug >= 1) {
     886                echo "SMTP -> ERROR: " . $this->error["error"] .
     887                         ": " . $rply . $this->CRLF;
     888            }
     889            return false;
     890        }
     891        return true;
     892    }
     893
     894    /**
     895     * Starts a mail transaction from the email address specified in
     896     * $from. Returns true if successful or false otherwise. If True
     897     * the mail transaction is started and then one or more Recipient
     898     * commands may be called followed by a Data command. This command
     899     * will send the message to the users terminal if they are logged
     900     * in or mail it to them if they are not.
     901     *
     902     * Implements rfc 821: SOML <SP> FROM:<reverse-path> <CRLF>
     903     *
     904     * SMTP CODE SUCCESS: 250
     905     * SMTP CODE SUCCESS: 552,451,452
     906     * SMTP CODE SUCCESS: 500,501,502,421
     907     * @access public
     908     * @return bool
     909     */
     910    function SendOrMail($from) {
     911        $this->error = null; # so no confusion is caused
     912
     913        if(!$this->connected()) {
     914            $this->error = array(
     915                "error" => "Called SendOrMail() without being connected");
     916            return false;
     917        }
     918
     919        fputs($this->smtp_conn,"SOML FROM:" . $from . $this->CRLF);
     920
     921        $rply = $this->get_lines();
     922        $code = substr($rply,0,3);
     923
     924        if($this->do_debug >= 2) {
     925            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
     926        }
     927
     928        if($code != 250) {
     929            $this->error =
     930                array("error" => "SOML not accepted from server",
     931                      "smtp_code" => $code,
     932                      "smtp_msg" => substr($rply,4));
     933            if($this->do_debug >= 1) {
     934                echo "SMTP -> ERROR: " . $this->error["error"] .
     935                         ": " . $rply . $this->CRLF;
     936            }
     937            return false;
     938        }
     939        return true;
     940    }
     941
     942    /**
     943     * This is an optional command for SMTP that this class does not
     944     * support. This method is here to make the RFC821 Definition
     945     * complete for this class and __may__ be implimented in the future
     946     *
     947     * Implements from rfc 821: TURN <CRLF>
     948     *
     949     * SMTP CODE SUCCESS: 250
     950     * SMTP CODE FAILURE: 502
     951     * SMTP CODE ERROR  : 500, 503
     952     * @access public
     953     * @return bool
     954     */
     955    function Turn() {
     956        $this->error = array("error" => "This method, TURN, of the SMTP ".
     957                                        "is not implemented");
     958        if($this->do_debug >= 1) {
     959            echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF;
     960        }
     961        return false;
     962    }
     963
     964    /**
     965     * Verifies that the name is recognized by the server.
     966     * Returns false if the name could not be verified otherwise
     967     * the response from the server is returned.
     968     *
     969     * Implements rfc 821: VRFY <SP> <string> <CRLF>
     970     *
     971     * SMTP CODE SUCCESS: 250,251
     972     * SMTP CODE FAILURE: 550,551,553
     973     * SMTP CODE ERROR  : 500,501,502,421
     974     * @access public
     975     * @return int
     976     */
     977    function Verify($name) {
     978        $this->error = null; # so no confusion is caused
     979
     980        if(!$this->connected()) {
     981            $this->error = array(
     982                    "error" => "Called Verify() without being connected");
     983            return false;
     984        }
     985
     986        fputs($this->smtp_conn,"VRFY " . $name . $this->CRLF);
     987
     988        $rply = $this->get_lines();
     989        $code = substr($rply,0,3);
     990
     991        if($this->do_debug >= 2) {
     992            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
     993        }
     994
     995        if($code != 250 && $code != 251) {
     996            $this->error =
     997                array("error" => "VRFY failed on name '$name'",
     998                      "smtp_code" => $code,
     999                      "smtp_msg" => substr($rply,4));
     1000            if($this->do_debug >= 1) {
     1001                echo "SMTP -> ERROR: " . $this->error["error"] .
     1002                         ": " . $rply . $this->CRLF;
     1003            }
     1004            return false;
     1005        }
     1006        return $rply;
     1007    }
     1008
     1009    /*******************************************************************
     1010     *                       INTERNAL FUNCTIONS                       *
     1011     ******************************************************************/
     1012
     1013    /**
     1014     * Read in as many lines as possible
     1015     * either before eof or socket timeout occurs on the operation.
     1016     * With SMTP we can tell if we have more lines to read if the
     1017     * 4th character is '-' symbol. If it is a space then we don't
     1018     * need to read anything else.
     1019     * @access private
     1020     * @return string
     1021     */
     1022    function get_lines() {
     1023        $data = "";
     1024        while($str = fgets($this->smtp_conn,515)) {
     1025            if($this->do_debug >= 4) {
     1026                echo "SMTP -> get_lines(): \$data was \"$data\"" .
     1027                         $this->CRLF;
     1028                echo "SMTP -> get_lines(): \$str is \"$str\"" .
     1029                         $this->CRLF;
     1030            }
     1031            $data .= $str;
     1032            if($this->do_debug >= 4) {
     1033                echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF;
     1034            }
     1035            # if the 4th character is a space then we are done reading
     1036            # so just break the loop
     1037            if(substr($str,3,1) == " ") { break; }
     1038        }
     1039        return $data;
     1040    }
     1041
     1042}
     1043
     1044
     1045 ?>