Beispiel #1
0
function getLastPost()
{
    global $scripturl, $modSettings, $board;
    // Find it by the board - better to order by board than sort the entire messages table.
    $request = smf_db_query('
		SELECT ml.poster_time, ml.subject, ml.id_topic, ml.poster_name, ml.body, ml.id_msg, b.id_board,
			ml.smileys_enabled
		FROM {db_prefix}boards AS b
			INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = b.id_last_msg)
		WHERE {query_wanna_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
			AND b.id_board != {int:recycle_board}' : '') . '
			AND ml.approved = {int:is_approved}
		ORDER BY b.id_msg_updated DESC
		LIMIT 1', array('recycle_board' => $modSettings['recycle_board'], 'is_approved' => 1));
    if (mysql_num_rows($request) == 0) {
        return array();
    }
    $row = mysql_fetch_assoc($request);
    mysql_free_result($request);
    $board = $row['id_board'];
    // Censor the subject and post...
    censorText($row['subject']);
    censorText($row['body']);
    $row['body'] = strip_tags(strtr(parse_bbc($row['body'], $row['smileys_enabled']), array('<br />' => '&#10;')));
    parse_bbc_stage2($row['body'], $row['id_msg']);
    if (commonAPI::strlen($row['body']) > 128) {
        $row['body'] = commonAPI::substr($row['body'], 0, 128) . '...';
    }
    // Send the data.
    return array('topic' => $row['id_topic'], 'subject' => $row['subject'], 'short_subject' => shorten_subject($row['subject'], 35), 'preview' => $row['body'], 'time' => timeformat($row['poster_time']), 'timestamp' => forum_time(true, $row['poster_time']), 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.new;topicseen#new', 'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.new;topicseen#new">' . $row['subject'] . '</a>');
}
Beispiel #2
0
function getLastPosts($latestPostOptions)
{
    global $scripturl, $txt, $user_info, $modSettings, $smcFunc, $context;
    // Find all the posts.  Newer ones will have higher IDs.  (assuming the last 20 * number are accessable...)
    // !!!SLOW This query is now slow, NEEDS to be fixed.  Maybe break into two?
    $request = smf_db_query('
		SELECT
			m.poster_time, m.subject, m.id_topic, m.id_member, m.id_msg, b.name, m1.subject AS first_subject,
			IFNULL(mem.real_name, m.poster_name) AS poster_name, t.id_board, b.name AS board_name,
			SUBSTRING(m.body, 1, 385) AS body, m.smileys_enabled
		FROM {db_prefix}messages AS m
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
			INNER JOIN {db_prefix}messages AS m1 ON (m1.id_msg = t.id_first_msg)
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
		WHERE m.id_msg >= {int:likely_max_msg}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
			AND b.id_board != {int:recycle_board}' : '') . '
			AND {query_wanna_see_board}' . ($modSettings['postmod_active'] ? '
			AND t.approved = {int:is_approved}
			AND m.approved = {int:is_approved}' : '') . '
		ORDER BY m.id_msg DESC
		LIMIT ' . $latestPostOptions['number_posts'], array('likely_max_msg' => max(0, $modSettings['maxMsgID'] - 50 * $latestPostOptions['number_posts']), 'recycle_board' => $modSettings['recycle_board'], 'is_approved' => 1));
    $posts = array();
    while ($row = mysql_fetch_assoc($request)) {
        // Censor the subject and post for the preview ;).
        censorText($row['subject']);
        censorText($row['body']);
        $row['body'] = strip_tags(strtr(parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']), array('<br />' => '&#10;')));
        if (commonAPI::strlen($row['body']) > 128) {
            $row['body'] = commonAPI::substr($row['body'], 0, 128) . '...';
        }
        $bhref = URL::board($row['id_board'], $row['board_name'], 0, true);
        $mhref = URL::user($row['id_member'], $row['poster_name']);
        $thref = URL::topic($row['id_topic'], $row['first_subject'], 0, false, '.msg' . $row['id_msg'], ';topicseen#msg' . $row['id_msg']);
        // Build the array.
        $posts[] = array('board' => array('id' => $row['id_board'], 'name' => $row['board_name'], 'href' => $bhref, 'link' => '<a href="' . $bhref . '">' . $row['board_name'] . '</a>'), 'topic' => $row['id_topic'], 'poster' => array('id' => $row['id_member'], 'name' => $row['poster_name'], 'href' => empty($row['id_member']) ? '' : $mhref, 'link' => empty($row['id_member']) ? $row['poster_name'] : '<a href="' . $mhref . '">' . $row['poster_name'] . '</a>'), 'subject' => $row['subject'], 'short_subject' => shorten_subject($row['subject'], 35), 'preview' => $row['body'], 'time' => timeformat($row['poster_time']), 'timestamp' => forum_time(true, $row['poster_time']), 'raw_timestamp' => $row['poster_time'], 'href' => $thref, 'link' => '<a href="' . $thref . '" rel="nofollow">' . $row['first_subject'] . '</a>');
    }
    mysql_free_result($request);
    return $posts;
}
Beispiel #3
0
function validatePassword($password, $username, $restrict_in = array())
{
    global $modSettings, $smcFunc;
    // Perform basic requirements first.
    if (commonAPI::strlen($password) < (empty($modSettings['password_strength']) ? 4 : 8)) {
        return 'short';
    }
    // Is this enough?
    if (empty($modSettings['password_strength'])) {
        return null;
    }
    // Otherwise, perform the medium strength test - checking if password appears in the restricted string.
    if (preg_match('~\\b' . preg_quote($password, '~') . '\\b~', implode(' ', $restrict_in)) != 0) {
        return 'restricted_words';
    } elseif (commonAPI::strpos($password, $username) !== false) {
        return 'restricted_words';
    }
    // !!! If pspell is available, use it on the word, and return restricted_words if it doesn't give "bad spelling"?
    // If just medium, we're done.
    if ($modSettings['password_strength'] == 1) {
        return null;
    }
    // Otherwise, hard test next, check for numbers and letters, uppercase too.
    $good = preg_match('~(\\D\\d|\\d\\D)~', $password) != 0;
    $good &= commonAPI::strtolower($password) != $password;
    return $good ? null : 'chars';
}
Beispiel #4
0
function JavaScriptModify()
{
    global $sourcedir, $modSettings, $board, $topic, $txt;
    global $user_info, $context, $language;
    // We have to have a topic!
    if (empty($topic)) {
        obExit(false);
    }
    checkSession('get');
    require_once $sourcedir . '/lib/Subs-Post.php';
    // Assume the first message if no message ID was given.
    $request = smf_db_query('
			SELECT
				t.locked, t.num_replies, t.id_member_started, t.id_first_msg,
				m.id_msg, m.id_member, m.poster_time, m.subject, m.smileys_enabled, m.body, m.icon,
				m.modified_time, m.modified_name, m.approved, ba.id_topic AS banned_from_topic
			FROM {db_prefix}messages AS m
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
				LEFT JOIN {db_prefix}topicbans AS ba ON (ba.id_topic = {int:current_topic} AND ba.id_member = {int:current_member})
			WHERE m.id_msg = {raw:id_msg}
				AND m.id_topic = {int:current_topic}' . (allowedTo('approve_posts') ? '' : (!$modSettings['postmod_active'] ? '
				AND (m.id_member != {int:guest_id} AND m.id_member = {int:current_member})' : '
				AND (m.approved = {int:is_approved} OR (m.id_member != {int:guest_id} AND m.id_member = {int:current_member}))')), array('current_member' => $user_info['id'], 'current_topic' => $topic, 'id_msg' => empty($_REQUEST['msg']) ? 't.id_first_msg' : (int) $_REQUEST['msg'], 'is_approved' => 1, 'guest_id' => 0));
    if (mysql_num_rows($request) == 0) {
        fatal_lang_error('no_board', false);
    }
    $row = mysql_fetch_assoc($request);
    mysql_free_result($request);
    // Change either body or subject requires permissions to modify messages.
    if (isset($_POST['message']) || isset($_POST['subject']) || isset($_REQUEST['icon'])) {
        if (!empty($row['locked'])) {
            isAllowedTo('moderate_board');
        }
        if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any')) {
            if ((!$modSettings['postmod_active'] || $row['approved']) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time()) {
                fatal_lang_error('modify_post_time_passed', false);
            } elseif ($row['id_member_started'] == $user_info['id'] && !allowedTo('modify_own')) {
                isAllowedTo('modify_replies');
            } else {
                isAllowedTo('modify_own');
            }
        } elseif ($row['id_member_started'] == $user_info['id'] && !allowedTo('modify_any')) {
            isAllowedTo('modify_replies');
        } else {
            isAllowedTo('modify_any');
        }
        // check topic bans
        if ($row['banned_from_topic'] != 0 && !$user_info['is_admin'] && !allowedTo('moderate_board') && !allowedTo('moderate_forum')) {
            fatal_lang_error('banned_from_topic');
        }
        // Only log this action if it wasn't your message.
        $moderationAction = $row['id_member'] != $user_info['id'];
    }
    $post_errors = array();
    if (isset($_POST['subject']) && commonAPI::htmltrim(commonAPI::htmlspecialchars($_POST['subject'])) !== '') {
        $_POST['subject'] = strtr(commonAPI::htmlspecialchars($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
        // Maximum number of characters.
        if (commonAPI::strlen($_POST['subject']) > 100) {
            $_POST['subject'] = commonAPI::substr($_POST['subject'], 0, 100);
        }
    } elseif (isset($_POST['subject'])) {
        $post_errors[] = 'no_subject';
        unset($_POST['subject']);
    }
    if (isset($_POST['message'])) {
        if (commonAPI::htmltrim(commonAPI::htmlspecialchars($_POST['message'])) === '') {
            $post_errors[] = 'no_message';
            unset($_POST['message']);
        } elseif (!empty($modSettings['max_messageLength']) && commonAPI::strlen($_POST['message']) > $modSettings['max_messageLength']) {
            $post_errors[] = 'long_message';
            unset($_POST['message']);
        } else {
            $_POST['message'] = commonAPI::htmlspecialchars($_POST['message'], ENT_QUOTES);
            preparsecode($_POST['message']);
            if (commonAPI::htmltrim(strip_tags(parse_bbc($_POST['message'], false), '<img>')) === '') {
                $post_errors[] = 'no_message';
                unset($_POST['message']);
            }
        }
    }
    if (isset($_POST['lock'])) {
        if (!allowedTo(array('lock_any', 'lock_own')) || !allowedTo('lock_any') && $user_info['id'] != $row['id_member']) {
            unset($_POST['lock']);
        } elseif (!allowedTo('lock_any')) {
            if ($row['locked'] == 1) {
                unset($_POST['lock']);
            } else {
                $_POST['lock'] = empty($_POST['lock']) ? 0 : 2;
            }
        } elseif (!empty($row['locked']) && !empty($_POST['lock']) || $_POST['lock'] == $row['locked']) {
            unset($_POST['lock']);
        } else {
            $_POST['lock'] = empty($_POST['lock']) ? 0 : 1;
        }
    }
    if (isset($_POST['sticky']) && !allowedTo('make_sticky')) {
        unset($_POST['sticky']);
    }
    if (empty($post_errors)) {
        $msgOptions = array('id' => $row['id_msg'], 'subject' => isset($_POST['subject']) ? $_POST['subject'] : null, 'body' => isset($_POST['message']) ? $_POST['message'] : null, 'icon' => isset($_REQUEST['icon']) ? preg_replace('~[\\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : null, 'id_owner' => $row['id_member']);
        $topicOptions = array('id' => $topic, 'board' => $board, 'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null, 'sticky_mode' => isset($_POST['sticky']) && !empty($modSettings['enableStickyTopics']) ? (int) $_POST['sticky'] : null, 'mark_as_read' => true);
        $posterOptions = array();
        // Only consider marking as editing if they have edited the subject, message or icon.
        if (isset($_POST['subject']) && $_POST['subject'] != $row['subject'] || isset($_POST['message']) && $_POST['message'] != $row['body'] || isset($_REQUEST['icon']) && $_REQUEST['icon'] != $row['icon']) {
            // And even then only if the time has passed...
            if (time() - $row['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $row['id_member']) {
                $msgOptions['modify_time'] = time();
                $msgOptions['modify_name'] = $user_info['name'];
            }
        } else {
            $moderationAction = false;
        }
        modifyPost($msgOptions, $topicOptions, $posterOptions);
        // If we didn't change anything this time but had before put back the old info.
        if (!isset($msgOptions['modify_time']) && !empty($row['modified_time'])) {
            $msgOptions['modify_time'] = $row['modified_time'];
            $msgOptions['modify_name'] = $row['modified_name'];
        }
        // Changing the first subject updates other subjects to 'Re: new_subject'.
        if (isset($_POST['subject']) && isset($_REQUEST['change_all_subjects']) && $row['id_first_msg'] == $row['id_msg'] && !empty($row['num_replies']) && (allowedTo('modify_any') || $row['id_member_started'] == $user_info['id'] && allowedTo('modify_replies'))) {
            // Get the proper (default language) response prefix first.
            if (!isset($context['response_prefix']) && !($context['response_prefix'] = CacheAPI::getCache('response_prefix'))) {
                if ($language === $user_info['language']) {
                    $context['response_prefix'] = $txt['response_prefix'];
                } else {
                    loadLanguage('index', $language, false);
                    $context['response_prefix'] = $txt['response_prefix'];
                    loadLanguage('index');
                }
                CacheAPI::putCache('response_prefix', $context['response_prefix'], 600);
            }
            smf_db_query('
				UPDATE {db_prefix}messages
				SET subject = {string:subject}
				WHERE id_topic = {int:current_topic}
					AND id_msg != {int:id_first_msg}', array('current_topic' => $topic, 'id_first_msg' => $row['id_first_msg'], 'subject' => $context['response_prefix'] . $_POST['subject']));
        }
        if (!empty($moderationAction)) {
            logAction('modify', array('topic' => $topic, 'message' => $row['id_msg'], 'member' => $row['id_member'], 'board' => $board));
        }
    }
    if (isset($_REQUEST['xml'])) {
        $context['sub_template'] = 'modifydone';
        if (empty($post_errors) && isset($msgOptions['subject']) && isset($msgOptions['body'])) {
            $context['message'] = array('id' => $row['id_msg'], 'modified' => array('time' => isset($msgOptions['modify_time']) ? timeformat($msgOptions['modify_time']) : '', 'timestamp' => isset($msgOptions['modify_time']) ? forum_time(true, $msgOptions['modify_time']) : 0, 'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : ''), 'subject' => $msgOptions['subject'], 'first_in_topic' => $row['id_msg'] == $row['id_first_msg'], 'body' => strtr($msgOptions['body'], array(']]>' => ']]]]><![CDATA[>')));
            censorText($context['message']['subject']);
            censorText($context['message']['body']);
            $cache_key = isset($msgOptions['modify_time']) ? $row['id_msg'] . '|' . $msgOptions['modify_time'] : $row['id_msg'];
            $context['message']['body'] = parse_bbc($context['message']['body'], $row['smileys_enabled'], $cache_key);
            parse_bbc_stage2($context['message']['body']);
        } elseif (empty($post_errors)) {
            $context['sub_template'] = 'modifytopicdone';
            $context['message'] = array('id' => $row['id_msg'], 'icon' => isset($_REQUEST['icon']) ? $_REQUEST['icon'] : '', 'modified' => array('time' => isset($msgOptions['modify_time']) ? timeformat($msgOptions['modify_time']) : '', 'timestamp' => isset($msgOptions['modify_time']) ? forum_time(true, $msgOptions['modify_time']) : 0, 'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : ''), 'subject' => isset($msgOptions['subject']) ? $msgOptions['subject'] : '');
            censorText($context['message']['subject']);
        } else {
            $context['message'] = array('id' => $row['id_msg'], 'errors' => array(), 'error_in_subject' => in_array('no_subject', $post_errors), 'error_in_body' => in_array('no_message', $post_errors) || in_array('long_message', $post_errors));
            loadLanguage('Errors');
            foreach ($post_errors as $post_error) {
                if ($post_error == 'long_message') {
                    $context['message']['errors'][] = sprintf($txt['error_' . $post_error], $modSettings['max_messageLength']);
                } else {
                    $context['message']['errors'][] = $txt['error_' . $post_error];
                }
            }
        }
    } else {
        obExit(false);
    }
}
Beispiel #5
0
function ssi_boardNews($board = null, $limit = null, $start = null, $length = null, $output_method = 'echo')
{
    global $scripturl, $db_prefix, $txt, $settings, $modSettings, $context;
    global $smcFunc;
    loadLanguage('Stats');
    // Must be integers....
    if ($limit === null) {
        $limit = isset($_GET['limit']) ? (int) $_GET['limit'] : 5;
    } else {
        $limit = (int) $limit;
    }
    if ($start === null) {
        $start = isset($_GET['start']) ? (int) $_GET['start'] : 0;
    } else {
        $start = (int) $start;
    }
    if ($board !== null) {
        $board = (int) $board;
    } elseif (isset($_GET['board'])) {
        $board = (int) $_GET['board'];
    }
    if ($length === null) {
        $length = isset($_GET['length']) ? (int) $_GET['length'] : 0;
    } else {
        $length = (int) $length;
    }
    $limit = max(0, $limit);
    $start = max(0, $start);
    // Make sure guests can see this board.
    $request = $smcFunc['db_query']('', '
		SELECT id_board
		FROM {db_prefix}boards
		WHERE ' . ($board === null ? '' : 'id_board = {int:current_board}
			AND ') . 'FIND_IN_SET(-1, member_groups)
		LIMIT 1', array('current_board' => $board));
    if ($smcFunc['db_num_rows']($request) == 0) {
        if ($output_method == 'echo') {
            die($txt['ssi_no_guests']);
        } else {
            return array();
        }
    }
    list($board) = $smcFunc['db_fetch_row']($request);
    $smcFunc['db_free_result']($request);
    // Load the message icons - the usual suspects.
    $stable_icons = array('xx', 'thumbup', 'thumbdown', 'exclamation', 'question', 'lamp', 'smiley', 'angry', 'cheesy', 'grin', 'sad', 'wink', 'moved', 'recycled', 'wireless');
    $icon_sources = array();
    foreach ($stable_icons as $icon) {
        $icon_sources[$icon] = 'images_url';
    }
    // Find the post ids.
    $request = $smcFunc['db_query']('', '
		SELECT id_first_msg
		FROM {db_prefix}topics
		WHERE id_board = {int:current_board}' . ($modSettings['postmod_active'] ? '
			AND approved = {int:is_approved}' : '') . '
		ORDER BY id_first_msg DESC
		LIMIT ' . $start . ', ' . $limit, array('current_board' => $board, 'is_approved' => 1));
    $posts = array();
    while ($row = $smcFunc['db_fetch_assoc']($request)) {
        $posts[] = $row['id_first_msg'];
    }
    $smcFunc['db_free_result']($request);
    if (empty($posts)) {
        return array();
    }
    // Find the posts.
    $request = $smcFunc['db_query']('', '
		SELECT
			m.icon, m.subject, m.body, IFNULL(mem.real_name, m.poster_name) AS poster_name, m.poster_time,
			t.num_replies, t.id_topic, m.id_member, m.smileys_enabled, m.id_msg, t.locked, t.id_last_msg
		FROM {db_prefix}topics AS t
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
		WHERE t.id_first_msg IN ({array_int:post_list})
		ORDER BY t.id_first_msg DESC
		LIMIT ' . count($posts), array('post_list' => $posts));
    $return = array();
    while ($row = $smcFunc['db_fetch_assoc']($request)) {
        // If we want to limit the length of the post.
        if (!empty($length) && commonAPI::strlen($row['body']) > $length) {
            $row['body'] = commonAPI::substr($row['body'], 0, $length);
            // The first space or line break. (<br />, etc.)
            $cutoff = max(strrpos($row['body'], ' '), strrpos($row['body'], '<'));
            if ($cutoff !== false) {
                $row['body'] = commonAPI::substr($row['body'], 0, $cutoff);
            }
            $row['body'] .= '...';
        }
        $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
        // Check that this message icon is there...
        if (empty($modSettings['messageIconChecks_disable']) && !isset($icon_sources[$row['icon']])) {
            $icon_sources[$row['icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['icon'] . '.gif') ? 'images_url' : 'default_images_url';
        }
        censorText($row['subject']);
        censorText($row['body']);
        $return[] = array('id' => $row['id_topic'], 'message_id' => $row['id_msg'], 'icon' => '<img src="' . $settings[$icon_sources[$row['icon']]] . '/post/' . $row['icon'] . '.gif" alt="' . $row['icon'] . '" />', 'subject' => $row['subject'], 'time' => timeformat($row['poster_time']), 'timestamp' => forum_time(true, $row['poster_time']), 'body' => $row['body'], 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0', 'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['num_replies'] . ' ' . ($row['num_replies'] == 1 ? $txt['ssi_comment'] : $txt['ssi_comments']) . '</a>', 'replies' => $row['num_replies'], 'comment_href' => !empty($row['locked']) ? '' : $scripturl . '?action=post;topic=' . $row['id_topic'] . '.' . $row['num_replies'] . ';last_msg=' . $row['id_last_msg'], 'comment_link' => !empty($row['locked']) ? '' : '<a href="' . $scripturl . '?action=post;topic=' . $row['id_topic'] . '.' . $row['num_replies'] . ';last_msg=' . $row['id_last_msg'] . '">' . $txt['ssi_write_comment'] . '</a>', 'new_comment' => !empty($row['locked']) ? '' : '<a href="' . $scripturl . '?action=post;topic=' . $row['id_topic'] . '.' . $row['num_replies'] . '">' . $txt['ssi_write_comment'] . '</a>', 'poster' => array('id' => $row['id_member'], 'name' => $row['poster_name'], 'href' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '', 'link' => !empty($row['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>' : $row['poster_name']), 'locked' => !empty($row['locked']), 'is_last' => false);
    }
    $smcFunc['db_free_result']($request);
    if (empty($return)) {
        return $return;
    }
    $return[count($return) - 1]['is_last'] = true;
    if ($output_method != 'echo') {
        return $return;
    }
    foreach ($return as $news) {
        echo '
			<div class="news_item">
				<h3 class="news_header">
					', $news['icon'], '
					<a href="', $news['href'], '">', $news['subject'], '</a>
				</h3>
				<div class="news_timestamp">', $news['time'], ' ', $txt['by'], ' ', $news['poster']['link'], '</div>
				<div class="news_body" style="padding: 2ex 0;">', $news['body'], '</div>
				', $news['link'], $news['locked'] ? '' : ' | ' . $news['comment_link'], '
			</div>';
        if (!$news['is_last']) {
            echo '
			<hr />';
        }
    }
}
Beispiel #6
0
 public function prepareIndexes($word, &$wordsSearch, &$wordsExclude, $isExcluded)
 {
     global $modSettings, $smcFunc;
     $subwords = text2words($word, $this->min_word_length, true);
     if (empty($modSettings['search_force_index'])) {
         $wordsSearch['words'][] = $word;
     }
     // Excluded phrases don't benefit from being split into subwords.
     if (count($subwords) > 1 && $isExcluded) {
         continue;
     } else {
         foreach ($subwords as $subword) {
             if (commonAPI::strlen($subword) >= $this->min_word_length && !in_array($subword, $this->bannedWords)) {
                 $wordsSearch['indexed_words'][] = $subword;
                 if ($isExcluded) {
                     $wordsExclude[] = $subword;
                 }
             }
         }
     }
 }
Beispiel #7
0
function MoveTopic2()
{
    global $txt, $board, $topic, $scripturl, $sourcedir, $modSettings, $context;
    global $board, $language, $user_info, $smcFunc;
    if (empty($topic)) {
        fatal_lang_error('no_access', false);
    }
    // You can't choose to have a redirection topic and use an empty reason.
    if (isset($_POST['postRedirect']) && (!isset($_POST['reason']) || trim($_POST['reason']) == '')) {
        fatal_lang_error('movetopic_no_reason', false);
    }
    // Make sure this form hasn't been submitted before.
    checkSubmitOnce('check');
    $request = smf_db_query('
		SELECT id_member_started, id_first_msg, approved
		FROM {db_prefix}topics
		WHERE id_topic = {int:current_topic}
		LIMIT 1', array('current_topic' => $topic));
    list($id_member_started, $id_first_msg, $context['is_approved']) = mysql_fetch_row($request);
    mysql_free_result($request);
    // Can they see it?
    if (!$context['is_approved']) {
        isAllowedTo('approve_posts');
    }
    // Can they move topics on this board?
    if (!allowedTo('move_any')) {
        if ($id_member_started == $user_info['id']) {
            isAllowedTo('move_own');
            $boards = array_merge(boardsAllowedTo('move_own'), boardsAllowedTo('move_any'));
        } else {
            isAllowedTo('move_any');
        }
    } else {
        $boards = boardsAllowedTo('move_any');
    }
    // If this topic isn't approved don't let them move it if they can't approve it!
    if ($modSettings['postmod_active'] && !$context['is_approved'] && !allowedTo('approve_posts')) {
        // Only allow them to move it to other boards they can't approve it in.
        $can_approve = boardsAllowedTo('approve_posts');
        $boards = array_intersect($boards, $can_approve);
    }
    checkSession();
    require_once $sourcedir . '/lib/Subs-Post.php';
    // The destination board must be numeric.
    $_POST['toboard'] = (int) $_POST['toboard'];
    // Make sure they can see the board they are trying to move to (and get whether posts count in the target board).
    $request = smf_db_query('
		SELECT b.count_posts, b.name, m.subject
		FROM {db_prefix}boards AS b
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
		WHERE {query_see_board}
			AND b.id_board = {int:to_board}
			AND b.redirect = {string:blank_redirect}
		LIMIT 1', array('current_topic' => $topic, 'to_board' => $_POST['toboard'], 'blank_redirect' => ''));
    if (mysql_num_rows($request) == 0) {
        fatal_lang_error('no_board');
    }
    list($pcounter, $board_name, $subject) = mysql_fetch_row($request);
    mysql_free_result($request);
    // Remember this for later.
    $_SESSION['move_to_topic'] = $_POST['toboard'];
    // Rename the topic...
    if (isset($_POST['reset_subject'], $_POST['custom_subject']) && $_POST['custom_subject'] != '') {
        $_POST['custom_subject'] = strtr(commonAPI::htmltrim(commonAPI::htmlspecialchars($_POST['custom_subject'])), array("\r" => '', "\n" => '', "\t" => ''));
        // Keep checking the length.
        if (commonAPI::strlen($_POST['custom_subject']) > 100) {
            $_POST['custom_subject'] = commonAPI::substr($_POST['custom_subject'], 0, 100);
        }
        // If it's still valid move onwards and upwards.
        if ($_POST['custom_subject'] != '') {
            if (isset($_POST['enforce_subject'])) {
                // Get a response prefix, but in the forum's default language.
                if (!isset($context['response_prefix']) && !($context['response_prefix'] = CacheAPI::getCache('response_prefix'))) {
                    if ($language === $user_info['language']) {
                        $context['response_prefix'] = $txt['response_prefix'];
                    } else {
                        loadLanguage('index', $language, false);
                        $context['response_prefix'] = $txt['response_prefix'];
                        loadLanguage('index');
                    }
                    CacheAPI::putCache('response_prefix', $context['response_prefix'], 600);
                }
                smf_db_query('
					UPDATE {db_prefix}messages
					SET subject = {string:subject}
					WHERE id_topic = {int:current_topic}', array('current_topic' => $topic, 'subject' => $context['response_prefix'] . $_POST['custom_subject']));
            }
            smf_db_query('
				UPDATE {db_prefix}messages
				SET subject = {string:custom_subject}
				WHERE id_msg = {int:id_first_msg}', array('id_first_msg' => $id_first_msg, 'custom_subject' => $_POST['custom_subject']));
            // Fix the subject cache.
            updateStats('subject', $topic, $_POST['custom_subject']);
        }
    }
    // Create a link to this in the old board.
    //!!! Does this make sense if the topic was unapproved before? I'd just about say so.
    if (isset($_POST['postRedirect'])) {
        // Should be in the boardwide language.
        if ($user_info['language'] != $language) {
            loadLanguage('index', $language);
        }
        $_POST['reason'] = commonAPI::htmlspecialchars($_POST['reason'], ENT_QUOTES);
        preparsecode($_POST['reason']);
        // Add a URL onto the message.
        $_POST['reason'] = strtr($_POST['reason'], array($txt['movetopic_auto_board'] => '[url=' . $scripturl . '?board=' . $_POST['toboard'] . '.0]' . $board_name . '[/url]', $txt['movetopic_auto_topic'] => '[iurl]' . $scripturl . '?topic=' . $topic . '.0[/iurl]'));
        $msgOptions = array('subject' => $txt['moved'] . ': ' . $subject, 'body' => $_POST['reason'], 'icon' => 'moved', 'smileys_enabled' => 1);
        $topicOptions = array('board' => $board, 'lock_mode' => 1, 'mark_as_read' => true, 'topic_prefix' => 0, 'topic_layout' => 0);
        $posterOptions = array('id' => $user_info['id'], 'update_post_count' => empty($pcounter));
        createPost($msgOptions, $topicOptions, $posterOptions);
    }
    $request = smf_db_query('
		SELECT count_posts
		FROM {db_prefix}boards
		WHERE id_board = {int:current_board}
		LIMIT 1', array('current_board' => $board));
    list($pcounter_from) = mysql_fetch_row($request);
    mysql_free_result($request);
    if ($pcounter_from != $pcounter) {
        $request = smf_db_query('
			SELECT id_member
			FROM {db_prefix}messages
			WHERE id_topic = {int:current_topic}
				AND approved = {int:is_approved}', array('current_topic' => $topic, 'is_approved' => 1));
        $posters = array();
        while ($row = mysql_fetch_assoc($request)) {
            if (!isset($posters[$row['id_member']])) {
                $posters[$row['id_member']] = 0;
            }
            $posters[$row['id_member']]++;
        }
        mysql_free_result($request);
        foreach ($posters as $id_member => $posts) {
            // The board we're moving from counted posts, but not to.
            if (empty($pcounter_from)) {
                updateMemberData($id_member, array('posts' => 'posts - ' . $posts));
            } else {
                updateMemberData($id_member, array('posts' => 'posts + ' . $posts));
            }
        }
    }
    // Do the move (includes statistics update needed for the redirect topic).
    moveTopics($topic, $_POST['toboard']);
    // Log that they moved this topic.
    if (!allowedTo('move_own') || $id_member_started != $user_info['id']) {
        logAction('move', array('topic' => $topic, 'board_from' => $board, 'board_to' => $_POST['toboard']));
    }
    // Notify people that this topic has been moved?
    sendNotifications($topic, 'move');
    // Why not go back to the original board in case they want to keep moving?
    if (!isset($_REQUEST['goback'])) {
        redirectexit('board=' . $board . '.0');
    } else {
        redirectexit('topic=' . $topic . '.0');
    }
}
Beispiel #8
0
function iCalDownload()
{
    global $smcFunc, $sourcedir, $forum_version, $context, $modSettings;
    // Goes without saying that this is required.
    if (!isset($_REQUEST['eventid'])) {
        fatal_lang_error('no_access', false);
    }
    // This is kinda wanted.
    require_once $sourcedir . '/lib/Subs-Calendar.php';
    // Load up the event in question and check it exists.
    $event = getEventProperties($_REQUEST['eventid']);
    if ($event === false) {
        fatal_lang_error('no_access', false);
    }
    // Check the title isn't too long - iCal requires some formatting if so.
    $title = str_split($event['title'], 30);
    foreach ($title as $id => $line) {
        if ($id != 0) {
            $title[$id] = ' ' . $title[$id];
        }
        $title[$id] .= "\n";
    }
    // Format the date.
    $date = $event['year'] . '-' . ($event['month'] < 10 ? '0' . $event['month'] : $event['month']) . '-' . ($event['day'] < 10 ? '0' . $event['day'] : $event['day']) . 'T';
    $date .= '1200:00:00Z';
    // This is what we will be sending later.
    $filecontents = '';
    $filecontents .= 'BEGIN:VCALENDAR' . "\n";
    $filecontents .= 'VERSION:2.0' . "\n";
    $filecontents .= 'PRODID:-//SimpleMachines//SMF ' . (empty($forum_version) ? 1.0 : strtr($forum_version, array('SMF ' => ''))) . '//EN' . "\n";
    $filecontents .= 'BEGIN:VEVENT' . "\n";
    $filecontents .= 'DTSTART:' . $date . "\n";
    $filecontents .= 'DTEND:' . $date . "\n";
    $filecontents .= 'SUMMARY:' . implode('', $title);
    $filecontents .= 'END:VEVENT' . "\n";
    $filecontents .= 'END:VCALENDAR';
    // Send some standard headers.
    ob_end_clean();
    if (!empty($modSettings['enableCompressedOutput'])) {
        @ob_start('ob_gzhandler');
    } else {
        ob_start();
    }
    // Send the file headers
    header('Pragma: ');
    header('Cache-Control: no-cache');
    if (!$context['browser']['is_gecko']) {
        header('Content-Transfer-Encoding: binary');
    }
    header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 525600 * 60) . ' GMT');
    header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . 'GMT');
    header('Accept-Ranges: bytes');
    header('Connection: close');
    header('Content-Disposition: attachment; filename=' . $event['title'] . '.ics');
    // How big is it?
    if (empty($modSettings['enableCompressedOutput'])) {
        header('Content-Length: ' . commonAPI::strlen($filecontents));
    }
    // This is a calendar item!
    header('Content-Type: text/calendar');
    // Chuck out the card.
    echo $filecontents;
    // Off we pop - lovely!
    obExit(false);
}
Beispiel #9
0
function registerMember(&$regOptions, $return_errors = false)
{
    global $scripturl, $txt, $modSettings, $context, $sourcedir;
    global $user_info, $options, $settings, $smcFunc;
    loadLanguage('Login');
    // We'll need some external functions.
    require_once $sourcedir . '/lib/Subs-Auth.php';
    require_once $sourcedir . '/lib/Subs-Post.php';
    // Put any errors in here.
    $reg_errors = array();
    // Registration from the admin center, let them sweat a little more.
    if ($regOptions['interface'] == 'admin') {
        is_not_guest();
        isAllowedTo('moderate_forum');
    } elseif ($regOptions['interface'] == 'guest') {
        // You cannot register twice...
        if (empty($user_info['is_guest'])) {
            redirectexit();
        }
        // Make sure they didn't just register with this session.
        if (!empty($_SESSION['just_registered']) && empty($modSettings['disableRegisterCheck'])) {
            fatal_lang_error('register_only_once', false);
        }
    }
    // What method of authorization are we going to use?
    if (empty($regOptions['auth_method']) || !in_array($regOptions['auth_method'], array('password', 'openid'))) {
        if (!empty($regOptions['openid'])) {
            $regOptions['auth_method'] = 'openid';
        } else {
            $regOptions['auth_method'] = 'password';
        }
    }
    // No name?!  How can you register with no name?
    if (empty($regOptions['username'])) {
        $reg_errors[] = array('lang', 'need_username');
    }
    // Spaces and other odd characters are evil...
    $regOptions['username'] = preg_replace('~[\\t\\n\\r\\x0B\\0' . ($context['server']['complex_preg_chars'] ? '\\x{A0}' : " ") . ']+~u', ' ', $regOptions['username']);
    // Don't use too long a name.
    if (commonAPI::strlen($regOptions['username']) > 25) {
        $reg_errors[] = array('lang', 'error_long_name');
    }
    // Only these characters are permitted.
    if (preg_match('~[<>&"\'=\\\\]~', preg_replace('~&#(?:\\d{1,7}|x[0-9a-fA-F]{1,6});~', '', $regOptions['username'])) != 0 || $regOptions['username'] == '_' || $regOptions['username'] == '|' || strpos($regOptions['username'], '[code') !== false || strpos($regOptions['username'], '[/code') !== false) {
        $reg_errors[] = array('lang', 'error_invalid_characters_username');
    }
    if (commonAPI::strtolower($regOptions['username']) === commonAPI::strtolower($txt['guest_title'])) {
        $reg_errors[] = array('lang', 'username_reserved', 'general', array($txt['guest_title']));
    }
    // !!! Separate the sprintf?
    if (empty($regOptions['email']) || preg_match('~^[0-9A-Za-z=_+\\-/][0-9A-Za-z=_\'+\\-/\\.]*@[\\w\\-]+(\\.[\\w\\-]+)*(\\.[\\w]{2,6})$~', $regOptions['email']) === 0 || strlen($regOptions['email']) > 255) {
        $reg_errors[] = array('done', sprintf($txt['valid_email_needed'], commonAPI::htmlspecialchars($regOptions['username'])));
    }
    if (!empty($regOptions['check_reserved_name']) && isReservedName($regOptions['username'], 0, false)) {
        if ($regOptions['password'] == 'chocolate cake') {
            $reg_errors[] = array('done', 'Sorry, I don\'t take bribes... you\'ll need to come up with a different name.');
        }
        $reg_errors[] = array('done', '(' . htmlspecialchars($regOptions['username']) . ') ' . $txt['name_in_use']);
    }
    // Generate a validation code if it's supposed to be emailed.
    $validation_code = '';
    if ($regOptions['require'] == 'activation') {
        $validation_code = generateValidationCode();
    }
    // If you haven't put in a password generate one.
    if ($regOptions['interface'] == 'admin' && $regOptions['password'] == '' && $regOptions['auth_method'] == 'password') {
        mt_srand(time() + 1277);
        $regOptions['password'] = generateValidationCode();
        $regOptions['password_check'] = $regOptions['password'];
    } elseif ($regOptions['password'] != $regOptions['password_check'] && $regOptions['auth_method'] == 'password') {
        $reg_errors[] = array('lang', 'passwords_dont_match');
    }
    // That's kind of easy to guess...
    if ($regOptions['password'] == '') {
        if ($regOptions['auth_method'] == 'password') {
            $reg_errors[] = array('lang', 'no_password');
        } else {
            $regOptions['password'] = sha1(mt_rand());
        }
    }
    // Now perform hard password validation as required.
    if (!empty($regOptions['check_password_strength'])) {
        $passwordError = validatePassword($regOptions['password'], $regOptions['username'], array($regOptions['email']));
        // Password isn't legal?
        if ($passwordError != null) {
            $reg_errors[] = array('lang', 'profile_error_password_' . $passwordError);
        }
    }
    // If they are using an OpenID that hasn't been verified yet error out.
    // !!! Change this so they can register without having to attempt a login first
    if ($regOptions['auth_method'] == 'openid' && (empty($_SESSION['openid']['verified']) || $_SESSION['openid']['openid_uri'] != $regOptions['openid'])) {
        $reg_errors[] = array('lang', 'openid_not_verified');
    }
    // You may not be allowed to register this email.
    if (!empty($regOptions['check_email_ban'])) {
        isBannedEmail($regOptions['email'], 'cannot_register', $txt['ban_register_prohibited']);
    }
    // Check if the email address is in use.
    $request = smf_db_query('
		SELECT id_member
		FROM {db_prefix}members
		WHERE email_address = {string:email_address}
			OR email_address = {string:username}
		LIMIT 1', array('email_address' => $regOptions['email'], 'username' => $regOptions['username']));
    // !!! Separate the sprintf?
    if (mysql_num_rows($request) != 0) {
        $reg_errors[] = array('lang', 'email_in_use', false, array(htmlspecialchars($regOptions['email'])));
    }
    mysql_free_result($request);
    // If we found any errors we need to do something about it right away!
    foreach ($reg_errors as $key => $error) {
        /* Note for each error:
        			0 = 'lang' if it's an index, 'done' if it's clear text.
        			1 = The text/index.
        			2 = Whether to log.
        			3 = sprintf data if necessary. */
        if ($error[0] == 'lang') {
            loadLanguage('Errors');
        }
        $message = $error[0] == 'lang' ? empty($error[3]) ? $txt[$error[1]] : vsprintf($txt[$error[1]], $error[3]) : $error[1];
        // What to do, what to do, what to do.
        if ($return_errors) {
            if (!empty($error[2])) {
                log_error($message, $error[2]);
            }
            $reg_errors[$key] = $message;
        } else {
            fatal_error($message, empty($error[2]) ? false : $error[2]);
        }
    }
    // If there's any errors left return them at once!
    if (!empty($reg_errors)) {
        return $reg_errors;
    }
    $reservedVars = array('actual_theme_url', 'actual_images_url', 'base_theme_dir', 'base_theme_url', 'default_images_url', 'default_theme_dir', 'default_theme_url', 'default_template', 'images_url', 'number_recent_posts', 'smiley_sets_default', 'theme_dir', 'theme_id', 'theme_layers', 'theme_templates', 'theme_url');
    // Can't change reserved vars.
    if (isset($regOptions['theme_vars']) && array_intersect($regOptions['theme_vars'], $reservedVars) != array()) {
        fatal_lang_error('no_theme');
    }
    // Some of these might be overwritten. (the lower ones that are in the arrays below.)
    $regOptions['register_vars'] = array('member_name' => $regOptions['username'], 'email_address' => $regOptions['email'], 'passwd' => sha1(strtolower($regOptions['username']) . $regOptions['password']), 'password_salt' => substr(md5(mt_rand()), 0, 4), 'posts' => 0, 'date_registered' => time(), 'member_ip' => $regOptions['interface'] == 'admin' ? '127.0.0.1' : $user_info['ip'], 'member_ip2' => $regOptions['interface'] == 'admin' ? '127.0.0.1' : $_SERVER['BAN_CHECK_IP'], 'validation_code' => $validation_code, 'real_name' => $regOptions['username'], 'personal_text' => $modSettings['default_personal_text'], 'pm_email_notify' => 1, 'id_theme' => 0, 'id_post_group' => 4, 'lngfile' => '', 'buddy_list' => '', 'pm_ignore_list' => '', 'message_labels' => '', 'location' => '', 'time_format' => '', 'signature' => '', 'avatar' => '', 'usertitle' => '', 'secret_question' => '', 'secret_answer' => '', 'additional_groups' => '', 'ignore_boards' => '', 'smiley_set' => '', 'openid_uri' => !empty($regOptions['openid']) ? $regOptions['openid'] : '');
    // Setup the activation status on this new account so it is correct - firstly is it an under age account?
    if ($regOptions['require'] == 'coppa') {
        $regOptions['register_vars']['is_activated'] = 5;
        // !!! This should be changed.  To what should be it be changed??
        $regOptions['register_vars']['validation_code'] = '';
    } elseif ($regOptions['require'] == 'nothing') {
        $regOptions['register_vars']['is_activated'] = 1;
    } elseif ($regOptions['require'] == 'activation') {
        $regOptions['register_vars']['is_activated'] = 0;
    } else {
        $regOptions['register_vars']['is_activated'] = 3;
    }
    if (isset($regOptions['memberGroup'])) {
        // Make sure the id_group will be valid, if this is an administator.
        $regOptions['register_vars']['id_group'] = $regOptions['memberGroup'] == 1 && !allowedTo('admin_forum') ? 0 : $regOptions['memberGroup'];
        // Check if this group is assignable.
        $unassignableGroups = array(-1, 3);
        $request = smf_db_query('
			SELECT id_group
			FROM {db_prefix}membergroups
			WHERE min_posts != {int:min_posts}' . (allowedTo('admin_forum') ? '' : '
				OR group_type = {int:is_protected}'), array('min_posts' => -1, 'is_protected' => 1));
        while ($row = mysql_fetch_assoc($request)) {
            $unassignableGroups[] = $row['id_group'];
        }
        mysql_free_result($request);
        if (in_array($regOptions['register_vars']['id_group'], $unassignableGroups)) {
            $regOptions['register_vars']['id_group'] = 0;
        }
    }
    // Integrate optional member settings to be set.
    if (!empty($regOptions['extra_register_vars'])) {
        foreach ($regOptions['extra_register_vars'] as $var => $value) {
            $regOptions['register_vars'][$var] = $value;
        }
    }
    // Integrate optional user theme options to be set.
    $theme_vars = array();
    if (!empty($regOptions['theme_vars'])) {
        foreach ($regOptions['theme_vars'] as $var => $value) {
            $theme_vars[$var] = $value;
        }
    }
    // Call an optional function to validate the users' input.
    HookAPI::callHook('integrate_register', array(&$regOptions, &$theme_vars));
    // Right, now let's prepare for insertion.
    $knownInts = array('date_registered', 'posts', 'id_group', 'last_login', 'instant_messages', 'unread_messages', 'new_pm', 'pm_prefs', 'gender', 'hide_email', 'show_online', 'pm_email_notify', 'karma_good', 'karma_bad', 'notify_announcements', 'notify_send_body', 'notify_regularity', 'notify_types', 'id_theme', 'is_activated', 'id_msg_last_visit', 'id_post_group', 'total_time_logged_in', 'warning');
    $knownFloats = array('time_offset');
    $column_names = array();
    $values = array();
    foreach ($regOptions['register_vars'] as $var => $val) {
        $type = 'string';
        if (in_array($var, $knownInts)) {
            $type = 'int';
        } elseif (in_array($var, $knownFloats)) {
            $type = 'float';
        } elseif ($var == 'birthdate') {
            $type = 'date';
        }
        $column_names[$var] = $type;
        $values[$var] = $val;
    }
    // Register them into the database.
    smf_db_insert('', '{db_prefix}members', $column_names, $values, array('id_member'));
    $memberID = smf_db_insert_id('{db_prefix}members', 'id_member');
    // Update the number of members and latest member's info - and pass the name, but remove the 's.
    if ($regOptions['register_vars']['is_activated'] == 1) {
        updateStats('member', $memberID, $regOptions['register_vars']['real_name']);
    } else {
        updateStats('member');
    }
    // Theme variables too?
    if (!empty($theme_vars)) {
        $inserts = array();
        foreach ($theme_vars as $var => $val) {
            $inserts[] = array($memberID, $var, $val);
        }
        smf_db_insert('insert', '{db_prefix}themes', array('id_member' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), $inserts, array('id_member', 'variable'));
    }
    // If it's enabled, increase the registrations for today.
    trackStats(array('registers' => '+'));
    // Administrative registrations are a bit different...
    if ($regOptions['interface'] == 'admin') {
        if ($regOptions['require'] == 'activation') {
            $email_message = 'admin_register_activate';
        } elseif (!empty($regOptions['send_welcome_email'])) {
            $email_message = 'admin_register_immediate';
        }
        if (isset($email_message)) {
            $replacements = array('REALNAME' => $regOptions['register_vars']['real_name'], 'USERNAME' => $regOptions['username'], 'PASSWORD' => $regOptions['password'], 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder', 'ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $memberID . ';code=' . $validation_code, 'ACTIVATIONLINKWITHOUTCODE' => $scripturl . '?action=activate;u=' . $memberID, 'ACTIVATIONCODE' => $validation_code);
            $emaildata = loadEmailTemplate($email_message, $replacements);
            sendmail($regOptions['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0);
        }
        // All admins are finished here.
        return $memberID;
    }
    // Can post straight away - welcome them to your fantastic community...
    if ($regOptions['require'] == 'nothing') {
        if (!empty($regOptions['send_welcome_email'])) {
            $replacements = array('REALNAME' => $regOptions['register_vars']['real_name'], 'USERNAME' => $regOptions['username'], 'PASSWORD' => $regOptions['password'], 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder', 'OPENID' => !empty($regOptions['openid']) ? $regOptions['openid'] : '');
            $emaildata = loadEmailTemplate('register_' . ($regOptions['auth_method'] == 'openid' ? 'openid_' : '') . 'immediate', $replacements);
            sendmail($regOptions['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0);
        }
        // Send admin their notification.
        adminNotify('standard', $memberID, $regOptions['username']);
    } elseif ($regOptions['require'] == 'activation' || $regOptions['require'] == 'coppa') {
        $replacements = array('REALNAME' => $regOptions['register_vars']['real_name'], 'USERNAME' => $regOptions['username'], 'PASSWORD' => $regOptions['password'], 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder', 'OPENID' => !empty($regOptions['openid']) ? $regOptions['openid'] : '');
        if ($regOptions['require'] == 'activation') {
            $replacements += array('ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $memberID . ';code=' . $validation_code, 'ACTIVATIONLINKWITHOUTCODE' => $scripturl . '?action=activate;u=' . $memberID, 'ACTIVATIONCODE' => $validation_code);
        } else {
            $replacements += array('COPPALINK' => $scripturl . '?action=coppa;u=' . $memberID);
        }
        $emaildata = loadEmailTemplate('register_' . ($regOptions['auth_method'] == 'openid' ? 'openid_' : '') . ($regOptions['require'] == 'activation' ? 'activate' : 'coppa'), $replacements);
        sendmail($regOptions['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0);
    } else {
        $replacements = array('REALNAME' => $regOptions['register_vars']['real_name'], 'USERNAME' => $regOptions['username'], 'PASSWORD' => $regOptions['password'], 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder', 'OPENID' => !empty($regOptions['openid']) ? $regOptions['openid'] : '');
        $emaildata = loadEmailTemplate('register_' . ($regOptions['auth_method'] == 'openid' ? 'openid_' : '') . 'pending', $replacements);
        sendmail($regOptions['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0);
        // Admin gets informed here...
        adminNotify('approval', $memberID, $regOptions['username']);
    }
    // Okay, they're for sure registered... make sure the session is aware of this for security. (Just married :P!)
    $_SESSION['just_registered'] = 1;
    return $memberID;
}
Beispiel #10
0
function prepareTopicArray($topic_ids)
{
    global $scripturl, $txt, $db_prefix, $modSettings, $options, $context;
    global $user_info, $board_info, $settings, $smcFunc;
    // Setup the default topic icons...
    $stable_icons = array('xx', 'thumbup', 'thumbdown', 'exclamation', 'question', 'lamp', 'smiley', 'angry', 'cheesy', 'grin', 'sad', 'wink', 'moved', 'recycled', 'wireless');
    $context['icon_sources'] = array();
    foreach ($stable_icons as $icon) {
        $context['icon_sources'][$icon] = 'images_url';
    }
    if (empty($topic_ids)) {
        return false;
    }
    $context['messages_per_page'] = commonAPI::getMessagesPerPage();
    $result = smf_db_query('
		SELECT
			t.id_topic, t.num_replies, t.locked, t.num_views, t.is_sticky, t.id_poll, t.id_previous_board,
			' . ($user_info['is_guest'] ? '0' : 'IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1') . ' AS new_from,
			t.id_last_msg, t.approved, t.unapproved_posts, ml.poster_time AS last_poster_time,
			ml.id_msg_modified, ml.subject AS last_subject, ml.icon AS last_icon,
			ml.poster_name AS last_member_name, ml.id_member AS last_id_member,
			IFNULL(meml.real_name, ml.poster_name) AS last_display_name, t.id_first_msg,
			mf.poster_time AS first_poster_time, mf.subject AS first_subject, mf.icon AS first_icon,
			mf.poster_name AS first_member_name, mf.id_member AS first_id_member,
			IFNULL(memf.real_name, mf.poster_name) AS first_display_name, SUBSTRING(ml.body, 1, 385) AS last_body,
			SUBSTRING(mf.body, 1, 385) AS first_body, ml.smileys_enabled AS last_smileys,
			mf.smileys_enabled AS first_smileys, b.id_board, b.name As board_name
		FROM {db_prefix}topics AS t
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
			INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
			INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
			LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member)
			LEFT JOIN {db_prefix}members AS memf ON (memf.id_member = mf.id_member)' . ($user_info['is_guest'] ? '' : '
			LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
			LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})') . '
		WHERE t.id_topic IN ({array_int:topic_list})
			AND (t.approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR t.id_member_started = {int:current_member}') . ')', array('current_member' => $user_info['id'], 'topic_list' => $topic_ids, 'is_approved' => 1));
    // Stolen from SMF
    while ($row = mysql_fetch_assoc($result)) {
        if ($row['id_poll'] > 0 && $modSettings['pollMode'] == '0') {
            continue;
        }
        if (!empty($settings['message_index_preview'])) {
            // Limit them to 128 characters - do this FIRST because it's a lot of wasted censoring otherwise.
            $row['first_body'] = strip_tags(strtr(parse_bbc($row['first_body'], $row['first_smileys'], $row['id_first_msg']), array('<br />' => '&#10;')));
            if (commonAPI::strlen($row['first_body']) > 128) {
                $row['first_body'] = commonAPI::substr($row['first_body'], 0, 128) . '...';
            }
            $row['last_body'] = strip_tags(strtr(parse_bbc($row['last_body'], $row['last_smileys'], $row['id_last_msg']), array('<br />' => '&#10;')));
            if (commonAPI::strlen($row['last_body']) > 128) {
                $row['last_body'] = commonAPI::substr($row['last_body'], 0, 128) . '...';
            }
            // Censor the subject and message preview.
            censorText($row['first_subject']);
            censorText($row['first_body']);
            // Don't censor them twice!
            if ($row['id_first_msg'] == $row['id_last_msg']) {
                $row['last_subject'] = $row['first_subject'];
                $row['last_body'] = $row['first_body'];
            } else {
                censorText($row['last_subject']);
                censorText($row['last_body']);
            }
        } else {
            $row['first_body'] = '';
            $row['last_body'] = '';
            censorText($row['first_subject']);
            if ($row['id_first_msg'] == $row['id_last_msg']) {
                $row['last_subject'] = $row['first_subject'];
            } else {
                censorText($row['last_subject']);
            }
        }
        // Decide how many pages the topic should have.
        $topic_length = $row['num_replies'] + 1;
        if ($topic_length > $context['messages_per_page']) {
            $tmppages = array();
            $tmpa = 1;
            for ($tmpb = 0; $tmpb < $topic_length; $tmpb += $context['messages_per_page']) {
                $tmppages[] = '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.' . $tmpb . '">' . $tmpa . '</a>';
                $tmpa++;
            }
            // Show links to all the pages?
            if (count($tmppages) <= 5) {
                $pages = '&#171; ' . implode(' ', $tmppages);
            } else {
                $pages = '&#171; ' . $tmppages[0] . ' ' . $tmppages[1] . ' ... ' . $tmppages[count($tmppages) - 2] . ' ' . $tmppages[count($tmppages) - 1];
            }
            if (!empty($modSettings['enableAllMessages']) && $topic_length < $modSettings['enableAllMessages']) {
                $pages .= ' &nbsp;<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0;all">' . $txt['all'] . '</a>';
            }
            $pages .= ' &#187;';
        } else {
            $pages = '';
        }
        // We need to check the topic icons exist...
        if (empty($modSettings['messageIconChecks_disable'])) {
            if (!isset($context['icon_sources'][$row['first_icon']])) {
                $context['icon_sources'][$row['first_icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['first_icon'] . '.gif') ? 'images_url' : 'default_images_url';
            }
            if (!isset($context['icon_sources'][$row['last_icon']])) {
                $context['icon_sources'][$row['last_icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['last_icon'] . '.gif') ? 'images_url' : 'default_images_url';
            }
        } else {
            if (!isset($context['icon_sources'][$row['first_icon']])) {
                $context['icon_sources'][$row['first_icon']] = 'images_url';
            }
            if (!isset($context['icon_sources'][$row['last_icon']])) {
                $context['icon_sources'][$row['last_icon']] = 'images_url';
            }
        }
        $context['related_topics'][$row['id_topic']] = array('id' => $row['id_topic'], 'board' => array('id' => $row['id_board'], 'name' => $row['board_name'], 'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['board_name'] . '</a>', 'can_approve_posts' => in_array(0, $context['can_approve_posts_boards']) || in_array($row['id_board'], $context['can_approve_posts_boards'])), 'first_post' => array('id' => $row['id_first_msg'], 'member' => array('username' => $row['first_member_name'], 'name' => $row['first_display_name'], 'id' => $row['first_id_member'], 'href' => !empty($row['first_id_member']) ? $scripturl . '?action=profile;u=' . $row['first_id_member'] : '', 'link' => !empty($row['first_id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['first_id_member'] . '" title="' . $txt['profile_of'] . ' ' . $row['first_display_name'] . '">' . $row['first_display_name'] . '</a>' : $row['first_display_name']), 'time' => timeformat($row['first_poster_time']), 'timestamp' => forum_time(true, $row['first_poster_time']), 'subject' => $row['first_subject'], 'preview' => $row['first_body'], 'icon' => $row['first_icon'], 'icon_url' => $settings[$context['icon_sources'][$row['first_icon']]] . '/post/' . $row['first_icon'] . '.gif', 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0', 'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['first_subject'] . '</a>'), 'last_post' => array('id' => $row['id_last_msg'], 'member' => array('username' => $row['last_member_name'], 'name' => $row['last_display_name'], 'id' => $row['last_id_member'], 'href' => !empty($row['last_id_member']) ? $scripturl . '?action=profile;u=' . $row['last_id_member'] : '', 'link' => !empty($row['last_id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['last_id_member'] . '">' . $row['last_display_name'] . '</a>' : $row['last_display_name']), 'time' => timeformat($row['last_poster_time']), 'timestamp' => forum_time(true, $row['last_poster_time']), 'subject' => $row['last_subject'], 'preview' => $row['last_body'], 'icon' => $row['last_icon'], 'icon_url' => $settings[$context['icon_sources'][$row['last_icon']]] . '/post/' . $row['last_icon'] . '.gif', 'href' => $scripturl . '?topic=' . $row['id_topic'] . ($row['num_replies'] == 0 ? '.0' : '.msg' . $row['id_last_msg']) . '#new', 'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . ($row['num_replies'] == 0 ? '.0' : '.msg' . $row['id_last_msg']) . '#new" ' . ($row['num_replies'] == 0 ? '' : 'rel="nofollow"') . '>' . $row['last_subject'] . '</a>'), 'is_sticky' => !empty($modSettings['enableStickyTopics']) && !empty($row['is_sticky']), 'is_locked' => !empty($row['locked']), 'is_poll' => $modSettings['pollMode'] == '1' && $row['id_poll'] > 0, 'is_hot' => $row['num_replies'] >= $modSettings['hotTopicPosts'], 'is_very_hot' => $row['num_replies'] >= $modSettings['hotTopicVeryPosts'], 'is_posted_in' => false, 'icon' => $row['first_icon'], 'icon_url' => $settings[$context['icon_sources'][$row['first_icon']]] . '/post/' . $row['first_icon'] . '.gif', 'subject' => $row['first_subject'], 'new' => $row['new_from'] <= $row['id_msg_modified'], 'new_from' => $row['new_from'], 'newtime' => $row['new_from'], 'new_href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['new_from'] . '#new', 'pages' => $pages, 'replies' => $row['num_replies'], 'views' => $row['num_views'], 'approved' => $row['approved'], 'unapproved_posts' => $row['unapproved_posts']);
        determineTopicClass($context['related_topics'][$row['id_topic']]);
    }
    mysql_free_result($result);
}
Beispiel #11
0
function MergeExecute($topics = array())
{
    global $user_info, $txt, $context, $scripturl, $sourcedir;
    global $smcFunc, $language, $modSettings;
    // Check the session.
    checkSession('request');
    // Handle URLs from MergeIndex.
    if (!empty($_GET['from']) && !empty($_GET['to'])) {
        $topics = array((int) $_GET['from'], (int) $_GET['to']);
    }
    // If we came from a form, the topic IDs came by post.
    if (!empty($_POST['topics']) && is_array($_POST['topics'])) {
        $topics = $_POST['topics'];
    }
    // There's nothing to merge with just one topic...
    if (empty($topics) || !is_array($topics) || count($topics) == 1) {
        fatal_lang_error('merge_need_more_topics');
    }
    // Make sure every topic is numeric, or some nasty things could be done with the DB.
    foreach ($topics as $id => $topic) {
        $topics[$id] = (int) $topic;
    }
    // Joy of all joys, make sure they're not pi**ing about with unapproved topics they can't see :P
    if ($modSettings['postmod_active']) {
        $can_approve_boards = boardsAllowedTo('approve_posts');
    }
    // Get info about the topics and polls that will be merged.
    $request = smf_db_query('
		SELECT
			t.id_topic, t.id_board, t.id_poll, t.num_views, t.is_sticky, t.approved, t.num_replies, t.unapproved_posts,
			m1.subject, m1.poster_time AS time_started, IFNULL(mem1.id_member, 0) AS id_member_started, IFNULL(mem1.real_name, m1.poster_name) AS name_started,
			m2.poster_time AS time_updated, IFNULL(mem2.id_member, 0) AS id_member_updated, IFNULL(mem2.real_name, m2.poster_name) AS name_updated
		FROM {db_prefix}topics AS t
			INNER JOIN {db_prefix}messages AS m1 ON (m1.id_msg = t.id_first_msg)
			INNER JOIN {db_prefix}messages AS m2 ON (m2.id_msg = t.id_last_msg)
			LEFT JOIN {db_prefix}members AS mem1 ON (mem1.id_member = m1.id_member)
			LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = m2.id_member)
		WHERE t.id_topic IN ({array_int:topic_list})
		ORDER BY t.id_first_msg
		LIMIT ' . count($topics), array('topic_list' => $topics));
    if (mysql_num_rows($request) < 2) {
        fatal_lang_error('no_topic_id');
    }
    $num_views = 0;
    $is_sticky = 0;
    $boardTotals = array();
    $boards = array();
    $polls = array();
    while ($row = mysql_fetch_assoc($request)) {
        // Make a note for the board counts...
        if (!isset($boardTotals[$row['id_board']])) {
            $boardTotals[$row['id_board']] = array('posts' => 0, 'topics' => 0, 'unapproved_posts' => 0, 'unapproved_topics' => 0);
        }
        // We can't see unapproved topics here?
        if ($modSettings['postmod_active'] && !$row['approved'] && $can_approve_boards != array(0) && in_array($row['id_board'], $can_approve_boards)) {
            continue;
        } elseif (!$row['approved']) {
            $boardTotals[$row['id_board']]['unapproved_topics']++;
        } else {
            $boardTotals[$row['id_board']]['topics']++;
        }
        $boardTotals[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts'];
        $boardTotals[$row['id_board']]['posts'] += $row['num_replies'] + ($row['approved'] ? 1 : 0);
        $topic_data[$row['id_topic']] = array('id' => $row['id_topic'], 'board' => $row['id_board'], 'poll' => $row['id_poll'], 'num_views' => $row['num_views'], 'subject' => $row['subject'], 'started' => array('time' => timeformat($row['time_started']), 'timestamp' => forum_time(true, $row['time_started']), 'href' => empty($row['id_member_started']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member_started'], 'link' => empty($row['id_member_started']) ? $row['name_started'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_started'] . '">' . $row['name_started'] . '</a>'), 'updated' => array('time' => timeformat($row['time_updated']), 'timestamp' => forum_time(true, $row['time_updated']), 'href' => empty($row['id_member_updated']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member_updated'], 'link' => empty($row['id_member_updated']) ? $row['name_updated'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_updated'] . '">' . $row['name_updated'] . '</a>'));
        $num_views += $row['num_views'];
        $boards[] = $row['id_board'];
        // If there's no poll, id_poll == 0...
        if ($row['id_poll'] > 0) {
            $polls[] = $row['id_poll'];
        }
        // Store the id_topic with the lowest id_first_msg.
        if (empty($firstTopic)) {
            $firstTopic = $row['id_topic'];
        }
        $is_sticky = max($is_sticky, $row['is_sticky']);
    }
    mysql_free_result($request);
    // If we didn't get any topics then they've been messing with unapproved stuff.
    if (empty($topic_data)) {
        fatal_lang_error('no_topic_id');
    }
    $boards = array_values(array_unique($boards));
    // The parameters of MergeExecute were set, so this must've been an internal call.
    if (!empty($topics)) {
        isAllowedTo('merge_any', $boards);
        loadTemplate('SplitTopics');
    }
    // Get the boards a user is allowed to merge in.
    $merge_boards = boardsAllowedTo('merge_any');
    if (empty($merge_boards)) {
        fatal_lang_error('cannot_merge_any', 'user');
    }
    // Make sure they can see all boards....
    $request = smf_db_query('
		SELECT b.id_board
		FROM {db_prefix}boards AS b
		WHERE b.id_board IN ({array_int:boards})
			AND {query_see_board}' . (!in_array(0, $merge_boards) ? '
			AND b.id_board IN ({array_int:merge_boards})' : '') . '
		LIMIT ' . count($boards), array('boards' => $boards, 'merge_boards' => $merge_boards));
    // If the number of boards that's in the output isn't exactly the same as we've put in there, you're in trouble.
    if (mysql_num_rows($request) != count($boards)) {
        fatal_lang_error('no_board');
    }
    mysql_free_result($request);
    if (empty($_REQUEST['sa']) || $_REQUEST['sa'] == 'options') {
        if (count($polls) > 1) {
            $request = smf_db_query('
				SELECT t.id_topic, t.id_poll, m.subject, p.question
				FROM {db_prefix}polls AS p
					INNER JOIN {db_prefix}topics AS t ON (t.id_poll = p.id_poll)
					INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
				WHERE p.id_poll IN ({array_int:polls})
				LIMIT ' . count($polls), array('polls' => $polls));
            while ($row = mysql_fetch_assoc($request)) {
                $context['polls'][] = array('id' => $row['id_poll'], 'topic' => array('id' => $row['id_topic'], 'subject' => $row['subject']), 'question' => $row['question'], 'selected' => $row['id_topic'] == $firstTopic);
            }
            mysql_free_result($request);
        }
        if (count($boards) > 1) {
            $request = smf_db_query('
				SELECT id_board, name
				FROM {db_prefix}boards
				WHERE id_board IN ({array_int:boards})
				ORDER BY name
				LIMIT ' . count($boards), array('boards' => $boards));
            while ($row = mysql_fetch_assoc($request)) {
                $context['boards'][] = array('id' => $row['id_board'], 'name' => $row['name'], 'selected' => $row['id_board'] == $topic_data[$firstTopic]['board']);
            }
            mysql_free_result($request);
        }
        $context['topics'] = $topic_data;
        foreach ($topic_data as $id => $topic) {
            $context['topics'][$id]['selected'] = $topic['id'] == $firstTopic;
        }
        $context['page_title'] = $txt['merge'];
        $context['sub_template'] = 'merge_extra_options';
        return;
    }
    // Determine target board.
    $target_board = count($boards) > 1 ? (int) $_REQUEST['board'] : $boards[0];
    if (!in_array($target_board, $boards)) {
        fatal_lang_error('no_board');
    }
    // Determine which poll will survive and which polls won't.
    $target_poll = count($polls) > 1 ? (int) $_POST['poll'] : (count($polls) == 1 ? $polls[0] : 0);
    if ($target_poll > 0 && !in_array($target_poll, $polls)) {
        fatal_lang_error('no_access', false);
    }
    $deleted_polls = empty($target_poll) ? $polls : array_diff($polls, array($target_poll));
    // Determine the subject of the newly merged topic - was a custom subject specified?
    if (empty($_POST['subject']) && isset($_POST['custom_subject']) && $_POST['custom_subject'] != '') {
        $target_subject = strtr(commonAPI::htmltrim(commonAPI::htmlspecialchars($_POST['custom_subject'])), array("\r" => '', "\n" => '', "\t" => ''));
        // Keep checking the length.
        if (commonAPI::strlen($target_subject) > 100) {
            $target_subject = commonAPI::substr($target_subject, 0, 100);
        }
        // Nothing left - odd but pick the first topics subject.
        if ($target_subject == '') {
            $target_subject = $topic_data[$firstTopic]['subject'];
        }
    } elseif (!empty($topic_data[(int) $_POST['subject']]['subject'])) {
        $target_subject = $topic_data[(int) $_POST['subject']]['subject'];
    } else {
        $target_subject = $topic_data[$firstTopic]['subject'];
    }
    // Get the first and last message and the number of messages....
    $request = smf_db_query('
		SELECT approved, MIN(id_msg) AS first_msg, MAX(id_msg) AS last_msg, COUNT(*) AS message_count
		FROM {db_prefix}messages
		WHERE id_topic IN ({array_int:topics})
		GROUP BY approved
		ORDER BY approved DESC', array('topics' => $topics));
    $topic_approved = 1;
    while ($row = mysql_fetch_assoc($request)) {
        // If this is approved, or is fully unapproved.
        if ($row['approved'] || !isset($first_msg)) {
            $first_msg = $row['first_msg'];
            $last_msg = $row['last_msg'];
            if ($row['approved']) {
                $num_replies = $row['message_count'] - 1;
                $num_unapproved = 0;
            } else {
                $topic_approved = 0;
                $num_replies = 0;
                $num_unapproved = $row['message_count'];
            }
        } else {
            // If this has a lower first_msg then the first post is not approved and hence the number of replies was wrong!
            if ($first_msg > $row['first_msg']) {
                $first_msg = $row['first_msg'];
                $num_replies++;
                $topic_approved = 0;
            }
            $num_unapproved = $row['message_count'];
        }
    }
    mysql_free_result($request);
    // Ensure we have a board stat for the target board.
    if (!isset($boardTotals[$target_board])) {
        $boardTotals[$target_board] = array('posts' => 0, 'topics' => 0, 'unapproved_posts' => 0, 'unapproved_topics' => 0);
    }
    // Fix the topic count stuff depending on what the new one counts as.
    if ($topic_approved) {
        $boardTotals[$target_board]['topics']--;
    } else {
        $boardTotals[$target_board]['unapproved_topics']--;
    }
    $boardTotals[$target_board]['unapproved_posts'] -= $num_unapproved;
    $boardTotals[$target_board]['posts'] -= $topic_approved ? $num_replies + 1 : $num_replies;
    // Get the member ID of the first and last message.
    $request = smf_db_query('
		SELECT id_member
		FROM {db_prefix}messages
		WHERE id_msg IN ({int:first_msg}, {int:last_msg})
		ORDER BY id_msg
		LIMIT 2', array('first_msg' => $first_msg, 'last_msg' => $last_msg));
    list($member_started) = mysql_fetch_row($request);
    list($member_updated) = mysql_fetch_row($request);
    // First and last message are the same, so only row was returned.
    if ($member_updated === NULL) {
        $member_updated = $member_started;
    }
    mysql_free_result($request);
    // Assign the first topic ID to be the merged topic.
    $id_topic = min($topics);
    // Delete the remaining topics.
    $deleted_topics = array_diff($topics, array($id_topic));
    smf_db_query('
		DELETE FROM {db_prefix}topics
		WHERE id_topic IN ({array_int:deleted_topics})', array('deleted_topics' => $deleted_topics));
    smf_db_query('
		DELETE FROM {db_prefix}log_search_subjects
		WHERE id_topic IN ({array_int:deleted_topics})', array('deleted_topics' => $deleted_topics));
    // Asssign the properties of the newly merged topic.
    smf_db_query('
		UPDATE {db_prefix}topics
		SET
			id_board = {int:id_board},
			id_member_started = {int:id_member_started},
			id_member_updated = {int:id_member_updated},
			id_first_msg = {int:id_first_msg},
			id_last_msg = {int:id_last_msg},
			id_poll = {int:id_poll},
			num_replies = {int:num_replies},
			unapproved_posts = {int:unapproved_posts},
			num_views = {int:num_views},
			is_sticky = {int:is_sticky},
			approved = {int:approved}
		WHERE id_topic = {int:id_topic}', array('id_board' => $target_board, 'is_sticky' => $is_sticky, 'approved' => $topic_approved, 'id_topic' => $id_topic, 'id_member_started' => $member_started, 'id_member_updated' => $member_updated, 'id_first_msg' => $first_msg, 'id_last_msg' => $last_msg, 'id_poll' => $target_poll, 'num_replies' => $num_replies, 'unapproved_posts' => $num_unapproved, 'num_views' => $num_views));
    // Grab the response prefix (like 'Re: ') in the default forum language.
    if (!isset($context['response_prefix']) && !($context['response_prefix'] = CacheAPI::getCache('response_prefix'))) {
        if ($language === $user_info['language']) {
            $context['response_prefix'] = $txt['response_prefix'];
        } else {
            loadLanguage('index', $language, false);
            $context['response_prefix'] = $txt['response_prefix'];
            loadLanguage('index');
        }
        CacheAPI::putCache('response_prefix', $context['response_prefix'], 600);
    }
    // Change the topic IDs of all messages that will be merged.  Also adjust subjects if 'enforce subject' was checked.
    smf_db_query('
		UPDATE {db_prefix}messages
		SET
			id_topic = {int:id_topic},
			id_board = {int:target_board}' . (empty($_POST['enforce_subject']) ? '' : ',
			subject = {string:subject}') . '
		WHERE id_topic IN ({array_int:topic_list})', array('topic_list' => $topics, 'id_topic' => $id_topic, 'target_board' => $target_board, 'subject' => $context['response_prefix'] . $target_subject));
    // Any reported posts should reflect the new board.
    smf_db_query('
		UPDATE {db_prefix}log_reported
		SET
			id_topic = {int:id_topic},
			id_board = {int:target_board}
		WHERE id_topic IN ({array_int:topics_list})', array('topics_list' => $topics, 'id_topic' => $id_topic, 'target_board' => $target_board));
    // Change the subject of the first message...
    smf_db_query('
		UPDATE {db_prefix}messages
		SET subject = {string:target_subject}
		WHERE id_msg = {int:first_msg}', array('first_msg' => $first_msg, 'target_subject' => $target_subject));
    // Adjust all calendar events to point to the new topic.
    smf_db_query('
		UPDATE {db_prefix}calendar
		SET
			id_topic = {int:id_topic},
			id_board = {int:target_board}
		WHERE id_topic IN ({array_int:deleted_topics})', array('deleted_topics' => $deleted_topics, 'id_topic' => $id_topic, 'target_board' => $target_board));
    // Merge log topic entries.
    $request = smf_db_query('
		SELECT id_member, MIN(id_msg) AS new_id_msg
		FROM {db_prefix}log_topics
		WHERE id_topic IN ({array_int:topics})
		GROUP BY id_member', array('topics' => $topics));
    if (mysql_num_rows($request) > 0) {
        $replaceEntries = array();
        while ($row = mysql_fetch_assoc($request)) {
            $replaceEntries[] = array($row['id_member'], $id_topic, $row['new_id_msg']);
        }
        smf_db_insert('replace', '{db_prefix}log_topics', array('id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int'), $replaceEntries, array('id_member', 'id_topic'));
        unset($replaceEntries);
        // Get rid of the old log entries.
        smf_db_query('
			DELETE FROM {db_prefix}log_topics
			WHERE id_topic IN ({array_int:deleted_topics})', array('deleted_topics' => $deleted_topics));
    }
    mysql_free_result($request);
    // Merge topic notifications.
    $notifications = isset($_POST['notifications']) && is_array($_POST['notifications']) ? array_intersect($topics, $_POST['notifications']) : array();
    if (!empty($notifications)) {
        $request = smf_db_query('
			SELECT id_member, MAX(sent) AS sent
			FROM {db_prefix}log_notify
			WHERE id_topic IN ({array_int:topics_list})
			GROUP BY id_member', array('topics_list' => $notifications));
        if (mysql_num_rows($request) > 0) {
            $replaceEntries = array();
            while ($row = mysql_fetch_assoc($request)) {
                $replaceEntries[] = array($row['id_member'], $id_topic, 0, $row['sent']);
            }
            smf_db_insert('replace', '{db_prefix}log_notify', array('id_member' => 'int', 'id_topic' => 'int', 'id_board' => 'int', 'sent' => 'int'), $replaceEntries, array('id_member', 'id_topic', 'id_board'));
            unset($replaceEntries);
            smf_db_query('
				DELETE FROM {db_prefix}log_topics
				WHERE id_topic IN ({array_int:deleted_topics})', array('deleted_topics' => $deleted_topics));
        }
        mysql_free_result($request);
    }
    // Get rid of the redundant polls.
    if (!empty($deleted_polls)) {
        smf_db_query('
			DELETE FROM {db_prefix}polls
			WHERE id_poll IN ({array_int:deleted_polls})', array('deleted_polls' => $deleted_polls));
        smf_db_query('
			DELETE FROM {db_prefix}poll_choices
			WHERE id_poll IN ({array_int:deleted_polls})', array('deleted_polls' => $deleted_polls));
        smf_db_query('
			DELETE FROM {db_prefix}log_polls
			WHERE id_poll IN ({array_int:deleted_polls})', array('deleted_polls' => $deleted_polls));
    }
    // Cycle through each board...
    foreach ($boardTotals as $id_board => $stats) {
        smf_db_query('
			UPDATE {db_prefix}boards
			SET
				num_topics = CASE WHEN {int:topics} > num_topics THEN 0 ELSE num_topics - {int:topics} END,
				unapproved_topics = CASE WHEN {int:unapproved_topics} > unapproved_topics THEN 0 ELSE unapproved_topics - {int:unapproved_topics} END,
				num_posts = CASE WHEN {int:posts} > num_posts THEN 0 ELSE num_posts - {int:posts} END,
				unapproved_posts = CASE WHEN {int:unapproved_posts} > unapproved_posts THEN 0 ELSE unapproved_posts - {int:unapproved_posts} END
			WHERE id_board = {int:id_board}', array('id_board' => $id_board, 'topics' => $stats['topics'], 'unapproved_topics' => $stats['unapproved_topics'], 'posts' => $stats['posts'], 'unapproved_posts' => $stats['unapproved_posts']));
    }
    // Determine the board the final topic resides in
    $request = smf_db_query('
		SELECT id_board
		FROM {db_prefix}topics
		WHERE id_topic = {int:id_topic}
		LIMIT 1', array('id_topic' => $id_topic));
    list($id_board) = mysql_fetch_row($request);
    mysql_free_result($request);
    require_once $sourcedir . '/lib/Subs-Post.php';
    // Update all the statistics.
    updateStats('topic');
    updateStats('subject', $id_topic, $target_subject);
    updateLastMessages($boards);
    logAction('merge', array('topic' => $id_topic, 'board' => $id_board));
    // Notify people that these topics have been merged?
    sendNotifications($id_topic, 'merge');
    // Send them to the all done page.
    redirectexit('action=mergetopics;sa=done;to=' . $id_topic . ';targetboard=' . $target_board);
}
Beispiel #12
0
function ManageLabels()
{
    global $txt, $context, $user_info, $scripturl, $smcFunc;
    EoS_Smarty::loadTemplate('pm/base');
    EoS_Smarty::getConfigInstance()->registerHookTemplate('pm_content_area', 'pm/manage_labels');
    // Build the link tree elements...
    $context['linktree'][] = array('url' => $scripturl . '?action=pm;sa=manlabels', 'name' => $txt['pm_manage_labels']);
    $context['page_title'] = $txt['pm_manage_labels'];
    $context['sub_template'] = 'labels';
    $the_labels = array();
    // Add all existing labels to the array to save, slashing them as necessary...
    foreach ($context['labels'] as $label) {
        if ($label['id'] != -1) {
            $the_labels[$label['id']] = $label['name'];
        }
    }
    if (isset($_POST[$context['session_var']])) {
        checkSession('post');
        // This will be for updating messages.
        $message_changes = array();
        $new_labels = array();
        $rule_changes = array();
        // Will most likely need this.
        LoadRules();
        // Adding a new label?
        if (isset($_POST['add'])) {
            $_POST['label'] = strtr(commonAPI::htmlspecialchars(trim($_POST['label'])), array(',' => '&#044;'));
            if (commonAPI::strlen($_POST['label']) > 30) {
                $_POST['label'] = commonAPI::substr($_POST['label'], 0, 30);
            }
            if ($_POST['label'] != '') {
                $the_labels[] = $_POST['label'];
            }
        } elseif (isset($_POST['delete'], $_POST['delete_label'])) {
            $i = 0;
            foreach ($the_labels as $id => $name) {
                if (isset($_POST['delete_label'][$id])) {
                    unset($the_labels[$id]);
                    $message_changes[$id] = true;
                } else {
                    $new_labels[$id] = $i++;
                }
            }
        } elseif (isset($_POST['save']) && !empty($_POST['label_name'])) {
            $i = 0;
            foreach ($the_labels as $id => $name) {
                if ($id == -1) {
                    continue;
                } elseif (isset($_POST['label_name'][$id])) {
                    $_POST['label_name'][$id] = trim(strtr(commonAPI::htmlspecialchars($_POST['label_name'][$id]), array(',' => '&#044;')));
                    if (commonAPI::strlen($_POST['label_name'][$id]) > 30) {
                        $_POST['label_name'][$id] = commonAPI::substr($_POST['label_name'][$id], 0, 30);
                    }
                    if ($_POST['label_name'][$id] != '') {
                        $the_labels[(int) $id] = $_POST['label_name'][$id];
                        $new_labels[$id] = $i++;
                    } else {
                        unset($the_labels[(int) $id]);
                        $message_changes[(int) $id] = true;
                    }
                } else {
                    $new_labels[$id] = $i++;
                }
            }
        }
        // Save the label status.
        updateMemberData($user_info['id'], array('message_labels' => implode(',', $the_labels)));
        // Update all the messages currently with any label changes in them!
        if (!empty($message_changes)) {
            $searchArray = array_keys($message_changes);
            if (!empty($new_labels)) {
                for ($i = max($searchArray) + 1, $n = max(array_keys($new_labels)); $i <= $n; $i++) {
                    $searchArray[] = $i;
                }
            }
            // Now find the messages to change.
            $request = smf_db_query('
				SELECT id_pm, labels
				FROM {db_prefix}pm_recipients
				WHERE FIND_IN_SET({raw:find_label_implode}, labels) != 0
					AND id_member = {int:current_member}', array('current_member' => $user_info['id'], 'find_label_implode' => '\'' . implode('\', labels) != 0 OR FIND_IN_SET(\'', $searchArray) . '\''));
            while ($row = mysql_fetch_assoc($request)) {
                // Do the long task of updating them...
                $toChange = explode(',', $row['labels']);
                foreach ($toChange as $key => $value) {
                    if (in_array($value, $searchArray)) {
                        if (isset($new_labels[$value])) {
                            $toChange[$key] = $new_labels[$value];
                        } else {
                            unset($toChange[$key]);
                        }
                    }
                }
                if (empty($toChange)) {
                    $toChange[] = '-1';
                }
                // Update the message.
                smf_db_query('
					UPDATE {db_prefix}pm_recipients
					SET labels = {string:new_labels}
					WHERE id_pm = {int:id_pm}
						AND id_member = {int:current_member}', array('current_member' => $user_info['id'], 'id_pm' => $row['id_pm'], 'new_labels' => implode(',', array_unique($toChange))));
            }
            mysql_free_result($request);
            // Now do the same the rules - check through each rule.
            foreach ($context['rules'] as $k => $rule) {
                // Each action...
                foreach ($rule['actions'] as $k2 => $action) {
                    if ($action['t'] != 'lab' || !in_array($action['v'], $searchArray)) {
                        continue;
                    }
                    $rule_changes[] = $rule['id'];
                    // If we're here we have a label which is either changed or gone...
                    if (isset($new_labels[$action['v']])) {
                        $context['rules'][$k]['actions'][$k2]['v'] = $new_labels[$action['v']];
                    } else {
                        unset($context['rules'][$k]['actions'][$k2]);
                    }
                }
            }
        }
        // If we have rules to change do so now.
        if (!empty($rule_changes)) {
            $rule_changes = array_unique($rule_changes);
            // Update/delete as appropriate.
            foreach ($rule_changes as $k => $id) {
                if (!empty($context['rules'][$id]['actions'])) {
                    smf_db_query('
						UPDATE {db_prefix}pm_rules
						SET actions = {string:actions}
						WHERE id_rule = {int:id_rule}
							AND id_member = {int:current_member}', array('current_member' => $user_info['id'], 'id_rule' => $id, 'actions' => serialize($context['rules'][$id]['actions'])));
                    unset($rule_changes[$k]);
                }
            }
            // Anything left here means it's lost all actions...
            if (!empty($rule_changes)) {
                smf_db_query('
					DELETE FROM {db_prefix}pm_rules
					WHERE id_rule IN ({array_int:rule_list})
							AND id_member = {int:current_member}', array('current_member' => $user_info['id'], 'rule_list' => $rule_changes));
            }
        }
        // Make sure we're not caching this!
        CacheAPI::putCache('labelCounts:' . $user_info['id'], null, 720);
        // To make the changes appear right away, redirect.
        redirectexit('action=pm;sa=manlabels');
    }
}
Beispiel #13
0
function validateEventPost()
{
    global $modSettings, $txt, $sourcedir, $smcFunc;
    if (!isset($_POST['deleteevent'])) {
        // No month?  No year?
        if (!isset($_POST['month'])) {
            fatal_lang_error('event_month_missing', false);
        }
        if (!isset($_POST['year'])) {
            fatal_lang_error('event_year_missing', false);
        }
        // Check the month and year...
        if ($_POST['month'] < 1 || $_POST['month'] > 12) {
            fatal_lang_error('invalid_month', false);
        }
        if ($_POST['year'] < $modSettings['cal_minyear'] || $_POST['year'] > $modSettings['cal_maxyear']) {
            fatal_lang_error('invalid_year', false);
        }
    }
    // Make sure they're allowed to post...
    isAllowedTo('calendar_post');
    if (isset($_POST['span'])) {
        // Make sure it's turned on and not some fool trying to trick it.
        if (empty($modSettings['cal_allowspan'])) {
            fatal_lang_error('no_span', false);
        }
        if ($_POST['span'] < 1 || $_POST['span'] > $modSettings['cal_maxspan']) {
            fatal_lang_error('invalid_days_numb', false);
        }
    }
    // There is no need to validate the following values if we are just deleting the event.
    if (!isset($_POST['deleteevent'])) {
        // No day?
        if (!isset($_POST['day'])) {
            fatal_lang_error('event_day_missing', false);
        }
        if (!isset($_POST['evtitle']) && !isset($_POST['subject'])) {
            fatal_lang_error('event_title_missing', false);
        } elseif (!isset($_POST['evtitle'])) {
            $_POST['evtitle'] = $_POST['subject'];
        }
        // Bad day?
        if (!checkdate($_POST['month'], $_POST['day'], $_POST['year'])) {
            fatal_lang_error('invalid_date', false);
        }
        // No title?
        if (commonAPI::htmltrim($_POST['evtitle']) === '') {
            fatal_lang_error('no_event_title', false);
        }
        if (commonAPI::strlen($_POST['evtitle']) > 30) {
            $_POST['evtitle'] = commonAPI::substr($_POST['evtitle'], 0, 30);
        }
        $_POST['evtitle'] = str_replace(';', '', $_POST['evtitle']);
    }
}
Beispiel #14
0
function getXmlRecent($xml_format)
{
    global $user_info, $scripturl, $modSettings, $board;
    global $query_this_board, $smcFunc, $settings, $context;
    $done = false;
    $loops = 0;
    while (!$done) {
        $optimize_msg = implode(' AND ', $context['optimize_msg']);
        $request = smf_db_query('
			SELECT m.id_msg
			FROM {db_prefix}messages AS m
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
			WHERE ' . $query_this_board . (empty($optimize_msg) ? '' : '
				AND {raw:optimize_msg}') . (empty($board) ? '' : '
				AND m.id_board = {int:current_board}') . ($modSettings['postmod_active'] ? '
				AND m.approved = {int:is_approved}' : '') . '
			ORDER BY m.id_msg DESC
			LIMIT {int:limit}', array('limit' => $_GET['limit'], 'current_board' => $board, 'is_approved' => 1, 'optimize_msg' => $optimize_msg));
        // If we don't have $_GET['limit'] results, try again with an unoptimized version covering all rows.
        if ($loops < 2 && mysql_num_rows($request) < $_GET['limit']) {
            mysql_free_result($request);
            if (empty($_REQUEST['boards']) && empty($board)) {
                unset($context['optimize_msg']['lowest']);
            } else {
                $context['optimize_msg']['lowest'] = $loops ? 'm.id_msg >= t.id_first_msg' : 'm.id_msg >= (t.id_last_msg - t.id_first_msg) / 2';
            }
            $loops++;
        } else {
            $done = true;
        }
    }
    $messages = array();
    while ($row = mysql_fetch_assoc($request)) {
        $messages[] = $row['id_msg'];
    }
    mysql_free_result($request);
    if (empty($messages)) {
        return array();
    }
    // Find the most recent posts this user can see.
    $request = smf_db_query('
		SELECT
			m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, m.id_topic, t.id_board,
			b.name AS bname, t.num_replies, m.id_member, m.icon, mf.id_member AS id_first_member,
			IFNULL(mem.real_name, m.poster_name) AS poster_name, mf.subject AS first_subject,
			IFNULL(memf.real_name, mf.poster_name) AS first_poster_name, mem.hide_email,
			IFNULL(mem.email_address, m.poster_email) AS poster_email, m.modified_time
		FROM {db_prefix}messages AS m
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
			INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
			LEFT JOIN {db_prefix}members AS memf ON (memf.id_member = mf.id_member)
		WHERE m.id_msg IN ({array_int:message_list})
			' . (empty($board) ? '' : 'AND t.id_board = {int:current_board}') . '
		ORDER BY m.id_msg DESC
		LIMIT {int:limit}', array('limit' => $_GET['limit'], 'current_board' => $board, 'message_list' => $messages));
    $data = array();
    while ($row = mysql_fetch_assoc($request)) {
        // Limit the length of the message, if the option is set.
        if (!empty($modSettings['xmlnews_maxlen']) && commonAPI::strlen(str_replace('<br />', "\n", $row['body'])) > $modSettings['xmlnews_maxlen']) {
            $row['body'] = strtr(commonAPI::substr(str_replace('<br />', "\n", $row['body']), 0, $modSettings['xmlnews_maxlen'] - 3), array("\n" => '<br />')) . '...';
        }
        $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
        censorText($row['body']);
        censorText($row['subject']);
        // Doesn't work as well as news, but it kinda does..
        if ($xml_format == 'rss' || $xml_format == 'rss2') {
            $data[] = array('title' => $row['subject'], 'link' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], 'description' => cdata_parse($row['body']), 'author' => in_array(showEmailAddress(!empty($row['hide_email']), $row['id_member']), array('yes', 'yes_permission_override')) ? $row['poster_email'] : null, 'category' => cdata_parse($row['bname']), 'comments' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0', 'pubDate' => gmdate('D, d M Y H:i:s \\G\\M\\T', $row['poster_time']), 'guid' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg']);
        } elseif ($xml_format == 'rdf') {
            $data[] = array('title' => $row['subject'], 'link' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], 'description' => cdata_parse($row['body']));
        } elseif ($xml_format == 'atom') {
            $data[] = array('title' => $row['subject'], 'link' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], 'summary' => cdata_parse($row['body']), 'category' => array('term' => $row['id_board'], 'label' => cdata_parse($row['bname'])), 'author' => array('name' => $row['poster_name'], 'email' => in_array(showEmailAddress(!empty($row['hide_email']), $row['id_member']), array('yes', 'yes_permission_override')) ? $row['poster_email'] : null, 'uri' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : ''), 'published' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row['poster_time']), 'updated' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', empty($row['modified_time']) ? $row['poster_time'] : $row['modified_time']), 'id' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], 'icon' => $settings['images_url'] . '/icons/' . $row['icon'] . '.gif');
        } else {
            $data[] = array('time' => htmlspecialchars(strip_tags(timeformat($row['poster_time']))), 'id' => $row['id_msg'], 'subject' => cdata_parse($row['subject']), 'body' => cdata_parse($row['body']), 'starter' => array('name' => cdata_parse($row['first_poster_name']), 'id' => $row['id_first_member'], 'link' => !empty($row['id_first_member']) ? $scripturl . '?action=profile;u=' . $row['id_first_member'] : ''), 'poster' => array('name' => cdata_parse($row['poster_name']), 'id' => $row['id_member'], 'link' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : ''), 'topic' => array('subject' => cdata_parse($row['first_subject']), 'id' => $row['id_topic'], 'link' => $scripturl . '?topic=' . $row['id_topic'] . '.new#new'), 'board' => array('name' => cdata_parse($row['bname']), 'id' => $row['id_board'], 'link' => $scripturl . '?board=' . $row['id_board'] . '.0'), 'link' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg']);
        }
    }
    mysql_free_result($request);
    return $data;
}
Beispiel #15
0
function prepareSearchContext($reset = false)
{
    global $txt, $modSettings, $scripturl, $user_info, $sourcedir;
    global $memberContext, $context, $options, $messages_request;
    global $boards_can, $participants, $output;
    // Remember which message this is.  (ie. reply #83)
    static $counter = null;
    if ($counter == null || $reset) {
        $counter = $_REQUEST['start'] + 1;
    }
    // If the query returned false, bail.
    if ($messages_request == false) {
        return false;
    }
    // Start from the beginning...
    if ($reset) {
        return @mysql_data_seek($messages_request, 0);
    }
    // Attempt to get the next message.
    $message = mysql_fetch_assoc($messages_request);
    if (!$message) {
        return false;
    }
    // Can't have an empty subject can we?
    $message['subject'] = $message['subject'] != '' ? $message['subject'] : $txt['no_subject'];
    $message['first_subject'] = $message['first_subject'] != '' ? $message['first_subject'] : $txt['no_subject'];
    $message['last_subject'] = $message['last_subject'] != '' ? $message['last_subject'] : $txt['no_subject'];
    // If it couldn't load, or the user was a guest.... someday may be done with a guest table.
    if (!loadMemberContext($message['id_member'])) {
        // Notice this information isn't used anywhere else.... *cough guest table cough*.
        $memberContext[$message['id_member']]['name'] = $message['poster_name'];
        $memberContext[$message['id_member']]['id'] = 0;
        $memberContext[$message['id_member']]['group'] = $txt['guest_title'];
        $memberContext[$message['id_member']]['link'] = $message['poster_name'];
        $memberContext[$message['id_member']]['email'] = $message['poster_email'];
    }
    $memberContext[$message['id_member']]['ip'] = $message['poster_ip'];
    // Do the censor thang...
    censorText($message['body']);
    censorText($message['subject']);
    censorText($message['first_subject']);
    censorText($message['last_subject']);
    // Shorten this message if necessary.
    if ($context['compact']) {
        // Set the number of characters before and after the searched keyword.
        $charLimit = 50;
        $message['body'] = strtr($message['body'], array("\n" => ' ', '<br />' => "\n"));
        $message['body'] = parse_bbc($message['body'], $message['smileys_enabled'], $message['id_msg']);
        $message['body'] = strip_tags(strtr($message['body'], array('</div>' => '<br />', '</li>' => '<br />')), '<br>');
        if (commonAPI::strlen($message['body']) > $charLimit) {
            if (empty($context['key_words'])) {
                $message['body'] = commonAPI::substr($message['body'], 0, $charLimit) . '<strong>...</strong>';
            } else {
                $matchString = '';
                $force_partial_word = false;
                foreach ($context['key_words'] as $keyword) {
                    $keyword = preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~e', 'commonAPI::entity_fix(\'\\1\')', strtr($keyword, array('\\\'' => '\'', '&' => '&amp;')));
                    if (preg_match('~[\'\\.,/@%&;:(){}\\[\\]_\\-+\\\\]$~', $keyword) != 0 || preg_match('~^[\'\\.,/@%&;:(){}\\[\\]_\\-+\\\\]~', $keyword) != 0) {
                        $force_partial_word = true;
                    }
                    $matchString .= strtr(preg_quote($keyword, '/'), array('\\*' => '.+?')) . '|';
                }
                $matchString = substr($matchString, 0, -1);
                $message['body'] = un_htmlspecialchars(strtr($message['body'], array('&nbsp;' => ' ', '<br />' => "\n", '&#91;' => '[', '&#93;' => ']', '&#58;' => ':', '&#64;' => '@')));
                if (empty($modSettings['search_method']) || $force_partial_word) {
                    preg_match_all('/([^\\s\\W]{' . $charLimit . '}[\\s\\W]|[\\s\\W].{0,' . $charLimit . '}?|^)(' . $matchString . ')(.{0,' . $charLimit . '}[\\s\\W]|[^\\s\\W]{' . $charLimit . '})/isu', $message['body'], $matches);
                } else {
                    preg_match_all('/([^\\s\\W]{' . $charLimit . '}[\\s\\W]|[\\s\\W].{0,' . $charLimit . '}?[\\s\\W]|^)(' . $matchString . ')([\\s\\W].{0,' . $charLimit . '}[\\s\\W]|[\\s\\W][^\\s\\W]{' . $charLimit . '})/isu', $message['body'], $matches);
                }
                $message['body'] = '';
                foreach ($matches[0] as $index => $match) {
                    $match = strtr(htmlspecialchars($match, ENT_QUOTES), array("\n" => '&nbsp;'));
                    $message['body'] .= '<strong>......</strong>&nbsp;' . $match . '&nbsp;<strong>......</strong>';
                }
            }
            // Re-fix the international characters.
            $message['body'] = preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~e', 'commonAPI::entity_fix(\'\\1\')', $message['body']);
        }
    } else {
        // Run BBC interpreter on the message.
        $message['body'] = parse_bbc($message['body'], $message['smileys_enabled'], $message['id_msg']);
    }
    // Make sure we don't end up with a practically empty message body.
    $message['body'] = preg_replace('~^(?:&nbsp;)+$~', '', $message['body']);
    // Do we have quote tag enabled?
    $quote_enabled = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']));
    $href = URL::topic($message['id_topic'], $message['first_subject'], 0);
    $mhref = URL::user($message['first_member_id'], $message['first_member_name']);
    $lhref = URL::topic($message['id_topic'], $message['last_subject'], 0, $message['num_replies'] == 0 ? true : false, $message['num_replies'] == 0 ? '' : '.msg' . $message['last_msg'], $message['num_replies'] == 0 ? '' : '#msg' . $message['last_msg']);
    $lmhref = URL::user($message['last_member_id'], $message['last_member_name']);
    $bhref = URL::board($message['id_board'], $message['board_name'], 0, true);
    $output = array_merge($context['topics'][$message['id_msg']], array('id' => $message['id_topic'], 'is_sticky' => !empty($modSettings['enableStickyTopics']) && !empty($message['is_sticky']), 'is_locked' => !empty($message['locked']), 'is_poll' => $modSettings['pollMode'] == '1' && $message['id_poll'] > 0, 'is_hot' => $message['num_replies'] >= $modSettings['hotTopicPosts'], 'is_very_hot' => $message['num_replies'] >= $modSettings['hotTopicVeryPosts'], 'posted_in' => !empty($participants[$message['id_topic']]), 'views' => $message['num_views'], 'replies' => $message['num_replies'], 'can_reply' => in_array($message['id_board'], $boards_can['post_reply_any']) || in_array(0, $boards_can['post_reply_any']), 'can_quote' => (in_array($message['id_board'], $boards_can['post_reply_any']) || in_array(0, $boards_can['post_reply_any'])) && $quote_enabled, 'can_mark_notify' => in_array($message['id_board'], $boards_can['mark_any_notify']) || in_array(0, $boards_can['mark_any_notify']) && !$context['user']['is_guest'], 'first_post' => array('id' => $message['first_msg'], 'time' => timeformat($message['first_poster_time']), 'timestamp' => forum_time(true, $message['first_poster_time']), 'subject' => $message['first_subject'], 'href' => $href, 'link' => '<a href="' . $href . '">' . $message['first_subject'] . '</a>', 'icon' => $message['first_icon'], 'icon_url' => getPostIcon($message['first_icon']), 'member' => array('id' => $message['first_member_id'], 'name' => $message['first_member_name'], 'href' => !empty($message['first_member_id']) ? $mhref : '', 'link' => !empty($message['first_member_id']) ? '<a href="' . $mhref . '" title="' . $txt['profile_of'] . ' ' . $message['first_member_name'] . '">' . $message['first_member_name'] . '</a>' : $message['first_member_name'])), 'last_post' => array('id' => $message['last_msg'], 'time' => timeformat($message['last_poster_time']), 'timestamp' => forum_time(true, $message['last_poster_time']), 'subject' => $message['last_subject'], 'href' => $lhref, 'link' => '<a href="' . $lhref . '">' . $message['last_subject'] . '</a>', 'icon' => $message['last_icon'], 'icon_url' => getPostIcon($message['last_icon']), 'member' => array('id' => $message['last_member_id'], 'name' => $message['last_member_name'], 'href' => !empty($message['last_member_id']) ? $lmhref : '', 'link' => !empty($message['last_member_id']) ? '<a href="' . $lmhref . '" title="' . $txt['profile_of'] . ' ' . $message['last_member_name'] . '">' . $message['last_member_name'] . '</a>' : $message['last_member_name'])), 'board' => array('id' => $message['id_board'], 'name' => $message['board_name'], 'href' => $bhref, 'link' => '<a href="' . $bhref . '">' . $message['board_name'] . '</a>'), 'category' => array('id' => $message['id_cat'], 'name' => $message['cat_name'], 'href' => $scripturl . '#c' . $message['id_cat'], 'link' => '<a href="' . $scripturl . '#c' . $message['id_cat'] . '">' . $message['cat_name'] . '</a>')));
    determineTopicClass($output);
    if ($output['posted_in']) {
        $output['class'] = 'my_' . $output['class'];
    }
    $body_highlighted = $message['body'];
    $subject_highlighted = $message['subject'];
    if (!empty($options['display_quick_mod'])) {
        $started = $output['first_post']['member']['id'] == $user_info['id'];
        $output['quick_mod'] = array('lock' => in_array(0, $boards_can['lock_any']) || in_array($output['board']['id'], $boards_can['lock_any']) || $started && (in_array(0, $boards_can['lock_own']) || in_array($output['board']['id'], $boards_can['lock_own'])), 'sticky' => (in_array(0, $boards_can['make_sticky']) || in_array($output['board']['id'], $boards_can['make_sticky'])) && !empty($modSettings['enableStickyTopics']), 'move' => in_array(0, $boards_can['move_any']) || in_array($output['board']['id'], $boards_can['move_any']) || $started && (in_array(0, $boards_can['move_own']) || in_array($output['board']['id'], $boards_can['move_own'])), 'remove' => in_array(0, $boards_can['remove_any']) || in_array($output['board']['id'], $boards_can['remove_any']) || $started && (in_array(0, $boards_can['remove_own']) || in_array($output['board']['id'], $boards_can['remove_own'])));
        $context['can_lock'] |= $output['quick_mod']['lock'];
        $context['can_sticky'] |= $output['quick_mod']['sticky'];
        $context['can_move'] |= $output['quick_mod']['move'];
        $context['can_remove'] |= $output['quick_mod']['remove'];
        $context['can_merge'] |= in_array($output['board']['id'], $boards_can['merge_any']);
        // If we've found a message we can move, and we don't already have it, load the destinations.
        if ($options['display_quick_mod'] && !isset($context['move_to_boards']) && $context['can_move']) {
            require_once $sourcedir . '/lib/Subs-MessageIndex.php';
            $boardListOptions = array('use_permissions' => true, 'not_redirection' => true, 'selected_board' => empty($_SESSION['move_to_topic']) ? null : $_SESSION['move_to_topic']);
            $context['move_to_boards'] = getBoardList($boardListOptions);
        }
    }
    foreach ($context['key_words'] as $query) {
        // Fix the international characters in the keyword too.
        $query = strtr(commonAPI::htmlspecialchars($query), array('\\\'' => '\''));
        $body_highlighted = preg_replace('/((<[^>]*)|' . preg_quote(strtr($query, array('\'' => '&#039;')), '/') . ')/ieu', "'\$2' == '\$1' ? stripslashes('\$1') : '<strong class=\"highlight\">\$1</strong>'", $body_highlighted);
        $subject_highlighted = preg_replace('/(' . preg_quote($query, '/') . ')/iu', '<strong class="highlight">$1</strong>', $subject_highlighted);
    }
    $mhref = URL::topic($message['id_topic'], $message['subject'], 0, false, '.msg' . $message['id_msg'], '#msg' . $message['id_msg']);
    $output['matches'][] = array('id' => $message['id_msg'], 'attachment' => loadAttachmentContext($message['id_msg']), 'alternate' => $counter % 2, 'member' => &$memberContext[$message['id_member']], 'icon' => $message['icon'], 'icon_url' => getPostIcon($message['icon']), 'subject' => $message['subject'], 'subject_highlighted' => $subject_highlighted, 'time' => timeformat($message['poster_time']), 'timestamp' => forum_time(true, $message['poster_time']), 'counter' => $counter, 'modified' => array('time' => timeformat($message['modified_time']), 'timestamp' => forum_time(true, $message['modified_time']), 'name' => $message['modified_name']), 'body' => $body_highlighted, 'body_highlighted' => $body_highlighted, 'start' => 'msg' . $message['id_msg'], 'href' => $mhref, 'link' => '<a href="' . $mhref . '">' . $message['subject'] . '</a>');
    $counter++;
    return $output;
}
Beispiel #16
0
function RegisterCheckUsername()
{
    global $sourcedir, $context, $txt;
    // This is XML!
    loadTemplate('Xml');
    $context['sub_template'] = 'check_username';
    $context['checked_username'] = isset($_GET['username']) ? $_GET['username'] : '';
    $context['valid_username'] = true;
    // Clean it up like mother would.
    $context['checked_username'] = preg_replace('~[\\t\\n\\r\\x0B\\0' . ($context['server']['complex_preg_chars'] ? '\\x{A0}' : " ") . ']+~u', ' ', $context['checked_username']);
    if (commonAPI::strlen($context['checked_username']) > 25) {
        $context['checked_username'] = commonAPI::htmltrim(commonAPI::substr($context['checked_username'], 0, 25));
    }
    // Only these characters are permitted.
    if (preg_match('~[<>&"\'=\\\\]~', preg_replace('~&#(?:\\d{1,7}|x[0-9a-fA-F]{1,6});~', '', $context['checked_username'])) != 0 || $context['checked_username'] == '_' || $context['checked_username'] == '|' || strpos($context['checked_username'], '[code') !== false || strpos($context['checked_username'], '[/code') !== false) {
        $context['valid_username'] = false;
    }
    if (stristr($context['checked_username'], $txt['guest_title']) !== false) {
        $context['valid_username'] = false;
    }
    if (trim($context['checked_username']) == '') {
        $context['valid_username'] = false;
    } else {
        require_once $sourcedir . '/lib/Subs-Members.php';
        $context['valid_username'] &= isReservedName($context['checked_username'], 0, false, false) ? 0 : 1;
    }
}
Beispiel #17
0
function profileValidateSignature(&$value)
{
    global $sourcedir, $modSettings, $smcFunc, $txt;
    require_once $sourcedir . '/lib/Subs-Post.php';
    // Admins can do whatever they hell they want!
    if (!allowedTo('admin_forum')) {
        if (!allowedTo('profile_signature')) {
            $_POST['signature'] = $value = '';
            return false;
        }
        // Load all the signature limits.
        list($sig_limits, $sig_bbc) = explode(':', $modSettings['signature_settings']);
        $sig_limits = explode(',', $sig_limits);
        $disabledTags = !empty($sig_bbc) ? explode(',', $sig_bbc) : array();
        $unparsed_signature = strtr(un_htmlspecialchars($value), array("\r" => '', '&#039' => '\''));
        // Too long?
        if (!empty($sig_limits[1]) && commonAPI::strlen($unparsed_signature) > $sig_limits[1]) {
            $_POST['signature'] = trim(htmlspecialchars(commonAPI::substr($unparsed_signature, 0, $sig_limits[1]), ENT_QUOTES));
            $txt['profile_error_signature_max_length'] = sprintf($txt['profile_error_signature_max_length'], $sig_limits[1]);
            return 'signature_max_length';
        }
        // Too many lines?
        if (!empty($sig_limits[2]) && substr_count($unparsed_signature, "\n") >= $sig_limits[2]) {
            $txt['profile_error_signature_max_lines'] = sprintf($txt['profile_error_signature_max_lines'], $sig_limits[2]);
            return 'signature_max_lines';
        }
        // Too many images?!
        if (!empty($sig_limits[3]) && substr_count(strtolower($unparsed_signature), '[img') + substr_count(strtolower($unparsed_signature), '<img') > $sig_limits[3]) {
            $txt['profile_error_signature_max_image_count'] = sprintf($txt['profile_error_signature_max_image_count'], $sig_limits[3]);
            return 'signature_max_image_count';
        }
        // What about too many smileys!
        $smiley_parsed = $unparsed_signature;
        parsesmileys($smiley_parsed);
        $smiley_count = substr_count(strtolower($smiley_parsed), '<img') - substr_count(strtolower($unparsed_signature), '<img');
        if (!empty($sig_limits[4]) && $sig_limits[4] == -1 && $smiley_count > 0) {
            return 'signature_allow_smileys';
        } elseif (!empty($sig_limits[4]) && $sig_limits[4] > 0 && $smiley_count > $sig_limits[4]) {
            $txt['profile_error_signature_max_smileys'] = sprintf($txt['profile_error_signature_max_smileys'], $sig_limits[4]);
            return 'signature_max_smileys';
        }
        // Maybe we are abusing font sizes?
        if (!empty($sig_limits[7]) && preg_match_all('~\\[size=([\\d\\.]+)?(px|pt|em|x-large|larger)~i', $unparsed_signature, $matches) !== false && isset($matches[2])) {
            foreach ($matches[1] as $ind => $size) {
                $limit_broke = 0;
                // Attempt to allow all sizes of abuse, so to speak.
                if ($matches[2][$ind] == 'px' && $size > $sig_limits[7]) {
                    $limit_broke = $sig_limits[7] . 'px';
                } elseif ($matches[2][$ind] == 'pt' && $size > $sig_limits[7] * 0.75) {
                    $limit_broke = (int) $sig_limits[7] * 0.75 . 'pt';
                } elseif ($matches[2][$ind] == 'em' && $size > (double) $sig_limits[7] / 16) {
                    $limit_broke = (double) $sig_limits[7] / 16 . 'em';
                } elseif ($matches[2][$ind] != 'px' && $matches[2][$ind] != 'pt' && $matches[2][$ind] != 'em' && $sig_limits[7] < 18) {
                    $limit_broke = 'large';
                }
                if ($limit_broke) {
                    $txt['profile_error_signature_max_font_size'] = sprintf($txt['profile_error_signature_max_font_size'], $limit_broke);
                    return 'signature_max_font_size';
                }
            }
        }
        // The difficult one - image sizes! Don't error on this - just fix it.
        if (!empty($sig_limits[5]) || !empty($sig_limits[6])) {
            // Get all BBC tags...
            preg_match_all('~\\[img(\\s+width=([\\d]+))?(\\s+height=([\\d]+))?(\\s+width=([\\d]+))?\\s*\\](?:<br />)*([^<">]+?)(?:<br />)*\\[/img\\]~i', $unparsed_signature, $matches);
            // ... and all HTML ones.
            preg_match_all('~<img\\s+src=(?:")?((?:http://|ftp://|https://|ftps://).+?)(?:")?(?:\\s+alt=(?:")?(.*?)(?:")?)?(?:\\s?/)?>~i', $unparsed_signature, $matches2, PREG_PATTERN_ORDER);
            // And stick the HTML in the BBC.
            if (!empty($matches2)) {
                foreach ($matches2[0] as $ind => $dummy) {
                    $matches[0][] = $matches2[0][$ind];
                    $matches[1][] = '';
                    $matches[2][] = '';
                    $matches[3][] = '';
                    $matches[4][] = '';
                    $matches[5][] = '';
                    $matches[6][] = '';
                    $matches[7][] = $matches2[1][$ind];
                }
            }
            $replaces = array();
            // Try to find all the images!
            if (!empty($matches)) {
                foreach ($matches[0] as $key => $image) {
                    $width = -1;
                    $height = -1;
                    // Does it have predefined restraints? Width first.
                    if ($matches[6][$key]) {
                        $matches[2][$key] = $matches[6][$key];
                    }
                    if ($matches[2][$key] && $sig_limits[5] && $matches[2][$key] > $sig_limits[5]) {
                        $width = $sig_limits[5];
                        $matches[4][$key] = $matches[4][$key] * ($width / $matches[2][$key]);
                    } elseif ($matches[2][$key]) {
                        $width = $matches[2][$key];
                    }
                    // ... and height.
                    if ($matches[4][$key] && $sig_limits[6] && $matches[4][$key] > $sig_limits[6]) {
                        $height = $sig_limits[6];
                        if ($width != -1) {
                            $width = $width * ($height / $matches[4][$key]);
                        }
                    } elseif ($matches[4][$key]) {
                        $height = $matches[4][$key];
                    }
                    // If the dimensions are still not fixed - we need to check the actual image.
                    if ($width == -1 && $sig_limits[5] || $height == -1 && $sig_limits[6]) {
                        $sizes = url_image_size($matches[7][$key]);
                        if (is_array($sizes)) {
                            // Too wide?
                            if ($sizes[0] > $sig_limits[5] && $sig_limits[5]) {
                                $width = $sig_limits[5];
                                $sizes[1] = $sizes[1] * ($width / $sizes[0]);
                            }
                            // Too high?
                            if ($sizes[1] > $sig_limits[6] && $sig_limits[6]) {
                                $height = $sig_limits[6];
                                if ($width == -1) {
                                    $width = $sizes[0];
                                }
                                $width = $width * ($height / $sizes[1]);
                            } elseif ($width != -1) {
                                $height = $sizes[1];
                            }
                        }
                    }
                    // Did we come up with some changes? If so remake the string.
                    if ($width != -1 || $height != -1) {
                        $replaces[$image] = '[img' . ($width != -1 ? ' width=' . round($width) : '') . ($height != -1 ? ' height=' . round($height) : '') . ']' . $matches[7][$key] . '[/img]';
                    }
                }
                if (!empty($replaces)) {
                    $value = str_replace(array_keys($replaces), array_values($replaces), $value);
                }
            }
        }
        // Any disabled BBC?
        $disabledSigBBC = implode('|', $disabledTags);
        if (!empty($disabledSigBBC)) {
            if (preg_match('~\\[(' . $disabledSigBBC . ')~i', $unparsed_signature, $matches) !== false && isset($matches[1])) {
                $disabledTags = array_unique($disabledTags);
                $txt['profile_error_signature_disabled_bbc'] = sprintf($txt['profile_error_signature_disabled_bbc'], implode(', ', $disabledTags));
                return 'signature_disabled_bbc';
            }
        }
    }
    preparsecode($value);
    return true;
}
Beispiel #18
0
function AutoSuggest_Search_Member()
{
    global $user_info, $txt, $smcFunc, $context;
    $_REQUEST['search'] = trim(commonAPI::strtolower($_REQUEST['search'])) . '*';
    $_REQUEST['search'] = strtr($_REQUEST['search'], array('%' => '\\%', '_' => '\\_', '*' => '%', '?' => '_', '&#038;' => '&amp;'));
    // Find the member.
    $request = smf_db_query('
		SELECT id_member, real_name
		FROM {db_prefix}members
		WHERE real_name LIKE {string:search}' . (!empty($context['search_param']['buddies']) ? '
			AND id_member IN ({array_int:buddy_list})' : '') . '
			AND is_activated IN (1, 11)
		LIMIT ' . (commonAPI::strlen($_REQUEST['search']) <= 2 ? '100' : '800'), array('buddy_list' => $user_info['buddies'], 'search' => $_REQUEST['search']));
    $xml_data = array('items' => array('identifier' => 'item', 'children' => array()));
    while ($row = mysql_fetch_assoc($request)) {
        $row['real_name'] = strtr($row['real_name'], array('&amp;' => '&#038;', '&lt;' => '&#060;', '&gt;' => '&#062;', '&quot;' => '&#034;'));
        $xml_data['items']['children'][] = array('attributes' => array('id' => $row['id_member']), 'value' => $row['real_name']);
    }
    mysql_free_result($request);
    return $xml_data;
}
Beispiel #19
0
function parse_bbc($message, $smileys = true, $cache_id = '', $parse_tags = array())
{
    global $txt, $scripturl, $context, $modSettings, $user_info, $board;
    static $bbc_codes = array(), $itemcodes = array(), $no_autolink_tags = array();
    static $disabled;
    if ($cache_id != '' && stripos($cache_id, '|') > 0) {
        list($cache_id, $cache_unique) = explode('|', $cache_id);
    } else {
        $cache_unique =& $message;
    }
    //echo 'id=',$cache_id,' unique=',$cache_unique;
    // Don't waste cycles
    if ($message === '') {
        return '';
    }
    if ($smileys !== null && ($smileys == '1' || $smileys == '0')) {
        $smileys = (bool) $smileys;
    }
    if (empty($modSettings['enableBBC']) && $message !== false) {
        if ($smileys === true) {
            parsesmileys($message);
        }
        return $message;
    }
    // Just in case it wasn't determined yet whether UTF-8 is enabled.
    $context['utf8'] = true;
    // If we are not doing every tag then we don't cache this run.
    if (!empty($parse_tags) && !empty($bbc_codes)) {
        $temp_bbc = $bbc_codes;
        $bbc_codes = array();
    }
    // Sift out the bbc for a performance improvement.
    if (empty($bbc_codes) || $message === false || !empty($parse_tags)) {
        if (!empty($modSettings['disabledBBC'])) {
            $temp = explode(',', strtolower($modSettings['disabledBBC']));
            foreach ($temp as $tag) {
                $disabled[trim($tag)] = true;
            }
        }
        $disabled['flash'] = true;
        /* The following bbc are formatted as an array, with keys as follows:
        
        			tag: the tag's name - should be lowercase!
        
        			type: one of...
        				- (missing): [tag]parsed content[/tag]
        				- unparsed_equals: [tag=xyz]parsed content[/tag]
        				- parsed_equals: [tag=parsed data]parsed content[/tag]
        				- unparsed_content: [tag]unparsed content[/tag]
        				- closed: [tag], [tag/], [tag /]
        				- unparsed_commas: [tag=1,2,3]parsed content[/tag]
        				- unparsed_commas_content: [tag=1,2,3]unparsed content[/tag]
        				- unparsed_equals_content: [tag=...]unparsed content[/tag]
        
        			parameters: an optional array of parameters, for the form
        			  [tag abc=123]content[/tag].  The array is an associative array
        			  where the keys are the parameter names, and the values are an
        			  array which may contain the following:
        				- match: a regular expression to validate and match the value.
        				- quoted: true if the value should be quoted.
        				- validate: callback to evaluate on the data, which is $data.
        				- value: a string in which to replace $1 with the data.
        				  either it or validate may be used, not both.
        				- optional: true if the parameter is optional.
        
        			test: a regular expression to test immediately after the tag's
        			  '=', ' ' or ']'.  Typically, should have a \] at the end.
        			  Optional.
        
        			content: only available for unparsed_content, closed,
        			  unparsed_commas_content, and unparsed_equals_content.
        			  $1 is replaced with the content of the tag.  Parameters
        			  are replaced in the form {param}.  For unparsed_commas_content,
        			  $2, $3, ..., $n are replaced.
        
        			before: only when content is not used, to go before any
        			  content.  For unparsed_equals, $1 is replaced with the value.
        			  For unparsed_commas, $1, $2, ..., $n are replaced.
        
        			after: similar to before in every way, except that it is used
        			  when the tag is closed.
        
        			disabled_content: used in place of content when the tag is
        			  disabled.  For closed, default is '', otherwise it is '$1' if
        			  block_level is false, '<div>$1</div>' elsewise.
        
        			disabled_before: used in place of before when disabled.  Defaults
        			  to '<div>' if block_level, '' if not.
        
        			disabled_after: used in place of after when disabled.  Defaults
        			  to '</div>' if block_level, '' if not.
        
        			block_level: set to true the tag is a "block level" tag, similar
        			  to HTML.  Block level tags cannot be nested inside tags that are
        			  not block level, and will not be implicitly closed as easily.
        			  One break following a block level tag may also be removed.
        
        			trim: if set, and 'inside' whitespace after the begin tag will be
        			  removed.  If set to 'outside', whitespace after the end tag will
        			  meet the same fate.
        
        			validate: except when type is missing or 'closed', a callback to
        			  validate the data as $data.  Depending on the tag's type, $data
        			  may be a string or an array of strings (corresponding to the
        			  replacement.)
        
        			quoted: when type is 'unparsed_equals' or 'parsed_equals' only,
        			  may be not set, 'optional', or 'required' corresponding to if
        			  the content may be quoted.  This allows the parser to read
        			  [tag="abc]def[esdf]"] properly.
        
        			require_parents: an array of tag names, or not set.  If set, the
        			  enclosing tag *must* be one of the listed tags, or parsing won't
        			  occur.
        
        			require_children: similar to require_parents, if set children
        			  won't be parsed if they are not in the list.
        
        			disallow_children: similar to, but very different from,
        			  require_children, if it is set the listed tags will not be
        			  parsed inside the tag.
        
        			parsed_tags_allowed: an array restricting what BBC can be in the
        			  parsed_equals parameter, if desired.
        		*/
        $codes = array(array('tag' => 'abbr', 'type' => 'unparsed_equals', 'before' => '<abbr title="$1">', 'after' => '</abbr>', 'quoted' => 'optional', 'disabled_after' => ' ($1)'), array('tag' => 'acronym', 'type' => 'unparsed_equals', 'before' => '<acronym title="$1">', 'after' => '</acronym>', 'quoted' => 'optional', 'disabled_after' => ' ($1)'), array('tag' => 'align', 'type' => 'unparsed_equals', 'before' => '<div style="text-align:$1;">', 'after' => '</div>', 'test' => '(left|right|center|justify)\\]', 'block_level' => true), array('tag' => 'anchor', 'type' => 'unparsed_equals', 'test' => '[#]?([A-Za-z][A-Za-z0-9_\\-]*)\\]', 'before' => '<span id="post_$1">', 'after' => '</span>'), array('tag' => 'b', 'before' => '<strong>', 'after' => '</strong>'), array('tag' => 'bdo', 'type' => 'unparsed_equals', 'before' => '<bdo dir="$1">', 'after' => '</bdo>', 'test' => '(rtl|ltr)\\]', 'block_level' => true), array('tag' => 'br', 'type' => 'closed', 'content' => '<br />'), array('tag' => 'code', 'type' => 'unparsed_content', 'content' => '<div class="codeheader">' . $txt['code'] . ': </div><pre class="prettyprint lang-php linenums:1">$1</pre>', 'block_level' => true), array('tag' => 'code', 'type' => 'unparsed_equals_content', 'content' => '<div class="codeheader">' . $txt['code'] . ': ($2)</div><pre class="prettyprint lang-$2 linenums:1">$1</pre>', 'block_level' => true), array('tag' => 'color', 'type' => 'unparsed_equals', 'test' => '(#[\\da-fA-F]{3}|#[\\da-fA-F]{6}|[A-Za-z]{1,20}|rgb\\(\\d{1,3}, ?\\d{1,3}, ?\\d{1,3}\\))\\]', 'before' => '<span style="color: $1;" class="bbc_color">', 'after' => '</span>'), array('tag' => 'columns', 'type' => 'unparsed_equals', 'test' => '([1-9])\\]', 'before' => '<div class="bbc_columns" style="column-count:$1;-webkit-column-count:$1;-moz-column-count:$1;">', 'block_level' => true, 'after' => '</div>'), array('tag' => 'css', 'type' => 'unparsed_equals', 'test' => '([a-zA-Z1-9_-\\s]+?)\\]', 'before' => '<div class="$1">', 'block_level' => true, 'after' => '</div>'), array('tag' => 'yt', 'type' => 'unparsed_content', 'validate' => function (&$tag, &$data, $disabled) {
            global $txt, $context;
            $link = !is_array($data) ? $data : $data[0];
            $link = trim(strtr($link, array('<br />' => '')));
            if (preg_match('~^(?:http://((?:www|au|br|ca|es|fr|de|hk|ie|in|il|it|jp|kr|mx|nl|nz|pl|ru|tw|uk)\\.)?youtube\\.com/(?:[^"]*?)(?:(?:video_)?id=|(?:v|p)(?:/|=)))?([0-9a-f]{16}|[0-9a-z-_]{11})~i' . 'u', $link, $matches)) {
                $site = !empty($matches[1]) ? strtolower($matches[1]) : 'www.';
                $type = strlen($matches[2]) == 11 ? 1 : 0;
                if (!is_array($data) || ($data[1] > 780 || $data[1] < 100 || $data[2] > 780 || $data[2] < 100)) {
                    $data = array(0, 425, $type ? 350 : 355);
                }
                $data[0] = $matches[2];
                unset($matches, $link);
                // Set the Content (With conditions on disabled types of BBCode)
                if (isset($disabled['url']) && isset($disabled['youtube'])) {
                    $tag['content'] = "http://" . $site . "youtube.com/" . ($type ? "watch?v" : "view_play_list?p") . "=" . $data[0];
                } elseif (isset($disabled['youtube'])) {
                    $tag['content'] = "<a href=\"http://" . $site . "youtube.com/" . ($type ? "watch?v" : "view_play_list?p") . "=" . $data[0] . "\" target=\"_blank\">http://" . $site . "youtube.com/" . ($type ? "watch?v" : "view_play_list?p") . "=" . $data[0] . "</a>";
                } else {
                    $tag['content'] = '';
                    $tag['content'] = "<div class=\"blue_container mediumpadding\" style=\"width:auto;margin:auto;text-align:center;\"><iframe style=\"width:640px;height:385px;border:0;\" class=\"youtube-player\" src=\"http://www.youtube.com/embed/" . $data[0] . "\"></iframe></div>";
                    //$tag[\'content\'] = "<div class=\"blue_container mediumpadding\" style=\"text-align:center;\"><a rel=\"prettyPhoto\" href=\"http://www.youtube.com/watch?v=".$data[0]."?width=640&height=385\"><img src=\"http://img.youtube.com/vi/".$data[0]."/0.jpg\" alt=\"thumb\" /></a></div>";
                }
            } else {
                $tag['content'] = $txt['youtube_invalid'];
            }
        }, 'disabled_content' => '$1'), array('tag' => 'email', 'type' => 'unparsed_content', 'content' => '<a href="mailto:$1" class="bbc_email">$1</a>', 'validate' => function (&$tag, &$data, $disabled) {
            $data = strtr($data, array('<br />' => ''));
        }), array('tag' => 'email', 'type' => 'unparsed_equals', 'before' => '<a href="mailto:$1" class="bbc_email">', 'after' => '</a>', 'disallow_children' => array('email', 'ftp', 'url', 'iurl'), 'disabled_after' => ' ($1)'), array('tag' => 'font', 'type' => 'unparsed_equals', 'test' => '[A-Za-z0-9_,\\-\\s]+?\\]', 'before' => '<span style="font-family: $1;" class="bbc_font">', 'after' => '</span>'), array('tag' => 'html', 'type' => 'unparsed_content', 'content' => '$1', 'block_level' => true, 'disabled_content' => '$1'), array('tag' => 'h', 'type' => 'unparsed_equals', 'test' => '([1-4])\\]', 'before' => '<span class="bbc_head l$1">', 'block_level' => false, 'after' => '</span>'), array('tag' => 'hr', 'type' => 'closed', 'content' => '<hr />', 'block_level' => true), array('tag' => 'i', 'before' => '<em>', 'after' => '</em>'), array('tag' => 'img', 'type' => 'unparsed_content', 'parameters' => array('alt' => array('optional' => true), 'width' => array('optional' => true, 'value' => 'width:$1px;', 'match' => '(\\d+)'), 'height' => array('optional' => true, 'value' => 'height:$1px;', 'match' => '(\\d+)'), 'resized' => array('optional' => true, 'value' => '_$1', 'match' => '(\\d+)')), 'content' => '<div class="bbc_img_resizer" style="display:none;">' . $txt['img_resizebar_msg'] . '</div><img style="{width}{height}" src="$1" alt="{alt}" class="bbc_img resize{resized}" />', 'validate' => function (&$tag, &$data, $disabled) {
            $data = strtr($data, array('<br />' => ''));
            if (strpos($data, 'http://') !== 0 && strpos($data, 'https://') !== 0) {
                $data = 'http://' . $data;
            }
        }, 'disabled_content' => '($1)'), array('tag' => 'img', 'type' => 'unparsed_content', 'content' => '<img src="$1" alt="" class="bbc_img" />', 'validate' => function (&$tag, &$data, $disabled) {
            $data = strtr($data, array('<br />' => ''));
            if (strpos($data, 'http://') !== 0 && strpos($data, 'https://') !== 0) {
                $data = 'http://' . $data;
            }
        }, 'disabled_content' => '($1)'), array('tag' => 'li', 'before' => '<li>', 'after' => '</li>', 'trim' => 'outside', 'require_parents' => array('list'), 'block_level' => true, 'disabled_before' => '', 'disabled_after' => '<br />'), array('tag' => 'list', 'before' => '<ul class="bbc_list">', 'after' => '</ul>', 'trim' => 'inside', 'require_children' => array('li', 'list'), 'block_level' => true), array('tag' => 'list', 'parameters' => array('type' => array('match' => '(none|disc|circle|square|decimal|decimal-leading-zero|lower-roman|upper-roman|lower-alpha|upper-alpha|lower-greek|lower-latin|upper-latin|hebrew|armenian|georgian|cjk-ideographic|hiragana|katakana|hiragana-iroha|katakana-iroha)')), 'before' => '<ul class="bbc_list" style="list-style-type: {type};">', 'after' => '</ul>', 'trim' => 'inside', 'require_children' => array('li'), 'block_level' => true), array('tag' => 'ilink', 'before' => '<a class="bbc_link" href="' . $scripturl . '?topic={topic};msg={post}#msg{post}">', 'after' => '</a>', 'parameters' => array('topic' => array('match' => '([^<>]{1,192}?)'), 'post' => array('match' => '([^<>]{1,192}?)'))), array('tag' => 'ltr', 'before' => '<div dir="ltr">', 'after' => '</div>', 'block_level' => true), array('tag' => 'me', 'type' => 'unparsed_equals', 'before' => '<div class="meaction">* $1 ', 'after' => '</div>', 'quoted' => 'optional', 'block_level' => true, 'disabled_before' => '/me ', 'disabled_after' => '<br />'), array('tag' => 'nobbc', 'type' => 'unparsed_content', 'content' => '$1'), array('tag' => 'php', 'type' => 'unparsed_content', 'content' => '<div class="codeheader">PHP:</div><pre class="prettyprint lang-php linenums:1">$1</pre>', 'block_level' => false, 'disabled_content' => '$1'), array('tag' => 'pre', 'before' => '<pre><code class="bbc_code">', 'after' => '</code></pre>'), array('tag' => 'quote', 'before' => '<div class="quoteheader">' . $txt['quote'] . '</div><blockquote>', 'after' => '</blockquote><div class="quotefooter"></div>', 'block_level' => true, 'disallow_children' => array('yt')), array('tag' => 'quote', 'parameters' => array('author' => array('match' => '(.{1,192}?)', 'quoted' => true)), 'before' => '<div class="quoteheader">{author} ' . $txt['said'] . ':</div><blockquote>', 'after' => '</blockquote><div class="quotefooter"></div>', 'block_level' => true, 'disallow_children' => array('yt')), array('tag' => 'quote', 'type' => 'parsed_equals', 'before' => '<div class="quoteheader">$1 ' . $txt['said'] . ':</div><blockquote>', 'after' => '</blockquote><div class="quotefooter"></div>', 'quoted' => 'optional', 'parsed_tags_allowed' => array('url', 'iurl', 'ftp'), 'block_level' => true, 'disallow_children' => array('yt')), array('tag' => 'quote', 'parameters' => array('author' => array('match' => '([^<>]{1,192}?)'), 'link' => array('match' => '(?:board=\\d+;)?((?:topic|threadid)=[\\dmsg#\\./]{1,40}(?:;start=[\\dmsg#\\./]{1,40})?|action=profile;u=\\d+)'), 'date' => array('match' => '(\\d+)', 'validate' => 'timeformat')), 'before' => '<div class="quoteheader"><a href="' . $scripturl . '?{link}">{author} ' . $txt['said'] . ', {date}</a></div><blockquote>', 'after' => '</blockquote><div class="quotefooter"></div>', 'block_level' => true, 'disallow_children' => array('yt')), array('tag' => 'quote', 'parameters' => array('author' => array('match' => '(.{1,192}?)')), 'before' => '<div class="quoteheader">{author} ' . $txt['said'] . ':</div><blockquote>', 'after' => '</blockquote><div class="quotefooter"></div>', 'block_level' => true, 'disallow_children' => array('yt')), array('tag' => 'rtl', 'before' => '<div dir="rtl">', 'after' => '</div>', 'block_level' => true), array('tag' => 's', 'before' => '<del>', 'after' => '</del>'), array('tag' => 'size', 'type' => 'unparsed_equals', 'test' => '([1-9][\\d]?p[xt]|small(?:er)?|large[r]?|x[x]?-(?:small|large)|medium|(0\\.[1-9]|[1-9](\\.[\\d][\\d]?)?)?em)\\]', 'before' => '<span style="font-size: $1;" class="bbc_size">', 'after' => '</span>'), array('tag' => 'size', 'type' => 'unparsed_equals', 'test' => '[1-7]\\]', 'before' => '<span style="font-size: $1;" class="bbc_size">', 'after' => '</span>', 'validate' => function (&$tag, &$data, $disabled) {
            $sizes = array(1 => 0.7, 2 => 1.0, 3 => 1.35, 4 => 1.45, 5 => 2.0, 6 => 2.65, 7 => 3.95);
            $data = $sizes[$data] . 'em';
        }), array('tag' => 'spoiler', 'before' => '<div class="spoiler head">' . $txt['spoiler_title'] . '</div><div class="spoiler content" style="display:none">', 'after' => '</div>', 'block_level' => true), array('tag' => 'spoiler', 'type' => 'unparsed_equals', 'before' => '<div class="spoiler head">' . sprintf($txt['spoiler_intro'], '$1') . '</div><div class="spoiler content" style="display:none">', 'after' => '</div>', 'block_level' => true), array('tag' => 'sub', 'before' => '<sub>', 'after' => '</sub>'), array('tag' => 'sup', 'before' => '<sup>', 'after' => '</sup>'), array('tag' => 'table', 'before' => '<table class="bbc_table">', 'after' => '</table>', 'trim' => 'inside', 'require_children' => array('tr'), 'block_level' => true), array('tag' => 'td', 'before' => '<td>', 'after' => '</td>', 'require_parents' => array('tr'), 'trim' => 'outside', 'block_level' => true, 'disabled_before' => '', 'disabled_after' => ''), array('tag' => 'time', 'type' => 'unparsed_content', 'content' => '$1', 'validate' => function (&$tag, &$data, $disabled) {
            if (is_numeric($data)) {
                $data = timeformat($data);
            } else {
                $tag['content'] = '[time]$1[/time]';
            }
        }), array('tag' => 'tr', 'before' => '<tr>', 'after' => '</tr>', 'require_parents' => array('table'), 'require_children' => array('td'), 'trim' => 'both', 'block_level' => true, 'disabled_before' => '', 'disabled_after' => ''), array('tag' => 'tt', 'before' => '<tt class="bbc_tt">', 'after' => '</tt>'), array('tag' => 'u', 'before' => '<span class="bbc_u">', 'after' => '</span>'), array('tag' => 'user', 'type' => 'unparsed_content', 'parameters' => array('id' => array('match' => '(\\d+)', 'value' => '$1')), 'content' => '<a onclick="getMcard({id},$(this));return(false);" href="' . $scripturl . '?action=profile;u={id}">$1</a>'), array('tag' => 'url', 'type' => 'unparsed_content', 'content' => '<a href="$1" class="bbc_link" target="_blank">$1</a>', 'validate' => function (&$tag, &$data, $disabled) {
            $data = strtr($data, array('<br />' => ''));
            if (strpos($data, 'http://') !== 0 && strpos($data, 'https://') !== 0) {
                $data = 'http://' . $data;
            }
        }), array('tag' => 'url', 'type' => 'unparsed_equals', 'before' => '<a href="$1" class="bbc_link" target="_blank">', 'after' => '</a>', 'validate' => function (&$tag, &$data, $disabled) {
            if (strpos($data, 'http://') !== 0 && strpos($data, 'https://') !== 0) {
                $data = 'http://' . $data;
            }
        }, 'disallow_children' => array('email', 'ftp', 'url', 'iurl'), 'disabled_after' => ' ($1)'));
        // Let mods add new BBC without hassle.
        HookAPI::callHook('bbc_codes', array(&$codes));
        // This is mainly for the bbc manager, so it's easy to add tags above.  Custom BBC should be added above this line.
        if ($message === false) {
            if (isset($temp_bbc)) {
                $bbc_codes = $temp_bbc;
            }
            return $codes;
        }
        // So the parser won't skip them.
        $itemcodes = array('*' => 'disc', '@' => 'disc', '+' => 'square', 'x' => 'square', '#' => 'square', 'o' => 'circle', 'O' => 'circle', '0' => 'circle');
        if (!isset($disabled['li']) && !isset($disabled['list'])) {
            foreach ($itemcodes as $c => $dummy) {
                $bbc_codes[$c] = array();
            }
        }
        // Inside these tags autolink is not recommendable.
        $no_autolink_tags = array('url', 'iurl', 'ftp', 'email');
        foreach ($codes as $code) {
            // If we are not doing every tag only do ones we are interested in.
            if (empty($parse_tags) || in_array($code['tag'], $parse_tags)) {
                $bbc_codes[substr($code['tag'], 0, 1)][] = $code;
            }
        }
        $codes = null;
    }
    // Shall we take the time to cache this?
    if ($cache_id != '' && $modSettings['cache_enable'] >= 2) {
        // It's likely this will change if the message is modified.
        $cache_key = 'parse:' . $cache_id . '-' . md5(md5($cache_unique) . '-' . $smileys . (empty($disabled) ? '' : implode(',', array_keys($disabled))) . $txt['lang_locale'] . $user_info['time_offset'] . $user_info['time_format']);
        if (($temp = CacheAPI::getCache($cache_key, 1200)) != null) {
            return $temp;
        }
        $cache_t = microtime();
    }
    if ($smileys === 'print') {
        // [glow], [shadow], and [move] can't really be printed.
        $disabled['glow'] = true;
        $disabled['shadow'] = true;
        $disabled['move'] = true;
        // Colors can't well be displayed... supposed to be black and white.
        $disabled['color'] = true;
        $disabled['black'] = true;
        $disabled['blue'] = true;
        $disabled['white'] = true;
        $disabled['red'] = true;
        $disabled['green'] = true;
        $disabled['me'] = true;
        // Color coding doesn't make sense.
        $disabled['php'] = true;
        // Links are useless on paper... just show the link.
        $disabled['ftp'] = true;
        $disabled['url'] = true;
        $disabled['iurl'] = true;
        $disabled['email'] = true;
        $disabled['flash'] = true;
        // !!! Change maybe?
        if (!isset($_GET['images'])) {
            $disabled['img'] = true;
        }
        // !!! Interface/setting to add more?
    }
    $open_tags = array();
    $message = strtr($message, array("\n" => '<br />'));
    // The non-breaking-space looks a bit different each time.
    $non_breaking_space = $context['server']['complex_preg_chars'] ? '\\x{A0}' : " ";
    // This saves time by doing our break long words checks here.
    if (!empty($modSettings['fixLongWords']) && $modSettings['fixLongWords'] > 5) {
        if ($context['browser']['is_gecko'] || $context['browser']['is_konqueror']) {
            $breaker = '<span style="margin: 0 -0.5ex 0 0;"> </span>';
        } elseif ($context['browser']['is_opera']) {
            $breaker = '<span style="margin: 0 -0.65ex 0 -1px;"> </span>';
        } else {
            $breaker = '<span style="width: 0; margin: 0 -0.6ex 0 -1px;"> </span>';
        }
        // PCRE will not be happy if we don't give it a short.
        $modSettings['fixLongWords'] = (int) min(65535, $modSettings['fixLongWords']);
    }
    $pos = -1;
    while ($pos !== false) {
        $last_pos = isset($last_pos) ? max($pos, $last_pos) : $pos;
        $pos = strpos($message, '[', $pos + 1);
        // Failsafe.
        if ($pos === false || $last_pos > $pos) {
            $pos = strlen($message) + 1;
        }
        // Can't have a one letter smiley, URL, or email! (sorry.)
        if ($last_pos < $pos - 1) {
            // Make sure the $last_pos is not negative.
            $last_pos = max($last_pos, 0);
            // Pick a block of data to do some raw fixing on.
            $data = substr($message, $last_pos, $pos - $last_pos);
            // Take care of some HTML!
            if (!empty($modSettings['enablePostHTML']) && strpos($data, '&lt;') !== false) {
                $data = preg_replace('~&lt;a\\s+href=((?:&quot;)?)((?:https?://|ftps?://|mailto:)\\S+?)\\1&gt;~i', '[url=$2]', $data);
                $data = preg_replace('~&lt;/a&gt;~i', '[/url]', $data);
                // <br /> should be empty.
                $empty_tags = array('br', 'hr');
                foreach ($empty_tags as $tag) {
                    $data = str_replace(array('&lt;' . $tag . '&gt;', '&lt;' . $tag . '/&gt;', '&lt;' . $tag . ' /&gt;'), '[' . $tag . ' /]', $data);
                }
                // b, u, i, s, pre... basic tags.
                $closable_tags = array('b', 'u', 'i', 's', 'em', 'ins', 'del', 'pre', 'blockquote');
                foreach ($closable_tags as $tag) {
                    $diff = substr_count($data, '&lt;' . $tag . '&gt;') - substr_count($data, '&lt;/' . $tag . '&gt;');
                    $data = strtr($data, array('&lt;' . $tag . '&gt;' => '<' . $tag . '>', '&lt;/' . $tag . '&gt;' => '</' . $tag . '>'));
                    if ($diff > 0) {
                        $data = substr($data, 0, -1) . str_repeat('</' . $tag . '>', $diff) . substr($data, -1);
                    }
                }
                // Do <img ... /> - with security... action= -> action-.
                preg_match_all('~&lt;img\\s+src=((?:&quot;)?)((?:https?://|ftps?://)\\S+?)\\1(?:\\s+alt=(&quot;.*?&quot;|\\S*?))?(?:\\s?/)?&gt;~i', $data, $matches, PREG_PATTERN_ORDER);
                if (!empty($matches[0])) {
                    $replaces = array();
                    foreach ($matches[2] as $match => $imgtag) {
                        $alt = empty($matches[3][$match]) ? '' : ' alt=' . preg_replace('~^&quot;|&quot;$~', '', $matches[3][$match]);
                        // Remove action= from the URL - no funny business, now.
                        if (preg_match('~action(=|%3d)(?!dlattach)~i', $imgtag) != 0) {
                            $imgtag = preg_replace('~action(?:=|%3d)(?!dlattach)~i', 'action-', $imgtag);
                        }
                        // Check if the image is larger than allowed.
                        if (!empty($modSettings['max_image_width']) && !empty($modSettings['max_image_height'])) {
                            list($width, $height) = url_image_size($imgtag);
                            if (!empty($modSettings['max_image_width']) && $width > $modSettings['max_image_width']) {
                                $height = (int) ($modSettings['max_image_width'] * $height / $width);
                                $width = $modSettings['max_image_width'];
                            }
                            if (!empty($modSettings['max_image_height']) && $height > $modSettings['max_image_height']) {
                                $width = (int) ($modSettings['max_image_height'] * $width / $height);
                                $height = $modSettings['max_image_height'];
                            }
                            // Set the new image tag.
                            $replaces[$matches[0][$match]] = '[img resized=1 width=' . $width . ' height=' . $height . $alt . ']' . $imgtag . '[/img]';
                        } else {
                            $replaces[$matches[0][$match]] = '[img' . $alt . ']' . $imgtag . '[/img]';
                        }
                    }
                    $data = strtr($data, $replaces);
                }
            }
            if (!empty($modSettings['autoLinkUrls'])) {
                // Are we inside tags that should be auto linked?
                $no_autolink_area = false;
                if (!empty($open_tags)) {
                    foreach ($open_tags as $open_tag) {
                        if (in_array($open_tag['tag'], $no_autolink_tags)) {
                            $no_autolink_area = true;
                        }
                    }
                }
                // Don't go backwards.
                //!!! Don't think is the real solution....
                $lastAutoPos = isset($lastAutoPos) ? $lastAutoPos : 0;
                if ($pos < $lastAutoPos) {
                    $no_autolink_area = true;
                }
                $lastAutoPos = $pos;
                if (!$no_autolink_area) {
                    // Parse any URLs.... have to get rid of the @ problems some things cause... stupid email addresses.
                    if (!isset($disabled['url']) && (strpos($data, '://') !== false || strpos($data, 'www.') !== false) && strpos($data, '[url') === false) {
                        // Switch out quotes really quick because they can cause problems.
                        $data = strtr($data, array('&#039;' => '\'', '&nbsp;' => " ", '&quot;' => '>">', '"' => '<"<', '&lt;' => '<lt<'));
                        // Only do this if the preg survives.
                        if (is_string($result = preg_replace(array('~(?<=[\\s>\\.(;\'"]|^)((?:http|https)://[\\w\\-_%@:|]+(?:\\.[\\w\\-_%]+)*(?::\\d+)?(?:/[\\w\\-_\\~%\\.@!,\\?&;=#(){}+:\'\\\\]*)*[/\\w\\-_\\~%@\\?;=#}\\\\])~i', '~(?<=[\\s>\\.(;\'"]|^)((?:ftp|ftps)://[\\w\\-_%@:|]+(?:\\.[\\w\\-_%]+)*(?::\\d+)?(?:/[\\w\\-_\\~%\\.@,\\?&;=#(){}+:\'\\\\]*)*[/\\w\\-_\\~%@\\?;=#}\\\\])~i', '~(?<=[\\s>(\'<]|^)(www(?:\\.[\\w\\-_]+)+(?::\\d+)?(?:/[\\w\\-_\\~%\\.@!,\\?&;=#(){}+:\'\\\\]*)*[/\\w\\-_\\~%@\\?;=#}\\\\])~i'), array('[url]$1[/url]', '[ftp]$1[/ftp]', '[url=http://$1]$1[/url]'), $data))) {
                            $data = $result;
                        }
                        $data = strtr($data, array('\'' => '&#039;', " " => '&nbsp;', '>">' => '&quot;', '<"<' => '"', '<lt<' => '&lt;'));
                    }
                    // Next, emails...
                    if (!isset($disabled['email']) && strpos($data, '@') !== false && strpos($data, '[email') === false) {
                        $data = preg_replace('~(?<=[\\?\\s' . $non_breaking_space . '\\[\\]()*\\\\;>]|^)([\\w\\-\\.]{1,80}@[\\w\\-]+\\.[\\w\\-\\.]+[\\w\\-])(?=[?,\\s' . $non_breaking_space . '\\[\\]()*\\\\]|$|<br />|&nbsp;|&gt;|&lt;|&quot;|&#039;|\\.(?:\\.|;|&nbsp;|\\s|$|<br />))~u', '[email]$1[/email]', $data);
                        $data = preg_replace('~(?<=<br />)([\\w\\-\\.]{1,80}@[\\w\\-]+\\.[\\w\\-\\.]+[\\w\\-])(?=[?\\.,;\\s' . $non_breaking_space . '\\[\\]()*\\\\]|$|<br />|&nbsp;|&gt;|&lt;|&quot;|&#039;)~u', '[email]$1[/email]', $data);
                    }
                }
            }
            $data = strtr($data, array("\t" => '&nbsp;&nbsp;&nbsp;'));
            if (!empty($modSettings['fixLongWords']) && $modSettings['fixLongWords'] > 5) {
                // The idea is, find words xx long, and then replace them with xx + space + more.
                if (commonAPI::strlen($data) > $modSettings['fixLongWords']) {
                    // This is done in a roundabout way because $breaker has "long words" :P.
                    $data = strtr($data, array($breaker => '< >', '&nbsp;' => " "));
                    $data = preg_replace('~(?<=[>;:!? ' . $non_breaking_space . '\\]()]|^)([\\w' . '\\pL' . '\\.]{' . $modSettings['fixLongWords'] . ',})~eu', 'preg_replace(\'/(.{' . ($modSettings['fixLongWords'] - 1) . '})/u' . '\', \'\\$1< >\', \'$1\')', $data);
                    $data = strtr($data, array('< >' => $breaker, " " => '&nbsp;'));
                }
            }
            // If it wasn't changed, no copying or other boring stuff has to happen!
            if ($data != substr($message, $last_pos, $pos - $last_pos)) {
                $message = substr($message, 0, $last_pos) . $data . substr($message, $pos);
                // Since we changed it, look again in case we added or removed a tag.  But we don't want to skip any.
                $old_pos = strlen($data) + $last_pos;
                $pos = strpos($message, '[', $last_pos);
                $pos = $pos === false ? $old_pos : min($pos, $old_pos);
            }
        }
        // Are we there yet?  Are we there yet?
        if ($pos >= strlen($message) - 1) {
            break;
        }
        $tags = strtolower(substr($message, $pos + 1, 1));
        if ($tags == '/' && !empty($open_tags)) {
            $pos2 = strpos($message, ']', $pos + 1);
            if ($pos2 == $pos + 2) {
                continue;
            }
            $look_for = strtolower(substr($message, $pos + 2, $pos2 - $pos - 2));
            $to_close = array();
            $block_level = null;
            do {
                $tag = array_pop($open_tags);
                if (!$tag) {
                    break;
                }
                if (!empty($tag['block_level'])) {
                    // Only find out if we need to.
                    if ($block_level === false) {
                        array_push($open_tags, $tag);
                        break;
                    }
                    // The idea is, if we are LOOKING for a block level tag, we can close them on the way.
                    if (strlen($look_for) > 0 && isset($bbc_codes[$look_for[0]])) {
                        foreach ($bbc_codes[$look_for[0]] as $temp) {
                            if ($temp['tag'] == $look_for) {
                                $block_level = !empty($temp['block_level']);
                                break;
                            }
                        }
                    }
                    if ($block_level !== true) {
                        $block_level = false;
                        array_push($open_tags, $tag);
                        break;
                    }
                }
                $to_close[] = $tag;
            } while ($tag['tag'] != $look_for);
            // Did we just eat through everything and not find it?
            if (empty($open_tags) && (empty($tag) || $tag['tag'] != $look_for)) {
                $open_tags = $to_close;
                continue;
            } elseif (!empty($to_close) && $tag['tag'] != $look_for) {
                if ($block_level === null && isset($look_for[0], $bbc_codes[$look_for[0]])) {
                    foreach ($bbc_codes[$look_for[0]] as $temp) {
                        if ($temp['tag'] == $look_for) {
                            $block_level = !empty($temp['block_level']);
                            break;
                        }
                    }
                }
                // We're not looking for a block level tag (or maybe even a tag that exists...)
                if (!$block_level) {
                    foreach ($to_close as $tag) {
                        array_push($open_tags, $tag);
                    }
                    continue;
                }
            }
            foreach ($to_close as $tag) {
                $message = substr($message, 0, $pos) . "\n" . $tag['after'] . "\n" . substr($message, $pos2 + 1);
                $pos += strlen($tag['after']) + 2;
                $pos2 = $pos - 1;
                // See the comment at the end of the big loop - just eating whitespace ;).
                if (!empty($tag['block_level']) && substr($message, $pos, 6) == '<br />') {
                    $message = substr($message, 0, $pos) . substr($message, $pos + 6);
                }
                if (!empty($tag['trim']) && $tag['trim'] != 'inside' && preg_match('~(<br />|&nbsp;|\\s)*~', substr($message, $pos), $matches) != 0) {
                    $message = substr($message, 0, $pos) . substr($message, $pos + strlen($matches[0]));
                }
            }
            if (!empty($to_close)) {
                $to_close = array();
                $pos--;
            }
            continue;
        }
        // No tags for this character, so just keep going (fastest possible course.)
        if (!isset($bbc_codes[$tags])) {
            continue;
        }
        $inside = empty($open_tags) ? null : $open_tags[count($open_tags) - 1];
        $tag = null;
        foreach ($bbc_codes[$tags] as $possible) {
            $_len = strlen($possible['tag']);
            // Not a match?
            if (strtolower(substr($message, $pos + 1, $_len)) != $possible['tag']) {
                continue;
            }
            $next_c = substr($message, $pos + 1 + $_len, 1);
            // A test validation?
            if (isset($possible['test']) && preg_match('~^' . $possible['test'] . '~', substr($message, $pos + 1 + $_len + 1)) == 0) {
                continue;
            } elseif (!empty($possible['parameters'])) {
                if ($next_c != ' ') {
                    continue;
                }
            } elseif (isset($possible['type'])) {
                // Do we need an equal sign?
                if (in_array($possible['type'], array('unparsed_equals', 'unparsed_commas', 'unparsed_commas_content', 'unparsed_equals_content', 'parsed_equals')) && $next_c != '=') {
                    continue;
                }
                // Maybe we just want a /...
                if ($possible['type'] == 'closed' && $next_c != ']' && substr($message, $pos + 1 + $_len, 2) != '/]' && substr($message, $pos + 1 + $_len, 3) != ' /]') {
                    continue;
                }
                // An immediate ]?
                if ($possible['type'] == 'unparsed_content' && $next_c != ']') {
                    continue;
                }
            } elseif ($next_c != ']') {
                continue;
            }
            // Check allowed tree?
            if (isset($possible['require_parents']) && ($inside === null || !in_array($inside['tag'], $possible['require_parents']))) {
                continue;
            } elseif (isset($inside['require_children']) && !in_array($possible['tag'], $inside['require_children'])) {
                continue;
            } elseif (isset($inside['disallow_children']) && in_array($possible['tag'], $inside['disallow_children'])) {
                continue;
            }
            $pos1 = $pos + 1 + $_len + 1;
            // Quotes can have alternate styling, we do this php-side due to all the permutations of quotes.
            if ($possible['tag'] == 'quote') {
                // Start with standard
                $quote_alt = false;
                foreach ($open_tags as $open_quote) {
                    // Every parent quote this quote has flips the styling
                    if ($open_quote['tag'] == 'quote') {
                        $quote_alt = !$quote_alt;
                    }
                }
                // Add a class to the quote to style alternating blockquotes
                $possible['before'] = strtr($possible['before'], array('<blockquote>' => '<blockquote class="bbc_' . ($quote_alt ? 'alternate' : 'standard') . '_quote">'));
            }
            // This is long, but it makes things much easier and cleaner.
            if (!empty($possible['parameters'])) {
                $preg = array();
                foreach ($possible['parameters'] as $p => $info) {
                    $preg[] = '(\\s+' . $p . '=' . (empty($info['quoted']) ? '' : '&quot;') . (isset($info['match']) ? $info['match'] : '(.+?)') . (empty($info['quoted']) ? '' : '&quot;') . ')' . (empty($info['optional']) ? '' : '?');
                }
                // Okay, this may look ugly and it is, but it's not going to happen much and it is the best way of allowing any order of parameters but still parsing them right.
                $match = false;
                $orders = permute($preg);
                foreach ($orders as $p) {
                    if (preg_match('~^' . implode('', $p) . '\\]~i', substr($message, $pos1 - 1), $matches) != 0) {
                        $match = true;
                        break;
                    }
                }
                // Didn't match our parameter list, try the next possible.
                if (!$match) {
                    continue;
                }
                $params = array();
                for ($i = 1, $n = count($matches); $i < $n; $i += 2) {
                    $key = strtok(ltrim($matches[$i]), '=');
                    if (isset($possible['parameters'][$key]['value'])) {
                        $params['{' . $key . '}'] = strtr($possible['parameters'][$key]['value'], array('$1' => $matches[$i + 1]));
                    } elseif (isset($possible['parameters'][$key]['validate'])) {
                        $params['{' . $key . '}'] = $possible['parameters'][$key]['validate']($matches[$i + 1]);
                    } else {
                        $params['{' . $key . '}'] = $matches[$i + 1];
                    }
                    // Just to make sure: replace any $ or { so they can't interpolate wrongly.
                    $params['{' . $key . '}'] = strtr($params['{' . $key . '}'], array('$' => '&#036;', '{' => '&#123;'));
                }
                foreach ($possible['parameters'] as $p => $info) {
                    if (!isset($params['{' . $p . '}'])) {
                        $params['{' . $p . '}'] = '';
                    }
                }
                $tag = $possible;
                // Put the parameters into the string.
                if (isset($tag['before'])) {
                    $tag['before'] = strtr($tag['before'], $params);
                }
                if (isset($tag['after'])) {
                    $tag['after'] = strtr($tag['after'], $params);
                }
                if (isset($tag['content'])) {
                    $tag['content'] = strtr($tag['content'], $params);
                }
                $pos1 += strlen($matches[0]) - 1;
            } else {
                $tag = $possible;
            }
            break;
        }
        // Item codes are complicated buggers... they are implicit [li]s and can make [list]s!
        if ($smileys !== false && $tag === null && isset($itemcodes[substr($message, $pos + 1, 1)]) && substr($message, $pos + 2, 1) == ']' && !isset($disabled['list']) && !isset($disabled['li'])) {
            if (substr($message, $pos + 1, 1) == '0' && !in_array(substr($message, $pos - 1, 1), array(';', ' ', "\t", '>'))) {
                continue;
            }
            $tag = $itemcodes[substr($message, $pos + 1, 1)];
            // First let's set up the tree: it needs to be in a list, or after an li.
            if ($inside === null || $inside['tag'] != 'list' && $inside['tag'] != 'li') {
                $open_tags[] = array('tag' => 'list', 'after' => '</ul>', 'block_level' => true, 'require_children' => array('li'), 'disallow_children' => isset($inside['disallow_children']) ? $inside['disallow_children'] : null);
                $code = '<ul class="bbc_list">';
            } elseif ($inside['tag'] == 'li') {
                array_pop($open_tags);
                $code = '</li>';
            } else {
                $code = '';
            }
            // Now we open a new tag.
            $open_tags[] = array('tag' => 'li', 'after' => '</li>', 'trim' => 'outside', 'block_level' => true, 'disallow_children' => isset($inside['disallow_children']) ? $inside['disallow_children'] : null);
            // First, open the tag...
            $code .= '<li' . ($tag == '' ? '' : ' type="' . $tag . '"') . '>';
            $message = substr($message, 0, $pos) . "\n" . $code . "\n" . substr($message, $pos + 3);
            $pos += strlen($code) - 1 + 2;
            // Next, find the next break (if any.)  If there's more itemcode after it, keep it going - otherwise close!
            $pos2 = strpos($message, '<br />', $pos);
            $pos3 = strpos($message, '[/', $pos);
            if ($pos2 !== false && ($pos2 <= $pos3 || $pos3 === false)) {
                preg_match('~^(<br />|&nbsp;|\\s|\\[)+~', substr($message, $pos2 + 6), $matches);
                $message = substr($message, 0, $pos2) . "\n" . (!empty($matches[0]) && substr($matches[0], -1) == '[' ? '[/li]' : '[/li][/list]') . "\n" . substr($message, $pos2);
                $open_tags[count($open_tags) - 2]['after'] = '</ul>';
            } else {
                // Move the li over, because we're not sure what we'll hit.
                $open_tags[count($open_tags) - 1]['after'] = '';
                $open_tags[count($open_tags) - 2]['after'] = '</li></ul>';
            }
            continue;
        }
        // Implicitly close lists and tables if something other than what's required is in them.  This is needed for itemcode.
        if ($tag === null && $inside !== null && !empty($inside['require_children'])) {
            array_pop($open_tags);
            $message = substr($message, 0, $pos) . "\n" . $inside['after'] . "\n" . substr($message, $pos);
            $pos += strlen($inside['after']) - 1 + 2;
        }
        // No tag?  Keep looking, then.  Silly people using brackets without actual tags.
        if ($tag === null) {
            continue;
        }
        // Propagate the list to the child (so wrapping the disallowed tag won't work either.)
        if (isset($inside['disallow_children'])) {
            $tag['disallow_children'] = isset($tag['disallow_children']) ? array_unique(array_merge($tag['disallow_children'], $inside['disallow_children'])) : $inside['disallow_children'];
        }
        // Is this tag disabled?
        if (isset($disabled[$tag['tag']])) {
            if (!isset($tag['disabled_before']) && !isset($tag['disabled_after']) && !isset($tag['disabled_content'])) {
                $tag['before'] = !empty($tag['block_level']) ? '<div>' : '';
                $tag['after'] = !empty($tag['block_level']) ? '</div>' : '';
                $tag['content'] = isset($tag['type']) && $tag['type'] == 'closed' ? '' : (!empty($tag['block_level']) ? '<div>$1</div>' : '$1');
            } elseif (isset($tag['disabled_before']) || isset($tag['disabled_after'])) {
                $tag['before'] = isset($tag['disabled_before']) ? $tag['disabled_before'] : (!empty($tag['block_level']) ? '<div>' : '');
                $tag['after'] = isset($tag['disabled_after']) ? $tag['disabled_after'] : (!empty($tag['block_level']) ? '</div>' : '');
            } else {
                $tag['content'] = $tag['disabled_content'];
            }
        }
        // The only special case is 'html', which doesn't need to close things.
        if (!empty($tag['block_level']) && $tag['tag'] != 'html' && empty($inside['block_level'])) {
            $n = count($open_tags) - 1;
            while (empty($open_tags[$n]['block_level']) && $n >= 0) {
                $n--;
            }
            // Close all the non block level tags so this tag isn't surrounded by them.
            for ($i = count($open_tags) - 1; $i > $n; $i--) {
                $message = substr($message, 0, $pos) . "\n" . $open_tags[$i]['after'] . "\n" . substr($message, $pos);
                $pos += strlen($open_tags[$i]['after']) + 2;
                $pos1 += strlen($open_tags[$i]['after']) + 2;
                // Trim or eat trailing stuff... see comment at the end of the big loop.
                if (!empty($open_tags[$i]['block_level']) && substr($message, $pos, 6) == '<br />') {
                    $message = substr($message, 0, $pos) . substr($message, $pos + 6);
                }
                if (!empty($open_tags[$i]['trim']) && $tag['trim'] != 'inside' && preg_match('~(<br />|&nbsp;|\\s)*~', substr($message, $pos), $matches) != 0) {
                    $message = substr($message, 0, $pos) . substr($message, $pos + strlen($matches[0]));
                }
                array_pop($open_tags);
            }
        }
        // No type means 'parsed_content'.
        if (!isset($tag['type'])) {
            // !!! Check for end tag first, so people can say "I like that [i] tag"?
            $open_tags[] = $tag;
            $message = substr($message, 0, $pos) . "\n" . $tag['before'] . "\n" . substr($message, $pos1);
            $pos += strlen($tag['before']) - 1 + 2;
        } elseif ($tag['type'] == 'unparsed_content') {
            $pos2 = stripos($message, '[/' . substr($message, $pos + 1, strlen($tag['tag'])) . ']', $pos1);
            if ($pos2 === false) {
                continue;
            }
            $data = substr($message, $pos1, $pos2 - $pos1);
            if (!empty($tag['block_level']) && substr($data, 0, 6) == '<br />') {
                $data = substr($data, 6);
            }
            if (isset($tag['validate'])) {
                $tag['validate']($tag, $data, $disabled);
            }
            $code = strtr($tag['content'], array('$1' => $data));
            $message = substr($message, 0, $pos) . "\n" . $code . "\n" . substr($message, $pos2 + 3 + strlen($tag['tag']));
            $pos += strlen($code) - 1 + 2;
            $last_pos = $pos + 1;
        } elseif ($tag['type'] == 'unparsed_equals_content') {
            // The value may be quoted for some tags - check.
            if (isset($tag['quoted'])) {
                $quoted = substr($message, $pos1, 6) == '&quot;';
                if ($tag['quoted'] != 'optional' && !$quoted) {
                    continue;
                }
                if ($quoted) {
                    $pos1 += 6;
                }
            } else {
                $quoted = false;
            }
            $pos2 = strpos($message, $quoted == false ? ']' : '&quot;]', $pos1);
            if ($pos2 === false) {
                continue;
            }
            $pos3 = stripos($message, '[/' . substr($message, $pos + 1, strlen($tag['tag'])) . ']', $pos2);
            if ($pos3 === false) {
                continue;
            }
            $data = array(substr($message, $pos2 + ($quoted == false ? 1 : 7), $pos3 - ($pos2 + ($quoted == false ? 1 : 7))), substr($message, $pos1, $pos2 - $pos1));
            if (!empty($tag['block_level']) && substr($data[0], 0, 6) == '<br />') {
                $data[0] = substr($data[0], 6);
            }
            // Validation for my parking, please!
            if (isset($tag['validate'])) {
                $tag['validate']($tag, $data, $disabled);
            }
            $code = strtr($tag['content'], array('$1' => $data[0], '$2' => $data[1]));
            $message = substr($message, 0, $pos) . "\n" . $code . "\n" . substr($message, $pos3 + 3 + strlen($tag['tag']));
            $pos += strlen($code) - 1 + 2;
        } elseif ($tag['type'] == 'closed') {
            $pos2 = strpos($message, ']', $pos);
            $message = substr($message, 0, $pos) . "\n" . $tag['content'] . "\n" . substr($message, $pos2 + 1);
            $pos += strlen($tag['content']) - 1 + 2;
        } elseif ($tag['type'] == 'unparsed_commas_content') {
            $pos2 = strpos($message, ']', $pos1);
            if ($pos2 === false) {
                continue;
            }
            $pos3 = stripos($message, '[/' . substr($message, $pos + 1, strlen($tag['tag'])) . ']', $pos2);
            if ($pos3 === false) {
                continue;
            }
            // We want $1 to be the content, and the rest to be csv.
            $data = explode(',', ',' . substr($message, $pos1, $pos2 - $pos1));
            $data[0] = substr($message, $pos2 + 1, $pos3 - $pos2 - 1);
            if (isset($tag['validate'])) {
                $tag['validate']($tag, $data, $disabled);
            }
            $code = $tag['content'];
            foreach ($data as $k => $d) {
                $code = strtr($code, array('$' . ($k + 1) => trim($d)));
            }
            $message = substr($message, 0, $pos) . "\n" . $code . "\n" . substr($message, $pos3 + 3 + strlen($tag['tag']));
            $pos += strlen($code) - 1 + 2;
        } elseif ($tag['type'] == 'unparsed_commas') {
            $pos2 = strpos($message, ']', $pos1);
            if ($pos2 === false) {
                continue;
            }
            $data = explode(',', substr($message, $pos1, $pos2 - $pos1));
            if (isset($tag['validate'])) {
                $tag['validate']($tag, $data, $disabled);
            }
            // Fix after, for disabled code mainly.
            foreach ($data as $k => $d) {
                $tag['after'] = strtr($tag['after'], array('$' . ($k + 1) => trim($d)));
            }
            $open_tags[] = $tag;
            // Replace them out, $1, $2, $3, $4, etc.
            $code = $tag['before'];
            foreach ($data as $k => $d) {
                $code = strtr($code, array('$' . ($k + 1) => trim($d)));
            }
            $message = substr($message, 0, $pos) . "\n" . $code . "\n" . substr($message, $pos2 + 1);
            $pos += strlen($code) - 1 + 2;
        } elseif ($tag['type'] == 'unparsed_equals' || $tag['type'] == 'parsed_equals') {
            // The value may be quoted for some tags - check.
            if (isset($tag['quoted'])) {
                $quoted = substr($message, $pos1, 6) == '&quot;';
                if ($tag['quoted'] != 'optional' && !$quoted) {
                    continue;
                }
                if ($quoted) {
                    $pos1 += 6;
                }
            } else {
                $quoted = false;
            }
            $pos2 = strpos($message, $quoted == false ? ']' : '&quot;]', $pos1);
            if ($pos2 === false) {
                continue;
            }
            $data = substr($message, $pos1, $pos2 - $pos1);
            // Validation for my parking, please!
            if (isset($tag['validate'])) {
                $tag['validate']($tag, $data, $disabled);
            }
            // For parsed content, we must recurse to avoid security problems.
            if ($tag['type'] != 'unparsed_equals') {
                $data = parse_bbc($data, !empty($tag['parsed_tags_allowed']) ? false : true, '', !empty($tag['parsed_tags_allowed']) ? $tag['parsed_tags_allowed'] : array());
            }
            $tag['after'] = strtr($tag['after'], array('$1' => $data));
            $open_tags[] = $tag;
            $code = strtr($tag['before'], array('$1' => $data));
            $message = substr($message, 0, $pos) . "\n" . $code . "\n" . substr($message, $pos2 + ($quoted == false ? 1 : 7));
            $pos += strlen($code) - 1 + 2;
        }
        // If this is block level, eat any breaks after it.
        if (!empty($tag['block_level']) && substr($message, $pos + 1, 6) == '<br />') {
            $message = substr($message, 0, $pos + 1) . substr($message, $pos + 7);
        }
        // Are we trimming outside this tag?
        if (!empty($tag['trim']) && $tag['trim'] != 'outside' && preg_match('~(<br />|&nbsp;|\\s)*~', substr($message, $pos + 1), $matches) != 0) {
            $message = substr($message, 0, $pos + 1) . substr($message, $pos + 1 + strlen($matches[0]));
        }
    }
    // Close any remaining tags.
    while ($tag = array_pop($open_tags)) {
        $message .= "\n" . $tag['after'] . "\n";
    }
    // Parse the smileys within the parts where it can be done safely.
    if ($smileys === true) {
        $message_parts = explode("\n", $message);
        for ($i = 0, $n = count($message_parts); $i < $n; $i += 2) {
            parsesmileys($message_parts[$i]);
        }
        $message = implode('', $message_parts);
    } else {
        $message = strtr($message, array("\n" => ''));
    }
    if (substr($message, 0, 1) == ' ') {
        $message = '&nbsp;' . substr($message, 1);
    }
    // Cleanup whitespace.
    $message = strtr($message, array('  ' => ' &nbsp;', "\r" => '', "\n" => '<br />', '<br /> ' => '<br />&nbsp;', '&#13;' => "\n"));
    /*
     * experimental hook... this could be used to support mods like footnotes, for example
     */
    HookAPI::callHook('parse_bbc_after', array(&$message, &$parse_tags, &$smileys));
    // Cache the output if it took some time...
    if (isset($cache_key, $cache_t) && array_sum(explode(' ', microtime())) - array_sum(explode(' ', $cache_t)) > 0.03) {
        //if (isset($cache_key, $cache_t)) // && array_sum(explode(' ', microtime())) - array_sum(explode(' ', $cache_t)) > 0.05)
        CacheAPI::putCache($cache_key, $message, 1200);
    }
    // If this was a force parse revert if needed.
    if (!empty($parse_tags)) {
        if (empty($temp_bbc)) {
            $bbc_codes = array();
        } else {
            $bbc_codes = $temp_bbc;
            unset($temp_bbc);
        }
    }
    return $message;
}