<?php
// translator ready
// addnews ready
// mail ready

require_once("lib/arraytourl.php");

$injected_modules = array(1=>array(),0=>array());

function injectmodule($modulename,$force=false){
	global $mostrecentmodule,$injected_modules;
	//try to circumvent the array_key_exists() problem we've been having.
	if ($force) $force = 1; else $force = 0;

	//early escape if we already called injectmodule this hit with the
	//same args.
	if (isset($injected_modules[$force][$modulename])) {
		$mostrecentmodule=$modulename;
		return $injected_modules[$force][$modulename];
	}

	$modulename = modulename_sanitize($modulename);
	$modulefilename = "modules/{$modulename}.php";
	if (file_exists($modulefilename)){
		tlschema("module-{$modulename}");
		$sql = "SELECT active,filemoddate,infokeys,version FROM " . db_prefix("modules") . " WHERE modulename='$modulename'";
		$result = db_query_cached($sql, "inject-$modulename", 3600);
		if (!$force) {
			//our chance to abort if this module isn't currently installed
			//or doesn't meet the prerequisites.
			if (db_num_rows($result)==0) {
				tlschema();
			 	output_notl("`n`3Module `#%s`3 is not installed, but was attempted to be injected.`n",$modulename);
				$injected_modules[$force][$modulename]=false;
				return false;
			}
			$row = db_fetch_assoc($result);
			if ($row['active']){ } else {
				tlschema();
			 	output("`n`3Module `#%s`3 is not active, but was attempted to be injected.`n",$modulename);
				$injected_modules[$force][$modulename]=false;
				return false;
			}
		}
		require_once($modulefilename);
		$mostrecentmodule = $modulename;
		$info = "";
		if (!$force){
			//avoid calling the function if we're forcing the module
			$fname = $modulename."_getmoduleinfo";
			$info = $fname();
			if (!isset($info['requires'])) $info['requires'] = array();
			if (!is_array($info['requires'])) $info['requires'] = array();
			if (!isset($info['download'])) $info['download']="";
			if (!isset($info['description'])) $info['description']="";
			if (!module_check_requirements($info['requires'])) {
				$injected_modules[$force][$modulename]=false;
				tlschema();
				output("`n`3Module `#%s`3 does not meet its prerequisites.`n",$modulename);
				return false;
			}
		}
		//check to see if the module needs to be upgraded.
		if (db_num_rows($result)>0){
			if (!isset($row)) $row = db_fetch_assoc($result);
			$filemoddate = date("Y-m-d H:i:s",filemtime($modulefilename));
			if ($row['filemoddate']!=$filemoddate || $row['infokeys']=="" ||
					$row['infokeys'][0] != '|' || $row['version']==''){
				//The file has recently been modified, lock tables and
				//check again (knowing we're the only one who can do this
				//at one shot)
				$sql = "LOCK TABLES " . db_prefix("modules") . " WRITE";
				db_query($sql);
				//check again after the table has been locked.
				$sql = "SELECT filemoddate FROM " . db_prefix("modules") . " WHERE modulename='$modulename'";
				$result = db_query($sql);
				$row = db_fetch_assoc($result);
				if ($row['filemoddate']!=$filemoddate ||
						!isset($row['infokeys']) || $row['infokeys']=="" || $row['infokeys'][0] != '|' ||
						$row['version']==''){
					//the file mod time is still different from that
					//recorded in the database, time to update the database
					//and upgrade the module.
					debug("The module $modulename was found to have updated, upgrading the module now.");
					if (!is_array($info)){
						//we might have gotten this info above, if not,
						//we need it now.
						$fname = $modulename."_getmoduleinfo";
						$info = $fname();
						if (!isset($info['download']))
							$info['download']="";
						if (!isset($info['version']))
							$info['version']="0.0";
						if (!isset($info['description']))
							$info['description'] = '';
					}
					//Everyone else will block at the initial lock tables,
					//we'll update, and on their second check, they'll fail.
					//Only we will update the table.

					$keys = "|".join(array_keys($info), "|")."|";

					$sql = "UPDATE ". db_prefix("modules") . " SET moduleauthor='".addslashes($info['author'])."', category='".addslashes($info['category'])."', formalname='".addslashes($info['name'])."', description='".addslashes($info['description'])."', filemoddate='$filemoddate', infokeys='$keys',version='".addslashes($info['version'])."',download='".addslashes($info['download'])."' WHERE modulename='$modulename'";
					db_query($sql);
					debug($sql);
					$sql = "UNLOCK TABLES";
					db_query($sql);
					// Remove any old hooks (install will reset them)
					module_wipehooks();
					$fname = $modulename."_install";
					if ($fname() === false) {
						return false;
					}
					invalidatedatacache("inject-$modulename");

				}else{
					$sql = "UNLOCK TABLES";
					db_query($sql);
				}
			}
		}
		tlschema();
		$injected_modules[$force][$modulename]=true;
		return true;
	}else{
	 	output("`n`\$Module `^%s`\$ was not found in the modules directory.`n",$modulename);
		$injected_modules[$force][$modulename]=false;
		return false;
	}
}

/*
 * Returns the status of a module as a bitfield
 *
 * @param string $modulename The module name
 * @param string $version The version to check for (false for don't care)
 * @return int The status codes for the module
 */
function module_status($modulename, $version=false) {
	global $injected_modules;

	$modulename = modulename_sanitize($modulename);
	$modulefilename = "modules/$modulename.php";
	$status = MODULE_NO_INFO;
	if (file_exists($modulefilename)) {
		$sql = "SELECT active,filemoddate,infokeys,version FROM " . db_prefix("modules") . " WHERE modulename='$modulename'";
		$result = db_query_cached($sql, "inject-$modulename", 3600);
		if (db_num_rows($result) > 0) {
			// The module is installed
			$status = MODULE_INSTALLED;
			$row = db_fetch_assoc($result);
			if ($row['active']) {
				// Module is here and active
				$status |= MODULE_ACTIVE;
				// In this case, the module could have been force injected or
				// not.  We still want to mark it either way.
				if (array_key_exists($modulename, $injected_modules[0]) &&
						$injected_modules[0][$modulename])
					$status |= MODULE_INJECTED;
				if (array_key_exists($modulename, $injected_modules[1]) &&
						$injected_modules[1][$modulename])
					$status |= MODULE_INJECTED;
			} else {
				// Force-injected modules can be injected but not active.
				if (array_key_exists($modulename, $injected_modules[1]) &&
						$injected_modules[1][$modulename])
					$status |= MODULE_INJECTED;
			}
			// Check the version number
			if ($version===false) {
				$status |= MODULE_VERSION_OK;
			} else {
				if (module_compare_versions($row['version'], $version) < 0) {
					$status |= MODULE_VERSION_TOO_LOW;
				} else {
					$status |= MODULE_VERSION_OK;
				}
			}
		} else {
			// The module isn't installed
			$status = MODULE_NOT_INSTALLED;
		}
	} else {
		// The module file doesn't exist.
		$status = MODULE_FILE_NOT_PRESENT;
	}
	return $status;
}


