<?php
error_reporting(-1);

// place this file just above the directory you want to track
// to ignore changes in any directory (and below), place a marker file '.xtc' in the dirctory

// typical cron job
// 30	4	*	*	*	"php -q <full server path to file>/trackercron.php --cron"

$trkext = '.trackc';
$mailSubj = 'Tracker Report';
$mailFrom = 'no-reply@mydomain.net';
$mailTo = 'me@mymail.com';

// get the directory where this script is located
$ldir = dirname(__FILE__);

$cron = (isset($_SERVER['argv'][1]) and $_SERVER['argv'][1] == '--cron');

$msg = '';
$tbase = '';
$ttype = 'fs';
$mStyle = '<style>
	.err {color:red}
	.err2 {color:magenta}
	.yay {color:green}
</style>';

$tracks = array();
$dirs = getDirs($ldir);

if ($cron) {
	checkDirs($dirs);
	if ($msg) {
		$headers = 'From: ' . $mailFrom . "\r\n";
		$headers .= 'MIME-Version: 1.0' . "\r\n";
		$headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";
		mail($mailTo, $mailSubj, '<!DOCTYPE html><html><head>'.$mStyle.'</head><body>'.$msg.'</body></html>', $headers);
	}
	exit();
}

$tdir = isset($_POST['dir']) ? $_POST['dir'] : '';

if (isset($_POST['check'])) {
	$cdirs = array();
	if (isset($_POST['all'])) {
		$cdirs = $dirs;
	} else {
		$cdirs[] = $tdir;
	}
	checkDirs($cdirs);
}

if (isset($_POST['create'])) {
	$ttype = isset($_POST['ttype']) ? $_POST['ttype'] : 'fs';
	$tracks[-1] = $ttype;
	$tbase = $ldir.'/'.$tdir.'/';
	doTracks('');
	file_put_contents($tdir.$trkext, json_encode($tracks, JSON_PRETTY_PRINT+JSON_UNESCAPED_SLASHES));
	$msg = 'Track file created for '.$tdir;
}

function checkDirs ($dirs)
{
	global $ldir, $tbase, $trkext, $tracks, $msg, $isu;

	foreach ($dirs as $adir) {
		$dir = $ldir.'/'.$adir;
		if (file_exists($dir.$trkext)) {
			$tracks = json_decode(file_get_contents($dir.$trkext), true);
			if (isset($tracks[-1])) $ttype = $tracks[-1];
			$msg .= "\n".'<br />@@@ - '.$adir.'<br />';
			$isu = 0;
			$tbase = $dir.'/';
			doTracks('', true);
			if ($isu === 0) {
				$msg .= "\n".'<p>No discrepencies found in <span class="yay">'.$adir.'</span> (yay!)</p>';
			}
		}
	}
}

// walk a directory heirarchy, collecting or verifying file marker values
function doTracks ($dir, $v=false)
{
	global $tbase, $tracks, $msg, $isu;

	if (file_exists($tbase.$dir.'/.xtc')) return;
	if ($dh = opendir($tbase.$dir)) {
		while (($file = readdir($dh)) !== false) {
			if ($file == '.' || $file == '..') continue;
			$path = $dir.($dir?'/':'').$file;
			if (is_dir($tbase.$path)) {
				doTracks($path, $v);
			} else {
				$tval = trackedVal($tbase.$path);
				if ($v) {
					if (isset($tracks[$path])) {
						if ($tracks[$path] != $tval) {
							$msg .= "\n".'<span class="err">Modified file:</span> '.htmlspecialchars($path).'<br />';
							++$isu;
						}
					} else {
						$msg .= "\n".'<span class="err2">New file:</span> '.htmlspecialchars($path).'<br />';
						++$isu;
					}
				} else {
					$tracks[$path] = $tval;
				}
			}
		}
		closedir($dh);
	}
}

// get a representative value marker for a file
function trackedVal ($fp)
{
	global $ttype;

	switch ($ttype) {
		case 'fs':
			$val = filesize($fp);
			break;
		case 'm5':
			$val = md5_file($fp);
			break;
	}

	return $val;
}

// return a sorted list of directory names at the given path
function getDirs ($path)
{
	$dirs = array();
	if ($dh = opendir($path)) {
		while (($file = readdir($dh)) !== false) {
			if ($file == '.' || $file == '..') continue;
			if (is_dir($path.'/'.$file)) {
				$dirs[] = $file;
			}
		}
		closedir($dh);
	}
	sort($dirs);
	return $dirs;
}

$i = 0;
?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" dir="ltr">
<head>
	<title>Dir/Files Integrity Tracker</title>
	<style>
		.err {color:red}
		.err2 {color:magenta}
		.yay {color:green}
		.chk {color:blue}
		.cdiv {margin:1em 0}
		.adiv {display:inline-block;vertical-align:top;padding-right:6em}
	</style>
</head>
<body>
<?php echo $msg; ?>
<form action="" method="post">
<div class="cdiv"><div class="adiv">
<?php foreach ($dirs as $dir) {
	$cm = file_exists($dir.$trkext) ? ' <span class="chk">&#x2713;</span>' : '';
	echo <<<EOT
	<input type="radio" name="dir" id="dir" value="{$dir}" /> <label for="dir">{$dir}{$cm}</label><br />
EOT;
	if (++$i == 25) {
		echo '</div><div class="adiv">';
		$i = 0;
	}
}
?>
</div></div>
<button type="submit" name="check">Check</button>
<input type="checkbox" name="all" id="all" value="1" />
<label for="all">All profiled directories</label><br />
<button type="submit" name="create">Create Track Profile</button>
<select name="ttype">
	<option value="fs">File Size</option>
	<option value="m5">MD5 Hash</option>
</select>
</form>
</body>
</html>