[ Index ]

PHP Cross Reference of MyBB 1.8.27

title

Body

[close]

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

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


2005 - 2021 © MyBB.de | Alle Rechte vorbehalten! | Sponsor: netcup Cross-referenced by PHPXref