[ Index ] |
PHP Cross Reference of MyBB 1.8.30 |
[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(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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
2005 - 2021 © MyBB.de | Alle Rechte vorbehalten! | Sponsor: netcup | Cross-referenced by PHPXref |