/**
 * Determines if a module is activated
 *
 * @param string $modulename The module name
 * @return bool If the module is active or not
 */
function is_module_active($modulename){
	return (module_status($modulename) & MODULE_ACTIVE);
}

/**
 * Determines if a module is installed
 *
 * @param string $modulename The module name
 * @param string $version The version to check for
 * @return bool If the module is installed
 */
function is_module_installed($modulename,$version=false){
	// Status will say the version is okay if we don't care about the
	// version or if the version is actually correct
	return (module_status($modulename, $version) &
			(MODULE_INSTALLED|MODULE_VERSION_OK));
}

/**
 * Checks if the module requirements are satisfied.  Should a module require
 * other modules to be installed and active, then optionally makes them so
 *
 * @param array $reqs Requirements of a module from _getmoduleinfo()
 * @return bool If successful or not
 */
function module_check_requirements($reqs, $forceinject=false){
	// Since we can inject here, we need to save off the module we're on
	global $mostrecentmodule;

	$oldmodule = $mostrecentmodule;
	$result = true;

	if (!is_array($reqs)) return false;

	// Check the requirements.
	reset($reqs);
	while (list($key,$val)=each($reqs)){
		$info = explode("|",$val);
		if (!is_module_installed($key,$info[0])) {
			return false;
		}
		// This is actually cheap since we cache the result
		$status = module_status($key);
		// If it's not injected and we should force it, do so.
		if (!($status & MODULE_INJECTED) && $forceinject) {
			$result = $result && injectmodule($key);
		}
	}

	$mostrecentmodule = $oldmodule;
	return $result;
}

/**
 * Blocks a module from being able to hook for the rest of the page hit.
 * Please note, any hooks already executed by the blocked module will
 * not be undone, so this function is pretty flaky all around.
 *
 * The only way to use this safely would be to block/unblock modules from
 * the everyhit hook and make sure to shortcircuit for any page other than
 * the one you care about.
 *
 * @param mixed $modulename The name of the module you wish to block or true if you want to block all modules.
 * @return void
 */
$block_all_modules = false;
$blocked_modules = array();
function blockmodule($modulename) {
	global $blocked_modules, $block_all_modules, $currenthook;

	if ($modulename === true) {
		$block_all_modules = true;
		return;
	}
	$blocked_modules[$modulename]=1;
}

/**
 * Unblocks a module from being able to hook for the rest of the page hit.
 * Please note, any hooks already blocked for the module being unblocked
 * have been lost, so this function is pretty flaky all around.
 *
 * The only way to use this safely would be to block/unblock modules from
 * the everyhit hook and make sure to shortcircuit for any page other than
 * the one you care about.
 *
 * @param mixed $modulename The name of the module you wish to unblock or true if you want to unblock all modules.
 * @return void
 */
$unblocked_modules = array();
function unblockmodule($modulename) {
	global $unblocked_modules, $block_all_modules;

	if ($modulename === true) {
		$block_all_modules = false;
		return;
	}
	$unblocked_modules[$modulename]=1;
}

$module_preload = array();
/**
 * Preloads data for multiple modules in one shot rather than
 * having to make SQL calls for each hook, when many of the hooks
 * are found on every page.
 * @param array $hooknames Names of hooks whose attached modules should be preloaded.
 * @return bool Success
 */
function mass_module_prepare($hooknames){
	sort($hooknames);
	$Pmodules = db_prefix("modules");
	$Pmodule_hooks = db_prefix("module_hooks");
	$Pmodule_settings = db_prefix("module_settings");
	$Pmodule_userprefs = db_prefix("module_userprefs");

	global $modulehook_queries;
	global $module_preload;
	global $module_settings;
	global $module_prefs;
	global $session;

	//collect the modules who attach to these hooks.
	$sql =
		"SELECT
			$Pmodule_hooks.modulename,
			$Pmodule_hooks.location,
			$Pmodule_hooks.function,
			$Pmodule_hooks.whenactive
		FROM
			$Pmodule_hooks
		INNER JOIN
			$Pmodules
		ON	$Pmodules.modulename = $Pmodule_hooks.modulename
		WHERE
			active = 1
		AND	location IN ('".join("', '",$hooknames)."')
		ORDER BY
			$Pmodule_hooks.location,
			$Pmodule_hooks.priority,
			$Pmodule_hooks.modulename";
	$result = db_query_cached($sql,"moduleprepare-".md5(join($hooknames)));
	$modulenames = array();
	while ($row = db_fetch_assoc($result)){
		$modulenames[$row['modulename']] = $row['modulename'];
		if (!isset($module_preload[$row['location']])) {
			$module_preload[$row['location']] = array();
			$modulehook_queries[$row['location']] = array();
		}
		//a little black magic trickery: formatting entries in
		//$modulehook_queries the same way that db_query_cached
		//returns query results.
		array_push($modulehook_queries[$row['location']],$row);
		$module_preload[$row['location']][$row['modulename']] = $row['function'];
	}
	//SQL IN() syntax for the modules involved here.
	$modulelist = "'".join("', '",$modulenames)."'";

	//Load the settings for the modules on these hooks.
	$sql =
		"SELECT
			modulename,
			setting,
			value
		FROM
			$Pmodule_settings
		WHERE
			modulename IN ($modulelist)";
	$result = db_query($sql);
	while ($row = db_fetch_assoc($result)){
		$module_settings[$row['modulename']][$row['setting']] = $row['value'];
	}

	//Load the current user's prefs for the modules on these hooks.
	$sql =
		"SELECT
			modulename,
			setting,
			userid,
			value
		FROM
			$Pmodule_userprefs
		WHERE
			modulename IN ($modulelist)
		AND	userid = ".(int)$session['user']['acctid'];
	$result = db_query($sql);
	while ($row = db_fetch_assoc($result)){
		$module_prefs[$row['userid']][$row['modulename']][$row['setting']] = $row['value'];
	}
	return true;
}

/**
 * An event that should be triggered
 *
 * @param string $hookname The name of the event to raise
 * @param array $args Arguments that should be passed to the event handler
 * @param bool $allowinactive Allow inactive modules
 * @param bool $only Only this module?
 * @return array The args modified by the event handlers
 */
