[ Index ] |
PHP Cross Reference of phpBB-3.1.12-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * This file is part of the phpBB Forum Software package. 5 * 6 * @copyright (c) phpBB Limited <https://www.phpbb.com> 7 * @license GNU General Public License, version 2 (GPL-2.0) 8 * 9 * For full copyright and license information, please see 10 * the docs/CREDITS.txt file. 11 * 12 */ 13 14 /** 15 * @ignore 16 */ 17 if (!defined('IN_PHPBB')) 18 { 19 exit; 20 } 21 22 /** 23 * Messenger 24 */ 25 class messenger 26 { 27 var $msg, $extra_headers, $replyto, $from, $subject; 28 var $addresses = array(); 29 30 var $mail_priority = MAIL_NORMAL_PRIORITY; 31 var $use_queue = true; 32 33 /** @var \phpbb\template\template */ 34 protected $template; 35 36 var $eol = "\n"; 37 38 /** 39 * Constructor 40 */ 41 function messenger($use_queue = true) 42 { 43 global $config; 44 45 $this->use_queue = (!$config['email_package_size']) ? false : $use_queue; 46 $this->subject = ''; 47 48 // Determine EOL character (\n for UNIX, \r\n for Windows and \r for Mac) 49 $this->eol = (!defined('PHP_EOL')) ? (($eol = strtolower(substr(PHP_OS, 0, 3))) == 'win') ? "\r\n" : (($eol == 'mac') ? "\r" : "\n") : PHP_EOL; 50 $this->eol = (!$this->eol) ? "\n" : $this->eol; 51 } 52 53 /** 54 * Resets all the data (address, template file, etc etc) to default 55 */ 56 function reset() 57 { 58 $this->addresses = $this->extra_headers = array(); 59 $this->msg = $this->replyto = $this->from = ''; 60 $this->mail_priority = MAIL_NORMAL_PRIORITY; 61 } 62 63 /** 64 * Set addresses for to/im as available 65 * 66 * @param array $user User row 67 */ 68 function set_addresses($user) 69 { 70 if (isset($user['user_email']) && $user['user_email']) 71 { 72 $this->to($user['user_email'], (isset($user['username']) ? $user['username'] : '')); 73 } 74 75 if (isset($user['user_jabber']) && $user['user_jabber']) 76 { 77 $this->im($user['user_jabber'], (isset($user['username']) ? $user['username'] : '')); 78 } 79 } 80 81 /** 82 * Sets an email address to send to 83 */ 84 function to($address, $realname = '') 85 { 86 global $config; 87 88 if (!trim($address)) 89 { 90 return; 91 } 92 93 $pos = isset($this->addresses['to']) ? sizeof($this->addresses['to']) : 0; 94 95 $this->addresses['to'][$pos]['email'] = trim($address); 96 97 // If empty sendmail_path on windows, PHP changes the to line 98 if (!$config['smtp_delivery'] && DIRECTORY_SEPARATOR == '\\') 99 { 100 $this->addresses['to'][$pos]['name'] = ''; 101 } 102 else 103 { 104 $this->addresses['to'][$pos]['name'] = trim($realname); 105 } 106 } 107 108 /** 109 * Sets an cc address to send to 110 */ 111 function cc($address, $realname = '') 112 { 113 if (!trim($address)) 114 { 115 return; 116 } 117 118 $pos = isset($this->addresses['cc']) ? sizeof($this->addresses['cc']) : 0; 119 $this->addresses['cc'][$pos]['email'] = trim($address); 120 $this->addresses['cc'][$pos]['name'] = trim($realname); 121 } 122 123 /** 124 * Sets an bcc address to send to 125 */ 126 function bcc($address, $realname = '') 127 { 128 if (!trim($address)) 129 { 130 return; 131 } 132 133 $pos = isset($this->addresses['bcc']) ? sizeof($this->addresses['bcc']) : 0; 134 $this->addresses['bcc'][$pos]['email'] = trim($address); 135 $this->addresses['bcc'][$pos]['name'] = trim($realname); 136 } 137 138 /** 139 * Sets a im contact to send to 140 */ 141 function im($address, $realname = '') 142 { 143 // IM-Addresses could be empty 144 if (!trim($address)) 145 { 146 return; 147 } 148 149 $pos = isset($this->addresses['im']) ? sizeof($this->addresses['im']) : 0; 150 $this->addresses['im'][$pos]['uid'] = trim($address); 151 $this->addresses['im'][$pos]['name'] = trim($realname); 152 } 153 154 /** 155 * Set the reply to address 156 */ 157 function replyto($address) 158 { 159 $this->replyto = trim($address); 160 } 161 162 /** 163 * Set the from address 164 */ 165 function from($address) 166 { 167 $this->from = trim($address); 168 } 169 170 /** 171 * set up subject for mail 172 */ 173 function subject($subject = '') 174 { 175 $this->subject = trim($subject); 176 } 177 178 /** 179 * set up extra mail headers 180 */ 181 function headers($headers) 182 { 183 $this->extra_headers[] = trim($headers); 184 } 185 186 /** 187 * Adds X-AntiAbuse headers 188 * 189 * @param array $config Configuration array 190 * @param user $user A user object 191 * 192 * @return null 193 */ 194 function anti_abuse_headers($config, $user) 195 { 196 $this->headers('X-AntiAbuse: Board servername - ' . mail_encode($config['server_name'])); 197 $this->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']); 198 $this->headers('X-AntiAbuse: Username - ' . mail_encode($user->data['username'])); 199 $this->headers('X-AntiAbuse: User IP - ' . $user->ip); 200 } 201 202 /** 203 * Set the email priority 204 */ 205 function set_mail_priority($priority = MAIL_NORMAL_PRIORITY) 206 { 207 $this->mail_priority = $priority; 208 } 209 210 /** 211 * Set email template to use 212 */ 213 function template($template_file, $template_lang = '', $template_path = '', $template_dir_prefix = '') 214 { 215 global $config, $phpbb_root_path, $phpEx, $user, $phpbb_extension_manager; 216 217 $template_dir_prefix = (!$template_dir_prefix || $template_dir_prefix[0] === '/') ? $template_dir_prefix : '/' . $template_dir_prefix; 218 219 $this->setup_template(); 220 221 if (!trim($template_file)) 222 { 223 trigger_error('No template file for emailing set.', E_USER_ERROR); 224 } 225 226 if (!trim($template_lang)) 227 { 228 // fall back to board default language if the user's language is 229 // missing $template_file. If this does not exist either, 230 // $this->template->set_filenames will do a trigger_error 231 $template_lang = basename($config['default_lang']); 232 } 233 234 $ext_template_paths = array( 235 array( 236 'name' => $template_lang . '_email', 237 'ext_path' => 'language/' . $template_lang . '/email' . $template_dir_prefix, 238 ), 239 ); 240 241 if ($template_path) 242 { 243 $template_paths = array( 244 $template_path . $template_dir_prefix, 245 ); 246 } 247 else 248 { 249 $template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/'; 250 $template_path .= $template_lang . '/email'; 251 252 $template_paths = array( 253 $template_path . $template_dir_prefix, 254 ); 255 256 $board_language = basename($config['default_lang']); 257 258 // we can only specify default language fallback when the path is not a custom one for which we 259 // do not know the default language alternative 260 if ($template_lang !== $board_language) 261 { 262 $fallback_template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/'; 263 $fallback_template_path .= $board_language . '/email'; 264 265 $template_paths[] = $fallback_template_path . $template_dir_prefix; 266 267 $ext_template_paths[] = array( 268 'name' => $board_language . '_email', 269 'ext_path' => 'language/' . $board_language . '/email' . $template_dir_prefix, 270 ); 271 } 272 // If everything fails just fall back to en template 273 if ($template_lang !== 'en' && $board_language !== 'en') 274 { 275 $fallback_template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/'; 276 $fallback_template_path .= 'en/email'; 277 278 $template_paths[] = $fallback_template_path . $template_dir_prefix; 279 280 $ext_template_paths[] = array( 281 'name' => 'en_email', 282 'ext_path' => 'language/en/email' . $template_dir_prefix, 283 ); 284 } 285 } 286 287 $this->set_template_paths($ext_template_paths, $template_paths); 288 289 $this->template->set_filenames(array( 290 'body' => $template_file . '.txt', 291 )); 292 293 return true; 294 } 295 296 /** 297 * assign variables to email template 298 */ 299 function assign_vars($vars) 300 { 301 $this->setup_template(); 302 303 $this->template->assign_vars($vars); 304 } 305 306 function assign_block_vars($blockname, $vars) 307 { 308 $this->setup_template(); 309 310 $this->template->assign_block_vars($blockname, $vars); 311 } 312 313 /** 314 * Send the mail out to the recipients set previously in var $this->addresses 315 * 316 * @param int $method User notification method NOTIFY_EMAIL|NOTIFY_IM|NOTIFY_BOTH 317 * @param bool $break Flag indicating if the function only formats the subject 318 * and the message without sending it 319 * 320 * @return bool 321 */ 322 function send($method = NOTIFY_EMAIL, $break = false) 323 { 324 global $config, $user, $phpbb_dispatcher; 325 326 // We add some standard variables we always use, no need to specify them always 327 $this->assign_vars(array( 328 'U_BOARD' => generate_board_url(), 329 'EMAIL_SIG' => str_replace('<br />', "\n", "-- \n" . htmlspecialchars_decode($config['board_email_sig'])), 330 'SITENAME' => htmlspecialchars_decode($config['sitename']), 331 )); 332 333 $subject = $this->subject; 334 $message = $this->msg; 335 /** 336 * Event to modify notification message text before parsing 337 * 338 * @event core.modify_notification_message 339 * @var int method User notification method NOTIFY_EMAIL|NOTIFY_IM|NOTIFY_BOTH 340 * @var bool break Flag indicating if the function only formats the subject 341 * and the message without sending it 342 * @var string subject The message subject 343 * @var string message The message text 344 * @since 3.1.11-RC1 345 */ 346 $vars = array( 347 'method', 348 'break', 349 'subject', 350 'message', 351 ); 352 extract($phpbb_dispatcher->trigger_event('core.modify_notification_message', compact($vars))); 353 $this->subject = $subject; 354 $this->msg = $message; 355 unset($subject, $message); 356 357 // Parse message through template 358 $this->msg = trim($this->template->assign_display('body')); 359 360 // Because we use \n for newlines in the body message we need to fix line encoding errors for those admins who uploaded email template files in the wrong encoding 361 $this->msg = str_replace("\r\n", "\n", $this->msg); 362 363 // We now try and pull a subject from the email body ... if it exists, 364 // do this here because the subject may contain a variable 365 $drop_header = ''; 366 $match = array(); 367 if (preg_match('#^(Subject:(.*?))$#m', $this->msg, $match)) 368 { 369 $this->subject = (trim($match[2]) != '') ? trim($match[2]) : (($this->subject != '') ? $this->subject : $user->lang['NO_EMAIL_SUBJECT']); 370 $drop_header .= '[\r\n]*?' . preg_quote($match[1], '#'); 371 } 372 else 373 { 374 $this->subject = (($this->subject != '') ? $this->subject : $user->lang['NO_EMAIL_SUBJECT']); 375 } 376 377 if ($drop_header) 378 { 379 $this->msg = trim(preg_replace('#' . $drop_header . '#s', '', $this->msg)); 380 } 381 382 if ($break) 383 { 384 return true; 385 } 386 387 switch ($method) 388 { 389 case NOTIFY_EMAIL: 390 $result = $this->msg_email(); 391 break; 392 393 case NOTIFY_IM: 394 $result = $this->msg_jabber(); 395 break; 396 397 case NOTIFY_BOTH: 398 $result = $this->msg_email(); 399 $this->msg_jabber(); 400 break; 401 } 402 403 $this->reset(); 404 return $result; 405 } 406 407 /** 408 * Add error message to log 409 */ 410 function error($type, $msg) 411 { 412 global $user, $phpEx, $phpbb_root_path, $config, $request; 413 414 // Session doesn't exist, create it 415 if (!isset($user->session_id) || $user->session_id === '') 416 { 417 $user->session_begin(); 418 } 419 420 $calling_page = htmlspecialchars_decode($request->server('PHP_SELF')); 421 422 $message = ''; 423 switch ($type) 424 { 425 case 'EMAIL': 426 $message = '<strong>EMAIL/' . (($config['smtp_delivery']) ? 'SMTP' : 'PHP/' . $config['email_function_name'] . '()') . '</strong>'; 427 break; 428 429 default: 430 $message = "<strong>$type</strong>"; 431 break; 432 } 433 434 $message .= '<br /><em>' . htmlspecialchars($calling_page) . '</em><br /><br />' . $msg . '<br />'; 435 add_log('critical', 'LOG_ERROR_' . $type, $message); 436 } 437 438 /** 439 * Save to queue 440 */ 441 function save_queue() 442 { 443 global $config; 444 445 if ($config['email_package_size'] && $this->use_queue && !empty($this->queue)) 446 { 447 $this->queue->save(); 448 return; 449 } 450 } 451 452 /** 453 * Generates a valid message id to be used in emails 454 * 455 * @return string message id 456 */ 457 function generate_message_id() 458 { 459 global $config, $request; 460 461 $domain = ($config['server_name']) ?: $request->server('SERVER_NAME', 'phpbb.generated'); 462 463 return md5(unique_id(time())) . '@' . $domain; 464 } 465 466 /** 467 * Return email header 468 */ 469 function build_header($to, $cc, $bcc) 470 { 471 global $config, $phpbb_dispatcher; 472 473 // We could use keys here, but we won't do this for 3.0.x to retain backwards compatibility 474 $headers = array(); 475 476 $headers[] = 'From: ' . $this->from; 477 478 if ($cc) 479 { 480 $headers[] = 'Cc: ' . $cc; 481 } 482 483 if ($bcc) 484 { 485 $headers[] = 'Bcc: ' . $bcc; 486 } 487 488 $headers[] = 'Reply-To: ' . $this->replyto; 489 $headers[] = 'Return-Path: <' . $config['board_email'] . '>'; 490 $headers[] = 'Sender: <' . $config['board_email'] . '>'; 491 $headers[] = 'MIME-Version: 1.0'; 492 $headers[] = 'Message-ID: <' . $this->generate_message_id() . '>'; 493 $headers[] = 'Date: ' . date('r', time()); 494 $headers[] = 'Content-Type: text/plain; charset=UTF-8'; // format=flowed 495 $headers[] = 'Content-Transfer-Encoding: 8bit'; // 7bit 496 497 $headers[] = 'X-Priority: ' . $this->mail_priority; 498 $headers[] = 'X-MSMail-Priority: ' . (($this->mail_priority == MAIL_LOW_PRIORITY) ? 'Low' : (($this->mail_priority == MAIL_NORMAL_PRIORITY) ? 'Normal' : 'High')); 499 $headers[] = 'X-Mailer: phpBB3'; 500 $headers[] = 'X-MimeOLE: phpBB3'; 501 $headers[] = 'X-phpBB-Origin: phpbb://' . str_replace(array('http://', 'https://'), array('', ''), generate_board_url()); 502 503 /** 504 * Event to modify email header entries 505 * 506 * @event core.modify_email_headers 507 * @var array headers Array containing email header entries 508 * @since 3.1.11-RC1 509 */ 510 $vars = array('headers'); 511 extract($phpbb_dispatcher->trigger_event('core.modify_email_headers', compact($vars))); 512 513 if (sizeof($this->extra_headers)) 514 { 515 $headers = array_merge($headers, $this->extra_headers); 516 } 517 518 return $headers; 519 } 520 521 /** 522 * Send out emails 523 */ 524 function msg_email() 525 { 526 global $config, $user; 527 528 if (empty($config['email_enable'])) 529 { 530 return false; 531 } 532 533 // Addresses to send to? 534 if (empty($this->addresses) || (empty($this->addresses['to']) && empty($this->addresses['cc']) && empty($this->addresses['bcc']))) 535 { 536 // Send was successful. ;) 537 return true; 538 } 539 540 $use_queue = false; 541 if ($config['email_package_size'] && $this->use_queue) 542 { 543 if (empty($this->queue)) 544 { 545 $this->queue = new queue(); 546 $this->queue->init('email', $config['email_package_size']); 547 } 548 $use_queue = true; 549 } 550 551 $contact_name = htmlspecialchars_decode($config['board_contact_name']); 552 $board_contact = (($contact_name !== '') ? '"' . mail_encode($contact_name) . '" ' : '') . '<' . $config['board_contact'] . '>'; 553 554 if (empty($this->replyto)) 555 { 556 $this->replyto = $board_contact; 557 } 558 559 if (empty($this->from)) 560 { 561 $this->from = $board_contact; 562 } 563 564 $encode_eol = ($config['smtp_delivery']) ? "\r\n" : $this->eol; 565 566 // Build to, cc and bcc strings 567 $to = $cc = $bcc = ''; 568 foreach ($this->addresses as $type => $address_ary) 569 { 570 if ($type == 'im') 571 { 572 continue; 573 } 574 575 foreach ($address_ary as $which_ary) 576 { 577 ${$type} .= ((${$type} != '') ? ', ' : '') . (($which_ary['name'] != '') ? mail_encode($which_ary['name'], $encode_eol) . ' <' . $which_ary['email'] . '>' : $which_ary['email']); 578 } 579 } 580 581 // Build header 582 $headers = $this->build_header($to, $cc, $bcc); 583 584 // Send message ... 585 if (!$use_queue) 586 { 587 $mail_to = ($to == '') ? 'undisclosed-recipients:;' : $to; 588 $err_msg = ''; 589 590 if ($config['smtp_delivery']) 591 { 592 $result = smtpmail($this->addresses, mail_encode($this->subject), wordwrap(utf8_wordwrap($this->msg), 997, "\n", true), $err_msg, $headers); 593 } 594 else 595 { 596 $result = phpbb_mail($mail_to, $this->subject, $this->msg, $headers, $this->eol, $err_msg); 597 } 598 599 if (!$result) 600 { 601 $this->error('EMAIL', $err_msg); 602 return false; 603 } 604 } 605 else 606 { 607 $this->queue->put('email', array( 608 'to' => $to, 609 'addresses' => $this->addresses, 610 'subject' => $this->subject, 611 'msg' => $this->msg, 612 'headers' => $headers) 613 ); 614 } 615 616 return true; 617 } 618 619 /** 620 * Send jabber message out 621 */ 622 function msg_jabber() 623 { 624 global $config, $db, $user, $phpbb_root_path, $phpEx; 625 626 if (empty($config['jab_enable']) || empty($config['jab_host']) || empty($config['jab_username']) || empty($config['jab_password'])) 627 { 628 return false; 629 } 630 631 if (empty($this->addresses['im'])) 632 { 633 // Send was successful. ;) 634 return true; 635 } 636 637 $use_queue = false; 638 if ($config['jab_package_size'] && $this->use_queue) 639 { 640 if (empty($this->queue)) 641 { 642 $this->queue = new queue(); 643 $this->queue->init('jabber', $config['jab_package_size']); 644 } 645 $use_queue = true; 646 } 647 648 $addresses = array(); 649 foreach ($this->addresses['im'] as $type => $uid_ary) 650 { 651 $addresses[] = $uid_ary['uid']; 652 } 653 $addresses = array_unique($addresses); 654 655 if (!$use_queue) 656 { 657 include_once($phpbb_root_path . 'includes/functions_jabber.' . $phpEx); 658 $this->jabber = new jabber($config['jab_host'], $config['jab_port'], $config['jab_username'], htmlspecialchars_decode($config['jab_password']), $config['jab_use_ssl'], $config['jab_verify_peer'], $config['jab_verify_peer_name'], $config['jab_allow_self_signed']); 659 660 if (!$this->jabber->connect()) 661 { 662 $this->error('JABBER', $user->lang['ERR_JAB_CONNECT'] . '<br />' . $this->jabber->get_log()); 663 return false; 664 } 665 666 if (!$this->jabber->login()) 667 { 668 $this->error('JABBER', $user->lang['ERR_JAB_AUTH'] . '<br />' . $this->jabber->get_log()); 669 return false; 670 } 671 672 foreach ($addresses as $address) 673 { 674 $this->jabber->send_message($address, $this->msg, $this->subject); 675 } 676 677 $this->jabber->disconnect(); 678 } 679 else 680 { 681 $this->queue->put('jabber', array( 682 'addresses' => $addresses, 683 'subject' => $this->subject, 684 'msg' => $this->msg) 685 ); 686 } 687 unset($addresses); 688 return true; 689 } 690 691 /** 692 * Setup template engine 693 */ 694 protected function setup_template() 695 { 696 global $config, $phpbb_path_helper, $user, $phpbb_extension_manager; 697 698 if ($this->template instanceof \phpbb\template\template) 699 { 700 return; 701 } 702 703 $this->template = new \phpbb\template\twig\twig($phpbb_path_helper, $config, $user, new \phpbb\template\context(), $phpbb_extension_manager); 704 } 705 706 /** 707 * Set template paths to load 708 */ 709 protected function set_template_paths($path_name, $paths) 710 { 711 $this->setup_template(); 712 713 $this->template->set_custom_style($path_name, $paths); 714 } 715 } 716 717 /** 718 * handling email and jabber queue 719 */ 720 class queue 721 { 722 var $data = array(); 723 var $queue_data = array(); 724 var $package_size = 0; 725 var $cache_file = ''; 726 var $eol = "\n"; 727 728 /** 729 * constructor 730 */ 731 function queue() 732 { 733 global $phpEx, $phpbb_root_path; 734 735 $this->data = array(); 736 $this->cache_file = "{$phpbb_root_path}cache/queue.$phpEx"; 737 738 // Determine EOL character (\n for UNIX, \r\n for Windows and \r for Mac) 739 $this->eol = (!defined('PHP_EOL')) ? (($eol = strtolower(substr(PHP_OS, 0, 3))) == 'win') ? "\r\n" : (($eol == 'mac') ? "\r" : "\n") : PHP_EOL; 740 $this->eol = (!$this->eol) ? "\n" : $this->eol; 741 } 742 743 /** 744 * Init a queue object 745 */ 746 function init($object, $package_size) 747 { 748 $this->data[$object] = array(); 749 $this->data[$object]['package_size'] = $package_size; 750 $this->data[$object]['data'] = array(); 751 } 752 753 /** 754 * Put object in queue 755 */ 756 function put($object, $scope) 757 { 758 $this->data[$object]['data'][] = $scope; 759 } 760 761 /** 762 * Process queue 763 * Using lock file 764 */ 765 function process() 766 { 767 global $db, $config, $phpEx, $phpbb_root_path, $user; 768 769 $lock = new \phpbb\lock\flock($this->cache_file); 770 $lock->acquire(); 771 772 // avoid races, check file existence once 773 $have_cache_file = file_exists($this->cache_file); 774 if (!$have_cache_file || $config['last_queue_run'] > time() - $config['queue_interval']) 775 { 776 if (!$have_cache_file) 777 { 778 set_config('last_queue_run', time(), true); 779 } 780 781 $lock->release(); 782 return; 783 } 784 785 set_config('last_queue_run', time(), true); 786 787 include($this->cache_file); 788 789 foreach ($this->queue_data as $object => $data_ary) 790 { 791 @set_time_limit(0); 792 793 if (!isset($data_ary['package_size'])) 794 { 795 $data_ary['package_size'] = 0; 796 } 797 798 $package_size = $data_ary['package_size']; 799 $num_items = (!$package_size || sizeof($data_ary['data']) < $package_size) ? sizeof($data_ary['data']) : $package_size; 800 801 /* 802 * This code is commented out because it causes problems on some web hosts. 803 * The core problem is rather restrictive email sending limits. 804 * This code is nly useful if you have no such restrictions from the 805 * web host and the package size setting is wrong. 806 807 // If the amount of emails to be sent is way more than package_size than we need to increase it to prevent backlogs... 808 if (sizeof($data_ary['data']) > $package_size * 2.5) 809 { 810 $num_items = sizeof($data_ary['data']); 811 } 812 */ 813 814 switch ($object) 815 { 816 case 'email': 817 // Delete the email queued objects if mailing is disabled 818 if (!$config['email_enable']) 819 { 820 unset($this->queue_data['email']); 821 continue 2; 822 } 823 break; 824 825 case 'jabber': 826 if (!$config['jab_enable']) 827 { 828 unset($this->queue_data['jabber']); 829 continue 2; 830 } 831 832 include_once($phpbb_root_path . 'includes/functions_jabber.' . $phpEx); 833 $this->jabber = new jabber($config['jab_host'], $config['jab_port'], $config['jab_username'], htmlspecialchars_decode($config['jab_password']), $config['jab_use_ssl'], $config['jab_verify_peer'], $config['jab_verify_peer_name'], $config['jab_allow_self_signed']); 834 835 if (!$this->jabber->connect()) 836 { 837 $messenger = new messenger(); 838 $messenger->error('JABBER', $user->lang['ERR_JAB_CONNECT']); 839 continue 2; 840 } 841 842 if (!$this->jabber->login()) 843 { 844 $messenger = new messenger(); 845 $messenger->error('JABBER', $user->lang['ERR_JAB_AUTH']); 846 continue 2; 847 } 848 849 break; 850 851 default: 852 $lock->release(); 853 return; 854 } 855 856 for ($i = 0; $i < $num_items; $i++) 857 { 858 // Make variables available... 859 extract(array_shift($this->queue_data[$object]['data'])); 860 861 switch ($object) 862 { 863 case 'email': 864 $err_msg = ''; 865 $to = (!$to) ? 'undisclosed-recipients:;' : $to; 866 867 if ($config['smtp_delivery']) 868 { 869 $result = smtpmail($addresses, mail_encode($subject), wordwrap(utf8_wordwrap($msg), 997, "\n", true), $err_msg, $headers); 870 } 871 else 872 { 873 $result = phpbb_mail($to, $subject, $msg, $headers, $this->eol, $err_msg); 874 } 875 876 if (!$result) 877 { 878 $messenger = new messenger(); 879 $messenger->error('EMAIL', $err_msg); 880 continue 2; 881 } 882 break; 883 884 case 'jabber': 885 foreach ($addresses as $address) 886 { 887 if ($this->jabber->send_message($address, $msg, $subject) === false) 888 { 889 $messenger = new messenger(); 890 $messenger->error('JABBER', $this->jabber->get_log()); 891 continue 3; 892 } 893 } 894 break; 895 } 896 } 897 898 // No more data for this object? Unset it 899 if (!sizeof($this->queue_data[$object]['data'])) 900 { 901 unset($this->queue_data[$object]); 902 } 903 904 // Post-object processing 905 switch ($object) 906 { 907 case 'jabber': 908 // Hang about a couple of secs to ensure the messages are 909 // handled, then disconnect 910 $this->jabber->disconnect(); 911 break; 912 } 913 } 914 915 if (!sizeof($this->queue_data)) 916 { 917 @unlink($this->cache_file); 918 } 919 else 920 { 921 if ($fp = @fopen($this->cache_file, 'wb')) 922 { 923 fwrite($fp, "<?php\nif (!defined('IN_PHPBB')) exit;\n\$this->queue_data = unserialize(" . var_export(serialize($this->queue_data), true) . ");\n\n?>"); 924 fclose($fp); 925 926 if (function_exists('opcache_invalidate')) 927 { 928 @opcache_invalidate($this->cache_file); 929 } 930 931 phpbb_chmod($this->cache_file, CHMOD_READ | CHMOD_WRITE); 932 } 933 } 934 935 $lock->release(); 936 } 937 938 /** 939 * Save queue 940 */ 941 function save() 942 { 943 if (!sizeof($this->data)) 944 { 945 return; 946 } 947 948 $lock = new \phpbb\lock\flock($this->cache_file); 949 $lock->acquire(); 950 951 if (file_exists($this->cache_file)) 952 { 953 include($this->cache_file); 954 955 foreach ($this->queue_data as $object => $data_ary) 956 { 957 if (isset($this->data[$object]) && sizeof($this->data[$object])) 958 { 959 $this->data[$object]['data'] = array_merge($data_ary['data'], $this->data[$object]['data']); 960 } 961 else 962 { 963 $this->data[$object]['data'] = $data_ary['data']; 964 } 965 } 966 } 967 968 if ($fp = @fopen($this->cache_file, 'w')) 969 { 970 fwrite($fp, "<?php\nif (!defined('IN_PHPBB')) exit;\n\$this->queue_data = unserialize(" . var_export(serialize($this->data), true) . ");\n\n?>"); 971 fclose($fp); 972 973 if (function_exists('opcache_invalidate')) 974 { 975 @opcache_invalidate($this->cache_file); 976 } 977 978 phpbb_chmod($this->cache_file, CHMOD_READ | CHMOD_WRITE); 979 980 $this->data = array(); 981 } 982 983 $lock->release(); 984 } 985 } 986 987 /** 988 * Replacement or substitute for PHP's mail command 989 */ 990 function smtpmail($addresses, $subject, $message, &$err_msg, $headers = false) 991 { 992 global $config, $user; 993 994 // Fix any bare linefeeds in the message to make it RFC821 Compliant. 995 $message = preg_replace("#(?<!\r)\n#si", "\r\n", $message); 996 997 if ($headers !== false) 998 { 999 if (!is_array($headers)) 1000 { 1001 // Make sure there are no bare linefeeds in the headers 1002 $headers = preg_replace('#(?<!\r)\n#si', "\n", $headers); 1003 $headers = explode("\n", $headers); 1004 } 1005 1006 // Ok this is rather confusing all things considered, 1007 // but we have to grab bcc and cc headers and treat them differently 1008 // Something we really didn't take into consideration originally 1009 $headers_used = array(); 1010 1011 foreach ($headers as $header) 1012 { 1013 if (strpos(strtolower($header), 'cc:') === 0 || strpos(strtolower($header), 'bcc:') === 0) 1014 { 1015 continue; 1016 } 1017 $headers_used[] = trim($header); 1018 } 1019 1020 $headers = chop(implode("\r\n", $headers_used)); 1021 } 1022 1023 if (trim($subject) == '') 1024 { 1025 $err_msg = (isset($user->lang['NO_EMAIL_SUBJECT'])) ? $user->lang['NO_EMAIL_SUBJECT'] : 'No email subject specified'; 1026 return false; 1027 } 1028 1029 if (trim($message) == '') 1030 { 1031 $err_msg = (isset($user->lang['NO_EMAIL_MESSAGE'])) ? $user->lang['NO_EMAIL_MESSAGE'] : 'Email message was blank'; 1032 return false; 1033 } 1034 1035 $mail_rcpt = $mail_to = $mail_cc = array(); 1036 1037 // Build correct addresses for RCPT TO command and the client side display (TO, CC) 1038 if (isset($addresses['to']) && sizeof($addresses['to'])) 1039 { 1040 foreach ($addresses['to'] as $which_ary) 1041 { 1042 $mail_to[] = ($which_ary['name'] != '') ? mail_encode(trim($which_ary['name'])) . ' <' . trim($which_ary['email']) . '>' : '<' . trim($which_ary['email']) . '>'; 1043 $mail_rcpt['to'][] = '<' . trim($which_ary['email']) . '>'; 1044 } 1045 } 1046 1047 if (isset($addresses['bcc']) && sizeof($addresses['bcc'])) 1048 { 1049 foreach ($addresses['bcc'] as $which_ary) 1050 { 1051 $mail_rcpt['bcc'][] = '<' . trim($which_ary['email']) . '>'; 1052 } 1053 } 1054 1055 if (isset($addresses['cc']) && sizeof($addresses['cc'])) 1056 { 1057 foreach ($addresses['cc'] as $which_ary) 1058 { 1059 $mail_cc[] = ($which_ary['name'] != '') ? mail_encode(trim($which_ary['name'])) . ' <' . trim($which_ary['email']) . '>' : '<' . trim($which_ary['email']) . '>'; 1060 $mail_rcpt['cc'][] = '<' . trim($which_ary['email']) . '>'; 1061 } 1062 } 1063 1064 $smtp = new smtp_class(); 1065 1066 $errno = 0; 1067 $errstr = ''; 1068 1069 $smtp->add_backtrace('Connecting to ' . $config['smtp_host'] . ':' . $config['smtp_port']); 1070 1071 // Ok we have error checked as much as we can to this point let's get on it already. 1072 if (!class_exists('\phpbb\error_collector')) 1073 { 1074 global $phpbb_root_path, $phpEx; 1075 include($phpbb_root_path . 'includes/error_collector.' . $phpEx); 1076 } 1077 $collector = new \phpbb\error_collector; 1078 $collector->install(); 1079 1080 $options = array(); 1081 $verify_peer = (bool) $config['smtp_verify_peer']; 1082 $verify_peer_name = (bool) $config['smtp_verify_peer_name']; 1083 $allow_self_signed = (bool) $config['smtp_allow_self_signed']; 1084 $remote_socket = $config['smtp_host'] . ':' . $config['smtp_port']; 1085 1086 // Set ssl context options, see http://php.net/manual/en/context.ssl.php 1087 $options['ssl'] = array('verify_peer' => $verify_peer, 'verify_peer_name' => $verify_peer_name, 'allow_self_signed' => $allow_self_signed); 1088 $socket_context = stream_context_create($options); 1089 1090 $smtp->socket = @stream_socket_client($remote_socket, $errno, $errstr, 20, STREAM_CLIENT_CONNECT, $socket_context); 1091 $collector->uninstall(); 1092 $error_contents = $collector->format_errors(); 1093 1094 if (!$smtp->socket) 1095 { 1096 if ($errstr) 1097 { 1098 $errstr = utf8_convert_message($errstr); 1099 } 1100 1101 $err_msg = (isset($user->lang['NO_CONNECT_TO_SMTP_HOST'])) ? sprintf($user->lang['NO_CONNECT_TO_SMTP_HOST'], $errno, $errstr) : "Could not connect to smtp host : $errno : $errstr"; 1102 $err_msg .= ($error_contents) ? '<br /><br />' . htmlspecialchars($error_contents) : ''; 1103 return false; 1104 } 1105 1106 // Wait for reply 1107 if ($err_msg = $smtp->server_parse('220', __LINE__)) 1108 { 1109 $smtp->close_session($err_msg); 1110 return false; 1111 } 1112 1113 // Let me in. This function handles the complete authentication process 1114 if ($err_msg = $smtp->log_into_server($config['smtp_host'], $config['smtp_username'], htmlspecialchars_decode($config['smtp_password']), $config['smtp_auth_method'])) 1115 { 1116 $smtp->close_session($err_msg); 1117 return false; 1118 } 1119 1120 // From this point onward most server response codes should be 250 1121 // Specify who the mail is from.... 1122 $smtp->server_send('MAIL FROM:<' . $config['board_email'] . '>'); 1123 if ($err_msg = $smtp->server_parse('250', __LINE__)) 1124 { 1125 $smtp->close_session($err_msg); 1126 return false; 1127 } 1128 1129 // Specify each user to send to and build to header. 1130 $to_header = implode(', ', $mail_to); 1131 $cc_header = implode(', ', $mail_cc); 1132 1133 // Now tell the MTA to send the Message to the following people... [TO, BCC, CC] 1134 $rcpt = false; 1135 foreach ($mail_rcpt as $type => $mail_to_addresses) 1136 { 1137 foreach ($mail_to_addresses as $mail_to_address) 1138 { 1139 // Add an additional bit of error checking to the To field. 1140 if (preg_match('#[^ ]+\@[^ ]+#', $mail_to_address)) 1141 { 1142 $smtp->server_send("RCPT TO:$mail_to_address"); 1143 if ($err_msg = $smtp->server_parse('250', __LINE__)) 1144 { 1145 // We continue... if users are not resolved we do not care 1146 if ($smtp->numeric_response_code != 550) 1147 { 1148 $smtp->close_session($err_msg); 1149 return false; 1150 } 1151 } 1152 else 1153 { 1154 $rcpt = true; 1155 } 1156 } 1157 } 1158 } 1159 1160 // We try to send messages even if a few people do not seem to have valid email addresses, but if no one has, we have to exit here. 1161 if (!$rcpt) 1162 { 1163 $user->session_begin(); 1164 $err_msg .= '<br /><br />'; 1165 $err_msg .= (isset($user->lang['INVALID_EMAIL_LOG'])) ? sprintf($user->lang['INVALID_EMAIL_LOG'], htmlspecialchars($mail_to_address)) : '<strong>' . htmlspecialchars($mail_to_address) . '</strong> possibly an invalid email address?'; 1166 $smtp->close_session($err_msg); 1167 return false; 1168 } 1169 1170 // Ok now we tell the server we are ready to start sending data 1171 $smtp->server_send('DATA'); 1172 1173 // This is the last response code we look for until the end of the message. 1174 if ($err_msg = $smtp->server_parse('354', __LINE__)) 1175 { 1176 $smtp->close_session($err_msg); 1177 return false; 1178 } 1179 1180 // Send the Subject Line... 1181 $smtp->server_send("Subject: $subject"); 1182 1183 // Now the To Header. 1184 $to_header = ($to_header == '') ? 'undisclosed-recipients:;' : $to_header; 1185 $smtp->server_send("To: $to_header"); 1186 1187 // Now the CC Header. 1188 if ($cc_header != '') 1189 { 1190 $smtp->server_send("CC: $cc_header"); 1191 } 1192 1193 // Now any custom headers.... 1194 if ($headers !== false) 1195 { 1196 $smtp->server_send("$headers\r\n"); 1197 } 1198 1199 // Ok now we are ready for the message... 1200 $smtp->server_send($message); 1201 1202 // Ok the all the ingredients are mixed in let's cook this puppy... 1203 $smtp->server_send('.'); 1204 if ($err_msg = $smtp->server_parse('250', __LINE__)) 1205 { 1206 $smtp->close_session($err_msg); 1207 return false; 1208 } 1209 1210 // Now tell the server we are done and close the socket... 1211 $smtp->server_send('QUIT'); 1212 $smtp->close_session($err_msg); 1213 1214 return true; 1215 } 1216 1217 /** 1218 * SMTP Class 1219 * Auth Mechanisms originally taken from the AUTH Modules found within the PHP Extension and Application Repository (PEAR) 1220 * See docs/AUTHORS for more details 1221 */ 1222 class smtp_class 1223 { 1224 var $server_response = ''; 1225 var $socket = 0; 1226 protected $socket_tls = false; 1227 var $responses = array(); 1228 var $commands = array(); 1229 var $numeric_response_code = 0; 1230 1231 var $backtrace = false; 1232 var $backtrace_log = array(); 1233 1234 function smtp_class() 1235 { 1236 // Always create a backtrace for admins to identify SMTP problems 1237 $this->backtrace = true; 1238 $this->backtrace_log = array(); 1239 } 1240 1241 /** 1242 * Add backtrace message for debugging 1243 */ 1244 function add_backtrace($message) 1245 { 1246 if ($this->backtrace) 1247 { 1248 $this->backtrace_log[] = utf8_htmlspecialchars($message); 1249 } 1250 } 1251 1252 /** 1253 * Send command to smtp server 1254 */ 1255 function server_send($command, $private_info = false) 1256 { 1257 fputs($this->socket, $command . "\r\n"); 1258 1259 (!$private_info) ? $this->add_backtrace("# $command") : $this->add_backtrace('# Omitting sensitive information'); 1260 1261 // We could put additional code here 1262 } 1263 1264 /** 1265 * We use the line to give the support people an indication at which command the error occurred 1266 */ 1267 function server_parse($response, $line) 1268 { 1269 global $user; 1270 1271 $this->server_response = ''; 1272 $this->responses = array(); 1273 $this->numeric_response_code = 0; 1274 1275 while (substr($this->server_response, 3, 1) != ' ') 1276 { 1277 if (!($this->server_response = fgets($this->socket, 256))) 1278 { 1279 return (isset($user->lang['NO_EMAIL_RESPONSE_CODE'])) ? $user->lang['NO_EMAIL_RESPONSE_CODE'] : 'Could not get mail server response codes'; 1280 } 1281 $this->responses[] = substr(rtrim($this->server_response), 4); 1282 $this->numeric_response_code = (int) substr($this->server_response, 0, 3); 1283 1284 $this->add_backtrace("LINE: $line <- {$this->server_response}"); 1285 } 1286 1287 if (!(substr($this->server_response, 0, 3) == $response)) 1288 { 1289 $this->numeric_response_code = (int) substr($this->server_response, 0, 3); 1290 return (isset($user->lang['EMAIL_SMTP_ERROR_RESPONSE'])) ? sprintf($user->lang['EMAIL_SMTP_ERROR_RESPONSE'], $line, $this->server_response) : "Ran into problems sending Mail at <strong>Line $line</strong>. Response: $this->server_response"; 1291 } 1292 1293 return 0; 1294 } 1295 1296 /** 1297 * Close session 1298 */ 1299 function close_session(&$err_msg) 1300 { 1301 fclose($this->socket); 1302 1303 if ($this->backtrace) 1304 { 1305 $message = '<h1>Backtrace</h1><p>' . implode('<br />', $this->backtrace_log) . '</p>'; 1306 $err_msg .= $message; 1307 } 1308 } 1309 1310 /** 1311 * Log into server and get possible auth codes if neccessary 1312 */ 1313 function log_into_server($hostname, $username, $password, $default_auth_method) 1314 { 1315 global $user; 1316 1317 $err_msg = ''; 1318 1319 // Here we try to determine the *real* hostname (reverse DNS entry preferrably) 1320 $local_host = $user->host; 1321 1322 if (function_exists('php_uname')) 1323 { 1324 $local_host = php_uname('n'); 1325 1326 // Able to resolve name to IP 1327 if (($addr = @gethostbyname($local_host)) !== $local_host) 1328 { 1329 // Able to resolve IP back to name 1330 if (($name = @gethostbyaddr($addr)) !== $addr) 1331 { 1332 $local_host = $name; 1333 } 1334 } 1335 } 1336 1337 // If we are authenticating through pop-before-smtp, we 1338 // have to login ones before we get authenticated 1339 // NOTE: on some configurations the time between an update of the auth database takes so 1340 // long that the first email send does not work. This is not a biggie on a live board (only 1341 // the install mail will most likely fail) - but on a dynamic ip connection this might produce 1342 // severe problems and is not fixable! 1343 if ($default_auth_method == 'POP-BEFORE-SMTP' && $username && $password) 1344 { 1345 global $config; 1346 1347 $errno = 0; 1348 $errstr = ''; 1349 1350 $this->server_send("QUIT"); 1351 fclose($this->socket); 1352 1353 $result = $this->pop_before_smtp($hostname, $username, $password); 1354 $username = $password = $default_auth_method = ''; 1355 1356 // We need to close the previous session, else the server is not 1357 // able to get our ip for matching... 1358 if (!$this->socket = @fsockopen($config['smtp_host'], $config['smtp_port'], $errno, $errstr, 10)) 1359 { 1360 if ($errstr) 1361 { 1362 $errstr = utf8_convert_message($errstr); 1363 } 1364 1365 $err_msg = (isset($user->lang['NO_CONNECT_TO_SMTP_HOST'])) ? sprintf($user->lang['NO_CONNECT_TO_SMTP_HOST'], $errno, $errstr) : "Could not connect to smtp host : $errno : $errstr"; 1366 return $err_msg; 1367 } 1368 1369 // Wait for reply 1370 if ($err_msg = $this->server_parse('220', __LINE__)) 1371 { 1372 $this->close_session($err_msg); 1373 return $err_msg; 1374 } 1375 } 1376 1377 $hello_result = $this->hello($local_host); 1378 if (!is_null($hello_result)) 1379 { 1380 return $hello_result; 1381 } 1382 1383 // SMTP STARTTLS (RFC 3207) 1384 if (!$this->socket_tls) 1385 { 1386 $this->socket_tls = $this->starttls(); 1387 1388 if ($this->socket_tls) 1389 { 1390 // Switched to TLS 1391 // RFC 3207: "The client MUST discard any knowledge obtained from the server, [...]" 1392 // So say hello again 1393 $hello_result = $this->hello($local_host); 1394 1395 if (!is_null($hello_result)) 1396 { 1397 return $hello_result; 1398 } 1399 } 1400 } 1401 1402 // If we are not authenticated yet, something might be wrong if no username and passwd passed 1403 if (!$username || !$password) 1404 { 1405 return false; 1406 } 1407 1408 if (!isset($this->commands['AUTH'])) 1409 { 1410 return (isset($user->lang['SMTP_NO_AUTH_SUPPORT'])) ? $user->lang['SMTP_NO_AUTH_SUPPORT'] : 'SMTP server does not support authentication'; 1411 } 1412 1413 // Get best authentication method 1414 $available_methods = explode(' ', $this->commands['AUTH']); 1415 1416 // Define the auth ordering if the default auth method was not found 1417 $auth_methods = array('PLAIN', 'LOGIN', 'CRAM-MD5', 'DIGEST-MD5'); 1418 $method = ''; 1419 1420 if (in_array($default_auth_method, $available_methods)) 1421 { 1422 $method = $default_auth_method; 1423 } 1424 else 1425 { 1426 foreach ($auth_methods as $_method) 1427 { 1428 if (in_array($_method, $available_methods)) 1429 { 1430 $method = $_method; 1431 break; 1432 } 1433 } 1434 } 1435 1436 if (!$method) 1437 { 1438 return (isset($user->lang['NO_SUPPORTED_AUTH_METHODS'])) ? $user->lang['NO_SUPPORTED_AUTH_METHODS'] : 'No supported authentication methods'; 1439 } 1440 1441 $method = strtolower(str_replace('-', '_', $method)); 1442 return $this->$method($username, $password); 1443 } 1444 1445 /** 1446 * SMTP EHLO/HELO 1447 * 1448 * @return mixed Null if the authentication process is supposed to continue 1449 * False if already authenticated 1450 * Error message (string) otherwise 1451 */ 1452 protected function hello($hostname) 1453 { 1454 // Try EHLO first 1455 $this->server_send("EHLO $hostname"); 1456 if ($err_msg = $this->server_parse('250', __LINE__)) 1457 { 1458 // a 503 response code means that we're already authenticated 1459 if ($this->numeric_response_code == 503) 1460 { 1461 return false; 1462 } 1463 1464 // If EHLO fails, we try HELO 1465 $this->server_send("HELO $hostname"); 1466 if ($err_msg = $this->server_parse('250', __LINE__)) 1467 { 1468 return ($this->numeric_response_code == 503) ? false : $err_msg; 1469 } 1470 } 1471 1472 foreach ($this->responses as $response) 1473 { 1474 $response = explode(' ', $response); 1475 $response_code = $response[0]; 1476 unset($response[0]); 1477 $this->commands[$response_code] = implode(' ', $response); 1478 } 1479 } 1480 1481 /** 1482 * SMTP STARTTLS (RFC 3207) 1483 * 1484 * @return bool Returns true if TLS was started 1485 * Otherwise false 1486 */ 1487 protected function starttls() 1488 { 1489 if (!function_exists('stream_socket_enable_crypto')) 1490 { 1491 return false; 1492 } 1493 1494 if (!isset($this->commands['STARTTLS'])) 1495 { 1496 return false; 1497 } 1498 1499 $this->server_send('STARTTLS'); 1500 1501 if ($err_msg = $this->server_parse('220', __LINE__)) 1502 { 1503 return false; 1504 } 1505 1506 $result = false; 1507 $stream_meta = stream_get_meta_data($this->socket); 1508 1509 if (socket_set_blocking($this->socket, 1)) 1510 { 1511 $result = stream_socket_enable_crypto($this->socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); 1512 socket_set_blocking($this->socket, (int) $stream_meta['blocked']); 1513 } 1514 1515 return $result; 1516 } 1517 1518 /** 1519 * Pop before smtp authentication 1520 */ 1521 function pop_before_smtp($hostname, $username, $password) 1522 { 1523 global $user; 1524 1525 if (!$this->socket = @fsockopen($hostname, 110, $errno, $errstr, 10)) 1526 { 1527 if ($errstr) 1528 { 1529 $errstr = utf8_convert_message($errstr); 1530 } 1531 1532 return (isset($user->lang['NO_CONNECT_TO_SMTP_HOST'])) ? sprintf($user->lang['NO_CONNECT_TO_SMTP_HOST'], $errno, $errstr) : "Could not connect to smtp host : $errno : $errstr"; 1533 } 1534 1535 $this->server_send("USER $username", true); 1536 if ($err_msg = $this->server_parse('+OK', __LINE__)) 1537 { 1538 return $err_msg; 1539 } 1540 1541 $this->server_send("PASS $password", true); 1542 if ($err_msg = $this->server_parse('+OK', __LINE__)) 1543 { 1544 return $err_msg; 1545 } 1546 1547 $this->server_send('QUIT'); 1548 fclose($this->socket); 1549 1550 return false; 1551 } 1552 1553 /** 1554 * Plain authentication method 1555 */ 1556 function plain($username, $password) 1557 { 1558 $this->server_send('AUTH PLAIN'); 1559 if ($err_msg = $this->server_parse('334', __LINE__)) 1560 { 1561 return ($this->numeric_response_code == 503) ? false : $err_msg; 1562 } 1563 1564 $base64_method_plain = base64_encode("\0" . $username . "\0" . $password); 1565 $this->server_send($base64_method_plain, true); 1566 if ($err_msg = $this->server_parse('235', __LINE__)) 1567 { 1568 return $err_msg; 1569 } 1570 1571 return false; 1572 } 1573 1574 /** 1575 * Login authentication method 1576 */ 1577 function login($username, $password) 1578 { 1579 $this->server_send('AUTH LOGIN'); 1580 if ($err_msg = $this->server_parse('334', __LINE__)) 1581 { 1582 return ($this->numeric_response_code == 503) ? false : $err_msg; 1583 } 1584 1585 $this->server_send(base64_encode($username), true); 1586 if ($err_msg = $this->server_parse('334', __LINE__)) 1587 { 1588 return $err_msg; 1589 } 1590 1591 $this->server_send(base64_encode($password), true); 1592 if ($err_msg = $this->server_parse('235', __LINE__)) 1593 { 1594 return $err_msg; 1595 } 1596 1597 return false; 1598 } 1599 1600 /** 1601 * cram_md5 authentication method 1602 */ 1603 function cram_md5($username, $password) 1604 { 1605 $this->server_send('AUTH CRAM-MD5'); 1606 if ($err_msg = $this->server_parse('334', __LINE__)) 1607 { 1608 return ($this->numeric_response_code == 503) ? false : $err_msg; 1609 } 1610 1611 $md5_challenge = base64_decode($this->responses[0]); 1612 $password = (strlen($password) > 64) ? pack('H32', md5($password)) : ((strlen($password) < 64) ? str_pad($password, 64, chr(0)) : $password); 1613 $md5_digest = md5((substr($password, 0, 64) ^ str_repeat(chr(0x5C), 64)) . (pack('H32', md5((substr($password, 0, 64) ^ str_repeat(chr(0x36), 64)) . $md5_challenge)))); 1614 1615 $base64_method_cram_md5 = base64_encode($username . ' ' . $md5_digest); 1616 1617 $this->server_send($base64_method_cram_md5, true); 1618 if ($err_msg = $this->server_parse('235', __LINE__)) 1619 { 1620 return $err_msg; 1621 } 1622 1623 return false; 1624 } 1625 1626 /** 1627 * digest_md5 authentication method 1628 * A real pain in the *** 1629 */ 1630 function digest_md5($username, $password) 1631 { 1632 global $config, $user; 1633 1634 $this->server_send('AUTH DIGEST-MD5'); 1635 if ($err_msg = $this->server_parse('334', __LINE__)) 1636 { 1637 return ($this->numeric_response_code == 503) ? false : $err_msg; 1638 } 1639 1640 $md5_challenge = base64_decode($this->responses[0]); 1641 1642 // Parse the md5 challenge - from AUTH_SASL (PEAR) 1643 $tokens = array(); 1644 while (preg_match('/^([a-z-]+)=("[^"]+(?<!\\\)"|[^,]+)/i', $md5_challenge, $matches)) 1645 { 1646 // Ignore these as per rfc2831 1647 if ($matches[1] == 'opaque' || $matches[1] == 'domain') 1648 { 1649 $md5_challenge = substr($md5_challenge, strlen($matches[0]) + 1); 1650 continue; 1651 } 1652 1653 // Allowed multiple "realm" and "auth-param" 1654 if (!empty($tokens[$matches[1]]) && ($matches[1] == 'realm' || $matches[1] == 'auth-param')) 1655 { 1656 if (is_array($tokens[$matches[1]])) 1657 { 1658 $tokens[$matches[1]][] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]); 1659 } 1660 else 1661 { 1662 $tokens[$matches[1]] = array($tokens[$matches[1]], preg_replace('/^"(.*)"$/', '\\1', $matches[2])); 1663 } 1664 } 1665 else if (!empty($tokens[$matches[1]])) // Any other multiple instance = failure 1666 { 1667 $tokens = array(); 1668 break; 1669 } 1670 else 1671 { 1672 $tokens[$matches[1]] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]); 1673 } 1674 1675 // Remove the just parsed directive from the challenge 1676 $md5_challenge = substr($md5_challenge, strlen($matches[0]) + 1); 1677 } 1678 1679 // Realm 1680 if (empty($tokens['realm'])) 1681 { 1682 $tokens['realm'] = (function_exists('php_uname')) ? php_uname('n') : $user->host; 1683 } 1684 1685 // Maxbuf 1686 if (empty($tokens['maxbuf'])) 1687 { 1688 $tokens['maxbuf'] = 65536; 1689 } 1690 1691 // Required: nonce, algorithm 1692 if (empty($tokens['nonce']) || empty($tokens['algorithm'])) 1693 { 1694 $tokens = array(); 1695 } 1696 $md5_challenge = $tokens; 1697 1698 if (!empty($md5_challenge)) 1699 { 1700 $str = ''; 1701 for ($i = 0; $i < 32; $i++) 1702 { 1703 $str .= chr(mt_rand(0, 255)); 1704 } 1705 $cnonce = base64_encode($str); 1706 1707 $digest_uri = 'smtp/' . $config['smtp_host']; 1708 1709 $auth_1 = sprintf('%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $username, $md5_challenge['realm'], $password))), $md5_challenge['nonce'], $cnonce); 1710 $auth_2 = 'AUTHENTICATE:' . $digest_uri; 1711 $response_value = md5(sprintf('%s:%s:00000001:%s:auth:%s', md5($auth_1), $md5_challenge['nonce'], $cnonce, md5($auth_2))); 1712 1713 $input_string = sprintf('username="%s",realm="%s",nonce="%s",cnonce="%s",nc="00000001",qop=auth,digest-uri="%s",response=%s,%d', $username, $md5_challenge['realm'], $md5_challenge['nonce'], $cnonce, $digest_uri, $response_value, $md5_challenge['maxbuf']); 1714 } 1715 else 1716 { 1717 return (isset($user->lang['INVALID_DIGEST_CHALLENGE'])) ? $user->lang['INVALID_DIGEST_CHALLENGE'] : 'Invalid digest challenge'; 1718 } 1719 1720 $base64_method_digest_md5 = base64_encode($input_string); 1721 $this->server_send($base64_method_digest_md5, true); 1722 if ($err_msg = $this->server_parse('334', __LINE__)) 1723 { 1724 return $err_msg; 1725 } 1726 1727 $this->server_send(' '); 1728 if ($err_msg = $this->server_parse('235', __LINE__)) 1729 { 1730 return $err_msg; 1731 } 1732 1733 return false; 1734 } 1735 } 1736 1737 /** 1738 * Encodes the given string for proper display in UTF-8. 1739 * 1740 * This version is using base64 encoded data. The downside of this 1741 * is if the mail client does not understand this encoding the user 1742 * is basically doomed with an unreadable subject. 1743 * 1744 * Please note that this version fully supports RFC 2045 section 6.8. 1745 * 1746 * @param string $eol End of line we are using (optional to be backwards compatible) 1747 */ 1748 function mail_encode($str, $eol = "\r\n") 1749 { 1750 // define start delimimter, end delimiter and spacer 1751 $start = "=?UTF-8?B?"; 1752 $end = "?="; 1753 $delimiter = "$eol "; 1754 1755 // Maximum length is 75. $split_length *must* be a multiple of 4, but <= 75 - strlen($start . $delimiter . $end)!!! 1756 $split_length = 60; 1757 $encoded_str = base64_encode($str); 1758 1759 // If encoded string meets the limits, we just return with the correct data. 1760 if (strlen($encoded_str) <= $split_length) 1761 { 1762 return $start . $encoded_str . $end; 1763 } 1764 1765 // If there is only ASCII data, we just return what we want, correctly splitting the lines. 1766 if (strlen($str) === utf8_strlen($str)) 1767 { 1768 return $start . implode($end . $delimiter . $start, str_split($encoded_str, $split_length)) . $end; 1769 } 1770 1771 // UTF-8 data, compose encoded lines 1772 $array = utf8_str_split($str); 1773 $str = ''; 1774 1775 while (sizeof($array)) 1776 { 1777 $text = ''; 1778 1779 while (sizeof($array) && intval((strlen($text . $array[0]) + 2) / 3) << 2 <= $split_length) 1780 { 1781 $text .= array_shift($array); 1782 } 1783 1784 $str .= $start . base64_encode($text) . $end . $delimiter; 1785 } 1786 1787 return substr($str, 0, -strlen($delimiter)); 1788 } 1789 1790 /** 1791 * Wrapper for sending out emails with the PHP's mail function 1792 */ 1793 function phpbb_mail($to, $subject, $msg, $headers, $eol, &$err_msg) 1794 { 1795 global $config, $phpbb_root_path, $phpEx; 1796 1797 // We use the EOL character for the OS here because the PHP mail function does not correctly transform line endings. On Windows SMTP is used (SMTP is \r\n), on UNIX a command is used... 1798 // Reference: http://bugs.php.net/bug.php?id=15841 1799 $headers = implode($eol, $headers); 1800 1801 if (!class_exists('\phpbb\error_collector')) 1802 { 1803 include($phpbb_root_path . 'includes/error_collector.' . $phpEx); 1804 } 1805 1806 $collector = new \phpbb\error_collector; 1807 $collector->install(); 1808 1809 // On some PHP Versions mail() *may* fail if there are newlines within the subject. 1810 // Newlines are used as a delimiter for lines in mail_encode() according to RFC 2045 section 6.8. 1811 // Because PHP can't decide what is wanted we revert back to the non-RFC-compliant way of separating by one space (Use '' as parameter to mail_encode() results in SPACE used) 1812 $result = $config['email_function_name']($to, mail_encode($subject, ''), wordwrap(utf8_wordwrap($msg), 997, "\n", true), $headers); 1813 1814 $collector->uninstall(); 1815 $err_msg = $collector->format_errors(); 1816 1817 return $result; 1818 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Jan 11 00:25:41 2018 | Cross-referenced by PHPXref 0.7.1 |