<?php

/*
** DGS Search
** $Id: filesystem.php,v 1.3 2006/01/24 07:08:09 sella Exp $
** Copyright (c) 2000-2001 Digital Genesis Software, LLC. All Rights Reserved.
** Released under the GPL Version 2 License.
** http://www.digitalgenesis.com
*/

function checkAccessFile($entry) {
// true - access file includes entry (or no access file)
// false - access file excludes entry
	clearstatcache();
	if (!file_exists('.htdgs'))
		return true;

	$done = false;
	$result = false;
	$handle = fopen('.htdgs', 'r');
	while (!feof($handle) && !$done) {
		if (($buffer = fgets($handle, 4096)) == false)
			return false;
		//Eat Comments and surrounding white space.
		$buffer = trim(ereg_replace('#..*$', '', $buffer));
		if (eregi('^(include|permit)[[:space:]]+', $buffer)) {
			$expr = eregi_replace('^(include|permit)[[:space:]]+', '', $buffer);
			if (!strcasecmp($expr, 'all')) {
				$done = true;
			} else {
				$done = eregi($expr, $entry);
			}
			$result = $done;
		} else if (eregi('^(exclude|deny)[[:space:]]+', $buffer)) {
			$expr = eregi_replace('^(exclude|deny)[[:space:]]+', '', $buffer);
			if (!strcasecmp($expr, 'all')) {
				$done = true;
			} else {
				$done = eregi($expr, $entry);
			}
			$result = !$done;
		}
		$expr = "";
		$buffer = "";
	}
	fclose($handle);

	return $result;
}

function findext($rootDir, $fileTypes) {
	global $config;

	$debug = $config['debug'];
	$warn = $config['warn'];
	$followLinks = $config['followLinks'];
	$cacheFile = $config['cacheFile'];
	$cacheTTL = $config['cacheTTL'];
	$thisDir = $config['thisDir'];
	$parentDir = $config['parentDir'];
	$fsExcludes = $config['fsExclude'];
	$extSeparator = $config['extSeparator'];
	$fileSeparator = $config['fileSeparator'];
	$tm = getTime();
	$matches = array();

	if ($debug)
		printf("-->Debug: findext()<BR>\n");

	clearstatcache(); //We want new info fro fileXtime() functions.

	if ($cacheTTL > 0 && filemtime($cacheFile) + $cacheTTL > (int) $tm) {
		if ($debug)
			printf("-->Debug: findext() - Filesystem cache file being used. Expires in %d seconds.<BR>\n", filemtime($cacheFile) + $cacheTTL - (int) $tm);
		$matches = readCacheFile();
		if (count($matches) > 0) {
			if ($debug)
				printf("-->Debug: findext() - Filesystem cache containted %d matches.<BR>\n", count($matches));
			return $matches;
		}
	}

	$fileTypeCount = is_array($fileTypes) ? count($fileTypes) : 0;
	$excludeCount = count($fsExcludes);
	$rootDir = ereg_replace('(^.*)[/\\]$', '\\1', $rootDir); //Make sure there is no trailing slash.
	$matchCount = 0;
	$sp = 0;

	if ($warn)
		$handle = opendir($rootDir);
	else
		$handle = @opendir($rootDir);

	if (!$handle) { //We don't have access to rootDir or file error.
		if ($debug)
			printf("-->Debug: findext() - Error: Unable to open root directory '%s'.<BR>\nVerify \$config['rootDir'] is correct and readable by your web server.<BR>\n", $rootDir);
		return;
	}

	$dirStack[$sp] = $handle;
	$currentDir = $rootDir . $fileSeparator;
	chdir($currentDir);

	reset($fileTypes);
	while (list($i, $fileType) = each($fileTypes)) {
		$fileType = ereg_replace('^\^?(.*)$', '^\\' . $extSeparator . '\\1$', trim($fileType));
		$fileTypes[$i] = strtolower(str_replace('$$', '$', $fileType));
	}

	$running = true;
	while ($running) {
		if ($file = readdir($dirStack[$sp])) {
			if (!$followLinks && is_link($file))
				continue;

			if (!checkAccessFile($file)) {
				if ($debug)
					printf("-->Debug: findext() - Queued: %s <B>[Filtered by .htdgs]</B><BR>\n", $currentDir . $file);
				continue;
			}

			if (!is_dir($file)) {
				$fullFile = $currentDir . $file;

				if ($debug)
					printf("-->Debug: findext() - File Queued: %s", $fullFile);

				if ($fileTypeCount > 0) {
					$isMatch = false;
					$ext = strtolower(strrchr($file, $extSeparator));
					reset($fileTypes);
					while (list(, $fileType) = each($fileTypes)) {
						if (ereg($fileType, $ext)) { 
							$isMatch = true;
							break;
						}
					}
					if (!$isMatch) {
						if ($debug)
							printf(" <B>[Miss Ext]</B><BR>\n");
						continue;
					}
				}

				reset($fsExcludes);
				while(list(, $fsExclude) = each($fsExcludes)) {
					if (ereg('^[/\\].*$', $fsExclude))
						$cmpName = $fullFile;
					else
						$cmpName = $file;
					if (ereg($fsExclude, $cmpName)) {
						$isMatch = false;
						break;
					}
				}

				if (!$isMatch) {
					if ($debug)
						printf(" <B>[Excluded: %s]</B><BR>\n", $fsExclude);
					continue;
				}

				if ($warn) //Make sure we can access this file.
					$handle = fopen($fullFile, 'r');
				else
					$handle = @fopen($fullFile, 'r');

				if (!$handle) {
					if ($debug)
						printf(" <B>[Read Failed]</B><BR>\n");
					continue;
				}
				fclose($handle);

				if ($debug)
					printf(" <B>[Match: %s]</B><BR>\n", $fileType);

				$matches[$matchCount++] = $fullFile;
			} else {
				if ($file == $thisDir || $file == $parentDir)
					continue;

				$queued = $currentDir . $file . $fileSeparator;

				if ($debug)
					printf("-->Debug: findext() - Dir Queued: %s", $queued);

				$isMatch = true;
				reset($fsExcludes);
				while(list(, $fsExclude) = each($fsExcludes)) {
					if (ereg('^[/\\].*$', $fsExclude))
						$cmpName = $queued;
					else
						$cmpName = basename($queued);
					if (ereg($fsExclude, $cmpName)) {
						$isMatch = false;
						if ($debug)
							printf(" <B>[Excluded: %s]</B><BR>\n", $fsExclude);
						break;
					}
				}

				if ($debug)
					printf("<BR>\n");

				if (!$isMatch)
					continue;

				if ($warn)
					$handle = opendir($file);
				else
					$handle = @opendir($file);

				if (!$handle) //Make sure we can access this directory.
					continue;

				$dirStack[++$sp] = $handle;
				chdir($file);

				if ($debug)
					printf("<BR>\n-->Debug: findext() - ==> %s<BR>\n", $queued);

				$currentDir = $queued;
			}
		} else {
			closedir($dirStack[$sp--]);

			if ($sp < 0) {
				$running = false;
			} else {
				if ($debug)
					printf("-->Debug: findext() - <== %s<BR>\n", $currentDir);
				$currentDir = dirname($currentDir) . $fileSeparator;
				chdir($currentDir);
			}
		}
	}
	
	if ($debug) //Time taken to locate all files.
		printf("-->Debug: findext() - Search Time: %.1f sec<BR><BR>\n", getTime() - $tm);

	//Sort cache file here if the user wanted it.
	if ($config['sortByModTime']) {
		sortFilesByModTime($matches, $config['sortByModTimeRev']);
	} else if ($config['sortBySize']) {
		sortFilesBySize($matches, $config['sortBySizeRev']);
	}

	if ($cacheTTL > 0) //If we are using cache, write it. We wouldn't be this far if it wasn't expired or missing.
		writeCacheFile($matches);

	if (count($matches) < 1) { //Filesystem search is worthless if there are no matches.
		if ($warn) {
			printf("<B>No files found to search. Check your config and .htdgs files. You can also enable debug to see why files are being excluded.</B><BR>\n");
		}
	}
	

	return $matches;
}