$currenthook = "";
function modulehook($hookname, $args=false, $allowinactive=false, $only=false){
	global $navsection, $mostrecentmodule;
	global $blocked_modules, $block_all_modules, $unblocked_modules;
	global $output, $session, $modulehook_queries;
	global $currenthook;
	$lasthook = $currenthook;
	$currenthook = $hookname;
	static $hookcomment = array();
	if ($args===false) $args = array();
	$active = "";
	if (!$allowinactive) $active = " ". db_prefix("modules") .".active=1 AND";
	if (!is_array($args)){
		$where = $mostrecentmodule;
		if (!$where) {
			global $SCRIPT_NAME;
			$where = $SCRIPT_NAME;
		}
		debug("Args parameter to modulehook $hookname from $where is not an array.");
	}
	if ($session['user']['superuser'] & SU_DEBUG_OUTPUT && !isset($hookcomment[$hookname])){
		rawoutput("<!--Module Hook: $hookname; allow inactive: ".($allowinactive?"true":"false")."; only this module: ".($only!==false?$only:"any module"));
		if (!is_array($args)) {
			$arg = $args . " (NOT AN ARRAY!)";
			rawoutput("  arg: $arg");
		} else {
			reset($args);
			while (list($key,$val)=each($args)){
				$arg = $key." = ";
				if (is_array($val)){
					$arg.="array(".count($val).")";
				}elseif (is_object($val)){
					$arg.="object(".get_class($val).")";
				}else{
					$arg.=htmlentities(substr($val,0,25), ENT_COMPAT, getsetting("charset", "ISO-8859-1"));
				}
				rawoutput("  arg: $arg");
			}
		}
		rawoutput("  -->");
		$hookcomment[$hookname]=true;
	}
	if (isset($modulehook_queries[$hookname]) //This data was pre fetched in mass_module_prepare
		&& $allowinactive == false //We only ever prefetch for active modules, if we're doing inactive, do the regular query.
		){
		$result = $modulehook_queries[$hookname];
	}else{
		$sql =
			"SELECT
				" . db_prefix("module_hooks") . ".modulename,
				" . db_prefix("module_hooks") . ".location,
				" . db_prefix("module_hooks") . ".function,
				" . db_prefix("module_hooks") . ".whenactive
			FROM
				" . db_prefix("module_hooks") . "
			INNER JOIN
				" . db_prefix("modules") . "
			ON	" . db_prefix("modules") . ".modulename = " . db_prefix("module_hooks") . ".modulename
			WHERE
				$active
				" . db_prefix("module_hooks") . ".location='$hookname'
			ORDER BY
				" . db_prefix("module_hooks") . ".priority,
				" . db_prefix("module_hooks") . ".modulename";
		$result = db_query_cached($sql,"hook-".$hookname);
	}
	// $args is an array passed by value and we take the output and pass it
	// back through
	// Try at least and fix up a bogus arg so it doesn't cause additional
	// problems later.
	if (!is_array($args)) {
		$args = array('bogus_args'=>$args);
	}

	// Save off the mostrecent module since having that change can change
	// behaviour especially if a module calls modulehooks itself or calls
	// library functions which cause them to be called.
	$mod = $mostrecentmodule;

	while ($row = db_fetch_assoc($result)){
		// If we are only running hooks for a specific module, skip all
		// others.
		if ($only !== false && $row['modulename']!=$only) continue;
		// Skip any module invocations which should be blocked.

		if (!array_key_exists($row['modulename'],$blocked_modules)){
			$blocked_modules[$row['modulename']] = false;
		}
		if (!array_key_exists($row['modulename'],$unblocked_modules)){
			$unblocked_modules[$row['modulename']] = false;
		}
		if (($block_all_modules || $blocked_modules[$row['modulename']]) &&
				!$unblocked_modules[$row['modulename']]) {
			continue;
		}

		if (injectmodule($row['modulename'], $allowinactive)) {
			$oldnavsection = $navsection;
			tlschema("module-{$row['modulename']}");
			// Pass the args into the function and reassign them to the
			// result of the function.
			// Note: each module gets the previous module's modified return
			// value if more than one hook here.
			// Order of operations could become an issue, modules are called
			// in alphabetical order by their module name (not display name).

			// Test the condition code
			if (!array_key_exists('whenactive',$row)) $row['whenactive'] = '';
			$cond = trim($row['whenactive']);
			if ($cond == "" || module_condition($cond) == true) {
				// call the module's hook code
				$outputbeforehook = $output;
				$output="";
/*******************************************************/
				$starttime = getmicrotime();
/*******************************************************/
				if (function_exists($row['function'])) {
					$res = $row['function']($hookname, $args);
				} else {
					trigger_error("Unknown function {$row['function']} for hoookname $hookname in module {$row['module']}.", E_USER_WARNING);
				}
/*******************************************************/
				$endtime = getmicrotime();
				if (($endtime - $starttime >= 1.00 && ($session['user']['superuser'] & SU_DEBUG_OUTPUT))){
					debug("Slow Hook (".round($endtime-$starttime,2)."s): $hookname - {$row['modulename']}`n");
				}
/*******************************************************/
				$outputafterhook = $output;
				$output=$outputbeforehook;
				// test to see if we had any output and if the module allows
				// us to collapse it
				$testout = trim(sanitize_html($outputafterhook));
				if (!is_array($res)) {
					trigger_error("<b>{$row['function']}</b> did not return an array in the module <b>{$row['modulename']}</b> for hook <b>$hookname</b>.",E_USER_WARNING);
					$res = $args;
				}
				if ($testout >"" &&
						$hookname!="collapse{" &&
						$hookname!="}collapse" &&
						$hookname!="collapse-nav{" &&
						$hookname!="}collapse-nav" &&
						!array_key_exists('nocollapse',$res)) {
					//restore the original output's reference
					modulehook("collapse{",
						array("name"=>'a-'.$row['modulename']));
					$output .= $outputafterhook;
					modulehook("}collapse");
				} else {
					$output .= $outputafterhook;
				}
				// Clear the collapse flag
				unset($res['nocollapse']);
				//handle return arguments.
				if (is_array($res)) $args = $res;
			}

			//revert the translation namespace
			tlschema();
			//revert nav section after we're done here.
			$navsection = $oldnavsection;
		}
	}

	$mostrecentmodule=$mod;
	$currenthook = $lasthook;

	// And hand them back so they can be used.
	return $args;
}

$module_settings = array();
function get_all_module_settings($module=false){
	//returns an associative array of all the settings for the given module
	global $module_settings,$mostrecentmodule;
	if ($module === false) $module = $mostrecentmodule;

	load_module_settings($module);
	return $module_settings[$module];
}

function get_module_setting($name,$module=false){
	global $module_settings,$mostrecentmodule;
	if ($module === false) $module = $mostrecentmodule;

	load_module_settings($module);
	if (isset($module_settings[$module][$name])) {
		return $module_settings[$module][$name];
	}else{
		$info = get_module_info($module);
		if (isset($info['settings'][$name])){
			if (is_array($info['settings'][$name])) {
				$v = $info['settings'][$name][0];
				$x = explode("|", $v);
			} else {
				$x = explode("|",$info['settings'][$name]);
			}
			if (isset($x[1])){
				return $x[1];
			}
		}
		return NULL;
	}
}

