<?php

/*
** DGS Search
** $Id: dgssearch.php,v 1.5 2004/01/19 07:34:25 sella Exp $
** Copyright (c) 2000-2001 Digital Genesis Software, LLC. All Rights Reserved.
** Released under the GPL Version 2 License.
** http://www.digitalgenesis.com
*/

// NOTE: These are support functions for dgssearch, not the whole thing.

function _processConfig($data) {
	global $config;

	if ($config['debug'])
		printf(">Debug: processConfig()<BR>\n");

	/* Put the config into a known state. */
	_normalizeConfig();

	/* Swap in config set for Virtual Host if needed */
	processVirtualHost($server);

	/* Swap in requested config set if needed. */
	_processConfigSet($data['config']);

	/* Load Language */
	$errors = _loadLanguage();

	/* Verify config */
	if (!$errors)
		$errors = _verifyConfig();

	/* Handle Errors */
	if ($errors) {
		printf("<STRONG>Error: Configuration error(s) in config.php. Details below:<BR>\n<BR>\n");
		reset($errors);
		while (list(, $error) = each($errors)) {
			printf("&nbsp;&nbsp;%s<BR>\n", $error);
		}
		printf("</STRONG><BR>\n");
		return false;
	}

	return true;
}

function processVirtualHost() {
	global $config, $vhost;
	$server = getEnvVar('SERVER_NAME');

	if ($config['debug'])
		printf("->Debug: processVirtualHost() - Server: '%s'<BR>\n", $server);

	if (isset($vhost[$server])) {
		if ($config['debug'])
			printf("->Debug: processVirtualHost() - Resolved Virtual Host '%s' to Config Set '%s'.<BR>\n", $server, $vhost[$server]);
		_processConfigSet($vhost[$server]);
	}
}

function _processConfigSet($set) {
	global $config, $confSet;
	$debug = $config['debug'];
	$installBase = $config['installBase'];
	$sep = $config['fileSeparator'];

	if (strlen($set) < 1) {
		if ($debug)
			printf("->Debug: processConfigSet() - Using default Config Set<BR>\n");
		return;
	} else if ($debug) {
		printf("->Debug: processConfigSet() - Requested Config Set: '%s'<BR>\n", $set);
	}

	if (isset($confSet[$set])) {
		$confFiles = array('config.php');
		while(list(, $file) = each($confFiles)) {
			$file = $installBase . $sep . $confSet[$set] . $sep . $file;
			if (is_file($file)) {
				include($file);
				_normalizeConfig(); /* We may have altered things, so normalize again. */
			} else if ($debug) {
				printf("->Debug: processConfigSet() - File '%s' not found. <STRONG>[Skip]</STRONG><BR>\n", $file);
			}
		}
	} else if ($debug) {
		printf("->Debug: processConfigSet() - Config Set '%s' isn't setup. <STRONG>[Skip]</STRONG><BR>\n", $set);
	}
}

function _loadLanguage() {
	global $config, $lang;
	$debug = $config['debug'];
	$langInclude = $config['installBase'] . $config['fileSeparator'] . 'libs' . $config['fileSeparator'] . 'language' . $config['fileSeparator'] . $config['language'] . '.php';

	if ($debug)
		printf('->Debug: loadLanguage() - Checking language pack \'%s\' stored in \'%s\'. ', $config['language'], $langInclude);

	/* Verify we can read the language pack. */
	if (is_readable($langInclude)) {
		if ($debug)
			printf(" <STRONG>[Readable]</STRONG><BR>\n");
		include($langInclude);
		if ($lang['charset']) { //This will fail when debug is enabled, since we have already written to stdout.
			@header('Content-Type: text/html; charset=' . $lang['charset']);
		}
	} else {
		if ($debug)
			printf(" <STRONG>[Not Readable]</STRONG><BR>\n");
		$error = array(sprintf("Unable to access language '%s' (%s).", $config['language'], $langInclude));
		if (function_exists('getcwd')) {
			if (strcmp(getcwd(), $config['installBase'])) {
				$error[] = sprintf('Your $config[\'installBase\'] may be incorrect. It looks like it should be \'%s\'.', getcwd());
			}
		}
		return $error;
	}

	return false;
}

function _normalizeConfig() {
	global $config;

	if ($config["debug"])
		printf("->Debug: normalizeConfig()<BR>\n");

	/* Change values as needed according to our rules. */
	$config['language'] = strtolower(trim($config['language']));
	$config['warn'] = ($config['debug']) ? true : $config['warn'];
	$config['verifyConfig'] = $config['verifyConfig'];
	$config['followLinks'] = (function_exists('is_link')) ? $config['followLinks'] : false;
	$config['maxSearchTime'] = ($config['maxSearchTime'] > 0) ? $config['maxSearchTime'] : 65535;

	/* Clean up trailing slashed. */
	$config['installBase'] = ereg_replace('(^.*)[/\\]$', '\\1', $config['installBase']);
	$config['urlBase'] = ereg_replace('(^.*)[/\\]$', '\\1', $config['urlBase']);
	$config['siteBase'] = ereg_replace('(^.*)[/\\]$', '\\1', $config['siteBase']);
	$config['fsBase'] = ereg_replace('(^.*)[/\\]$', '\\1', $config['fsBase']);

	return true;
}

