[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/lib/phpmailer/ -> class.smtp.php (source)

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


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1