function set_module_setting($name,$value,$module=false){
	if ($name == "showFormTabIndex") return true;
	global $module_settings,$mostrecentmodule;
	if ($module === false) $module = $mostrecentmodule;
	load_module_settings($module);
	if (isset($module_settings[$module][$name])){
		$sql = "UPDATE " . db_prefix("module_settings") . " SET value='".addslashes($value)."' WHERE modulename='$module' AND setting='".addslashes($name)."'";
		db_query($sql);
	}else{
		$sql = "INSERT INTO " . db_prefix("module_settings") . " (modulename,setting,value) VALUES ('$module','".addslashes($name)."','".addslashes($value)."')";
		db_query($sql);
	}
	invalidatedatacache("modulesettings-$module");
	$module_settings[$module][$name] = $value;
}

function increment_module_setting($name, $value=1, $module=false){
	global $module_settings,$mostrecentmodule;
	$value = (float)$value;
	if ($module === false) $module = $mostrecentmodule;
	load_module_settings($module);
	if (isset($module_settings[$module][$name])){
		$sql = "UPDATE " . db_prefix("module_settings") . " SET value=value+$value WHERE modulename='$module' AND setting='".addslashes($name)."'";
		db_query($sql);
	}else{
		$sql = "INSERT INTO " . db_prefix("module_settings") . " (modulename,setting,value) VALUES ('$module','".addslashes($name)."','".addslashes($value)."')";
		db_query($sql);
	}
	invalidatedatacache("modulesettings-$module");
	$module_settings[$module][$name] += $value;
}

function clear_module_settings($module=false){
	global $module_settings,$mostrecentmodule;
	if ($module === false) $module = $mostrecentmodule;
	if (isset($module_settings[$module])){
		debug("Deleted module settings cache for $module.");
		unset($module_settings[$module]);
		invalidatedatacache("modulesettings-$module");
	}
}

function load_module_settings($module){
	global $module_settings;
	if (!isset($module_settings[$module])){
		$module_settings[$module] = array();
		$sql = "SELECT * FROM " . db_prefix("module_settings") . " WHERE modulename='$module'";
		$result = db_query_cached($sql,"modulesettings-$module");
		while ($row = db_fetch_assoc($result)){
			$module_settings[$module][$row['setting']] = $row['value'];
		}//end while
	}//end if
}//end function


function module_delete_objprefs($objtype, $objid)
{
	$sql = "DELETE FROM " . db_prefix("module_objprefs") . " WHERE objtype='$objtype' AND objid='$objid'";
	db_query($sql);
	massinvalidate("objpref-$objtype-$objid");
}

function get_module_objpref($type, $objid, $name, $module=false){
	global $mostrecentmodule;
	if ($module === false) $module = $mostrecentmodule;
	$sql = "SELECT value FROM ".db_prefix("module_objprefs")." WHERE modulename='$module' AND objtype='$type' AND setting='".addslashes($name)."' AND objid='$objid' ";
	$result = db_query_cached($sql, "objpref-$type-$objid-$name-$module", 86400);
	if (db_num_rows($result)>0){
		$row = db_fetch_assoc($result);
		return $row['value'];
	}
	//we couldn't find this elsewhere, load the default value if it exists.
	$info = get_module_info($module);
	if (isset($info['prefs-'.$type][$name])){
		if (is_array($info['prefs-'.$type][$name])) {
			$v = $info['prefs-'.$type][$name][0];
			$x = explode("|", $v);
		} else {
			$x = explode("|",$info['prefs-'.$type][$name]);
		}
		if (isset($x[1])){
			set_module_objpref($type,$objid,$name,$x[1],$module);
			return $x[1];
		}
	}
	return NULL;
}

function set_module_objpref($objtype,$objid,$name,$value,$module=false){
	global $mostrecentmodule;
	if ($module === false) $module = $mostrecentmodule;
	// Delete the old version and insert the new
	$sql = "REPLACE INTO " . db_prefix("module_objprefs") . "(modulename,objtype,setting,objid,value) VALUES ('$module', '$objtype', '$name', '$objid', '".addslashes($value)."')";
	db_query($sql);
	invalidatedatacache("objpref-$objtype-$objid-$name-$module");
}

function increment_module_objpref($objtype,$objid,$name,$value=1,$module=false) {
	global $mostrecentmodule;
	$value = (float)$value;
	if ($module === false) $module = $mostrecentmodule;
	$sql = "UPDATE " . db_prefix("module_objprefs") . " SET value=value+$value WHERE modulename='$module' AND setting='".addslashes($name)."' AND objtype='".addslashes($objtype)."' AND objid=$objid;";
	$result= db_query($sql);
	if (db_affected_rows($result)==0){
		//if the update did not do anything, insert the row
		$sql = "INSERT INTO " . db_prefix("module_objprefs") . "(modulename,objtype,setting,objid,value) VALUES ('$module', '$objtype', '$name', '$objid', '".addslashes($value)."')";
		db_query($sql);
	}
	invalidatedatacache("objpref-$objtype-$objid-$name-$module");
}


function module_delete_userprefs($user){
	$sql = "DELETE FROM " . db_prefix("module_userprefs") . " WHERE userid='$user'";
	db_query($sql);
}

$module_prefs=array();
function get_all_module_prefs($module=false,$user=false){
	global $module_prefs,$mostrecentmodule,$session;
	if ($module === false) $module = $mostrecentmodule;
	if ($user === false) $user = $session['user']['acctid'];
	load_module_prefs($module,$user);

	return $module_prefs[$user][$module];
}

function get_module_pref($name,$module=false,$user=false){
	global $module_prefs,$mostrecentmodule,$session;
	if ($module === false) $module = $mostrecentmodule;
	if ($user===false) {
		if(isset($session['user']['loggedin']) && $session['user']['loggedin']) $user = $session['user']['acctid'];
		else $user = 0;
	}

	if (isset($module_prefs[$user][$module][$name])) {
		return $module_prefs[$user][$module][$name];
	}

	//load here, not before
	load_module_prefs($module,$user);
	//check if *now* it's loaded
	if (isset($module_prefs[$user][$module][$name])) {
		return $module_prefs[$user][$module][$name];
	}

	if (!is_module_active($module)) return NULL;

	//we couldn't find this elsewhere, load the default value if it exists.
	$info = get_module_info($module);
	if (isset($info['prefs'][$name])){
		if (is_array($info['prefs'][$name])) {
			$v = $info['prefs'][$name][0];
			$x = explode("|", $v);
		} else {
			$x = explode("|",$info['prefs'][$name]);
		}
		if (isset($x[1])){
			set_module_pref($name,$x[1],$module,$user);
			return $x[1];
		}
	}
	return NULL;
}