function _verifyConfig() {
	global $config;

	$installBase = $config['installBase'];
	$siteBase = $config['siteBase'];
	$fsBase = $config['fsBase'];
	$verify = $config['verifyConfig'];

	if ($config['debug'])
		printf("->Debug: verifyConfig()<BR>\n");

	/* These checks are optional. */
	if ($verify) {
		$error = verifyPHPVersion(3,0,8);
		if (is_string($error))
			$errors[] = $error;
		if (!is_array($config['searchModules']))
			$errors[] = '$config[\'searchModules\'] is not set to an array type.';
		if (!is_array($config['displayModules']))
			$errors[] = '$config[\'displayModules\'] is not set to an array type.';
		if (!is_string($config['urlBase']))
			$errors[] = '$config[\'urlBase\'] is not set to a string type.';
		if (!is_string($installBase))
			$errors[] = '$config[\'installBase\'] is not set to a string type.';
		else if (!is_dir($installBase) || (is_link($installBase) && !is_dir(readlink($installBase))))
			$errors[] = '$config[\'installBase\'] is not set to a valid directory.';
		if (!is_string($siteBase))
			$errors[] = '$config[\'siteBase\'] is not set to a string type.';
		else if (!is_dir($siteBase) || (is_link($siteBase) && !is_dir(readlink($siteBase))))
			$errors[] = '$config[\'siteBase\'] is not a set to a valid directory.';
		if (!is_string($fsBase))
			$errors[] = '$config[\'fsBase\'] is not set to a string type.';
		else if (!is_dir($fsBase) || (is_link($fsBase) && !is_dir(readlink($fsBase))))
			$errors[] = '$config[\'fsBase\'] is not set to a valid directory.';
		if (!is_array($config['exts']))
			$errors[] = '$config[\'exts\'] is not set to an array type.';
		if (strlen($siteBase) > 0) {
			$index = strpos($fsBase, $siteBase);
			if (!is_long($index) || $index != 0)
				$errors[] = '$config[\'fsBase\'] must be a directory within $config[\'siteBase\'] directory.';
			$index = strpos($installBase, $siteBase);
			if (!is_long($index) || $index != 0)
				$errors[] = '$config[\'installBase\'] must be a directory within $config[\'siteBase\'] directory.';
		}
	}

	/* Load modules is always required. */
	$modules['search'] = $config['searchModules'];
	$modules['display'] = $config['displayModules'];

	/* Load and Verify modules is required. */
	$error = _verifyModules($modules);
	if (is_string($error))
		$errors[] = $error;

	if (!isset($errors))
		return false;
	return $errors;
}

function _verifyModules($modules) {
	global $config;

	$installBase = $config['installBase'];
	$debug = $config['debug'];

	if ($config['debug'])
		printf("-->Debug: verifyModules()<BR>\n");

	reset($modules);
	while (list($modType, $x) = each($modules)) {
		reset($modules[$modType]);
		while (list($x, $module) = each($modules[$modType])) {
			$modInclude = $installBase . $config['fileSeparator'] . 'libs' . $config['fileSeparator'] . $modType . $config['fileSeparator'] . $module . '.php';
			/* Verify function isn't already available. */
			if (!function_exists($module)) {
				if ($debug)
					printf('-->Debug: verifyModules() - Checking module \'%s\' stored in \'%s\'. ', $module, $modInclude);
				/* Verify we can read the module. */
				if (is_readable($modInclude)) {
					if ($debug)
						printf(" <STRONG>[Readable]</STRONG><BR>\n");
					include($modInclude);
				} else {
					if ($debug)
						printf(" <STRONG>[Not Readable]</STRONG><BR>\n");
					return sprintf("Unable to access %s module '%s' (%s).<BR>\n", $modType, $module, $modInclude);
				}
				/* Verify that the module contains a function of the same name. */
				if (!function_exists($module)) {
					return sprintf("Error: Module '%s' (%s) is not usable.<BR>\nThis module must contain the function '%s(\$retVal, \$value, \$param)'.<BR>\n", $module, $modInclude, $module);
				} else {
					$loadedModules[$module] = true;
				}
			} else if ($loadedModules[$module] != true) {
				return sprintf("Error: The function '%s' was available before loading module '%s'.<BR>\n'%s' may be a built in PHP function or another module may have the same name. If this is the case, you will need to rename the module.<BR>\n", $module, $modInclude, $module);
			}
		}
	}

	return true;
}

