[ Index ]

PHP Cross Reference of MyBB

title

Body

[close]

/inc/mailhandlers/ -> smtp.php (source)

   1  <?php
   2  /**
   3   * MyBB 1.6
   4   * Copyright 2010 MyBB Group, All Rights Reserved
   5   *
   6   * Website: http://mybb.com
   7   * License: http://mybb.com/about/license
   8   *
   9   * $Id$
  10   */
  11  
  12   // Disallow direct access to this file for security reasons
  13  if(!defined("IN_MYBB"))
  14  {
  15      die("Direct initialization of this file is not allowed.<br /><br />Please make sure IN_MYBB is defined.");
  16  }
  17  
  18  /**
  19   * SMTP mail handler class.
  20   */
  21   
  22  if(!defined('MYBB_SSL'))
  23  {
  24      define('MYBB_SSL', 1);
  25  }
  26  
  27  if(!defined('MYBB_TLS'))
  28  {
  29      define('MYBB_TLS', 2);
  30  }
  31  
  32  class SmtpMail extends MailHandler
  33  {
  34      /**
  35       * The SMTP connection.
  36       *
  37       * @var resource
  38       */
  39      public $connection;
  40  
  41      /**
  42       * SMTP username.
  43       *
  44       * @var string
  45       */
  46      public $username = '';
  47  
  48      /**
  49       * SMTP password.
  50       *
  51       * @var string
  52       */
  53      public $password = '';
  54  
  55      /**
  56       * Hello string sent to the smtp server with either HELO or EHLO.
  57       *
  58       * @var string
  59       */
  60      public $helo = 'localhost';
  61  
  62      /**
  63       * User authenticated or not.
  64       *
  65       * @var boolean
  66       */
  67      public $authenticated = false;
  68  
  69      /**
  70       * How long before timeout.
  71       *
  72       * @var integer
  73       */
  74      public $timeout = 5;
  75  
  76      /**
  77       * SMTP status.
  78       *
  79       * @var integer
  80       */
  81      public $status = 0;
  82  
  83      /**
  84       * SMTP default port.
  85       *
  86       * @var integer
  87       */
  88      public $port = 25;
  89      
  90      /**
  91       * SMTP default secure port.
  92       *
  93       * @var integer
  94       */
  95      public $secure_port = 465;
  96  
  97      /**
  98       * SMTP host.
  99       *
 100       * @var string
 101       */
 102      public $host = '';
 103      
 104      /**
 105       * The last received response from the SMTP server.
 106       *
 107       * @var string
 108       */
 109      public $data = '';
 110      
 111      /**
 112       * The last received response code from the SMTP server.
 113       *
 114       * @var string
 115       */
 116      public $code = 0;
 117      
 118      /**
 119       * The last received error message from the SMTP server.
 120       *
 121       * @var string
 122       */
 123      public $last_error = '';
 124      
 125      /**
 126       * Are we keeping the connection to the SMTP server alive?
 127       *
 128       * @var boolean
 129       */
 130      public $keep_alive = false;
 131  
 132      /**
 133       * Whether to use TLS encryption.
 134       *
 135       * @var boolean
 136       */
 137      public $use_tls = false;
 138  
 139  	function __construct()
 140      {
 141          global $mybb;
 142  
 143          $protocol = '';
 144          switch($mybb->settings['secure_smtp'])
 145          {
 146              case MYBB_SSL:
 147                  $protocol = 'ssl://';
 148                  break;
 149              case MYBB_TLS:
 150                  $this->use_tls = true;
 151                  break;
 152          }
 153  
 154          if(empty($mybb->settings['smtp_host']))
 155          {
 156              $this->host = @ini_get('SMTP');
 157          }
 158          else
 159          {
 160              $this->host = $mybb->settings['smtp_host'];
 161          }
 162          
 163          $this->helo = $this->host;
 164  
 165          $this->host = $protocol . $this->host;
 166  
 167          if(empty($mybb->settings['smtp_port']) && !empty($protocol) && !@ini_get('smtp_port'))
 168          {
 169              $this->port = $this->secure_port;
 170          }
 171          else if(empty($mybb->settings['smtp_port']) && @ini_get('smtp_port'))
 172          {
 173              $this->port = @ini_get('smtp_port');
 174          }
 175          else if(!empty($mybb->settings['smtp_port']))
 176          {
 177              $this->port = $mybb->settings['smtp_port'];
 178          }
 179      
 180          $this->password = $mybb->settings['smtp_pass'];
 181          $this->username = $mybb->settings['smtp_user'];
 182      }
 183  
 184      /**
 185       * Sends the email.
 186       *
 187       * @return true/false whether or not the email got sent or not.
 188       */
 189  	function send()
 190      {
 191          global $lang, $mybb;
 192  
 193          if(!$this->connected())
 194          {
 195              if(!$this->connect())
 196              {
 197                  $this->close();
 198              }
 199          }
 200          
 201          if($this->connected())
 202          {
 203              if(!$this->send_data('MAIL FROM:<'.$this->from.'>', '250'))
 204              {
 205                  $this->fatal_error("The mail server does not understand the MAIL FROM command. Reason: ".$this->get_error());
 206                  return false;
 207              }
 208              
 209              // Loop through recipients
 210              $emails = explode(',', $this->to);
 211              foreach($emails as $to)
 212              {
 213                  $to = trim($to);
 214                  if(!$this->send_data('RCPT TO:<'.$to.'>', '250'))
 215                  {
 216                      $this->fatal_error("The mail server does not understand the RCPT TO command. Reason: ".$this->get_error());
 217                      return false;
 218                  }
 219              }
 220  
 221              if($this->send_data('DATA', '354'))
 222              {
 223                  $this->send_data('Date: ' . gmdate('r'));
 224                  $this->send_data('To: ' . $this->to);
 225                  
 226                  $this->send_data('Subject: ' . $this->subject);
 227  
 228                  // Only send additional headers if we've got any
 229                  if(trim($this->headers))
 230                  {
 231                      $this->send_data(trim($this->headers));
 232                  }
 233                  
 234                  $this->send_data("");
 235  
 236                  // Queue the actual message
 237                  $this->message = str_replace("\n.", "\n..", $this->message);
 238                  $this->send_data($this->message);
 239              }
 240              else
 241              {
 242                  $this->fatal_error("The mail server did not understand the DATA command");
 243                  return false;
 244              }
 245  
 246              $this->send_data('.', '250');
 247  
 248              if(!$this->keep_alive)
 249              {
 250                  $this->close();
 251              }
 252              return true;
 253          }
 254          else
 255          {
 256              return false;
 257          }
 258      }
 259  
 260      /**
 261       * Connect to the SMTP server.
 262       *
 263       * @return boolean True if connection was successful
 264       */
 265  	function connect()
 266      {
 267          global $lang, $mybb;
 268  
 269          $this->connection = @fsockopen($this->host, $this->port, $error_number, $error_string, $this->timeout);
 270          
 271          // DIRECTORY_SEPARATOR checks if running windows
 272          if(function_exists('stream_set_timeout') && DIRECTORY_SEPARATOR != '\\')
 273          {
 274              @stream_set_timeout($this->connection, $this->timeout, 0);
 275          }
 276  
 277          if(is_resource($this->connection))
 278          {
 279              $this->status = 1;
 280              $this->get_data();
 281              if(!$this->check_status('220'))
 282              {
 283                  $this->fatal_error("The mail server is not ready, it did not respond with a 220 status message.");
 284                  return false;
 285              }
 286  
 287              if($this->use_tls || (!empty($this->username) && !empty($this->password)))
 288              {
 289                  $helo = 'EHLO';
 290              }
 291              else
 292              {
 293                  $helo = 'HELO';
 294              }
 295  
 296              $data = $this->send_data("{$helo} {$this->helo}", '250');
 297              if(!$data)
 298              {
 299                  $this->fatal_error("The server did not understand the {$helo} command");
 300                  return false;
 301              }
 302  
 303              if($this->use_tls && preg_match("#250( |-)STARTTLS#mi", $data))
 304              {
 305                  if(!$this->send_data('STARTTLS', '220'))
 306                  {
 307                          $this->fatal_error("The server did not understand the STARTTLS command. Reason: ".$this->get_error());
 308                      return false;
 309                  }
 310                  if(!@stream_socket_enable_crypto($this->connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT))
 311                  {
 312                      $this->fatal_error("Failed to start TLS encryption");
 313                      return false;
 314                  }
 315                  // Resend EHLO to get updated service list
 316                  $data = $this->send_data("{$helo} {$this->helo}", '250');
 317                  if(!$data)
 318                  {
 319                      $this->fatal_error("The server did not understand the EHLO command");
 320                      return false;
 321                  }
 322              }
 323  
 324              if(!empty($this->username) && !empty($this->password))
 325              {
 326                  preg_match("#250( |-)AUTH( |=)(.+)$#mi", $data, $matches);
 327                  if(!$this->auth($matches[3]))
 328                  {
 329                      return false;
 330                  }
 331              }
 332              return true;
 333          }
 334          else
 335          {
 336              $this->fatal_error("Unable to connect to the mail server with the given details. Reason: {$error_number}: {$error_string}");
 337              return false;
 338          }
 339      }
 340      
 341      /**
 342       * Authenticate against the SMTP server.
 343       *
 344       * @param string A list of authentication methods supported by the server
 345       * @return boolean True on success
 346       */
 347  	function auth($auth_methods)
 348      {
 349          global $lang, $mybb;
 350          
 351          $auth_methods = explode(" ", trim($auth_methods));
 352          
 353          if(in_array("LOGIN", $auth_methods))
 354          {
 355              if(!$this->send_data("AUTH LOGIN", 334))
 356              {
 357                  if($this->code == 503)
 358                  {
 359                      return true;
 360                  }
 361                  $this->fatal_error("The SMTP server did not respond correctly to the AUTH LOGIN command");
 362                  return false;
 363              }
 364              
 365              if(!$this->send_data(base64_encode($this->username), '334'))
 366              {
 367                  $this->fatal_error("The SMTP server rejected the supplied SMTP username. Reason: ".$this->get_error());
 368                  return false;
 369              }
 370              
 371              if(!$this->send_data(base64_encode($this->password), '235'))
 372              {
 373                  $this->fatal_error("The SMTP server rejected the supplied SMTP password. Reason: ".$this->get_error());
 374                  return false;
 375              }
 376          }
 377          else if(in_array("PLAIN", $auth_methods))
 378          {
 379              if(!$this->send_data("AUTH PLAIN", '334'))
 380              {
 381                  if($this->code == 503)
 382                  {
 383                      return true;
 384                  }
 385                  $this->fatal_error("The SMTP server did not respond correctly to the AUTH PLAIN command");
 386                  return false;
 387              }
 388              $auth = base64_encode(chr(0).$this->username.chr(0).$this->password);
 389              if(!$this->send_data($auth, 235))
 390              {
 391                  $this->fatal_error("The SMTP server rejected the supplied login username and password. Reason: ".$this->get_error());
 392                  return false;
 393              }
 394          }
 395          else
 396          {
 397              $this->fatal_error("The SMTP server does not support any of the AUTH methods that MyBB supports");
 398              return false;
 399          }
 400  
 401          // Still here, we're authenticated
 402          return true;
 403      }
 404  
 405      /**
 406       * Fetch data from the SMTP server.
 407       *
 408       * @return string The data from the SMTP server
 409       */
 410  	function get_data()
 411      {
 412          $string = '';
 413  
 414          while((($line = fgets($this->connection, 515)) !== false))
 415          {
 416              $string .= $line;
 417              if(substr($line, 3, 1) == ' ')
 418              {
 419                  break;
 420              }
 421          }
 422          $string = trim($string);
 423          $this->data = $string;
 424          $this->code = substr($this->data, 0, 3);
 425          return $string;
 426      }
 427  
 428      /**
 429       * Check if we're currently connected to an SMTP server
 430       *
 431       * @return boolean true if connected
 432       */
 433  	function connected()
 434      {
 435          if($this->status == 1)
 436          {
 437              return true;
 438          }
 439          return false;
 440      }
 441  
 442      /**
 443       * Send data through to the SMTP server.
 444       *
 445       * @param string The data to be sent
 446       * @param int The response code expected back from the server (if we have one)
 447       * @return boolean True on success
 448       */
 449  	function send_data($data, $status_num = false)
 450      {
 451          if($this->connected())
 452          {
 453              if(fwrite($this->connection, $data."\r\n"))
 454              {
 455                  if($status_num != false)
 456                  {
 457                      $rec = $this->get_data();
 458                      if($this->check_status($status_num))
 459                      {
 460                          return $rec;
 461                      }
 462                      else
 463                      {
 464                          $this->set_error($rec);
 465                          return false;
 466                      }
 467                  }
 468                  return true;
 469              }
 470              else
 471              {
 472                  $this->fatal_error("Unable to send the data to the SMTP server");
 473                  return false;
 474              }
 475          }
 476          return false;
 477      }
 478  
 479      /**
 480       * Checks if the received status code matches the one we expect.
 481       *
 482       * @param int The status code we expected back from the server
 483       * @param boolean True if it matches
 484       */
 485  	function check_status($status_num)
 486      {
 487          if($this->code == $status_num)
 488          {
 489              return $this->data;
 490          }
 491          else
 492          {
 493              return false;
 494          }
 495      }
 496      
 497      /**
 498       * Close the connection to the SMTP server.
 499       */
 500  	function close()
 501      {
 502          if($this->status == 1)
 503          {
 504              $this->send_data('QUIT');
 505              fclose($this->connection);
 506              $this->status = 0;
 507          }
 508      }
 509      
 510      /**
 511       * Get the last error message response from the SMTP server
 512       *
 513       * @return string The error message response from the SMTP server
 514       */
 515  	function get_error()
 516      {
 517          if(!$this->last_error)
 518          {
 519              $this->last_error = "N/A";
 520          }
 521          
 522          return $this->last_error;
 523      }
 524      
 525      /**
 526       * Set the last error message response from the SMTP server
 527       *
 528       * @param string The error message response
 529       */
 530  	function set_error($error)
 531      {
 532          $this->last_error = $error;
 533      }
 534  }
 535  ?>


Generated: Tue Oct 8 19:19:50 2013 Cross-referenced by PHPXref 0.7.1