function set_module_pref($name,$value,$module=false,$user=false){
	global $module_prefs,$mostrecentmodule,$session;
	if ($module === false) $module = $mostrecentmodule;
	if ($user === false) $uid=$session['user']['acctid'];
	else $uid = $user;
	load_module_prefs($module, $uid);

	//don't write to the DB if the user isn't logged in.
	if (!$session['user']['loggedin'] && !$user) {
		// We do need to save to the loaded copy here however
		$module_prefs[$uid][$module][$name] = $value;
		return;
	}

	if (isset($module_prefs[$uid][$module][$name])){
		$sql = "UPDATE " . db_prefix("module_userprefs") . " SET value='".addslashes($value)."' WHERE modulename='$module' AND setting='$name' AND userid='$uid'";
		db_query($sql);
	}else{
		$sql = "INSERT INTO " . db_prefix("module_userprefs"). " (modulename,setting,userid,value) VALUES ('$module','$name','$uid','".addslashes($value)."')";
		db_query($sql);
	}
	$module_prefs[$uid][$module][$name] = $value;
}

function increment_module_pref($name,$value=1,$module=false,$user=false){
	global $module_prefs,$mostrecentmodule,$session;
	$value = (float)$value;
	if ($module === false) $module = $mostrecentmodule;
	if ($user === false) $uid=$session['user']['acctid'];
	else $uid = $user;
	load_module_prefs($module, $uid);

	//don't write to the DB if the user isn't logged in.
	if (!$session['user']['loggedin'] && !$user) {
		// We do need to save to the loaded copy here however
		$module_prefs[$uid][$module][$name] += $value;
		return;
	}

	if (isset($module_prefs[$uid][$module][$name])){
		$sql = "UPDATE " . db_prefix("module_userprefs") . " SET value=value+$value WHERE modulename='$module' AND setting='$name' AND userid='$uid'";
		db_query($sql);
		$module_prefs[$uid][$module][$name] += $value;
	}else{
		$sql = "INSERT INTO " . db_prefix("module_userprefs"). " (modulename,setting,userid,value) VALUES ('$module','$name','$uid','".addslashes($value)."')";
		db_query($sql);
		$module_prefs[$uid][$module][$name] = $value;
	}
}

function clear_module_pref($name,$module=false,$user=false){
 	global $module_prefs,$mostrecentmodule,$session;
	if ($module === false) $module = $mostrecentmodule;
	if ($user === false) $uid=$session['user']['acctid'];
	else $uid = $user;
	load_module_prefs($module, $uid);

	//don't write to the DB if the user isn't logged in.
	if (!$session['user']['loggedin'] && !$user) {
		// We do need to trash the loaded copy here however
		unset($module_prefs[$uid][$module][$name]);
		return;
	}

	if (isset($module_prefs[$uid][$module][$name])){
		$sql = "DELETE FROM " . db_prefix("module_userprefs") . " WHERE modulename='$module' AND setting='$name' AND userid='$uid'";
		db_query($sql);
	}
	unset($module_prefs[$uid][$module][$name]);
}

function load_module_prefs($module, $user=false){
	global $module_prefs,$session;
	if ($user===false) $user = $session['user']['acctid'];
	if (!isset($module_prefs[$user])) $module_prefs[$user] = array();
	if (!isset($module_prefs[$user][$module])){
		$module_prefs[$user][$module] = array();
		$sql = "SELECT setting,value FROM " . db_prefix("module_userprefs") . " WHERE modulename='$module' AND userid='$user'";
		$result = db_query($sql);
		while ($row = db_fetch_assoc($result)){
			$module_prefs[$user][$module][$row['setting']] = $row['value'];
		}//end while
	}//end if
}//end function

function get_module_info($shortname){
	global $mostrecentmodule;

	$moduleinfo = array();

	// Save off the mostrecent module.
	$mod = $mostrecentmodule;

	if(injectmodule($shortname,true)) {
		$fname = $shortname."_getmoduleinfo";
		if (function_exists($fname)){
			tlschema("module-$shortname");
			$moduleinfo = $fname();
			tlschema();
			// Don't pick up this text unless we need it.
			if (!isset($moduleinfo['name']) ||
					!isset($moduleinfo['category']) ||
					!isset($moduleinfo['author']) ||
					!isset($moduleinfo['version'])) {
				$ns = translate_inline("Not specified","common");
			}
			if (!isset($moduleinfo['name']))
				$moduleinfo['name']="$ns ($shortname)";
			if (!isset($moduleinfo['category']))
				$moduleinfo['category']="$ns ($shortname)";
			if (!isset($moduleinfo['author']))
				$moduleinfo['author']="$ns ($shortname)";
			if (!isset($moduleinfo['version']))
				$moduleinfo['version']="0.0";
			if (!isset($moduleinfo['download']))
				$moduleinfo['download'] = "";
			if (!isset($moduleinfo['description']))
				$moduleinfo['description'] = "";
		}
		if (!is_array($moduleinfo) || count($moduleinfo)<2){
			$mf = translate_inline("Missing function","common");
			$moduleinfo = array(
					"name"=>"$mf ({$shortname}_getmoduleinfo)",
					"version"=>"0.0",
					"author"=>"$mf ({$shortname}_getmoduleinfo)",
					"category"=>"$mf ({$shortname}_getmoduleinfo)",
					"download"=>"",
				);
		}
	} else {
		// This module couldn't be injected at all.
		return array();
	}
	$mostrecentmodule = $mod;
	if (!isset($moduleinfo['requires']))
		$moduleinfo['requires'] = array();
	return $moduleinfo;
}

function module_wipehooks() {
	global $mostrecentmodule;
	//lock the module hooks table.
	$sql = "LOCK TABLES ".db_prefix("module_hooks")." WRITE";
	db_query($sql);

	//invalidate data caches for module hooks associated with this module.
	$sql = "SELECT location FROM ".db_prefix("module_hooks")." WHERE modulename='$mostrecentmodule'";
	$result = db_query($sql);
	while ($row = db_fetch_assoc($result)){
		invalidatedatacache("hook-".$row['location']);
	}
	invalidatedatacache("moduleprepare");

	debug("Removing all hooks for $mostrecentmodule");
	$sql = "DELETE FROM " . db_prefix("module_hooks"). " WHERE modulename='$mostrecentmodule'";
	db_query($sql);
	//unlock the module hooks table.
	$sql = "UNLOCK TABLES";
	db_query($sql);

	$sql = "DELETE FROM " . db_prefix("module_event_hooks") . " WHERE modulename='$mostrecentmodule'";
	db_query($sql);

}

