/**
* Processes a raw template for conditionals, phrases etc into PHP code for eval()
*
* @param	string	Template
*
* @return	string
*/
function compile_template($template, &$errors = array())
{
	$orig_template = $template;


	$template = preg_replace('#[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]#', '', $template);
	$new_syntax = (strpos($template, '<vb:') !== false OR strpos($template, '{vb:') !== false);
	$old_syntax = (strpos($template, '<if') !== false OR strpos($template, '<phrase') !== false);
	$maybe_old_syntax = preg_match('/(^|[^{])\$[a-z0-9_]+\[?/si', $template);

	if (!$new_syntax AND ($old_syntax OR $maybe_old_syntax))
	{
		$template = addslashes($template);
		$template = process_template_conditionals($template);
		$template = process_template_phrases('phrase', $template, 'parse_phrase_tag');
		$template = process_seo_urls($template);

		if (!function_exists('replace_template_variables') OR !function_exists('validate_string_for_interpolation'))
		{
			require_once(DIR . '/includes/functions_misc.php');
		}

		//only check the old style syntax, the new style doesn't use string interpolation and isn't affected
		//by this exploit.  The new syntax doesn't 100% pass this check.
		if(!validate_string_for_interpolation($template))
		{
			global $vbphrase;
			echo "<p>&nbsp;</p><p>&nbsp;</p>";
			print_form_header('', '', 0, 1, '', '65%');
			print_table_header($vbphrase['vbulletin_message']);
			print_description_row($vbphrase['template_text_not_safe']);
			print_table_footer(2, construct_button_code($vbphrase['go_back'], 'javascript:history.back(1)'));
			print_cp_footer();
			exit;
		}


		$template = replace_template_variables($template, false);

		$template = str_replace('\\\\$', '\\$', $template);

		if (function_exists('token_get_all'))
		{
			$tokens = @token_get_all('<?php $var = "' . $template . '"; ?>');

			foreach ($tokens AS $token)
			{
				if (is_array($token))
				{
					switch ($token[0])
					{
						case T_INCLUDE:
						case T_INCLUDE_ONCE:
						case T_REQUIRE:
						case T_REQUIRE_ONCE:
						{
							global $vbphrase;
							echo "<p>&nbsp;</p><p>&nbsp;</p>";
							print_form_header('', '', 0, 1, '', '65%');
							print_table_header($vbphrase['vbulletin_message']);
							print_description_row($vbphrase['file_inclusion_not_permitted']);
							print_table_footer(2, construct_button_code($vbphrase['go_back'], 'javascript:history.back(1)'));
							print_cp_footer();
							exit;
						}
					}
				}
			}
		}
	}
	else
	{
		require_once(DIR . '/includes/class_template_parser.php');
		$parser = new vB_TemplateParser($orig_template);

		try
		{
			$parser->validate($errors);
		}
		catch (vB_Exception_TemplateFatalError $e)
		{
			global $vbphrase;
			echo "<p>&nbsp;</p><p>&nbsp;</p>";
			print_form_header('', '', 0, 1, '', '65%');
			print_table_header($vbphrase['vbulletin_message']);
			print_description_row($vbphrase[$e->getMessage()]);
			print_table_footer(2, construct_button_code($vbphrase['go_back'], 'javascript:history.back(1)'));
			print_cp_footer();
			exit;
		}

		$template = $parser->compile();

		// TODO: Reimplement these - if done, $session[], $bbuserinfo[], $vboptions will parse in the template without using {vb:raw, which isn't what we
		// necessarily want to happen
		/*
		if (!function_exists('replace_template_variables'))
		{
			require_once(DIR . '/includes/functions_misc.php');
		}
		$template = replace_template_variables($template, false);
		*/
	}

	if (function_exists('verify_demo_template'))
	{
		verify_demo_template($template);
	}

	($hook = vBulletinHook::fetch_hook('template_compile')) ? eval($hook) : false;

	return $template;
}
示例#2
0
/**
* Processes a raw template for conditionals, phrases etc into PHP code for eval()
*
* @param	string	Template
*
* @return	string
*/
function compile_template($template)
{
    $orig_template = $template;
    $template = addslashes($template);
    $template = process_template_conditionals($template);
    $template = process_template_phrases('phrase', $template, 'parse_phrase_tag');
    if (!function_exists('replace_template_variables')) {
        require_once DIR . '/includes/functions_misc.php';
    }
    $template = replace_template_variables($template, false);
    ($hook = vBulletinHook::fetch_hook('template_compile')) ? eval($hook) : false;
    $template = str_replace('\\\\$', '\\$', $template);
    if (function_exists('token_get_all')) {
        $tokens = @token_get_all('<?php $var = "' . $template . '"; ?>');
        foreach ($tokens as $token) {
            if (is_array($token)) {
                switch ($token[0]) {
                    case T_INCLUDE:
                    case T_INCLUDE_ONCE:
                    case T_REQUIRE:
                    case T_REQUIRE_ONCE:
                        global $vbphrase;
                        echo "<p>&nbsp;</p><p>&nbsp;</p>";
                        print_form_header('', '', 0, 1, '', '65%');
                        print_table_header($vbphrase['vbulletin_message']);
                        print_description_row($vbphrase['file_inclusion_not_permitted']);
                        print_table_footer(2, construct_button_code($vbphrase['go_back'], 'javascript:history.back(1)'));
                        print_cp_footer();
                        exit;
                }
            }
        }
    }
    if (function_exists('verify_demo_template')) {
        verify_demo_template($template);
    }
    return $template;
}
function process_template_conditionals($template_cond)
{
    global $logger;
    $if_lookfor = '<if condition=';
    $if_location = -1;
    $if_end_lookfor = '</if>';
    $if_end_location = -1;
    $else_lookfor = '<else />';
    $else_location = -1;
    $condition_value = '';
    $true_value = '';
    $false_value = '';
    static $safe_functions;
    if (!is_array($safe_functions)) {
        $safe_functions = array(0 => 'and', 1 => 'or', 2 => 'xor', 'in_array', 'is_null', 'is_array', 'is_numeric', 'isset', 'empty', 'defined', 'number_format');
    }
    // #############################################################################
    while (1) {
        $condition_end = 0;
        $strlen = strlen($template_cond);
        $if_location = strpos($template_cond, $if_lookfor, $if_end_location + 1);
        // look for opening <if>
        if ($if_location === false) {
            // conditional started not found
            break;
        }
        $condition_start = $if_location + strlen($if_lookfor) + 2;
        // the beginning of the conditional
        $delimiter = $template_cond[$condition_start - 1];
        if ($delimiter != '"' and $delimiter != '\'') {
            // ensure the conditional is surrounded by a valid character
            $if_end_location = $if_location + 1;
            continue;
        }
        $if_end_location = strpos($template_cond, $if_end_lookfor, $condition_start + 3);
        // location of conditional terminator
        if ($if_end_location === false) {
            // move this code above the rest, if no end condition is found then the code below would get stuck
            return false;
            // no </if> found -- return the original template
        }
        for ($i = $condition_start; $i < $strlen; $i++) {
            // find the end of the conditional
            if ($template_cond["{$i}"] == $delimiter and $template_cond[$i - 2] != '\\' and $template_cond[$i + 1] == '>') {
                // this char is delimiter and not preceded by backslash
                $condition_end = $i - 1;
                break;
            }
        }
        if (!$condition_end) {
            // couldn't find an end to the condition, so don't even parse the template anymore
            return false;
        }
        $condition_value = substr($template_cond, $condition_start, $condition_end - $condition_start);
        if (empty($condition_value)) {
            // something went wrong
            $if_end_location = $if_location + 1;
            continue;
        } else {
            if (preg_match_all('#([a-z0-9_{}$>-]+)(\\s|/\\*.*\\*/|(\\#|//)[^\\r\\n]*(\\r|\\n))*\\(#si', $condition_value, $matches)) {
                $functions = array();
                foreach ($matches[1] as $key => $match) {
                    if (!in_array(strtolower($match), $safe_functions)) {
                        $funcpos = strpos($condition_value, $matches[0]["{$key}"]);
                        $functions[] = array('func' => stripslashes($match), 'usage' => substr($condition_value, $funcpos, strpos($condition_value, ')', $funcpos) - $funcpos + 1));
                    }
                }
                if (!empty($functions)) {
                    unset($safe_functions[0], $safe_functions[1], $safe_functions[2]);
                    $logger->log('process_template_conditionals', "You've used some 'unsafe' functions.");
                }
            }
        }
        if ($template_cond[$condition_end + 2] != '>') {
            // the > doesn't come right after the condition must be malformed
            $if_end_location = $if_location + 1;
            continue;
        }
        // look for recursive case in the if block -- need to do this so the correct </if> is looked at
        $recursive_if_loc = $if_location;
        while (1) {
            $recursive_if_loc = strpos($template_cond, $if_lookfor, $recursive_if_loc + 1);
            // find an if case
            if ($recursive_if_loc === false or $recursive_if_loc >= $if_end_location) {
                //not found or out of bounds
                break;
            }
            // the bump first level's recursion back one </if> at a time
            $recursive_if_end_loc = $if_end_location;
            $if_end_location = strpos($template_cond, $if_end_lookfor, $recursive_if_end_loc + 1);
            if ($if_end_location === false) {
                return false;
                // no </if> found -- return the original template
            }
        }
        $else_location = strpos($template_cond, $else_lookfor, $condition_end + 3);
        // location of false portion
        // this is needed to correctly identify the <else /> tag associated with the outermost level
        while (1) {
            if ($else_location === false or $else_location >= $if_end_location) {
                // else isn't found/in a valid area
                $else_location = -1;
                break;
            }
            $temp = substr($template_cond, $condition_end + 3, $else_location - $condition_end + 3);
            $opened_if = substr_count($temp, $if_lookfor);
            // <if> tags opened between the outermost <if> and the <else />
            $closed_if = substr_count($temp, $if_end_lookfor);
            // <if> tags closed under same conditions
            if ($opened_if == $closed_if) {
                // if this is true, we're back to the outermost level
                // and this is the correct else
                break;
            } else {
                // keep looking for correct else case
                $else_location = strpos($template_cond, $else_lookfor, $else_location + 1);
            }
        }
        if ($else_location == -1) {
            // no else clause
            $read_length = $if_end_location - strlen($if_end_lookfor) + 1 - $condition_end + 1;
            // number of chars to read
            $true_value = substr($template_cond, $condition_end + 3, $read_length);
            // the true portion
            $false_value = '';
        } else {
            $read_length = $else_location - $condition_end - 3;
            // number of chars to read
            $true_value = substr($template_cond, $condition_end + 3, $read_length);
            // the true portion
            $read_length = $if_end_location - strlen($if_end_lookfor) - $else_location - 3;
            // number of chars to read
            $false_value = substr($template_cond, $else_location + strlen($else_lookfor), $read_length);
            // the false portion
        }
        if (strpos($true_value, $if_lookfor) !== false) {
            $true_value = process_template_conditionals($true_value);
        }
        if (strpos($false_value, $if_lookfor) !== false) {
            $false_value = process_template_conditionals($false_value);
        }
        // clean up the extra slashes
        $str_find = array('\\"', '\\\\');
        $str_replace = array('"', '\\');
        if ($delimiter == "'") {
            $str_find[] = "\\'";
            $str_replace[] = "'";
        }
        $str_find[] = '\\$delimiter';
        $str_replace[] = $delimiter;
        $condition_value = str_replace($str_find, $str_replace, $condition_value);
        $conditional = "\".(({$condition_value}) ? (\"{$true_value}\") : (\"{$false_value}\")).\"";
        $template_cond = substr_replace($template_cond, $conditional, $if_location, $if_end_location + strlen($if_end_lookfor) - $if_location);
        $if_end_location = $if_location + strlen($conditional) - 1;
        // adjust searching position for the replacement above
    }
    $template_cond = str_replace("\\'", "'", $template_cond);
    return $template_cond;
}