function processWindow(&$content, $width, $height, $q, $matchCount) {
	global $config;

	$lowQ = strtolower($q);
	$qSize = strlen($q);
	$window = $width / 2;
	$descEnd = $config['descEnd'];
	$boldQuery = $config['boldQuery'];
	$stripTags = $config['stripTags'];
	$desc = array();

	$content = str_replace('<br', ' <br', $content);
	$content = str_replace('<Br', ' <Br', $content);
	$content = str_replace('<BR', ' <BR', $content);
	$content = str_replace('<p', ' <p', $content);
	$content = str_replace('<P', ' <P', $content);
	$content = str_replace('&nbsp;', ' ', $content); //'&nbsp' parts show up if they are broken, so convert to spaces.

	if ($stripTags) {
		$content = eregi_replace('<script..*</script>', '', $content);
		$content = strip_tags($content);
	}

	//Cleanup lines of these characters which make desc ugly or malformed.
	$content = ereg_replace('[[:space:]]{2,}', ' ', $content); //Consume extra whitespace from content.
	$content = ereg_replace('[_~]{2,}', ' ', $content); //Lines of _ or ~. (2 or more in a row)
	$content = ereg_replace('[+=\.-]{3,}', ' ', $content); //Lines of +=-. but not ++ or == in code.
	$content = trim($content);

	$lowContent = strtolower($content);
	$contentSize = strlen($content);

	//Escape regex characters in query.
	$escQ = ereg_replace("([\[\)\(\?\.\*\+])", "\\\\1", $q);

	$queryIdx = 0;
	for ($i = 0; $i < $height; $i++) {
		if (strlen($lowQ) > 0) {
			$queryIdx = strpos($lowContent, $lowQ, $queryIdx);
		}
		if (!is_int($queryIdx) && strlen($desc[$i]) < 1) { //If desc has no content and no $q was found, then lie and say we found $q at 0. This covers us on db searches.
			$queryIdx = 0;
			/* Commented this out. It was causing DB searches with no q found to have no desc at all. */
			//break;
		} else if ((!is_int($queryIdx) || $queryIdx >= $contentSize)) { //If we don't find $q, then there are no more matches.
			break;
		}

		/* Grab the section of text around the matching keyword. */
		$lBound = ($queryIdx - $window < 0) ? 0 : $queryIdx - $window;
		$uBound = ($lBound + $width > $contentSize) ? $contentSize : $lBound + $width;
		$lBound = ($uBound < $contentSize) ? $lBound : (($uBound - $width < 1) ? 0 : $uBound - $width);

		/* Slide our window to avoid cutting words. */
		$descAdj = $uBound - ($queryIdx + $qSize);
		for ($j = 0; $j < $descAdj; $j++) {
			if ($lBound - $j <= 0 || $content[$lBound - $j - 1] == ' ') {
				$lBound -= $j;
				$uBound -= $j;
				break;
			}
		}

		/* Shrink the uBound to avoid cutting words. */
		$descAdj = $uBound - ($queryIdx + $qSize);
		if ($uBound < $contentSize && $content[$uBound] != ' ') {
			for ($j = 1; $j < $descAdj; $j++) {
				if ($content[$uBound - $j] == ' ' && $uBound - $j > $lBound) {
					$uBound -= $j;
					break;
				}
			}
		}

		/* Cut the desc out of content and add descEnd. */
		$descBuf = '';
		if ($lBound > 0)
			$descBuf = $descEnd;
		$descBuf .= trim(substr($content, $lBound, $uBound - $lBound));
		if ($uBound < $contentSize)
			$descBuf .= $descEnd;

		//Cleanup lines of these characters which are ugly.
//		$descBuf = ereg_replace('[_\~]{2,}', ' ', $descBuf);
//		$descBuf = ereg_replace('[+=\.-]{3,}', ' ', $descBuf);

		if (!$boldQuery || strlen($escQ) < 1) {
			$desc[$i] = $descBuf;
		} else {
			$desc[$i] = eregi_replace("($escQ)","<STRONG>\\1</STRONG>", $descBuf);
		}

		$queryIdx = $uBound; //Jump the queryIdx to the end of the desc.
	}
	
	return $desc;
}

function search_getSpecificFormData() {
	/* The content of this function is specific to DGS Search. */
	global $r, $o, $s, $q, $debug, $option;

	$data['r'] = $r;
	$data['o'] = $o;
	$data['s'] = $s;
	$data['q'] = $q;
	$data['debug'] = $debug;
	$data['option'] = $option;

	return $data;
}

function _getSpecificFormData() {
	/* The content of this function is specific to DGS Search. */
	global $r, $o, $s, $q, $debug, $option;

	$data['r'] = $r;
	$data['o'] = $o;
	$data['s'] = $s;
	$data['q'] = $q;
	$data['debug'] = $debug;
	$data['option'] = $option;

	return $data;
}

/*
** Local Variables:
** c-basic-offset: 3
** tab-width: 3
** End:
** vim600: fdm=marker
** vim: noet ts=3 sw=3
*/

?>