function module_addeventhook($type, $chance){
	global $mostrecentmodule;
	debug("Adding an event hook on $type events for $mostrecentmodule");
	$sql = "DELETE FROM " . db_prefix("module_event_hooks") . " WHERE modulename='$mostrecentmodule' AND event_type='$type'";
	db_query($sql);
	$sql = "INSERT INTO " . db_prefix("module_event_hooks") . " (event_type,modulename,event_chance) VALUES ('$type', '$mostrecentmodule','".addslashes($chance)."')";
	db_query($sql);
	invalidatedatacache("event-".$type);
}

function module_drophook($hookname,$functioncall=false){
	global $mostrecentmodule;
	if ($functioncall===false)
		$functioncall=$mostrecentmodule."_dohook";
	$sql = "DELETE FROM " . db_prefix("module_hooks") . " WHERE modulename='$mostrecentmodule' AND location='".addslashes($hookname)."' AND function='".addslashes($functioncall)."'";
	db_query($sql);
	invalidatedatacache("hook-".$hookname);
	invalidatedatacache("moduleprepare");
}

/**
  * Called by modules to register themselves for a game module hook point, with default priority.
  * Modules with identical priorities will execute alphabetically.  Modules can only have one hook on a given hook name,
  * even if they call this function multiple times, unless they specify different values for the functioncall argument.
  *
  * @param string $hookname The hook to receive a notification for
  * @param string $functioncall The function that should be called, if not specified, use {modulename}_dohook() as the function
  * @param string $whenactive An expression that should be evaluated before triggering the event, if not specified, none.
   */
function module_addhook($hookname,$functioncall=false,$whenactive=false){
	module_addhook_priority($hookname,50,$functioncall,$whenactive);
}

/**
  * Called by modules to register themselves for a game module hook point, with a given priority -- lower numbers execute first.
  * Modules with identical priorities will execute alphabetically.  Modules can only have one hook on a given hook name,
  * even if they call this function multiple times, unless they specify different values for the functioncall argument.
  *
  * @param string $hookname The hook to receive a notification for
  * @param integer $priority The priority for this hooking -- lower numbers execute first.  < 50 means earlier-than-normal execution, > 50 means later than normal execution.  Priority only affects execution order compared to other events registered on the same hook, all events on a given hook will execute before the game resumes execution.
  * @param string $functioncall The function that should be called, if not specified, use {modulename}_dohook() as the function
  * @param string $whenactive An expression that should be evaluated before triggering the event, if not specified, none.
   */
function module_addhook_priority($hookname,$priority=50,$functioncall=false,$whenactive=false){
	global $mostrecentmodule;
	module_drophook($hookname,$functioncall);

	if ($functioncall===false) $functioncall=$mostrecentmodule."_dohook";
	if ($whenactive === false) $whenactive = '';

	debug("Adding a hook at $hookname for $mostrecentmodule to $functioncall which is active on condition '$whenactive'");
	//we want to do a replace in case there's any garbage left in this table which might block new clean data from going in.
	//normally that won't be the case, and so this doesn't have any performance implications.
	$sql = "REPLACE INTO " . db_prefix("module_hooks") . " (modulename,location,function,whenactive,priority) VALUES ('$mostrecentmodule','".addslashes($hookname)."','".addslashes($functioncall)."','".addslashes($whenactive)."','".addslashes($priority)."')";
	db_query($sql);
	invalidatedatacache("hook-".$hookname);
	invalidatedatacache("moduleprepare");
}

function module_sem_acquire(){
	//DANGER DANGER WILL ROBINSON
	//use of this function can be EXTREMELY DANGEROUS
	//If there is ANY WAY you can avoid using it, I strongly recommend you
	//do so.  That said, I recognize that at times you need to acquire a
	//semaphore so I'll provide a function to accomplish it.

	//PLEASE make sure you call module_sem_release() AS SOON AS YOU CAN.

	//Since Semaphore support in PHP is a compile time option that is off
	//by default, I'll rely on MySQL's semaphore on table lock.  Note this
	//is NOT as efficient as the PHP semaphore because it blocks other
	//things too.
	//If someone is feeling industrious, a smart function that uses the PHP
	//semaphore when available, and otherwise call the MySQL LOCK TABLES
	//code would be sincerely appreciated.
	$sql = "LOCK TABLES " . db_prefix("module_settings") . " WRITE";
	db_query($sql);
}

function module_sem_release(){
	//please see warnings in module_sem_acquire()
	$sql = "UNLOCK TABLES";
	db_query($sql);
}

function module_collect_events($type, $allowinactive=false)
{
	global $session, $playermount;
	global $blocked_modules, $block_all_modules, $unblocked_modules;
	$active = "";
	$events = array();
	if (!$allowinactive) $active = " active=1 AND";

	$sql = "SELECT " . db_prefix("module_event_hooks") . ".* FROM " . db_prefix("module_event_hooks") . " INNER JOIN " . db_prefix("modules") . " ON ". db_prefix("modules") . ".modulename = " . db_prefix("module_event_hooks") . ".modulename WHERE $active event_type='$type' ORDER BY RAND(".e_rand().")";
	$result = db_query_cached($sql,"event-".$type);
	while ($row = db_fetch_assoc($result)){
		// The event_chance bit needs to return a value, but it can do that
		// in any way it wants, and can have if/then or other logical
		// structures, so we cannot just force the 'return' syntax unlike
		// with buffs.
		ob_start();
		$chance = eval($row['event_chance'].";");
		$err = ob_get_contents();
		ob_end_clean();
		if ($err > ""){
			debug(array("error"=>$err,"Eval code"=>$row['event_chance']));
		}
		if ($chance < 0) $chance = 0;
		if ($chance > 100) $chance = 100;
		if (($block_all_modules || array_key_exists($row['modulename'],$blocked_modules) && $blocked_modules[$row['modulename']]) &&
				(!array_key_exists($row['modulename'],$unblocked_modules) || !$unblocked_modules[$row['modulename']])) {
			$chance = 0;
		}
		$events[] = array('modulename'=>$row['modulename'],
				'rawchance' => $chance);
	}

	// Now, normalize all of the event chances
	$sum = 0;
	reset($events);
	foreach($events as $event) {
		$sum += $event['rawchance'];
	}
	reset($events);
	foreach($events as $index=>$event) {
		if ($sum == 0) {
			$events[$index]['normchance'] = 0;
		} else {
			$events[$index]['normchance'] =
				round($event['rawchance']/$sum*100,3);
			// If an event requests 1% chance, don't give them more!
			if ($events[$index]['normchance'] > $event['rawchance'])
				$events[$index]['normchance'] = $event['rawchance'];
		}
	}
	return modulehook("collect-events", $events);
}

