Verzeichnisstruktur phpBB-3.1.0
- Veröffentlicht
- 27.10.2014
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
functions_messenger.php
0001 <?php
0002 /**
0003 *
0004 * This file is part of the phpBB Forum Software package.
0005 *
0006 * @copyright (c) phpBB Limited <https://www.phpbb.com>
0007 * @license GNU General Public License, version 2 (GPL-2.0)
0008 *
0009 * For full copyright and license information, please see
0010 * the docs/CREDITS.txt file.
0011 *
0012 */
0013
0014 /**
0015 * @ignore
0016 */
0017 if (!defined('IN_PHPBB'))
0018 {
0019 exit;
0020 }
0021
0022 /**
0023 * Messenger
0024 */
0025 class messenger
0026 {
0027 var $msg, $extra_headers, $replyto, $from, $subject;
0028 var $addresses = array();
0029
0030 var $mail_priority = MAIL_NORMAL_PRIORITY;
0031 var $use_queue = true;
0032
0033 /** @var \phpbb\template\template */
0034 protected $template;
0035
0036 var $eol = "\n";
0037
0038 /**
0039 * Constructor
0040 */
0041 function messenger($use_queue = true)
0042 {
0043 global $config;
0044
0045 $this->use_queue = (!$config['email_package_size']) ? false : $use_queue;
0046 $this->subject = '';
0047
0048 // Determine EOL character (\n for UNIX, \r\n for Windows and \r for Mac)
0049 $this->eol = (!defined('PHP_EOL')) ? (($eol = strtolower(substr(PHP_OS, 0, 3))) == 'win') ? "\r\n" : (($eol == 'mac') ? "\r" : "\n") : PHP_EOL;
0050 $this->eol = (!$this->eol) ? "\n" : $this->eol;
0051 }
0052
0053 /**
0054 * Resets all the data (address, template file, etc etc) to default
0055 */
0056 function reset()
0057 {
0058 $this->addresses = $this->extra_headers = array();
0059 $this->msg = $this->replyto = $this->from = '';
0060 $this->mail_priority = MAIL_NORMAL_PRIORITY;
0061 }
0062
0063 /**
0064 * Set addresses for to/im as available
0065 *
0066 * @param array $user User row
0067 */
0068 function set_addresses($user)
0069 {
0070 if (isset($user['user_email']) && $user['user_email'])
0071 {
0072 $this->to($user['user_email'], (isset($user['username']) ? $user['username'] : ''));
0073 }
0074
0075 if (isset($user['user_jabber']) && $user['user_jabber'])
0076 {
0077 $this->im($user['user_jabber'], (isset($user['username']) ? $user['username'] : ''));
0078 }
0079 }
0080
0081 /**
0082 * Sets an email address to send to
0083 */
0084 function to($address, $realname = '')
0085 {
0086 global $config;
0087
0088 if (!trim($address))
0089 {
0090 return;
0091 }
0092
0093 $pos = isset($this->addresses['to']) ? sizeof($this->addresses['to']) : 0;
0094
0095 $this->addresses['to'][$pos]['email'] = trim($address);
0096
0097 // If empty sendmail_path on windows, PHP changes the to line
0098 if (!$config['smtp_delivery'] && DIRECTORY_SEPARATOR == '\\')
0099 {
0100 $this->addresses['to'][$pos]['name'] = '';
0101 }
0102 else
0103 {
0104 $this->addresses['to'][$pos]['name'] = trim($realname);
0105 }
0106 }
0107
0108 /**
0109 * Sets an cc address to send to
0110 */
0111 function cc($address, $realname = '')
0112 {
0113 if (!trim($address))
0114 {
0115 return;
0116 }
0117
0118 $pos = isset($this->addresses['cc']) ? sizeof($this->addresses['cc']) : 0;
0119 $this->addresses['cc'][$pos]['email'] = trim($address);
0120 $this->addresses['cc'][$pos]['name'] = trim($realname);
0121 }
0122
0123 /**
0124 * Sets an bcc address to send to
0125 */
0126 function bcc($address, $realname = '')
0127 {
0128 if (!trim($address))
0129 {
0130 return;
0131 }
0132
0133 $pos = isset($this->addresses['bcc']) ? sizeof($this->addresses['bcc']) : 0;
0134 $this->addresses['bcc'][$pos]['email'] = trim($address);
0135 $this->addresses['bcc'][$pos]['name'] = trim($realname);
0136 }
0137
0138 /**
0139 * Sets a im contact to send to
0140 */
0141 function im($address, $realname = '')
0142 {
0143 // IM-Addresses could be empty
0144 if (!trim($address))
0145 {
0146 return;
0147 }
0148
0149 $pos = isset($this->addresses['im']) ? sizeof($this->addresses['im']) : 0;
0150 $this->addresses['im'][$pos]['uid'] = trim($address);
0151 $this->addresses['im'][$pos]['name'] = trim($realname);
0152 }
0153
0154 /**
0155 * Set the reply to address
0156 */
0157 function replyto($address)
0158 {
0159 $this->replyto = trim($address);
0160 }
0161
0162 /**
0163 * Set the from address
0164 */
0165 function from($address)
0166 {
0167 $this->from = trim($address);
0168 }
0169
0170 /**
0171 * set up subject for mail
0172 */
0173 function subject($subject = '')
0174 {
0175 $this->subject = trim($subject);
0176 }
0177
0178 /**
0179 * set up extra mail headers
0180 */
0181 function headers($headers)
0182 {
0183 $this->extra_headers[] = trim($headers);
0184 }
0185
0186 /**
0187 * Adds X-AntiAbuse headers
0188 *
0189 * @param array $config Configuration array
0190 * @param user $user A user object
0191 *
0192 * @return null
0193 */
0194 function anti_abuse_headers($config, $user)
0195 {
0196 $this->headers('X-AntiAbuse: Board servername - ' . mail_encode($config['server_name']));
0197 $this->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']);
0198 $this->headers('X-AntiAbuse: Username - ' . mail_encode($user->data['username']));
0199 $this->headers('X-AntiAbuse: User IP - ' . $user->ip);
0200 }
0201
0202 /**
0203 * Set the email priority
0204 */
0205 function set_mail_priority($priority = MAIL_NORMAL_PRIORITY)
0206 {
0207 $this->mail_priority = $priority;
0208 }
0209
0210 /**
0211 * Set email template to use
0212 */
0213 function template($template_file, $template_lang = '', $template_path = '')
0214 {
0215 global $config, $phpbb_root_path, $phpEx, $user, $phpbb_extension_manager;
0216
0217 $this->setup_template();
0218
0219 if (!trim($template_file))
0220 {
0221 trigger_error('No template file for emailing set.', E_USER_ERROR);
0222 }
0223
0224 if (!trim($template_lang))
0225 {
0226 // fall back to board default language if the user's language is
0227 // missing $template_file. If this does not exist either,
0228 // $this->template->set_filenames will do a trigger_error
0229 $template_lang = basename($config['default_lang']);
0230 }
0231
0232 if ($template_path)
0233 {
0234 $template_paths = array(
0235 $template_path,
0236 );
0237 }
0238 else
0239 {
0240 $template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/';
0241 $template_path .= $template_lang . '/email';
0242
0243 $template_paths = array(
0244 $template_path,
0245 );
0246
0247 // we can only specify default language fallback when the path is not a custom one for which we
0248 // do not know the default language alternative
0249 if ($template_lang !== basename($config['default_lang']))
0250 {
0251 $fallback_template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/';
0252 $fallback_template_path .= basename($config['default_lang']) . '/email';
0253
0254 $template_paths[] = $fallback_template_path;
0255 }
0256 }
0257
0258 $this->set_template_paths(array(
0259 array(
0260 'name' => $template_lang . '_email',
0261 'ext_path' => 'language/' . $template_lang . '/email'
0262 ),
0263 ), $template_paths);
0264
0265 $this->template->set_filenames(array(
0266 'body' => $template_file . '.txt',
0267 ));
0268
0269 return true;
0270 }
0271
0272 /**
0273 * assign variables to email template
0274 */
0275 function assign_vars($vars)
0276 {
0277 $this->setup_template();
0278
0279 $this->template->assign_vars($vars);
0280 }
0281
0282 function assign_block_vars($blockname, $vars)
0283 {
0284 $this->setup_template();
0285
0286 $this->template->assign_block_vars($blockname, $vars);
0287 }
0288
0289 /**
0290 * Send the mail out to the recipients set previously in var $this->addresses
0291 */
0292 function send($method = NOTIFY_EMAIL, $break = false)
0293 {
0294 global $config, $user;
0295
0296 // We add some standard variables we always use, no need to specify them always
0297 $this->assign_vars(array(
0298 'U_BOARD' => generate_board_url(),
0299 'EMAIL_SIG' => str_replace('<br />', "\n", "-- \n" . htmlspecialchars_decode($config['board_email_sig'])),
0300 'SITENAME' => htmlspecialchars_decode($config['sitename']),
0301 ));
0302
0303 // Parse message through template
0304 $this->msg = trim($this->template->assign_display('body'));
0305
0306 // 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
0307 $this->msg = str_replace("\r\n", "\n", $this->msg);
0308
0309 // We now try and pull a subject from the email body ... if it exists,
0310 // do this here because the subject may contain a variable
0311 $drop_header = '';
0312 $match = array();
0313 if (preg_match('#^(Subject:(.*?))$#m', $this->msg, $match))
0314 {
0315 $this->subject = (trim($match[2]) != '') ? trim($match[2]) : (($this->subject != '') ? $this->subject : $user->lang['NO_EMAIL_SUBJECT']);
0316 $drop_header .= '[\r\n]*?' . preg_quote($match[1], '#');
0317 }
0318 else
0319 {
0320 $this->subject = (($this->subject != '') ? $this->subject : $user->lang['NO_EMAIL_SUBJECT']);
0321 }
0322
0323 if ($drop_header)
0324 {
0325 $this->msg = trim(preg_replace('#' . $drop_header . '#s', '', $this->msg));
0326 }
0327
0328 if ($break)
0329 {
0330 return true;
0331 }
0332
0333 switch ($method)
0334 {
0335 case NOTIFY_EMAIL:
0336 $result = $this->msg_email();
0337 break;
0338
0339 case NOTIFY_IM:
0340 $result = $this->msg_jabber();
0341 break;
0342
0343 case NOTIFY_BOTH:
0344 $result = $this->msg_email();
0345 $this->msg_jabber();
0346 break;
0347 }
0348
0349 $this->reset();
0350 return $result;
0351 }
0352
0353 /**
0354 * Add error message to log
0355 */
0356 function error($type, $msg)
0357 {
0358 global $user, $phpEx, $phpbb_root_path, $config, $request;
0359
0360 // Session doesn't exist, create it
0361 if (!isset($user->session_id) || $user->session_id === '')
0362 {
0363 $user->session_begin();
0364 }
0365
0366 $calling_page = htmlspecialchars_decode($request->server('PHP_SELF'));
0367
0368 $message = '';
0369 switch ($type)
0370 {
0371 case 'EMAIL':
0372 $message = '<strong>EMAIL/' . (($config['smtp_delivery']) ? 'SMTP' : 'PHP/' . $config['email_function_name'] . '()') . '</strong>';
0373 break;
0374
0375 default:
0376 $message = "<strong>$type</strong>";
0377 break;
0378 }
0379
0380 $message .= '<br /><em>' . htmlspecialchars($calling_page) . '</em><br /><br />' . $msg . '<br />';
0381 add_log('critical', 'LOG_ERROR_' . $type, $message);
0382 }
0383
0384 /**
0385 * Save to queue
0386 */
0387 function save_queue()
0388 {
0389 global $config;
0390
0391 if ($config['email_package_size'] && $this->use_queue && !empty($this->queue))
0392 {
0393 $this->queue->save();
0394 return;
0395 }
0396 }
0397
0398 /**
0399 * Generates a valid message id to be used in emails
0400 *
0401 * @return string message id
0402 */
0403 function generate_message_id()
0404 {
0405 global $config, $request;
0406
0407 $domain = ($config['server_name']) ?: $request->server('SERVER_NAME', 'phpbb.generated');
0408
0409 return md5(unique_id(time())) . '@' . $domain;
0410 }
0411
0412 /**
0413 * Return email header
0414 */
0415 function build_header($to, $cc, $bcc)
0416 {
0417 global $config;
0418
0419 // We could use keys here, but we won't do this for 3.0.x to retain backwards compatibility
0420 $headers = array();
0421
0422 $headers[] = 'From: ' . $this->from;
0423
0424 if ($cc)
0425 {
0426 $headers[] = 'Cc: ' . $cc;
0427 }
0428
0429 if ($bcc)
0430 {
0431 $headers[] = 'Bcc: ' . $bcc;
0432 }
0433
0434 $headers[] = 'Reply-To: ' . $this->replyto;
0435 $headers[] = 'Return-Path: <' . $config['board_email'] . '>';
0436 $headers[] = 'Sender: <' . $config['board_email'] . '>';
0437 $headers[] = 'MIME-Version: 1.0';
0438 $headers[] = 'Message-ID: <' . $this->generate_message_id() . '>';
0439 $headers[] = 'Date: ' . date('r', time());
0440 $headers[] = 'Content-Type: text/plain; charset=UTF-8'; // format=flowed
0441 $headers[] = 'Content-Transfer-Encoding: 8bit'; // 7bit
0442
0443 $headers[] = 'X-Priority: ' . $this->mail_priority;
0444 $headers[] = 'X-MSMail-Priority: ' . (($this->mail_priority == MAIL_LOW_PRIORITY) ? 'Low' : (($this->mail_priority == MAIL_NORMAL_PRIORITY) ? 'Normal' : 'High'));
0445 $headers[] = 'X-Mailer: phpBB3';
0446 $headers[] = 'X-MimeOLE: phpBB3';
0447 $headers[] = 'X-phpBB-Origin: phpbb://' . str_replace(array('http://', 'https://'), array('', ''), generate_board_url());
0448
0449 if (sizeof($this->extra_headers))
0450 {
0451 $headers = array_merge($headers, $this->extra_headers);
0452 }
0453
0454 return $headers;
0455 }
0456
0457 /**
0458 * Send out emails
0459 */
0460 function msg_email()
0461 {
0462 global $config, $user;
0463
0464 if (empty($config['email_enable']))
0465 {
0466 return false;
0467 }
0468
0469 // Addresses to send to?
0470 if (empty($this->addresses) || (empty($this->addresses['to']) && empty($this->addresses['cc']) && empty($this->addresses['bcc'])))
0471 {
0472 // Send was successful. ;)
0473 return true;
0474 }
0475
0476 $use_queue = false;
0477 if ($config['email_package_size'] && $this->use_queue)
0478 {
0479 if (empty($this->queue))
0480 {
0481 $this->queue = new queue();
0482 $this->queue->init('email', $config['email_package_size']);
0483 }
0484 $use_queue = true;
0485 }
0486
0487 $contact_name = htmlspecialchars_decode($config['board_contact_name']);
0488 $board_contact = (($contact_name !== '') ? '"' . mail_encode($contact_name) . '" ' : '') . '<' . $config['board_contact'] . '>';
0489
0490 if (empty($this->replyto))
0491 {
0492 $this->replyto = $board_contact;
0493 }
0494
0495 if (empty($this->from))
0496 {
0497 $this->from = $board_contact;
0498 }
0499
0500 $encode_eol = ($config['smtp_delivery']) ? "\r\n" : $this->eol;
0501
0502 // Build to, cc and bcc strings
0503 $to = $cc = $bcc = '';
0504 foreach ($this->addresses as $type => $address_ary)
0505 {
0506 if ($type == 'im')
0507 {
0508 continue;
0509 }
0510
0511 foreach ($address_ary as $which_ary)
0512 {
0513 $$type .= (($$type != '') ? ', ' : '') . (($which_ary['name'] != '') ? mail_encode($which_ary['name'], $encode_eol) . ' <' . $which_ary['email'] . '>' : $which_ary['email']);
0514 }
0515 }
0516
0517 // Build header
0518 $headers = $this->build_header($to, $cc, $bcc);
0519
0520 // Send message ...
0521 if (!$use_queue)
0522 {
0523 $mail_to = ($to == '') ? 'undisclosed-recipients:;' : $to;
0524 $err_msg = '';
0525
0526 if ($config['smtp_delivery'])
0527 {
0528 $result = smtpmail($this->addresses, mail_encode($this->subject), wordwrap(utf8_wordwrap($this->msg), 997, "\n", true), $err_msg, $headers);
0529 }
0530 else
0531 {
0532 $result = phpbb_mail($mail_to, $this->subject, $this->msg, $headers, $this->eol, $err_msg);
0533 }
0534
0535 if (!$result)
0536 {
0537 $this->error('EMAIL', $err_msg);
0538 return false;
0539 }
0540 }
0541 else
0542 {
0543 $this->queue->put('email', array(
0544 'to' => $to,
0545 'addresses' => $this->addresses,
0546 'subject' => $this->subject,
0547 'msg' => $this->msg,
0548 'headers' => $headers)
0549 );
0550 }
0551
0552 return true;
0553 }
0554
0555 /**
0556 * Send jabber message out
0557 */
0558 function msg_jabber()
0559 {
0560 global $config, $db, $user, $phpbb_root_path, $phpEx;
0561
0562 if (empty($config['jab_enable']) || empty($config['jab_host']) || empty($config['jab_username']) || empty($config['jab_password']))
0563 {
0564 return false;
0565 }
0566
0567 if (empty($this->addresses['im']))
0568 {
0569 // Send was successful. ;)
0570 return true;
0571 }
0572
0573 $use_queue = false;
0574 if ($config['jab_package_size'] && $this->use_queue)
0575 {
0576 if (empty($this->queue))
0577 {
0578 $this->queue = new queue();
0579 $this->queue->init('jabber', $config['jab_package_size']);
0580 }
0581 $use_queue = true;
0582 }
0583
0584 $addresses = array();
0585 foreach ($this->addresses['im'] as $type => $uid_ary)
0586 {
0587 $addresses[] = $uid_ary['uid'];
0588 }
0589 $addresses = array_unique($addresses);
0590
0591 if (!$use_queue)
0592 {
0593 include_once($phpbb_root_path . 'includes/functions_jabber.' . $phpEx);
0594 $this->jabber = new jabber($config['jab_host'], $config['jab_port'], $config['jab_username'], htmlspecialchars_decode($config['jab_password']), $config['jab_use_ssl']);
0595
0596 if (!$this->jabber->connect())
0597 {
0598 $this->error('JABBER', $user->lang['ERR_JAB_CONNECT'] . '<br />' . $this->jabber->get_log());
0599 return false;
0600 }
0601
0602 if (!$this->jabber->login())
0603 {
0604 $this->error('JABBER', $user->lang['ERR_JAB_AUTH'] . '<br />' . $this->jabber->get_log());
0605 return false;
0606 }
0607
0608 foreach ($addresses as $address)
0609 {
0610 $this->jabber->send_message($address, $this->msg, $this->subject);
0611 }
0612
0613 $this->jabber->disconnect();
0614 }
0615 else
0616 {
0617 $this->queue->put('jabber', array(
0618 'addresses' => $addresses,
0619 'subject' => $this->subject,
0620 'msg' => $this->msg)
0621 );
0622 }
0623 unset($addresses);
0624 return true;
0625 }
0626
0627 /**
0628 * Setup template engine
0629 */
0630 protected function setup_template()
0631 {
0632 global $config, $phpbb_path_helper, $user, $phpbb_extension_manager;
0633
0634 if ($this->template instanceof \phpbb\template\template)
0635 {
0636 return;
0637 }
0638
0639 $this->template = new \phpbb\template\twig\twig($phpbb_path_helper, $config, $user, new \phpbb\template\context(), $phpbb_extension_manager);
0640 }
0641
0642 /**
0643 * Set template paths to load
0644 */
0645 protected function set_template_paths($path_name, $paths)
0646 {
0647 $this->setup_template();
0648
0649 $this->template->set_custom_style($path_name, $paths);
0650 }
0651 }
0652
0653 /**
0654 * handling email and jabber queue
0655 */
0656 class queue
0657 {
0658 var $data = array();
0659 var $queue_data = array();
0660 var $package_size = 0;
0661 var $cache_file = '';
0662 var $eol = "\n";
0663
0664 /**
0665 * constructor
0666 */
0667 function queue()
0668 {
0669 global $phpEx, $phpbb_root_path;
0670
0671 $this->data = array();
0672 $this->cache_file = "{$phpbb_root_path}cache/queue.$phpEx";
0673
0674 // Determine EOL character (\n for UNIX, \r\n for Windows and \r for Mac)
0675 $this->eol = (!defined('PHP_EOL')) ? (($eol = strtolower(substr(PHP_OS, 0, 3))) == 'win') ? "\r\n" : (($eol == 'mac') ? "\r" : "\n") : PHP_EOL;
0676 $this->eol = (!$this->eol) ? "\n" : $this->eol;
0677 }
0678
0679 /**
0680 * Init a queue object
0681 */
0682 function init($object, $package_size)
0683 {
0684 $this->data[$object] = array();
0685 $this->data[$object]['package_size'] = $package_size;
0686 $this->data[$object]['data'] = array();
0687 }
0688
0689 /**
0690 * Put object in queue
0691 */
0692 function put($object, $scope)
0693 {
0694 $this->data[$object]['data'][] = $scope;
0695 }
0696
0697 /**
0698 * Process queue
0699 * Using lock file
0700 */
0701 function process()
0702 {
0703 global $db, $config, $phpEx, $phpbb_root_path, $user;
0704
0705 $lock = new \phpbb\lock\flock($this->cache_file);
0706 $lock->acquire();
0707
0708 // avoid races, check file existence once
0709 $have_cache_file = file_exists($this->cache_file);
0710 if (!$have_cache_file || $config['last_queue_run'] > time() - $config['queue_interval'])
0711 {
0712 if (!$have_cache_file)
0713 {
0714 set_config('last_queue_run', time(), true);
0715 }
0716
0717 $lock->release();
0718 return;
0719 }
0720
0721 set_config('last_queue_run', time(), true);
0722
0723 include($this->cache_file);
0724
0725 foreach ($this->queue_data as $object => $data_ary)
0726 {
0727 @set_time_limit(0);
0728
0729 if (!isset($data_ary['package_size']))
0730 {
0731 $data_ary['package_size'] = 0;
0732 }
0733
0734 $package_size = $data_ary['package_size'];
0735 $num_items = (!$package_size || sizeof($data_ary['data']) < $package_size) ? sizeof($data_ary['data']) : $package_size;
0736
0737 /*
0738 * This code is commented out because it causes problems on some web hosts.
0739 * The core problem is rather restrictive email sending limits.
0740 * This code is nly useful if you have no such restrictions from the
0741 * web host and the package size setting is wrong.
0742
0743 // If the amount of emails to be sent is way more than package_size than we need to increase it to prevent backlogs...
0744 if (sizeof($data_ary['data']) > $package_size * 2.5)
0745 {
0746 $num_items = sizeof($data_ary['data']);
0747 }
0748 */
0749
0750 switch ($object)
0751 {
0752 case 'email':
0753 // Delete the email queued objects if mailing is disabled
0754 if (!$config['email_enable'])
0755 {
0756 unset($this->queue_data['email']);
0757 continue 2;
0758 }
0759 break;
0760
0761 case 'jabber':
0762 if (!$config['jab_enable'])
0763 {
0764 unset($this->queue_data['jabber']);
0765 continue 2;
0766 }
0767
0768 include_once($phpbb_root_path . 'includes/functions_jabber.' . $phpEx);
0769 $this->jabber = new jabber($config['jab_host'], $config['jab_port'], $config['jab_username'], htmlspecialchars_decode($config['jab_password']), $config['jab_use_ssl']);
0770
0771 if (!$this->jabber->connect())
0772 {
0773 $messenger = new messenger();
0774 $messenger->error('JABBER', $user->lang['ERR_JAB_CONNECT']);
0775 continue 2;
0776 }
0777
0778 if (!$this->jabber->login())
0779 {
0780 $messenger = new messenger();
0781 $messenger->error('JABBER', $user->lang['ERR_JAB_AUTH']);
0782 continue 2;
0783 }
0784
0785 break;
0786
0787 default:
0788 $lock->release();
0789 return;
0790 }
0791
0792 for ($i = 0; $i < $num_items; $i++)
0793 {
0794 // Make variables available...
0795 extract(array_shift($this->queue_data[$object]['data']));
0796
0797 switch ($object)
0798 {
0799 case 'email':
0800 $err_msg = '';
0801 $to = (!$to) ? 'undisclosed-recipients:;' : $to;
0802
0803 if ($config['smtp_delivery'])
0804 {
0805 $result = smtpmail($addresses, mail_encode($subject), wordwrap(utf8_wordwrap($msg), 997, "\n", true), $err_msg, $headers);
0806 }
0807 else
0808 {
0809 $result = phpbb_mail($to, $subject, $msg, $headers, $this->eol, $err_msg);
0810 }
0811
0812 if (!$result)
0813 {
0814 $messenger = new messenger();
0815 $messenger->error('EMAIL', $err_msg);
0816 continue 2;
0817 }
0818 break;
0819
0820 case 'jabber':
0821 foreach ($addresses as $address)
0822 {
0823 if ($this->jabber->send_message($address, $msg, $subject) === false)
0824 {
0825 $messenger = new messenger();
0826 $messenger->error('JABBER', $this->jabber->get_log());
0827 continue 3;
0828 }
0829 }
0830 break;
0831 }
0832 }
0833
0834 // No more data for this object? Unset it
0835 if (!sizeof($this->queue_data[$object]['data']))
0836 {
0837 unset($this->queue_data[$object]);
0838 }
0839
0840 // Post-object processing
0841 switch ($object)
0842 {
0843 case 'jabber':
0844 // Hang about a couple of secs to ensure the messages are
0845 // handled, then disconnect
0846 $this->jabber->disconnect();
0847 break;
0848 }
0849 }
0850
0851 if (!sizeof($this->queue_data))
0852 {
0853 @unlink($this->cache_file);
0854 }
0855 else
0856 {
0857 if ($fp = @fopen($this->cache_file, 'wb'))
0858 {
0859 fwrite($fp, "<?php\nif (!defined('IN_PHPBB')) exit;\n\$this->queue_data = unserialize(" . var_export(serialize($this->queue_data), true) . ");\n\n?>");
0860 fclose($fp);
0861
0862 phpbb_chmod($this->cache_file, CHMOD_READ | CHMOD_WRITE);
0863 }
0864 }
0865
0866 $lock->release();
0867 }
0868
0869 /**
0870 * Save queue
0871 */
0872 function save()
0873 {
0874 if (!sizeof($this->data))
0875 {
0876 return;
0877 }
0878
0879 $lock = new \phpbb\lock\flock($this->cache_file);
0880 $lock->acquire();
0881
0882 if (file_exists($this->cache_file))
0883 {
0884 include($this->cache_file);
0885
0886 foreach ($this->queue_data as $object => $data_ary)
0887 {
0888 if (isset($this->data[$object]) && sizeof($this->data[$object]))
0889 {
0890 $this->data[$object]['data'] = array_merge($data_ary['data'], $this->data[$object]['data']);
0891 }
0892 else
0893 {
0894 $this->data[$object]['data'] = $data_ary['data'];
0895 }
0896 }
0897 }
0898
0899 if ($fp = @fopen($this->cache_file, 'w'))
0900 {
0901 fwrite($fp, "<?php\nif (!defined('IN_PHPBB')) exit;\n\$this->queue_data = unserialize(" . var_export(serialize($this->data), true) . ");\n\n?>");
0902 fclose($fp);
0903
0904 phpbb_chmod($this->cache_file, CHMOD_READ | CHMOD_WRITE);
0905 }
0906
0907 $lock->release();
0908 }
0909 }
0910
0911 /**
0912 * Replacement or substitute for PHP's mail command
0913 */
0914 function smtpmail($addresses, $subject, $message, &$err_msg, $headers = false)
0915 {
0916 global $config, $user;
0917
0918 // Fix any bare linefeeds in the message to make it RFC821 Compliant.
0919 $message = preg_replace("#(?<!\r)\n#si", "\r\n", $message);
0920
0921 if ($headers !== false)
0922 {
0923 if (!is_array($headers))
0924 {
0925 // Make sure there are no bare linefeeds in the headers
0926 $headers = preg_replace('#(?<!\r)\n#si', "\n", $headers);
0927 $headers = explode("\n", $headers);
0928 }
0929
0930 // Ok this is rather confusing all things considered,
0931 // but we have to grab bcc and cc headers and treat them differently
0932 // Something we really didn't take into consideration originally
0933 $headers_used = array();
0934
0935 foreach ($headers as $header)
0936 {
0937 if (strpos(strtolower($header), 'cc:') === 0 || strpos(strtolower($header), 'bcc:') === 0)
0938 {
0939 continue;
0940 }
0941 $headers_used[] = trim($header);
0942 }
0943
0944 $headers = chop(implode("\r\n", $headers_used));
0945 }
0946
0947 if (trim($subject) == '')
0948 {
0949 $err_msg = (isset($user->lang['NO_EMAIL_SUBJECT'])) ? $user->lang['NO_EMAIL_SUBJECT'] : 'No email subject specified';
0950 return false;
0951 }
0952
0953 if (trim($message) == '')
0954 {
0955 $err_msg = (isset($user->lang['NO_EMAIL_MESSAGE'])) ? $user->lang['NO_EMAIL_MESSAGE'] : 'Email message was blank';
0956 return false;
0957 }
0958
0959 $mail_rcpt = $mail_to = $mail_cc = array();
0960
0961 // Build correct addresses for RCPT TO command and the client side display (TO, CC)
0962 if (isset($addresses['to']) && sizeof($addresses['to']))
0963 {
0964 foreach ($addresses['to'] as $which_ary)
0965 {
0966 $mail_to[] = ($which_ary['name'] != '') ? mail_encode(trim($which_ary['name'])) . ' <' . trim($which_ary['email']) . '>' : '<' . trim($which_ary['email']) . '>';
0967 $mail_rcpt['to'][] = '<' . trim($which_ary['email']) . '>';
0968 }
0969 }
0970
0971 if (isset($addresses['bcc']) && sizeof($addresses['bcc']))
0972 {
0973 foreach ($addresses['bcc'] as $which_ary)
0974 {
0975 $mail_rcpt['bcc'][] = '<' . trim($which_ary['email']) . '>';
0976 }
0977 }
0978
0979 if (isset($addresses['cc']) && sizeof($addresses['cc']))
0980 {
0981 foreach ($addresses['cc'] as $which_ary)
0982 {
0983 $mail_cc[] = ($which_ary['name'] != '') ? mail_encode(trim($which_ary['name'])) . ' <' . trim($which_ary['email']) . '>' : '<' . trim($which_ary['email']) . '>';
0984 $mail_rcpt['cc'][] = '<' . trim($which_ary['email']) . '>';
0985 }
0986 }
0987
0988 $smtp = new smtp_class();
0989
0990 $errno = 0;
0991 $errstr = '';
0992
0993 $smtp->add_backtrace('Connecting to ' . $config['smtp_host'] . ':' . $config['smtp_port']);
0994
0995 // Ok we have error checked as much as we can to this point let's get on it already.
0996 if (!class_exists('\phpbb\error_collector'))
0997 {
0998 global $phpbb_root_path, $phpEx;
0999 include($phpbb_root_path . 'includes/error_collector.' . $phpEx);
1000 }
1001 $collector = new \phpbb\error_collector;
1002 $collector->install();
1003 $smtp->socket = fsockopen($config['smtp_host'], $config['smtp_port'], $errno, $errstr, 20);
1004 $collector->uninstall();
1005 $error_contents = $collector->format_errors();
1006
1007 if (!$smtp->socket)
1008 {
1009 if ($errstr)
1010 {
1011 $errstr = utf8_convert_message($errstr);
1012 }
1013
1014 $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";
1015 $err_msg .= ($error_contents) ? '<br /><br />' . htmlspecialchars($error_contents) : '';
1016 return false;
1017 }
1018
1019 // Wait for reply
1020 if ($err_msg = $smtp->server_parse('220', __LINE__))
1021 {
1022 $smtp->close_session($err_msg);
1023 return false;
1024 }
1025
1026 // Let me in. This function handles the complete authentication process
1027 if ($err_msg = $smtp->log_into_server($config['smtp_host'], $config['smtp_username'], htmlspecialchars_decode($config['smtp_password']), $config['smtp_auth_method']))
1028 {
1029 $smtp->close_session($err_msg);
1030 return false;
1031 }
1032
1033 // From this point onward most server response codes should be 250
1034 // Specify who the mail is from....
1035 $smtp->server_send('MAIL FROM:<' . $config['board_email'] . '>');
1036 if ($err_msg = $smtp->server_parse('250', __LINE__))
1037 {
1038 $smtp->close_session($err_msg);
1039 return false;
1040 }
1041
1042 // Specify each user to send to and build to header.
1043 $to_header = implode(', ', $mail_to);
1044 $cc_header = implode(', ', $mail_cc);
1045
1046 // Now tell the MTA to send the Message to the following people... [TO, BCC, CC]
1047 $rcpt = false;
1048 foreach ($mail_rcpt as $type => $mail_to_addresses)
1049 {
1050 foreach ($mail_to_addresses as $mail_to_address)
1051 {
1052 // Add an additional bit of error checking to the To field.
1053 if (preg_match('#[^ ]+\@[^ ]+#', $mail_to_address))
1054 {
1055 $smtp->server_send("RCPT TO:$mail_to_address");
1056 if ($err_msg = $smtp->server_parse('250', __LINE__))
1057 {
1058 // We continue... if users are not resolved we do not care
1059 if ($smtp->numeric_response_code != 550)
1060 {
1061 $smtp->close_session($err_msg);
1062 return false;
1063 }
1064 }
1065 else
1066 {
1067 $rcpt = true;
1068 }
1069 }
1070 }
1071 }
1072
1073 // 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.
1074 if (!$rcpt)
1075 {
1076 $user->session_begin();
1077 $err_msg .= '<br /><br />';
1078 $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?';
1079 $smtp->close_session($err_msg);
1080 return false;
1081 }
1082
1083 // Ok now we tell the server we are ready to start sending data
1084 $smtp->server_send('DATA');
1085
1086 // This is the last response code we look for until the end of the message.
1087 if ($err_msg = $smtp->server_parse('354', __LINE__))
1088 {
1089 $smtp->close_session($err_msg);
1090 return false;
1091 }
1092
1093 // Send the Subject Line...
1094 $smtp->server_send("Subject: $subject");
1095
1096 // Now the To Header.
1097 $to_header = ($to_header == '') ? 'undisclosed-recipients:;' : $to_header;
1098 $smtp->server_send("To: $to_header");
1099
1100 // Now the CC Header.
1101 if ($cc_header != '')
1102 {
1103 $smtp->server_send("CC: $cc_header");
1104 }
1105
1106 // Now any custom headers....
1107 if ($headers !== false)
1108 {
1109 $smtp->server_send("$headers\r\n");
1110 }
1111
1112 // Ok now we are ready for the message...
1113 $smtp->server_send($message);
1114
1115 // Ok the all the ingredients are mixed in let's cook this puppy...
1116 $smtp->server_send('.');
1117 if ($err_msg = $smtp->server_parse('250', __LINE__))
1118 {
1119 $smtp->close_session($err_msg);
1120 return false;
1121 }
1122
1123 // Now tell the server we are done and close the socket...
1124 $smtp->server_send('QUIT');
1125 $smtp->close_session($err_msg);
1126
1127 return true;
1128 }
1129
1130 /**
1131 * SMTP Class
1132 * Auth Mechanisms originally taken from the AUTH Modules found within the PHP Extension and Application Repository (PEAR)
1133 * See docs/AUTHORS for more details
1134 */
1135 class smtp_class
1136 {
1137 var $server_response = '';
1138 var $socket = 0;
1139 protected $socket_tls = false;
1140 var $responses = array();
1141 var $commands = array();
1142 var $numeric_response_code = 0;
1143
1144 var $backtrace = false;
1145 var $backtrace_log = array();
1146
1147 function smtp_class()
1148 {
1149 // Always create a backtrace for admins to identify SMTP problems
1150 $this->backtrace = true;
1151 $this->backtrace_log = array();
1152 }
1153
1154 /**
1155 * Add backtrace message for debugging
1156 */
1157 function add_backtrace($message)
1158 {
1159 if ($this->backtrace)
1160 {
1161 $this->backtrace_log[] = utf8_htmlspecialchars($message);
1162 }
1163 }
1164
1165 /**
1166 * Send command to smtp server
1167 */
1168 function server_send($command, $private_info = false)
1169 {
1170 fputs($this->socket, $command . "\r\n");
1171
1172 (!$private_info) ? $this->add_backtrace("# $command") : $this->add_backtrace('# Omitting sensitive information');
1173
1174 // We could put additional code here
1175 }
1176
1177 /**
1178 * We use the line to give the support people an indication at which command the error occurred
1179 */
1180 function server_parse($response, $line)
1181 {
1182 global $user;
1183
1184 $this->server_response = '';
1185 $this->responses = array();
1186 $this->numeric_response_code = 0;
1187
1188 while (substr($this->server_response, 3, 1) != ' ')
1189 {
1190 if (!($this->server_response = fgets($this->socket, 256)))
1191 {
1192 return (isset($user->lang['NO_EMAIL_RESPONSE_CODE'])) ? $user->lang['NO_EMAIL_RESPONSE_CODE'] : 'Could not get mail server response codes';
1193 }
1194 $this->responses[] = substr(rtrim($this->server_response), 4);
1195 $this->numeric_response_code = (int) substr($this->server_response, 0, 3);
1196
1197 $this->add_backtrace("LINE: $line <- {$this->server_response}");
1198 }
1199
1200 if (!(substr($this->server_response, 0, 3) == $response))
1201 {
1202 $this->numeric_response_code = (int) substr($this->server_response, 0, 3);
1203 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";
1204 }
1205
1206 return 0;
1207 }
1208
1209 /**
1210 * Close session
1211 */
1212 function close_session(&$err_msg)
1213 {
1214 fclose($this->socket);
1215
1216 if ($this->backtrace)
1217 {
1218 $message = '<h1>Backtrace</h1><p>' . implode('<br />', $this->backtrace_log) . '</p>';
1219 $err_msg .= $message;
1220 }
1221 }
1222
1223 /**
1224 * Log into server and get possible auth codes if neccessary
1225 */
1226 function log_into_server($hostname, $username, $password, $default_auth_method)
1227 {
1228 global $user;
1229
1230 $err_msg = '';
1231
1232 // Here we try to determine the *real* hostname (reverse DNS entry preferrably)
1233 $local_host = $user->host;
1234
1235 if (function_exists('php_uname'))
1236 {
1237 $local_host = php_uname('n');
1238
1239 // Able to resolve name to IP
1240 if (($addr = @gethostbyname($local_host)) !== $local_host)
1241 {
1242 // Able to resolve IP back to name
1243 if (($name = @gethostbyaddr($addr)) !== $addr)
1244 {
1245 $local_host = $name;
1246 }
1247 }
1248 }
1249
1250 // If we are authenticating through pop-before-smtp, we
1251 // have to login ones before we get authenticated
1252 // NOTE: on some configurations the time between an update of the auth database takes so
1253 // long that the first email send does not work. This is not a biggie on a live board (only
1254 // the install mail will most likely fail) - but on a dynamic ip connection this might produce
1255 // severe problems and is not fixable!
1256 if ($default_auth_method == 'POP-BEFORE-SMTP' && $username && $password)
1257 {
1258 global $config;
1259
1260 $errno = 0;
1261 $errstr = '';
1262
1263 $this->server_send("QUIT");
1264 fclose($this->socket);
1265
1266 $result = $this->pop_before_smtp($hostname, $username, $password);
1267 $username = $password = $default_auth_method = '';
1268
1269 // We need to close the previous session, else the server is not
1270 // able to get our ip for matching...
1271 if (!$this->socket = @fsockopen($config['smtp_host'], $config['smtp_port'], $errno, $errstr, 10))
1272 {
1273 if ($errstr)
1274 {
1275 $errstr = utf8_convert_message($errstr);
1276 }
1277
1278 $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";
1279 return $err_msg;
1280 }
1281
1282 // Wait for reply
1283 if ($err_msg = $this->server_parse('220', __LINE__))
1284 {
1285 $this->close_session($err_msg);
1286 return $err_msg;
1287 }
1288 }
1289
1290 $hello_result = $this->hello($local_host);
1291 if (!is_null($hello_result))
1292 {
1293 return $hello_result;
1294 }
1295
1296 // SMTP STARTTLS (RFC 3207)
1297 if (!$this->socket_tls)
1298 {
1299 $this->socket_tls = $this->starttls();
1300
1301 if ($this->socket_tls)
1302 {
1303 // Switched to TLS
1304 // RFC 3207: "The client MUST discard any knowledge obtained from the server, [...]"
1305 // So say hello again
1306 $hello_result = $this->hello($local_host);
1307
1308 if (!is_null($hello_result))
1309 {
1310 return $hello_result;
1311 }
1312 }
1313 }
1314
1315 // If we are not authenticated yet, something might be wrong if no username and passwd passed
1316 if (!$username || !$password)
1317 {
1318 return false;
1319 }
1320
1321 if (!isset($this->commands['AUTH']))
1322 {
1323 return (isset($user->lang['SMTP_NO_AUTH_SUPPORT'])) ? $user->lang['SMTP_NO_AUTH_SUPPORT'] : 'SMTP server does not support authentication';
1324 }
1325
1326 // Get best authentication method
1327 $available_methods = explode(' ', $this->commands['AUTH']);
1328
1329 // Define the auth ordering if the default auth method was not found
1330 $auth_methods = array('PLAIN', 'LOGIN', 'CRAM-MD5', 'DIGEST-MD5');
1331 $method = '';
1332
1333 if (in_array($default_auth_method, $available_methods))
1334 {
1335 $method = $default_auth_method;
1336 }
1337 else
1338 {
1339 foreach ($auth_methods as $_method)
1340 {
1341 if (in_array($_method, $available_methods))
1342 {
1343 $method = $_method;
1344 break;
1345 }
1346 }
1347 }
1348
1349 if (!$method)
1350 {
1351 return (isset($user->lang['NO_SUPPORTED_AUTH_METHODS'])) ? $user->lang['NO_SUPPORTED_AUTH_METHODS'] : 'No supported authentication methods';
1352 }
1353
1354 $method = strtolower(str_replace('-', '_', $method));
1355 return $this->$method($username, $password);
1356 }
1357
1358 /**
1359 * SMTP EHLO/HELO
1360 *
1361 * @return mixed Null if the authentication process is supposed to continue
1362 * False if already authenticated
1363 * Error message (string) otherwise
1364 */
1365 protected function hello($hostname)
1366 {
1367 // Try EHLO first
1368 $this->server_send("EHLO $hostname");
1369 if ($err_msg = $this->server_parse('250', __LINE__))
1370 {
1371 // a 503 response code means that we're already authenticated
1372 if ($this->numeric_response_code == 503)
1373 {
1374 return false;
1375 }
1376
1377 // If EHLO fails, we try HELO
1378 $this->server_send("HELO $hostname");
1379 if ($err_msg = $this->server_parse('250', __LINE__))
1380 {
1381 return ($this->numeric_response_code == 503) ? false : $err_msg;
1382 }
1383 }
1384
1385 foreach ($this->responses as $response)
1386 {
1387 $response = explode(' ', $response);
1388 $response_code = $response[0];
1389 unset($response[0]);
1390 $this->commands[$response_code] = implode(' ', $response);
1391 }
1392 }
1393
1394 /**
1395 * SMTP STARTTLS (RFC 3207)
1396 *
1397 * @return bool Returns true if TLS was started
1398 * Otherwise false
1399 */
1400 protected function starttls()
1401 {
1402 if (!function_exists('stream_socket_enable_crypto'))
1403 {
1404 return false;
1405 }
1406
1407 if (!isset($this->commands['STARTTLS']))
1408 {
1409 return false;
1410 }
1411
1412 $this->server_send('STARTTLS');
1413
1414 if ($err_msg = $this->server_parse('220', __LINE__))
1415 {
1416 return false;
1417 }
1418
1419 $result = false;
1420 $stream_meta = stream_get_meta_data($this->socket);
1421
1422 if (socket_set_blocking($this->socket, 1))
1423 {
1424 $result = stream_socket_enable_crypto($this->socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
1425 socket_set_blocking($this->socket, (int) $stream_meta['blocked']);
1426 }
1427
1428 return $result;
1429 }
1430
1431 /**
1432 * Pop before smtp authentication
1433 */
1434 function pop_before_smtp($hostname, $username, $password)
1435 {
1436 global $user;
1437
1438 if (!$this->socket = @fsockopen($hostname, 110, $errno, $errstr, 10))
1439 {
1440 if ($errstr)
1441 {
1442 $errstr = utf8_convert_message($errstr);
1443 }
1444
1445 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";
1446 }
1447
1448 $this->server_send("USER $username", true);
1449 if ($err_msg = $this->server_parse('+OK', __LINE__))
1450 {
1451 return $err_msg;
1452 }
1453
1454 $this->server_send("PASS $password", true);
1455 if ($err_msg = $this->server_parse('+OK', __LINE__))
1456 {
1457 return $err_msg;
1458 }
1459
1460 $this->server_send('QUIT');
1461 fclose($this->socket);
1462
1463 return false;
1464 }
1465
1466 /**
1467 * Plain authentication method
1468 */
1469 function plain($username, $password)
1470 {
1471 $this->server_send('AUTH PLAIN');
1472 if ($err_msg = $this->server_parse('334', __LINE__))
1473 {
1474 return ($this->numeric_response_code == 503) ? false : $err_msg;
1475 }
1476
1477 $base64_method_plain = base64_encode("\0" . $username . "\0" . $password);
1478 $this->server_send($base64_method_plain, true);
1479 if ($err_msg = $this->server_parse('235', __LINE__))
1480 {
1481 return $err_msg;
1482 }
1483
1484 return false;
1485 }
1486
1487 /**
1488 * Login authentication method
1489 */
1490 function login($username, $password)
1491 {
1492 $this->server_send('AUTH LOGIN');
1493 if ($err_msg = $this->server_parse('334', __LINE__))
1494 {
1495 return ($this->numeric_response_code == 503) ? false : $err_msg;
1496 }
1497
1498 $this->server_send(base64_encode($username), true);
1499 if ($err_msg = $this->server_parse('334', __LINE__))
1500 {
1501 return $err_msg;
1502 }
1503
1504 $this->server_send(base64_encode($password), true);
1505 if ($err_msg = $this->server_parse('235', __LINE__))
1506 {
1507 return $err_msg;
1508 }
1509
1510 return false;
1511 }
1512
1513 /**
1514 * cram_md5 authentication method
1515 */
1516 function cram_md5($username, $password)
1517 {
1518 $this->server_send('AUTH CRAM-MD5');
1519 if ($err_msg = $this->server_parse('334', __LINE__))
1520 {
1521 return ($this->numeric_response_code == 503) ? false : $err_msg;
1522 }
1523
1524 $md5_challenge = base64_decode($this->responses[0]);
1525 $password = (strlen($password) > 64) ? pack('H32', md5($password)) : ((strlen($password) < 64) ? str_pad($password, 64, chr(0)) : $password);
1526 $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))));
1527
1528 $base64_method_cram_md5 = base64_encode($username . ' ' . $md5_digest);
1529
1530 $this->server_send($base64_method_cram_md5, true);
1531 if ($err_msg = $this->server_parse('235', __LINE__))
1532 {
1533 return $err_msg;
1534 }
1535
1536 return false;
1537 }
1538
1539 /**
1540 * digest_md5 authentication method
1541 * A real pain in the ***
1542 */
1543 function digest_md5($username, $password)
1544 {
1545 global $config, $user;
1546
1547 $this->server_send('AUTH DIGEST-MD5');
1548 if ($err_msg = $this->server_parse('334', __LINE__))
1549 {
1550 return ($this->numeric_response_code == 503) ? false : $err_msg;
1551 }
1552
1553 $md5_challenge = base64_decode($this->responses[0]);
1554
1555 // Parse the md5 challenge - from AUTH_SASL (PEAR)
1556 $tokens = array();
1557 while (preg_match('/^([a-z-]+)=("[^"]+(?<!\\\)"|[^,]+)/i', $md5_challenge, $matches))
1558 {
1559 // Ignore these as per rfc2831
1560 if ($matches[1] == 'opaque' || $matches[1] == 'domain')
1561 {
1562 $md5_challenge = substr($md5_challenge, strlen($matches[0]) + 1);
1563 continue;
1564 }
1565
1566 // Allowed multiple "realm" and "auth-param"
1567 if (!empty($tokens[$matches[1]]) && ($matches[1] == 'realm' || $matches[1] == 'auth-param'))
1568 {
1569 if (is_array($tokens[$matches[1]]))
1570 {
1571 $tokens[$matches[1]][] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
1572 }
1573 else
1574 {
1575 $tokens[$matches[1]] = array($tokens[$matches[1]], preg_replace('/^"(.*)"$/', '\\1', $matches[2]));
1576 }
1577 }
1578 else if (!empty($tokens[$matches[1]])) // Any other multiple instance = failure
1579 {
1580 $tokens = array();
1581 break;
1582 }
1583 else
1584 {
1585 $tokens[$matches[1]] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
1586 }
1587
1588 // Remove the just parsed directive from the challenge
1589 $md5_challenge = substr($md5_challenge, strlen($matches[0]) + 1);
1590 }
1591
1592 // Realm
1593 if (empty($tokens['realm']))
1594 {
1595 $tokens['realm'] = (function_exists('php_uname')) ? php_uname('n') : $user->host;
1596 }
1597
1598 // Maxbuf
1599 if (empty($tokens['maxbuf']))
1600 {
1601 $tokens['maxbuf'] = 65536;
1602 }
1603
1604 // Required: nonce, algorithm
1605 if (empty($tokens['nonce']) || empty($tokens['algorithm']))
1606 {
1607 $tokens = array();
1608 }
1609 $md5_challenge = $tokens;
1610
1611 if (!empty($md5_challenge))
1612 {
1613 $str = '';
1614 for ($i = 0; $i < 32; $i++)
1615 {
1616 $str .= chr(mt_rand(0, 255));
1617 }
1618 $cnonce = base64_encode($str);
1619
1620 $digest_uri = 'smtp/' . $config['smtp_host'];
1621
1622 $auth_1 = sprintf('%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $username, $md5_challenge['realm'], $password))), $md5_challenge['nonce'], $cnonce);
1623 $auth_2 = 'AUTHENTICATE:' . $digest_uri;
1624 $response_value = md5(sprintf('%s:%s:00000001:%s:auth:%s', md5($auth_1), $md5_challenge['nonce'], $cnonce, md5($auth_2)));
1625
1626 $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']);
1627 }
1628 else
1629 {
1630 return (isset($user->lang['INVALID_DIGEST_CHALLENGE'])) ? $user->lang['INVALID_DIGEST_CHALLENGE'] : 'Invalid digest challenge';
1631 }
1632
1633 $base64_method_digest_md5 = base64_encode($input_string);
1634 $this->server_send($base64_method_digest_md5, true);
1635 if ($err_msg = $this->server_parse('334', __LINE__))
1636 {
1637 return $err_msg;
1638 }
1639
1640 $this->server_send(' ');
1641 if ($err_msg = $this->server_parse('235', __LINE__))
1642 {
1643 return $err_msg;
1644 }
1645
1646 return false;
1647 }
1648 }
1649
1650 /**
1651 * Encodes the given string for proper display in UTF-8.
1652 *
1653 * This version is using base64 encoded data. The downside of this
1654 * is if the mail client does not understand this encoding the user
1655 * is basically doomed with an unreadable subject.
1656 *
1657 * Please note that this version fully supports RFC 2045 section 6.8.
1658 *
1659 * @param string $eol End of line we are using (optional to be backwards compatible)
1660 */
1661 function mail_encode($str, $eol = "\r\n")
1662 {
1663 // define start delimimter, end delimiter and spacer
1664 $start = "=?UTF-8?B?";
1665 $end = "?=";
1666 $delimiter = "$eol ";
1667
1668 // Maximum length is 75. $split_length *must* be a multiple of 4, but <= 75 - strlen($start . $delimiter . $end)!!!
1669 $split_length = 60;
1670 $encoded_str = base64_encode($str);
1671
1672 // If encoded string meets the limits, we just return with the correct data.
1673 if (strlen($encoded_str) <= $split_length)
1674 {
1675 return $start . $encoded_str . $end;
1676 }
1677
1678 // If there is only ASCII data, we just return what we want, correctly splitting the lines.
1679 if (strlen($str) === utf8_strlen($str))
1680 {
1681 return $start . implode($end . $delimiter . $start, str_split($encoded_str, $split_length)) . $end;
1682 }
1683
1684 // UTF-8 data, compose encoded lines
1685 $array = utf8_str_split($str);
1686 $str = '';
1687
1688 while (sizeof($array))
1689 {
1690 $text = '';
1691
1692 while (sizeof($array) && intval((strlen($text . $array[0]) + 2) / 3) << 2 <= $split_length)
1693 {
1694 $text .= array_shift($array);
1695 }
1696
1697 $str .= $start . base64_encode($text) . $end . $delimiter;
1698 }
1699
1700 return substr($str, 0, -strlen($delimiter));
1701 }
1702
1703 /**
1704 * Wrapper for sending out emails with the PHP's mail function
1705 */
1706 function phpbb_mail($to, $subject, $msg, $headers, $eol, &$err_msg)
1707 {
1708 global $config, $phpbb_root_path, $phpEx;
1709
1710 // 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...
1711 // Reference: http://bugs.php.net/bug.php?id=15841
1712 $headers = implode($eol, $headers);
1713
1714 if (!class_exists('\phpbb\error_collector'))
1715 {
1716 include($phpbb_root_path . 'includes/error_collector.' . $phpEx);
1717 }
1718
1719 $collector = new \phpbb\error_collector;
1720 $collector->install();
1721
1722 // On some PHP Versions mail() *may* fail if there are newlines within the subject.
1723 // Newlines are used as a delimiter for lines in mail_encode() according to RFC 2045 section 6.8.
1724 // 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)
1725 $result = $config['email_function_name']($to, mail_encode($subject, ''), wordwrap(utf8_wordwrap($msg), 997, "\n", true), $headers);
1726
1727 $collector->uninstall();
1728 $err_msg = $collector->format_errors();
1729
1730 return $result;
1731 }
1732