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