function sortFilesByModTime(&$files, $reverse = false) {
	$x = 0;
	reset($files);
	foreach($files as $i => $file) {
		$modTime = filemtime($file);
		if (!$modTime)
			$modTime = filectime($file);
		$idx = $modTime . sprintf('%08d', $x++);
		$mFiles[$idx] = $file;
	}

	$files = $mFiles;
	$mFiles = array();

	if ($reverse)
		ksort($files);
	else
		krsort($files);

	return true;
}

function sortFilesBySize(&$files, $reverse = false) {
	$x = 0;
	reset($files);
	foreach($files as $i => $file) {
		$fileSize = filesize($file);
		//Make a unique index, but based off of filesize
		$idx = sprintf('%012d', $fileSize) . sprintf('%08d', $x++);
		$sFiles[$idx] = $file;
	}

	$files = $sFiles;
	$sFiles = array();

	if ($reverse)
		krsort($files);
	else
		ksort($files);

	return true;
}

function sortFilesByExt(&$files, $reverse = false) {
	//Not done..
	$x = 0;
	reset($files);
	foreach($files as $i => $file) {
		$idx = 'notdone' . sprintf('%08d', $x++);
		$eFiles[$idx] = $file;
	}

	$files = $eFiles;
	$eFiles = array();

	if ($reverse)
		ksort($files);
	else
		krsort($files);

	return true;
}

function writeCacheFile($files) {
	global $config;
	$cacheFile = $config['cacheFile'];

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

	$handle = fopen($cacheFile, 'w');

	if (function_exists('set_file_buffer'))
		set_file_buffer($handle, 65535);

	reset($files);
	$buffer = implode("\n", $files);
	fwrite($handle, $buffer);
	fclose($handle);
}

function readCacheFile() {
	global $config;
	$installBase = $config['installBase'];
	$cacheFile = $config['cacheFile'];
	$configFile = $installBase . $config['fileSeparator'] . 'config' . $config['fileSeparator'] . 'config.php';
	$debug = $config['debug'];
	$files = array();

	if ($debug)
		printf("--->Debug: readCacheFile()<BR>\n");

	$handle = @fopen($cacheFile, 'r');
	if (!$handle) {
		if ($debug)
			printf("--->Debug: readCacheFile() - Cache file '%s' not found. Generating a new cache file.<BR>\n");
		$files = array();
		return $files;
	}

	if (filemtime($configFile) > filemtime($cacheFile)) {
		if ($debug)
			printf("--->Debug: readCacheFile() - Expiring cache file '%s' due to configuration change.<BR>\n", $cacheFile, $configFile);
		$files = array();
		return $files;
	}

	if (function_exists('set_file_buffer'))
		set_file_buffer($handle, 65535); /* Set a 64k buffer. */

	$buffer = fread($handle, filesize($cacheFile));
	fclose($handle);

	$files = explode("\n", $buffer);

	/* Verify this cache files contents are usable. */
	list(, $file) = each($files);
	if (!is_int(strpos($file, $config["fsBase"]))) {
		unset($files);
		$files = array();
		return $files;
	}

	return $files;
}

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

?>
