<?php
/*======================================================================*\
|| #################################################################### ||
|| # vBulletin Hack Management System                                 # ||
|| # ---------------------------------------------------------------- # ||
|| # All PHP code in this file is ©2004 GameCrash.org.                # ||
|| # http://www.gamecrash.org (soon) | http://www.letzplay.de         # ||
|| # ---------------------------------------------------------------- # ||
|| # This file may not be redistributed in whole or significant part. # ||
|| # ---------------- VBULLETIN IS NOT FREE SOFTWARE ---------------- # ||
|| # http://www.vbulletin.com | http://www.vbulletin.com/license.html # ||
|| #################################################################### ||
\*======================================================================*/

// #################### vbhms_print_xml_error ####################
// This is a wrapper for the vbhms_print_fatal_error function
function vbhms_print_xml_error($errormessage='Unknown Error')
{
	global $_xmltemp;
	
	vbhms_print_fatal_error("Could not read the xml file '{$_xmltemp['filename']}'.<br />$errormessage");
}

// #################### vbhms_import_infofile ####################
// Read the hack information file
function vbhms_import_infofile($hackname)
{
	global $_xmltemp;
	
	// Clear temp variable
	$_xmltemp = array();
	
	// Cache the xml file name for error messages
	$_xmltemp['filename'] = "./hacks/$hackname/info.xml";
	
	if (!is_readable($_xmltemp['filename']))
	{
		vbhms_print_xml_error('Please make sure it exists and set the correct permissions.');
	}
	
	// create parser
	$parser = xml_parser_create();

	// set parser options
	xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
	xml_set_element_handler($parser, 'vbhms_import_infofile_xmlopen', 'vbhms_import_infofile_xmlclose');
	xml_set_character_data_handler($parser, 'vbhms_import_infofile_xmldata');

	// parse the XML
	if (!@xml_parse($parser, file_read($_xmltemp['filename'])))
	{
		$errorcode = xml_get_error_code($parser);
		vbhms_print_xml_error(xml_error_string($errorcode) . " (errorcode $errorcode)");
	}

	// free the parser
	xml_parser_free($parser);
	
	// Give the data back
	return $_xmltemp['content'];
}

// #################### vbhms_import_infofile_xmlopen ####################
// Called when opening a new element
function vbhms_import_infofile_xmlopen($parser, $name, $attrs)
{
	global $_xmltemp;
	
	// Increment the deepth counter
	$_xmltemp['deepth']++;
	
	// If this is the base element, find out if we have a correct file
	if ($_xmltemp['deepth'] == 1)
	{
		if ($name != 'hackinfo')
		{
			vbhms_print_xml_error('Hack information files to have the base element \'hackinfo\'.');
		}
		return;
	}
	
	if ($_xmltemp['deepth'] != 2)
	{
		return;
	}
	
	$_xmltemp['currentelement'] = array();
	$_xmltemp['currentelement']['name'] = $name;
	if ($name == 'updatefile')
	{
		$_xmltemp['currentelement']['attributes'] = vbhms_import_xmlfile_attributes(array('fromversion' => array('musthave' => true, 'possiblevalues' => 'STR_NOHTML'), 'toversion' => array('musthave' => true, 'possiblevalues' => 'STR_NOHTML')), $attrs);
	}
	if ($name == 'infolink')
	{
		$_xmltemp['currentelement']['attributes'] = vbhms_import_xmlfile_attributes(array('title' => array('musthave' => true, 'possiblevalues' => 'STR_NOHTML')), $attrs);
	}
}

// #################### vbhms_import_infofile_xmldata ####################
// Called when the element has data
function vbhms_import_infofile_xmldata($parser, $data)
{
	global $_xmltemp;
	
	if ($_xmltemp['deepth'] == 2)
	{
		$_xmltemp['currentelement']['data'] .= $data;
	}
}

// #################### vbhms_import_infofile_xmlclose ####################
// Called when closing an element
function vbhms_import_infofile_xmlclose($parser, $name)
{
	global $_xmltemp;
	
	if ($_xmltemp['deepth'] == 2)
	{
		if (!$_xmltemp['currentelement']['data'] = vbhms_import_xmlfile_verifydata('STR_NOHTML', $_xmltemp['currentelement']['data']))
		{
			vbhms_print_xml_error("Incorrect data for element '{$_xmltemp['currentelement']['name']}'.");
		}
		
		if ($_xmltemp['currentelement']['name'] == 'updatefile')
		{
			$_xmltemp['content'][$_xmltemp['currentelement']['name']][$_xmltemp['currentelement']['attributes']['fromversion']] = array
			(
				'data' => $_xmltemp['currentelement']['data'],
				'attrs' => $_xmltemp['currentelement']['attributes']
			);
		}
		elseif ($_xmltemp['currentelement']['name'] == 'infolink')
		{
			$_xmltemp['content'][$_xmltemp['currentelement']['name']][] = array
			(
				'data' => $_xmltemp['currentelement']['data'],
				'attrs' => $_xmltemp['currentelement']['attributes']
			);
		}
		else
		{
			$_xmltemp['content'][$_xmltemp['currentelement']['name']] = array
			(
				'data' => $_xmltemp['currentelement']['data'],
				'attrs' => $_xmltemp['currentelement']['attributes']
			);
		}
	}
	
	// Decrement the deepth counter
	$_xmltemp['deepth']--;
}

