[ Index ] |
PHP Cross Reference of MyBB 1.8.38 |
[Summary view] [Print] [Text view]
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(is_resource($this->connection) && 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 318 $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; 319 // Fix for PHP >=5.6.7 and <7.2 not including TLS 1.1 and 1.2 320 if(defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) 321 { 322 $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; 323 $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; 324 } 325 326 if(!@stream_socket_enable_crypto($this->connection, true, $crypto_method)) 327 { 328 $this->fatal_error("Failed to start TLS encryption"); 329 return false; 330 } 331 // Resend EHLO to get updated service list 332 $data = $this->send_data("{$helo} {$this->helo}", 250); 333 if(!$data) 334 { 335 $this->fatal_error("The server did not understand the EHLO command"); 336 return false; 337 } 338 } 339 340 if(!empty($this->username) && !empty($this->password)) 341 { 342 if(!preg_match("#250( |-)AUTH( |=)(.+)$#mi", $data, $matches)) 343 { 344 $this->fatal_error("The server did not understand the AUTH command"); 345 return false; 346 } 347 if(!$this->auth($matches[3])) 348 { 349 return false; 350 } 351 } 352 return true; 353 } 354 else 355 { 356 $this->fatal_error("Unable to connect to the mail server with the given details. Reason: {$error_number}: {$error_string}"); 357 return false; 358 } 359 } 360 361 /** 362 * Authenticate against the SMTP server. 363 * 364 * @param string $auth_methods A list of authentication methods supported by the server 365 * @return boolean True on success 366 */ 367 function auth($auth_methods) 368 { 369 global $lang, $mybb; 370 371 $auth_methods = explode(" ", trim($auth_methods)); 372 373 if(in_array("LOGIN", $auth_methods)) 374 { 375 if(!$this->send_data("AUTH LOGIN", 334)) 376 { 377 if($this->code == 503) 378 { 379 return true; 380 } 381 $this->fatal_error("The SMTP server did not respond correctly to the AUTH LOGIN command"); 382 return false; 383 } 384 385 if(!$this->send_data(base64_encode($this->username), 334)) 386 { 387 $this->fatal_error("The SMTP server rejected the supplied SMTP username. Reason: ".$this->get_error()); 388 return false; 389 } 390 391 if(!$this->send_data(base64_encode($this->password), 235)) 392 { 393 $this->fatal_error("The SMTP server rejected the supplied SMTP password. Reason: ".$this->get_error()); 394 return false; 395 } 396 } 397 else if(in_array("PLAIN", $auth_methods)) 398 { 399 if(!$this->send_data("AUTH PLAIN", 334)) 400 { 401 if($this->code == 503) 402 { 403 return true; 404 } 405 $this->fatal_error("The SMTP server did not respond correctly to the AUTH PLAIN command"); 406 return false; 407 } 408 $auth = base64_encode(chr(0).$this->username.chr(0).$this->password); 409 if(!$this->send_data($auth, 235)) 410 { 411 $this->fatal_error("The SMTP server rejected the supplied login username and password. Reason: ".$this->get_error()); 412 return false; 413 } 414 } 415 else if(in_array("CRAM-MD5", $auth_methods)) 416 { 417 $data = $this->send_data("AUTH CRAM-MD5", 334); 418 if(!$data) 419 { 420 if($this->code == 503) 421 { 422 return true; 423 } 424 $this->fatal_error("The SMTP server did not respond correctly to the AUTH CRAM-MD5 command"); 425 return false; 426 } 427 428 $challenge = base64_decode(substr($data, 4)); 429 $auth = base64_encode($this->username.' '.$this->cram_md5_response($this->password, $challenge)); 430 431 if(!$this->send_data($auth, 235)) 432 { 433 $this->fatal_error("The SMTP server rejected the supplied login username and password. Reason: ".$this->get_error()); 434 return false; 435 } 436 } 437 else 438 { 439 $this->fatal_error("The SMTP server does not support any of the AUTH methods that MyBB supports"); 440 return false; 441 } 442 443 // Still here, we're authenticated 444 return true; 445 } 446 447 /** 448 * Fetch data from the SMTP server. 449 * 450 * @return string The data from the SMTP server 451 */ 452 function get_data() 453 { 454 $string = ''; 455 456 while((($line = fgets($this->connection, 515)) !== false)) 457 { 458 $string .= $line; 459 if(substr($line, 3, 1) == ' ') 460 { 461 break; 462 } 463 } 464 $string = trim($string); 465 $this->data = $string; 466 $this->code = substr($this->data, 0, 3); 467 return $string; 468 } 469 470 /** 471 * Check if we're currently connected to an SMTP server 472 * 473 * @return boolean true if connected 474 */ 475 function connected() 476 { 477 if($this->status == 1) 478 { 479 return true; 480 } 481 return false; 482 } 483 484 /** 485 * Send data through to the SMTP server. 486 * 487 * @param string $data The data to be sent 488 * @param int|bool $status_num The response code expected back from the server (if we have one) 489 * @return boolean True on success 490 */ 491 function send_data($data, $status_num = false) 492 { 493 if($this->connected()) 494 { 495 if(fwrite($this->connection, $data."\r\n")) 496 { 497 if($status_num != false) 498 { 499 $rec = $this->get_data(); 500 if($this->check_status($status_num)) 501 { 502 return $rec; 503 } 504 else 505 { 506 $this->set_error($rec); 507 return false; 508 } 509 } 510 return true; 511 } 512 else 513 { 514 $this->fatal_error("Unable to send the data to the SMTP server"); 515 return false; 516 } 517 } 518 return false; 519 } 520 521 /** 522 * Checks if the received status code matches the one we expect. 523 * 524 * @param int $status_num The status code we expected back from the server 525 * @return string|bool 526 */ 527 function check_status($status_num) 528 { 529 if($this->code == $status_num) 530 { 531 return $this->data; 532 } 533 else 534 { 535 return false; 536 } 537 } 538 539 /** 540 * Close the connection to the SMTP server. 541 */ 542 function close() 543 { 544 if($this->status == 1) 545 { 546 $this->send_data('QUIT'); 547 fclose($this->connection); 548 $this->status = 0; 549 } 550 } 551 552 /** 553 * Get the last error message response from the SMTP server 554 * 555 * @return string The error message response from the SMTP server 556 */ 557 function get_error() 558 { 559 if(!$this->last_error) 560 { 561 $this->last_error = "N/A"; 562 } 563 564 return $this->last_error; 565 } 566 567 /** 568 * Set the last error message response from the SMTP server 569 * 570 * @param string $error The error message response 571 */ 572 function set_error($error) 573 { 574 $this->last_error = $error; 575 } 576 577 /** 578 * Generate a CRAM-MD5 response from a server challenge. 579 * 580 * @param string $password Password. 581 * @param string $challenge Challenge sent from SMTP server. 582 * 583 * @return string CRAM-MD5 response. 584 */ 585 function cram_md5_response($password, $challenge) 586 { 587 if(strlen($password) > 64) 588 { 589 $password = pack('H32', md5($password)); 590 } 591 592 if(strlen($password) < 64) 593 { 594 $password = str_pad($password, 64, chr(0)); 595 } 596 597 $k_ipad = substr($password, 0, 64) ^ str_repeat(chr(0x36), 64); 598 $k_opad = substr($password, 0, 64) ^ str_repeat(chr(0x5C), 64); 599 600 $inner = pack('H32', md5($k_ipad.$challenge)); 601 602 return md5($k_opad.$inner); 603 } 604 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
2005 - 2021 © MyBB.de | Alle Rechte vorbehalten! | Sponsor: netcup | Cross-referenced by PHPXref |