function module_events($eventtype, $basechance, $baseLink = false) {
	if ($baseLink === false){
		global $PHP_SELF;
		$baseLink = substr($PHP_SELF,strrpos($PHP_SELF,"/")+1)."?";
	}else{
		//debug("Base link was specified as $baseLink");
		//debug(debug_backtrace());
	}
	if (e_rand(1, 100) <= $basechance) {
		global $PHP_SELF;
		$events = module_collect_events($eventtype);
		$chance = r_rand(1, 100);
		reset($events);
		$sum = 0;
		foreach($events as $event) {
			if ($event['rawchance'] == 0) {
				continue;
			}
			if ($chance > $sum && $chance <= $sum + $event['normchance']) {
				$_POST['i_am_a_hack'] = 'true';
				tlschema("events");
				output("`^`c`bSomething Special!`c`b`0");
				tlschema();
				$op = httpget('op');
				httpset('op', "");
				module_do_event($eventtype, $event['modulename'], false, $baseLink);
				httpset('op', $op);
				return 1;
			}
			$sum += $event['normchance'];
		}
	}
	return 0;
}

function module_do_event($type, $module, $allowinactive=false, $baseLink=false)
{
	global $navsection;

	if ($baseLink === false){
		global $PHP_SELF;
		$baseLink = substr($PHP_SELF,strrpos($PHP_SELF,"/")+1)."?";
	}else{
		//debug("Base link was specified as $baseLink");
		//debug(debug_backtrace());
	}
	// Save off the mostrecent module since having that change can change
	// behaviour especially if a module calls modulehooks itself or calls
	// library functions which cause them to be called.
	if (!isset($mostrecentmodule)) $mostrecentmodule = "";
	$mod = $mostrecentmodule;
	$_POST['i_am_a_hack'] = 'true';
	if(injectmodule($module, $allowinactive)) {
		$oldnavsection = $navsection;
		tlschema("module-$module");
		$fname = $module."_runevent";
		$fname($type,$baseLink);
		tlschema();
		//hook into the running event, but only in *this* running event, not in all
		modulehook("runevent_$module", array("type"=>$type, "baselink"=>$baseLink, "get"=>httpallget(), "post"=>httpallpost()));
		//revert nav section after we're done here.
		$navsection = $oldnavsection;
	}
	$mostrecentmodule=$mod;
}

function event_sort($a, $b)
{
	return strcmp($a['modulename'], $b['modulename']);
}

function module_display_events($eventtype, $forcescript=false) {
	global $PHP_SELF, $session;
	if (!($session['user']['superuser'] & SU_DEVELOPER)) return;
	if ($forcescript === false)
		$script = substr($PHP_SELF,strrpos($PHP_SELF,"/")+1);
	else
		$script = $forcescript;
	$events = module_collect_events($eventtype,true);

	if (!is_array($events) || count($events) == 0) return;

	usort($events, "event_sort");

	tlschema("events");
	output("`n`nSpecial event triggers:`n");
	$name = translate_inline("Name");
	$rchance = translate_inline("Raw Chance");
	$nchance = translate_inline("Normalized Chance");
	rawoutput("<table cellspacing='1' cellpadding='2' border='0' bgcolor='#999999'>");
	rawoutput("<tr class='trhead'>");
	rawoutput("<td>$name</td><td>$rchance</td><td>nchance</td>");
	rawoutput("</tr>");
	$i = 0;
	foreach($events as $event) {
		// Each event is an associative array of 'modulename',
		// 'rawchance' and 'normchance'
		rawoutput("<tr class='" . ($i%2==0?"trdark":"trlight")."'>");
		$i++;
		if ($event['modulename']) {
			$link = "module-{$event['modulename']}";
			$name = $event['modulename'];
		}
		$rlink = "$script?eventhandler=$link";
		$rlink = str_replace("?&","?",$rlink);
		$first = strpos($rlink, "?");
		$rl1 = substr($rlink, 0, $first+1);
		$rl2 = substr($rlink, $first+1);
		$rl2 = str_replace("?", "&", $rl2);
		$rlink = $rl1 . $rl2;
		rawoutput("<td><a href='$rlink'>$name</a></td>");
		addnav("", "$rlink");
		rawoutput("<td>{$event['rawchance']}</td>");
		rawoutput("<td>{$event['normchance']}</td>");
		rawoutput("</tr>");
	}
	rawoutput("</table>");
}

function module_editor_navs($like, $linkprefix)
{
	$sql = "SELECT formalname,modulename,active,category FROM " . db_prefix("modules") . " WHERE infokeys LIKE '%|$like|%' ORDER BY category,formalname";
	$result = db_query($sql);
	$curcat = "";
	while($row = db_fetch_assoc($result)) {
		if ($curcat != $row['category']) {
			$curcat = $row['category'];
			addnav(array("%s Modules",$curcat));
		}
		//I really think we should give keyboard shortcuts even if they're
		//susceptible to change (which only happens here when the admin changes
		//modules around).  This annoys me every single time I come in to this page.
		addnav_notl(($row['active'] ? "" : "`)") . $row['formalname']."`0",
				$linkprefix . $row['modulename']);
	}
}

function module_objpref_edit($type, $module, $id)
{
	$info = get_module_info($module);
	if (count($info['prefs-'.$type]) > 0) {
		$data = array();
		$msettings = array();
		while(list($key, $val) = each($info['prefs-'.$type])) {
			if (is_array($val)) {
				$v = $val[0];
				$x = explode("|", $v);
				$val[0] = $x[0];
				$x[0] = $val;
			} else {
				$x = explode("|", $val);
			}
			$msettings[$key]=$x[0];
			// Set up default
			if (isset($x[1])) $data[$key]=$x[1];
		}
		$sql = "SELECT setting, value FROM " . db_prefix("module_objprefs") . " WHERE modulename='$module' AND objtype='$type' AND objid='$id'";
		$result = db_query($sql);
		while($row = db_fetch_assoc($result)) {
			$data[$row['setting']] = $row['value'];
		}
		tlschema("module-$module");
		showform($msettings, $data);
		tlschema();
	}
}

function module_compare_versions($a,$b){
	//this function returns -1 when $a < $b, 1 when $a > $b, and 0 when $a == $b
	//insert alternate version detection and comparison algorithms here.

	//default case, typecast as float
	$a = (float)$a;
	$b = (float)$b;
	return ($a < $b ? -1 : ($a > $b ? 1 : 0) );
}

function activate_module($module){
	if (!is_module_installed($module)){
		if (!install_module($module)){
			return false;
		}
	}
	$sql = "UPDATE " . db_prefix("modules") . " SET active=1 WHERE modulename='$module'";
	db_query($sql);
	invalidatedatacache("inject-$module");
	massinvalidate("moduleprepare");
	if (db_affected_rows() <= 0){
		return false;
	}else{
		return true;
	}
}