// #################### vbhms_import_instructionfile ####################
// Read any xml file using the schema declared in $xmlschema
function vbhms_import_instructionfile($xmlfile, $schema)
{
	global $_xmltemp, $xmlschema;
	
	// Clear temp variable
	$_xmltemp = array();
	
	// Cache the xml file name for error messages
	$_xmltemp['filename'] = $xmlfile;
	
	if (!is_readable($xmlfile))
	{
		vbhms_print_xml_error('Please make sure it exists and set the correct permissions.');
	}
	
	if (!isset($xmlschema["$schema"]))
	{
		vbhms_print_xml_error("Trying to read an xml file using an not existent schema '$schema'.");
	}
	$_xmltemp['schema'] = $xmlschema["$schema"];
	
	// create parser
	$parser = xml_parser_create();

	// set parser options
	xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
	xml_set_element_handler($parser, 'vbhms_import_instructionfile_xmlopen', 'vbhms_import_instructionfile_xmlclose');
	xml_set_character_data_handler($parser, 'vbhms_import_instructionfile_xmldata');

	// parse the XML
	if (!@xml_parse($parser, file_read($xmlfile)))
	{
		$errorcode = xml_get_error_code($parser);
		vbhms_print_xml_error(xml_error_string($errorcode) . " (errorcode $errorcode)");
	}

	// free the parser
	xml_parser_free($parser);
	
	// Give the data back
	return $_xmltemp['content'];
}

// #################### vbhms_import_instructionfile_xmlopen ####################
// Called when opening a new element
function vbhms_import_instructionfile_xmlopen($parser, $name, $attrs)
{
	global $_xmltemp;
	
	// Increment the deepth counter
	$_xmltemp['deepth']++;
	
	if ($_xmltemp['deepth'] < 1)
	{
		return;
	}
	
	// If this is the base element, find out if we have a correct file
	if ($_xmltemp['deepth'] == 1)
	{
		if ($name != 'hackinfo')
		{
			vbhms_print_xml_error('Hack instruction files need to have the base element \'hackinfo\'.');
		}
		return;
	}
	
	if ($_xmltemp['deepth'] >= 5)
	{
		vbhms_print_xml_error('vBulletin Hack Management System does currently not support of deepth 5.');
	}
	
	if ($_xmltemp['deepth'] == 2)
	{
		if ($name != 'step')
		{
			vbhms_print_xml_error('Elements of deepth 2 must always be \'step\'.');
		}
		if (!isset($attrs['title']))
		{
			vbhms_print_xml_error('Every step must have a title attribute.');
		}
		$_xmltemp['currentstep']++;
		$_xmltemp['content'][$_xmltemp['currentstep']]['title'] = htmlspecialchars_uni($attrs['title']);
	}
	
	if ($_xmltemp['deepth'] == 3)
	{
		if (!is_array($_xmltemp['schema']['elements'][$name]))
		{
			vbhms_print_xml_error("Unknown element $name.");
		}
		$_xmltemp['currentelement'] = array();
		$_xmltemp['currentelement']['name'] = $name;
		$_xmltemp['currentelement']['info'] = $_xmltemp['schema']['elements'][$name];
		$_xmltemp['currentelement']['attributes'] = vbhms_import_xmlfile_attributes($_xmltemp['currentelement']['info']['attributes'], $attrs);
	}
	
	if ($_xmltemp['deepth'] == 4)
	{
		if (!is_array($_xmltemp['schema']['elements'][$_xmltemp['currentelement']['name']]['subelements'][$name]))
		{
			vbhms_print_xml_error("Unknown subelement $name for element {$_xmltemp['currentelement']['name']}.");
		}
		$_xmltemp['currentsubelement'] = array();
		$_xmltemp['currentsubelement']['name'] = $name;
		$_xmltemp['currentsubelement']['info'] = $_xmltemp['schema']['elements'][$_xmltemp['currentelement']['name']]['subelements'][$name];
		$_xmltemp['currentsubelement']['attributes'] = vbhms_import_xmlfile_attributes($_xmltemp['currentsubelement']['info']['attributes'], $attrs);
	}
}

// #################### vbhms_import_instructionfile_xmldata ####################
// Called when the element has data
function vbhms_import_instructionfile_xmldata($parser, $data)
{
	global $_xmltemp;
	
	if ($_xmltemp['deepth'] == 3)
	{
		$_xmltemp['currentelement']['data'] .= $data;
	}
	
	if ($_xmltemp['deepth'] == 4)
	{
		$_xmltemp['currentsubelement']['data'] .= $data;
	}
}

// #################### vbhms_import_instructionfile_xmlclose ####################
// Called when closing an element
function vbhms_import_instructionfile_xmlclose($parser, $name)
{
	global $_xmltemp;
	
	if ($_xmltemp['deepth'] == 3)
	{
		if ($_xmltemp['currentelement']['info']['possibledata'] == 'NONE')
		{
			$_xmltemp['currentelement']['data'] = trim($_xmltemp['currentelement']['data']);
			if (!empty($_xmltemp['currentelement']['data']))
			{
				vbhms_print_xml_error("The element '{$_xmltemp['currentelement']['name']}' can't have data.");
			}
		}
		else
		{
			$_xmltemp['currentelement']['data'] = vbhms_import_xmlfile_verifydata($_xmltemp['currentelement']['info']['possibledata'], $_xmltemp['currentelement']['data']);
			if ($_xmltemp['currentelement']['data'] === false)
			{
				vbhms_print_xml_error("Incorrect data for element '{$_xmltemp['currentelement']['name']}'.");
			}
		}
		
		while (list($key, $val) = each ($_xmltemp['currentelement']['info']['subelements']))
		{
			if ($val['musthave'])
			{
				$elementexists = false;
				for ($i = 0; $i < count($_xmltemp['currentelement']['subelements']); $i++)
				{
					if ($_xmltemp['currentelement']['subelements'][$i]['do'] == $key)
					{
						$elementexists = true;
					}
				}
				if (!$elementexists)
				{
					vbhms_print_xml_error("The element '{$_xmltemp['currentelement']['name']}' must have a subelement '$key'.");
				}
			}
		}
		
		$_xmltemp['content'][$_xmltemp['currentstep']]['actions'][] = array
		(
			'do' => $_xmltemp['currentelement']['name'],
			'data' => $_xmltemp['currentelement']['data'],
			'attrs' => $_xmltemp['currentelement']['attributes'],
			'subelements' => $_xmltemp['currentelement']['subelements']
		);
	}
	
	if ($_xmltemp['deepth'] == 4)
	{
		if ($_xmltemp['currentsubelement']['info']['possibledata'] == 'NONE')
		{
			$_xmltemp['currentsubelement']['data'] = trim($_xmltemp['currentsubelement']['data']);
			if (!empty($_xmltemp['currentsubelement']['data']))
			{
				vbhms_print_xml_error("The element '{$_xmltemp['currentsubelement']['name']}' can't have data.");
			}
		}
		else
		{
			$_xmltemp['currentsubelement']['data'] = vbhms_import_xmlfile_verifydata($_xmltemp['currentsubelement']['info']['possibledata'], $_xmltemp['currentsubelement']['data']);
			if ($_xmltemp['currentsubelement']['data'] === false)
			{
				vbhms_print_xml_error("Incorrect data for element '{$_xmltemp['currentsubelement']['name']}'.");
			}
		}
		
		$_xmltemp['currentelement']['subelements'][] = array
		(
			'do' => $_xmltemp['currentsubelement']['name'],
			'data' => $_xmltemp['currentsubelement']['data'],
			'attrs' => $_xmltemp['currentsubelement']['attributes']
		);
	}
	
	// Decrement the deepth counter
	$_xmltemp['deepth']--;
}

// #################### vbhms_import_xmlfile_attributes ####################
// Extract the attributes of an element
function vbhms_import_xmlfile_attributes($schema, $attrs)
{
	global $_xmltemp;
	
	if (!is_array($schema))
	{
		return null;
	}
	
	// for errormessages
	$elementname = $_xmltemp['currentelement']['name'];
	if ($_xmltemp['deepth'] == 4)
	{
		$elementname = $_xmltemp['currentsubelement']['name'];
	}
	
	$temp = array();
	
	while (list($key, $val) = each($schema))
	{
		if (!isset($attrs[$key]))
		{
			if ($val['musthave'])
			{
				vbhms_print_xml_error("The element '$elementname' must have the attribute '$key'.");
			}
			continue;
		}
		
		$temp[$key] = vbhms_import_xmlfile_verifydata($val['possiblevalues'], $attrs[$key]);
		if ($temp[$key] === false)
		{
			vbhms_print_xml_error("Incorrect data for attribute '$key'.");
		}
		
		unset($attrs[$key]);
	}
	
	if (count($attrs) > 0)
	{
		unset($key);
		while (list($key, $val) = each($attrs))
		{
			if (!empty($key))
			{
				vbhms_print_xml_error("Unknown attribute '$key' for element '$elementname'.");
			}
		}
	}
	
	return $temp;
}

// #################### vbhms_import_xmlfile_verifydata ####################
// Verify the content of cdata elements and attributes
function vbhms_import_xmlfile_verifydata($possiblevalues, $value)
{
	global $_xmltemp;
	
	$temp = false;
	
	if (is_array($possiblevalues))
	{
		foreach($possiblevalues as $possiblevalue)
			{
			if ($possiblevalue == $value)
			{
				$temp = $value;
			}
		}
	}
	else
	{
		switch ($possiblevalues)
		{
			case 'INT':
				if (intval($value) == $value)
				{
					$temp = $value;
				}
				break;
			case 'STR_NOHTML':
				$temp = htmlspecialchars_uni(trim($value));
				break;
			case 'STR':
				$temp = trim($value);
				break;
			default:
				$temp = $value;
		}
	}
	
	return $temp;
}

?>