function deactivate_module($module){
	if (!is_module_installed($module)){
		if (!install_module($module)){
			return false;
		}else{
			//modules that weren't installed go to deactivated state by default in install_module
			return true;
		}
	}
	$sql = "UPDATE " . db_prefix("modules") . " SET active=0 WHERE modulename='$module'";
	db_query($sql);
	invalidatedatacache("inject-$module");
	massinvalidate("moduleprepare");
	if (db_affected_rows() <= 0){
		return false;
	}else{
		return true;
	}
}

function uninstall_module($module){
	if (injectmodule($module,true)) {
		$fname = $module."_uninstall";
		output("Running module uninstall script`n");
		tlschema("module-{$module}");
		$fname();
		tlschema();

		output("Deleting module entry`n");
		$sql = "DELETE FROM " . db_prefix("modules") .
			" WHERE modulename='$module'";
		db_query($sql);

		output("Deleting module hooks`n");
		module_wipehooks();

		output("Deleting module settings`n");
		$sql = "DELETE FROM " . db_prefix("module_settings") .
			" WHERE modulename='$module'";
		db_query($sql);
		invalidatedatacache("modulesettings-$module");

		output("Deleting module user prefs`n");
		$sql = "DELETE FROM " . db_prefix("module_userprefs") .
			" WHERE modulename='$module'";
		db_query($sql);

		output("Deleting module object prefs`n");
		$sql = "DELETE FROM " . db_prefix("module_objprefs") .
			" WHERE modulename='$module'";
		db_query($sql);
		invalidatedatacache("inject-$module");
		massinvalidate("moduleprepare");
		return true;
	} else {
		return false;
	}
}

function install_module($module, $force=true){
 	global $mostrecentmodule, $session;
	$name = $session['user']['name'];
	if (!$name) $name = '`@System`0';

	require_once("lib/sanitize.php");
	if (modulename_sanitize($module)!=$module){
		output("Error, module file names can only contain alpha numeric characters and underscores before the trailing .php`n`nGood module names include 'testmodule.php', 'joesmodule2.php', while bad module names include, 'test.module.php' or 'joes module.php'`n");
		return false;
	}else{
		// If we are forcing an install, then whack the old version.
		if ($force) {
			$sql = "DELETE FROM " . db_prefix("modules") . " WHERE modulename='$module'";
			db_query($sql);
		}
		// We want to do the inject so that it auto-upgrades any installed
		// version correctly.
		if (injectmodule($module,true)) {
			// If we're not forcing and this is already installed, we are done
			if (!$force && is_module_installed($module))
				return true;
			$info = get_module_info($module);
			//check installation requirements
			if (!module_check_requirements($info['requires'])){
				output("`\$Module could not installed -- it did not meet its prerequisites.`n");
				return false;
			}else{
				$keys = "|".join(array_keys($info), "|")."|";
				$sql = "INSERT INTO " . db_prefix("modules") . " (modulename,formalname,moduleauthor,active,filename,installdate,installedby,category,infokeys,version,download,description) VALUES ('$mostrecentmodule','".addslashes($info['name'])."','".addslashes($info['author'])."',0,'{$mostrecentmodule}.php','".date("Y-m-d H:i:s")."','".addslashes($name)."','".addslashes($info['category'])."','$keys','".addslashes($info['version'])."','".addslashes($info['download'])."', '".addslashes($info['description'])."')";
				db_query($sql);
				$fname = $mostrecentmodule."_install";
				if (isset($info['settings']) && count($info['settings']) > 0) {
					foreach($info['settings'] as $key=>$val){
						if (is_array($val)) {
							$x = explode("|", $val[0]);
						} else {
							$x = explode("|",$val);
						}
						if (isset($x[1])){
							set_module_setting($key,$x[1]);
							debug("Setting $key to default {$x[1]}");
						}
					}
				}
				if ($fname() === false) {
					return false;
				}
				output("`^Module installed.  It is not yet active.`n");
				invalidatedatacache("inject-$mostrecentmodule");
				massinvalidate("moduleprepare");
				return true;
			}
		} else {
			output("`\$Module could not be injected.");
			output("Module not installed.");
			output("This is probably due to the module file having a parse error or not existing in the filesystem.`n");
			return false;
		}
	}

}

/**
  * Evaluates a PHP Expression
  *
  * @param string $condition The PHP condition to evaluate
  * @return bool The result of the evaluated expression
  */
function module_condition($condition) {
	global $session;
	$result = eval($condition);
	return (bool)$result;
}

function get_module_install_status(){
	// Collect the names of all installed modules.
	$seenmodules = array();
	$seencats = array();
	$sql = "SELECT modulename,category FROM " . db_prefix("modules");
	$result = @db_query($sql);
	if ($result !== false){
		while ($row = db_fetch_assoc($result)) {
			$seenmodules[$row['modulename'].".php"] = true;
			if (!array_key_exists($row['category'], $seencats))
				$seencats[$row['category']] = 1;
			else
				$seencats[$row['category']]++;
		}
	}

	$uninstmodules = array();
	if ($handle = opendir("modules")){
		$ucount=0;
		while (false !== ($file = readdir($handle))){
			if ($file[0] == ".") continue;
			if (preg_match("/\\.php$/", $file) && !isset($seenmodules[$file])){
				$ucount++;
				$uninstmodules[] = substr($file, 0, strlen($file)-4);
			}
		}
	}
	closedir($handle);
	sort($uninstmodules);
	return array('installedcategories'=>$seencats,'installedmodules'=>$seenmodules,'uninstalledmodules'=>$uninstmodules, 'uninstcount'=>$ucount);
}

function get_racename($thisuser=true) {
	if ($thisuser === true) {
		global $session;
		return translate_inline($session['user']['race'],"race");
	} else {
		return translate_inline($thisuser,"race");
	}
}

function module_delete_oldvalues($table,$key) {
    require_once 'lib/gamelog.php';
    $total = 0;
    $res = db_query("SELECT modulename FROM ".db_prefix('modules')." WHERE infokeys LIKE '%|$key|%'");
    while ($row = db_fetch_assoc($res)) {
        $mod = $row['modulename'];
        require_once "modules/{$mod}.php";
        $func = $mod."_getmoduleinfo";
        $info = $func();
		$keys = array_filter(array_keys($info[$key]), "module_pref_filter");
		$keys = array_map("addslashes", $keys);
        $keys = implode("','", $keys);
        if ($keys) db_query("DELETE FROM ".db_prefix($table)." WHERE modulename='$mod' AND setting NOT IN ('$keys')");
        $total += db_affected_rows();
    }
    gamelog("Cleaned up $total old values in $table that don't exist anymore", 'maintenance');
}

function module_pref_filter($a){ 
    return !is_numeric($a);
}

?>
