Import from the old rivettracker git repository at sourceforge (amisaph/amisapphire branch) master
Import from the old rivettracker git repository at sourceforge (amisaph/amisapphire branch)

file:b/.htaccess.dist (new)
--- /dev/null
+++ b/.htaccess.dist
@@ -1,1 +1,5 @@
+#Options -Indexes +FollowSymLinks
+#RewriteEngine On
+RewriteRule ^announce$ announce.php [NC,L]
+RewriteRule ^scrape$ scrape.php [NC,L]
 

file:b/BDecode.php (new)
--- /dev/null
+++ b/BDecode.php
@@ -1,1 +1,217 @@
-
+<?php
+/*
+
+	Programming info
+
+All functions output a small array, which we'll call $return for now.
+
+$return[0] is the data expected of the function
+$return[1] is the offset over the whole bencoded data of the next
+           piece of data.
+
+numberdecode returns [0] as the integer read, and [1]-1 points to the
+symbol that was interprented as the end of the interger (either "e" or
+":"). 
+numberdecode is used for integer decodes both for i11e and 11:hello there
+so it is tolerant of the ending symbol.
+
+decodelist returns $return[0] as an integer indexed array like you would use in C
+for all the entries. $return[1]-1 is the "e" that ends the list, so [1] is the next
+useful byte.
+
+decodeDict returns $return[0] as an array of text-indexed entries. For example,
+$return[0]["announce"] = "http://www.whatever.com:6969/announce";
+$return[1]-1 again points to the "e" that ends the dictionary.
+
+decodeEntry returns [0] as an integer in the case $offset points to
+i12345e or a string if $offset points to 11:hello there style strings.
+It also calls decodeDict or decodeList if it encounters a d or an l.
+
+
+Known bugs:
+- The program doesn't pay attention to the string it's working on.
+  A zero-sized or truncated data block will cause string offset errors
+  before they get rejected by the decoder. This is worked around by
+  suppressing errors.
+
+*/
+
+// Protect our namespace using a class
+class BDecode
+{
+
+function numberdecode($wholefile, $start)
+{
+	$ret[0] = 0;
+	$offset = $start;
+
+	// Funky handling of negative numbers and zero
+	$negative = false;
+	if ($wholefile[$offset] == '-')
+	{
+		$negative = true;
+		$offset++;
+	}
+	if ($wholefile[$offset] == '0')
+	{
+		$offset++;
+		if ($negative)
+			return array(false);
+		if ($wholefile[$offset] == ':' || $wholefile[$offset] == 'e')
+		{
+			$offset++;
+			$ret[0] = 0;
+			$ret[1] = $offset;
+			return $ret;
+		}
+		return array(false);
+	}
+	while (true)
+	{
+
+		if ($wholefile[$offset] >= '0' && $wholefile[$offset] <= '9')
+		{
+			
+			$ret[0] *= 10;
+			$ret[0] += ord($wholefile[$offset]) - ord("0");
+			$offset++;
+		}
+		// Tolerate : or e because this is a multiuse function
+		else if ($wholefile[$offset] == 'e' || $wholefile[$offset] == ':')
+		{
+			$ret[1] = $offset+1;
+			if ($negative)
+			{
+				if ($ret[0] == 0)
+					return array(false);
+				$ret[0] = - $ret[0];
+			}
+			return $ret;
+		}
+		else
+			return array(false);
+	}
+
+}
+
+function decodeEntry($wholefile, $offset=0)
+{
+	if ($wholefile[$offset] == 'd')
+		return $this->decodeDict($wholefile, $offset);
+	if ($wholefile[$offset] == 'l')
+		return $this->decodelist($wholefile, $offset);
+	if ($wholefile[$offset] == "i")
+	{
+		$offset++;
+		return $this->numberdecode($wholefile, $offset);
+	}
+	// String value: decode number, then grab substring
+	$info = $this->numberdecode($wholefile, $offset);
+	if ($info[0] === false)
+		return array(false);
+	$ret[0] = substr($wholefile, $info[1], $info[0]);
+	$ret[1] = $info[1]+strlen($ret[0]);
+	return $ret;
+}
+
+function decodeList($wholefile, $start)
+{
+	$offset = $start+1;
+	$i = 0;
+	if ($wholefile[$start] != 'l')
+		return array(false);
+	$ret = array();
+	while (true)
+	{
+		if ($wholefile[$offset] == 'e')
+			break;
+		$value = $this->decodeEntry($wholefile, $offset);
+		if ($value[0] === false)
+			return array(false);
+		$ret[$i] = $value[0];
+		$offset = $value[1];
+		$i ++;
+	}
+
+	// The empy list is an empty array. Seems fine.
+	$final[0] = $ret;
+	$final[1] = $offset+1;
+	return $final;
+
+
+
+}
+
+// Tries to construct an array
+function decodeDict($wholefile, $start=0)
+{
+	$offset = $start;
+	if ($wholefile[$offset] == 'l')
+		return $this->decodeList($wholefile, $start);
+	if ($wholefile[$offset] != 'd')
+		return false;
+	$ret = array();
+	$offset++;
+	while (true)
+	{	
+		if ($wholefile[$offset] == 'e')
+		{
+			$offset++;
+			break;
+		}
+		$left = $this->decodeEntry($wholefile, $offset);
+		if (!$left[0])
+			return false;
+		$offset = $left[1];
+		if ($wholefile[$offset] == 'd')
+		{
+			// Recurse
+			$value = $this->decodedict($wholefile, $offset);
+			if (!$value[0])
+				return false;
+			$ret[addslashes($left[0])] = $value[0];
+			$offset= $value[1];
+			continue;
+		}
+		else if ($wholefile[$offset] == 'l')
+		{
+			$value = $this->decodeList($wholefile, $offset);
+			if (!$value[0] && is_bool($value[0]))
+				return false;
+			$ret[addslashes($left[0])] = $value[0];
+			$offset = $value[1];
+		}
+		else
+		{
+ 			$value = $this->decodeEntry($wholefile, $offset);
+			if ($value[0] === false)
+				return false;
+			$ret[addslashes($left[0])] = $value[0];
+			$offset = $value[1];
+		}
+	}
+	if (empty($ret))
+		$final[0] = true;
+	else
+		$final[0] = $ret;
+	$final[1] = $offset;
+   	return $final;
+
+
+}
+
+
+} // End of class declaration.
+
+
+
+// Use this function. eg:  BDecode("d8:announce44:http://www. ... e");
+function BDecode($wholefile)
+{
+	$decoder = new BDecode;
+	$return = $decoder->decodeEntry($wholefile);
+	return $return[0];
+}
+
+
+?>

file:b/BEncode.php (new)
--- /dev/null
+++ b/BEncode.php
@@ -1,1 +1,122 @@
+<?php
 
+// Woohoo! Who needs mhash or PHP 4.3?
+// Don't require it. Still recommended, but not mandatory.
+if (!function_exists("sha1"))
+	@include_once("sha1lib.php");
+
+
+// We'll protect the namespace of our code
+// using a class
+class BEncode
+{
+
+// Dictionary keys must be sorted. foreach tends to iterate over the order
+// the array was made, so we make a new one in sorted order. :)
+/*
+function makeSorted($array)
+{
+	$i = 0;
+
+	// Shouldn't happen!
+	if (empty($array))
+		return $array;
+
+	foreach($array as $key => $value)
+		$keys[$i++] = stripslashes($key);
+	sort($keys);
+	for ($i=0 ; isset($keys[$i]); $i++)
+		$return[addslashes($keys[$i])] = $array[addslashes($keys[$i])];
+	return $return;
+}
+*/
+// Encodes strings, integers and empty dictionaries.
+// $unstrip is set to true when decoding dictionary keys
+function encodeEntry($entry, &$fd, $unstrip = false)
+{
+	if (is_bool($entry))
+	{
+		$fd .= "de";
+		return;
+	}
+	if (is_int($entry) || is_float($entry))
+	{
+		$fd .= "i".$entry."e";
+		return;
+	}
+	if ($unstrip)
+		$myentry = stripslashes($entry);
+	else
+		$myentry = $entry;
+	$length = strlen($myentry);
+	$fd .= $length.":".$myentry;
+	return;
+}
+
+// Encodes lists
+function encodeList($array, &$fd)
+{
+	$fd .= "l";
+
+	// The empty list is defined as array();
+	if (empty($array))
+	{
+		$fd .= "e";
+		return;
+	}
+	for ($i = 0; isset($array[$i]); $i++)
+		$this->decideEncode($array[$i], $fd);
+	$fd .= "e";
+}
+
+// Passes lists and dictionaries accordingly, and has encodeEntry handle
+// the strings and integers.
+function decideEncode($unknown, &$fd)
+{
+	if (is_array($unknown))
+	{
+		if (isset($unknown[0]) || empty($unknown))
+			return $this->encodeList($unknown, $fd);
+		else
+			return $this->encodeDict($unknown, $fd);
+	}
+	$this->encodeEntry($unknown, $fd);
+}
+
+// Encodes dictionaries
+function encodeDict($array, &$fd)
+{
+	$fd .= "d";
+	if (is_bool($array))
+	{
+		$fd .= "e";
+		return;
+	}
+	// NEED TO SORT!
+	//$newarray = $this->makeSorted($array);
+	ksort($array, SORT_STRING);
+
+	foreach($array as $left => $right)
+	{
+		$this->encodeEntry($left, $fd, true);
+		$this->decideEncode($right, $fd);
+	}
+	$fd .= "e";
+	return;
+}
+
+
+
+} // End of class declaration.
+
+// Use this function in your own code.
+function BEncode($array)
+{
+	$string = "";
+	$encoder = new BEncode;
+	$encoder->decideEncode($array, $string);
+	return $string;
+}
+
+
+?>

--- /dev/null
+++ b/DumpTorrentCGI.php
@@ -1,1 +1,45 @@
+<?php
+require ("config.php");
+require ("funcsv2.php");
+//Check session
+session_start();
 
+if (!$_SESSION['admin_logged_in'])
+{
+	//check fails
+	header("Location: authenticate.php?status=session");
+	exit();
+}
+?>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html><head><title>Torrent Information</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+	<link rel="stylesheet" href="./css/style.css" type="text/css" />
+</head><body>
+<?php
+require_once("torrent_functions.php");
+?>
+<table width="50%" border=0><tr><td>
+This script parses a torrent file and displays detailed information about it.
+</td></tr>
+</table><br>
+<form enctype="multipart/form-data" method="POST" action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>">
+Torrent file: <input type="file" name="torrent" size="40"><br>
+<br>
+OR
+<br><br>
+Torrent URL: <input type=text name="url" size="50"><br><br>
+Output type: <select name="output">
+<option value="-1">Auto-detect
+<option value="0">Classic (raw)
+<option value="1">.torrent file
+<option value="2">/scrape
+<option value="3">/announce
+</select><br><br>
+<input type="submit" value="Decode">
+</form>
+
+<a href="admin.php"><img src="images/admin.png" border="0" class="icon" alt="Admin Page" title="Admin Page" /></a><a href="admin.php">Return to Admin Page</a>
+</body></html>
+

file:b/admin.php (new)
--- /dev/null
+++ b/admin.php
@@ -1,1 +1,60 @@
+<?php
 
+require ("config.php");
+require ("funcsv2.php");
+//Check session
+session_start();
+
+if (!$_SESSION['admin_logged_in'])
+{
+	//check fails
+	header("Location: authenticate.php?status=session");
+	exit();
+}
+?>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+	<title>Admin Page</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+	<link rel="stylesheet" href="./css/style.css" type="text/css" />
+</head>
+<body>
+<h1>Admin Page</h1>
+
+<a href="newtorrents.php"><img src="images/add.png" border="0" class="icon" alt="Add Torrent" title="Add Torrent" /></a><a href="newtorrents.php">Add Torrent to Tracker Database</a><br>
+<a href="batch_upload.php"><img src="images/batch_upload.png" border="0" class="icon" alt="Batch Upload Torrents" title="Batch Upload Torrents" /></a><a href="batch_upload.php">Batch Upload Torrents</a><br>
+<a href="edit_database.php"><img src="images/database.png" border="0" class="icon" alt="Edit Torrent in Database" title="Edit Torrent in Database" /></a><a href="edit_database.php">Edit Torrent Already in Database</a><br>
+<a href="DumpTorrentCGI.php"><img src="images/torrent.png" border="0" class="icon" alt="Show Information on Torrent" title="Show Information on Torrent" /></a><a href="DumpTorrentCGI.php">Show Information on Torrent File</a><br>
+<a href="index.php"><img src="images/stats.png" border="0" class="icon" alt="Tracker Statistics" title="Tracker Statistics" /></a><a href="index.php">Show Current Tracker Statistics</a><br>
+<a href="sanity.php"><img src="images/check.png" border="0" class="icon" alt="Check for Expired Peers" title="Check for Expired Peers" /></a><a href="sanity.php">Check Tracker for Expired Peers</a><br>
+<a href="statistics.php"><img src="images/userstats.png" border="0" class="icon" alt="User Statistics" title="User Statistics" /></a><a href="statistics.php">Detailed User Statistics from Tracker</a><br>
+<a href="deleter.php"><img src="images/delete.png" border="0" class="icon" alt="Delete Torrent" title="Delete Torrent" /></a><a href="deleter.php">Delete Torrent from Tracker Database</a><br>
+<a href="editconfig.php"><img src="images/edit.png" border="0" class="icon" alt="Edit Config File" title="Edit Config File" /></a><a href="editconfig.php">Edit Configuration Settings</a><br>
+<a href="uploadstats.php"><img src="images/download.png" border="0" class="icon" alt="Upload Statistics" title="Upload Statistics" /></a><a href="uploadstats.php">Upload Statistics</a><br>
+<a href="css.php"><img src="images/color.png" border="0" class="icon" alt="Change CSS File" title="Change CSS File" /></a><a href="css.php">Change CSS File</a><br>
+<a href="./docs/help.html"><img src="images/help.png" border="0" class="icon" alt="Help" title="Help" /></a><a href="./docs/help.html">Help</a><br>
+<a href="authenticate.php?status=logout"><img src="images/logout.png" border="0" class="icon" alt="Logout" title="Logout" /></a><a href="authenticate.php?status=logout">Logout</a><br>
+
+<?php
+//Check for install.php file, security risk if still available
+if (file_exists("install.php"))
+{
+	echo errorMessage() . "Your install.php file has NOT been deleted.  This is a security risk, please delete it immediately.</p>\n";
+}
+
+if (!is_writeable("./torrents/"))
+{
+	echo errorMessage() . "The 'torrents' folder does not have write access, check the permissions.</p>\n";
+}
+
+if (!is_writeable("./rss/"))
+{
+	echo errorMessage() . "The 'rss' folder does not have write access, check the permissions.</p>\n";
+}
+
+?>
+</body>
+</html>

file:b/announce.php (new)
--- /dev/null
+++ b/announce.php
@@ -1,1 +1,10 @@
+<?php
 
+/// Use this file as an alternative to tracker.php/announce
+/// for TorrentSpy and other /scrape support.
+
+$_SERVER["PATH_INFO"] = "/announce";
+require("tracker.php");
+exit;
+
+?>

file:b/authenticate.php (new)
--- /dev/null
+++ b/authenticate.php
@@ -1,1 +1,66 @@
+<?php
+//Main Login Page
 
+//Destroy any previous session data
+//This way it requires a login
+session_start();
+session_destroy();
+
+//get status
+$status = $_GET['status'];
+
+?>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+	<title>Login</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+	<link rel="stylesheet" href="./css/style.css" type="text/css" />
+</head>
+<body>
+<center>
+<h1>Login</h1>
+<img src="images/lock.png" border="0" alt="Please Login" title="Please Login" />
+<h3>Please login with your username and password.</h3>
+<form action="login.php" method="POST">
+<table border="0">
+<tr><td class="right">
+Username:</td>
+<td class="left">
+<input type="text" size="20" name="f_user">
+</td></tr>
+<tr><td class="right">
+Password:</td>
+<td class="left">
+<input type="password" size="20" name="f_pass">
+</td></tr>
+<tr><td></td><td class="left">
+<input type="submit" name="LogIn" value="Log In">
+</td></tr>
+</table>
+<?php
+//Display legal stuff if file exists
+if (file_exists("legalterms.txt"))
+	echo "<br><input type=\"checkbox\" name=\"legalterms\"> I agree to the <a href=\"legalterms.txt\">use policy and terms of service.</a>";
+else //display hidden value, needed so that login.php can check the value
+	echo "<input type=\"hidden\" name=\"legalterms\" value=\"on\">";
+
+if ($status == "error")
+echo "<p class=\"error\">Error, username or password is incorrect.<br>Entries are cAsESEnsITiVE, do you have your capslock key on?...</p>";
+if ($status == "session")
+echo "<p class=\"error\">Your session has timed out, please re-login.</p>";
+if ($status == "logout")
+echo "<p class=\"success\">You have successfully logged out.</p>";
+if ($status == "indexlogin")
+echo "<p class=\"error\">Error, this tracker requires a username and password in order to view the main page.</p>";
+if ($status == "legalterms")
+echo "<p class=\"error\">You need to agree to the use policy and terms of service in order to log in.</p>";
+?>
+</form>
+<br>
+<a href="index.php"><img src="images/stats.png" border="0" class="icon" alt="Tracker Statistics" title="Tracker Statistics" /></a><a href="index.php">Tracker Statistics</a><br>
+</center>
+</body>
+</html>
+

file:b/batch_upload.php (new)
--- /dev/null
+++ b/batch_upload.php
@@ -1,1 +1,215 @@
-
+<?php
+
+require ("config.php");
+require ("funcsv2.php"); //required for errorMessage() function
+//Check session
+session_start();
+
+if (!$_SESSION['admin_logged_in'])
+{
+	//check fails
+	header("Location: authenticate.php?status=session");
+	exit();
+}
+?>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+	<title>Batch Upload Torrents</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+	<link rel="stylesheet" href="./css/style.css" type="text/css" />
+</head>
+<body>
+<center>
+<h1>Batch Upload Torrents</h1>
+</center>
+<br>
+
+<?php
+
+if (isset($_FILES["zipfile"]) && $_FILES["zipfile"]["error"] != 4 && isset($_FILES["zipfile"]["tmp_name"])) //4 corresponds to the error no file uploaded
+{
+	?>
+	<a href="admin.php"><img src="images/admin.png" border="0" class="icon" alt="Admin Page" title="Admin Page" /></a><a href="admin.php">Return to Admin Page</a>
+	<br><br>
+	<?php
+	$zip = zip_open($_FILES["zipfile"]["tmp_name"]);
+	
+	if ($zip == true)
+	{
+		$db = mysql_connect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Couldn't connect to the database, contact the administrator</p>");
+		mysql_select_db($database) or die(errorMessage() . "Can't open the database.</p>");
+	
+	   while ($zip_entry = zip_read($zip))
+	   {
+	   	echo "Name: " . zip_entry_name($zip_entry) . "<br>\n";
+	      if (substr(zip_entry_name($zip_entry), -8) == ".torrent")
+			{
+				$error_status = true;
+				if (zip_entry_open($zip, $zip_entry, "r"))
+			   {
+			   	//read in file from zip
+			  		$buffer = zip_entry_read($zip_entry, zip_entry_filesize($zip_entry));
+			      //go through each torrent file and add it if possible
+					require_once ("BDecode.php");
+					require_once ("BEncode.php");
+					
+					$tracker_url = $website_url . substr($_SERVER['REQUEST_URI'], 0, -16) . $announceurl;
+					
+					$array = BDecode($buffer);
+					if (!$array)
+					{
+						echo errorMessage() . "Error: The parser was unable to load this torrent.</p>\n";
+						$error_status = false;
+					}
+					if (isset($array["announce-list"])) {
+						//multiple trackers are listed
+						$found_tracker = false;
+						for ($i = 0; $i < count($array["announce-list"]); $i++) {
+							if (strtolower($array["announce-list"][$i][0]) == $tracker_url) {
+								$found_tracker = true;
+								break;
+							}
+						}
+						if ($found_tracker == false)
+						{
+							echo errorMessage() . "Error: Multiple trackers were found but none of them match the
+								announce URL:<br>$tracker_url<br>Please re-create and re-upload the torrent.</p>\n";
+							$error_status = false;
+							exit;
+						}
+					} else {
+						//a single tracker is listed
+						if (strtolower($array["announce"]) != $tracker_url) {
+							echo errorMessage() . "Error: The tracker announce URL does not match this:<br>$tracker_url<br>Please re-create and re-upload the torrent.</p>\n";
+							$error_status = false;
+							exit;
+						}
+					}
+					if (function_exists("sha1"))
+						$hash = @sha1(BEncode($array["info"]));
+					else
+					{
+						echo errorMessage() . "Error: It looks like you do not have a hash function available, this will not work.</p>\n";
+						$error_status = false;
+					}
+				
+					//figure out total size of all files in torrent, needed for insertion into database
+					$info = $array["info"];
+					$total_size = 0;
+					if (isset($info["files"]))
+					{
+						foreach ($info["files"] as $file)
+						{
+							$total_size = $total_size + $file["length"];
+						}
+					}
+					else
+					{
+						$total_size = $info["length"];
+					}
+					
+					//Validate torrent file, make sure everything is correct
+					$filename = $array["info"]["name"];
+					$filename = mysql_real_escape_string($filename);
+					$filename = stripslashes($filename);
+					$filename = clean($filename);
+				
+					if ((strlen($hash) != 40) || !verifyHash($hash))
+					{
+						echo errorMessage() . "Error: Info hash must be exactly 40 hex bytes.</p>\n";
+						$error_status = false;
+					}
+					
+				
+					if ($error_status == true)
+					{
+						$query = "INSERT INTO " . $prefix . "namemap (info_hash, title, filename, url, size, pubDate) VALUES (\"$hash\", \"$filename\", \"$filename\", \"$url\", \"$total_size\", \"" . date("$dateformat") . "\")";
+						$status = makeTorrent($hash, true);
+						quickQuery($query);
+						if ($status == true)
+						{
+							//create torrent file in folder, at this point we assume it's valid
+							if (!$handle = fopen("torrents/" . $filename . ".torrent", 'w'))
+							{
+	         				echo errorMessage() . "Error: Can't write to file.</p>\n";
+	        					break;
+	    					}
+							//populate file with contents
+					   	if (fwrite($handle, $buffer) === FALSE)
+					   	{
+					       	echo errorMessage() . "Error: Can't write to file.</p>\n";
+					      	break;
+					   	}
+					   	fclose($handle);
+							//make torrent file readable by all
+							chmod("torrents/" . $filename . ".torrent", 0644);
+							echo "<p class=\"success\">Torrent was added successfully.</p>\n";
+						}
+						else
+						{
+							echo errorMessage() . "There were some errors. Check if this torrent has been added previously.</p>\n";
+						}
+					}
+			
+			      zip_entry_close($zip_entry);
+			    }
+			} 
+			else
+				echo errorMessage() . "Unable to add torrent, it doesn't end in .torrent</p>\n";
+			
+		echo "<br>";
+	   }
+	   zip_close($zip);
+	}
+
+	//finished reading zip file
+	
+	//run RSS generator because we have new torrents in database
+	require_once("rss_generator.php");
+
+
+}
+else
+{
+	//display upload box
+	?>
+	<?php require("config.php"); $tracker_url = $website_url . substr($_SERVER['REQUEST_URI'], 0, -16) . $announceurl; ?>
+	<p>This page lets you upload a zip file containing multiple torrents and add them into the database.  The
+	zip file cannot have any folders in it.  This requires that you are running PHP with compiled zip support.
+	If you are unsure, check with your system administrator or phpinfo().  Any torrents that already exist in
+	the database will be skipped.  If you want to use HTTP seeding you'll need to add this feature to the torrent
+	files before you zip and upload the file.  If you are uploading a very large zip file this may take some time...
+	<br>
+	<br>
+	Notes:
+	<br>
+	[1] Even if the custom title option is enabled, the torrents will have the same title as the filename.  If you
+	have the custom title option enabled, you may change the titles to your preference after the batch upload has
+	finished.<br>[2] The torrents you are batch uploading should include the following Tracker URL:
+	<b><?php echo $tracker_url ?></b></p>
+	
+	<?php
+	if (function_exists("zip_open"))
+	{
+		?>
+		<form enctype="multipart/form-data" action="<?php echo htmlentities($_SERVER['PHP_SELF']);?>" method="post">
+		<b>Zip File:</b><input type="file" name="zipfile" size="50"/>
+		<input type="submit" value="Upload ZIP File"/>
+		</form>
+		<?php
+	}
+	else
+		echo errorMessage() . "Error: It looks like you don't have ZIP support compiled into PHP.</p>\n";
+}
+
+?>
+
+<br>
+<br>
+<a href="admin.php"><img src="images/admin.png" border="0" class="icon" alt="Admin Page" title="Admin Page" /></a><a href="admin.php">Return to Admin Page</a>
+</body>
+</html>
+

file:b/css.php (new)
--- /dev/null
+++ b/css.php
@@ -1,1 +1,270 @@
-
+<?php
+
+require ("config.php");
+require ("funcsv2.php"); //required for errorMessage() function
+//Check session
+session_start();
+
+if (!$_SESSION['admin_logged_in'])
+{
+	//check fails
+	header("Location: authenticate.php?status=session");
+	exit();
+}
+
+// Prep database, needed for cleaning function
+if ($GLOBALS["persist"])
+	$db = @mysql_pconnect($dbhost, $dbuser, $dbpass) or showError("Can't connect to database. Contact the webmaster.");
+else
+	$db = @mysql_connect($dbhost, $dbuser, $dbpass) or showError("Can't connect to database. Contact the webmaster.");
+@mysql_select_db($database) or showError("Can't open database. Contact the webmaster");
+
+?>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+	<title>Change CSS File</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+	<link rel="stylesheet" href="./css/style.css" type="text/css" />
+	<style type="text/css">
+		td.cell{
+			width: 2%;
+			}
+	</style>
+	<script type="text/javascript">
+	function changeColor(color)
+	{
+		document.getElementById("color_box").value = color;
+		document.getElementById("thecolor").style.backgroundColor = color;
+	}
+	</script>
+</head>
+<body>
+<center>
+<h1>Change CSS File</h1>
+</center>
+<br>
+
+<?php
+
+if (isset($_POST["set_css"]))
+{
+	//delete style.css file
+	if (copy("./css/" . filterData($_POST["set_css"]), "./css/style.css"))
+		echo "<p class=\"success\">style.css file has been replaced with " . filterData($_POST["set_css"]) . "</p>";
+	else
+	{
+		echo errorMessage() . "Error: Unable to copy over style.css, are the permissions correct?</p>";
+		exit();
+	}
+}
+elseif (isset($_POST["delete_css"]))
+{
+	//delete css file
+	if (unlink("./css/" . filterData($_POST["delete_css"])))
+		echo "<p class=\"success\">" . filterData($_POST["delete_css"]) . " has been deleted</p>";
+	else
+	{
+		echo errorMessage() . "Error: Unable to delete " . filterData($_POST["delete_css"]) . ", are you sure the permissions are correct?</p>";
+		exit();
+	}
+}
+elseif (isset($_POST["create_css"]))
+{
+	//create new css file by copying over style.css into new file
+	if (substr($_POST["create_css"], -4) == ".css")
+	{
+		if (!file_exists("./css/" . filterData($_POST["create_css"])))
+		{
+			if (copy("./css/style.css", "./css/" . filterData($_POST["create_css"])))
+				echo "<p class=\"success\">" . filterData($_POST["create_css"]) . ", was created successfuly</p>";
+			else
+			{
+				echo errorMessage() . "Error: Unable to create " . filterData($_POST["create_css"]) . ", are you sure the permissions are correct?</p>";
+				exit();			
+			}
+		}
+		else
+		{
+			echo errorMessage() . "Error: " . filterData($_POST["create_css"]) . " already exists, please choose a different name</p>";
+			exit();
+		}
+	}
+	else
+	{
+		echo errorMessage() . "Error: Your file doesn't end with .css</p>";
+		exit();
+	}
+}
+
+if (isset($_POST["create_css"]) || isset($_POST["edit_css"]))
+{
+	//display color picker
+	?>
+	<h2>Color Picker:</h2>
+	<table style="cursor: pointer;" border="0">
+	<?php
+	function rgbhex($red, $green, $blue)
+	{
+		return sprintf('#%02X%02X%02X', $red, $green, $blue);
+	}
+	
+	//create table of 216 web safe colors
+	for ($red = 0; $red < 256; $red = $red + 51)
+	{
+		echo "<tr>";
+		for ($green = 0; $green < 256; $green = $green + 51)
+		{
+			for ($blue = 0; $blue < 256; $blue = $blue + 51)
+			{
+				$hexcolor = rgbhex($red, $green, $blue);
+				echo "<td bgcolor='" . $hexcolor . "' title='" . $hexcolor . "' class='cell' onClick=\"changeColor('" . $hexcolor . "')\">&nbsp;</td>\n";
+			}
+		}
+		echo "</tr>";
+	}
+	
+	?>
+	</table>
+	<br>
+	<b>Color:</b>
+	<table border="0"><tr>
+	<td id="thecolor" align="left" bgcolor="#000000"><input type="text" id="color_box" value="#000000"/>
+	</td></tr>
+	</table>
+	<br>
+	<?php
+	
+	if (isset($_POST["create_css"]))
+		$filename = filterData($_POST["create_css"]);
+	if (isset($_POST["edit_css"]))
+		$filename = filterData($_POST["edit_css"]);
+	//display text box with css in it
+	?>
+	<h2>Editing File: <?php echo $filename;?></h2>
+	<form action="<?php echo htmlentities($_SERVER['PHP_SELF']);?>" method="post">
+	<input type="hidden" name="hidden_filename" value="<?php echo $filename;?>"/>
+	<input type="hidden" name="current_css_file" value="<?php echo $_POST['current_css_file'];?>"/>
+	<textarea name="file_contents" cols="120" rows="20"><?php
+	//open css file
+	readfile("./css/" . $filename);
+	?></textarea>
+	<br><br>
+	<input type="submit" value="Save File"/>
+	</form>
+	<?php
+	
+}
+
+if (isset($_POST["file_contents"]))
+{
+	//save previously edited text into file
+	if (is_writable("./css/" . filterData($_POST["hidden_filename"])))
+	{
+		//open file
+		$stream = fopen("./css/" . filterData($_POST["hidden_filename"]), "w");
+		fwrite($stream, filterData($_POST["file_contents"]));
+		fclose($stream);
+		echo "<p class=\"success\">" . filterData($_POST["hidden_filename"]) . ", was saved successfuly</p>";
+	}
+	else
+	{
+		echo errorMessage() . "Error: The file cannot be saved, check the permissions</p>";
+		exit();
+	}
+	//if editing the current css file, replace that too
+	if ($_POST["current_css_file"] == $_POST["hidden_filename"])
+	{
+		if (copy("./css/" . filterData($_POST["hidden_filename"]), "./css/style.css"))
+			echo "<p class=\"success\">style.css file has been replaced with " . filterData($_POST["hidden_filename"]) . "</p>";
+		else
+		{
+			echo errorMessage() . "Error: Unable to copy over style.css, are the permissions correct?</p>";
+			exit();
+		}
+	}
+}
+
+if (!isset($_POST["create_css"]) && !isset($_POST["edit_css"]) && !isset($_POST["delete_css"]) && 
+!isset($_POST["set_css"]) && !isset($_POST["file_contents"]))
+{
+	//save all files in css directory to array
+	$current_css_file = "";
+	$css_style_md5 = md5_file("./css/style.css");
+	$number_files = 0;
+	if ($dh = opendir("./css/"))
+	{
+		while (($file = readdir($dh)) !== false)
+		{
+			if (filetype("./css/" . $file) == "file" && $file != "index.php" && $file != "style.css" && substr($file, -4) == ".css")
+			{
+				if (md5_file("./css/" . $file) == $css_style_md5)
+					$current_css_file = $file;
+				$files_array[$number_files] = $file;
+				$number_files++;
+			}
+		}
+		closedir($dh);
+	}
+	echo "<b>Currently Used CSS File: " . $current_css_file . "</b><br><br>";
+	?>
+	
+	<form action="<?php echo htmlentities($_SERVER['PHP_SELF']);?>" method="post">
+	<b>Set CSS File:</b><select name="set_css">
+	<?php
+	for ($i = 0; $i < $number_files; $i++)
+	{
+		if ($files_array[$i] != $current_css_file) //no point setting it to itself...
+			echo "<option value=\"" . $files_array[$i] . "\">" . $files_array[$i] . "</option>\n\t";
+	}
+	?>
+	</select>
+	<input type="submit" value="Set CSS File"/>
+	</form>
+	<br><br>
+	
+	<form action="<?php echo htmlentities($_SERVER['PHP_SELF']);?>" method="post"> 
+	<b>Delete CSS File:</b><select name="delete_css">
+	<?php
+	for ($i = 0; $i < $number_files; $i++)
+	{
+		if ($files_array[$i] != $current_css_file) //can't delete the file if it's already being used...
+			echo "<option value=\"" . $files_array[$i] . "\">" . $files_array[$i] . "</option>\n\t";
+	}
+	?>
+	</select>
+	<input type="submit" value="Delete CSS File"/>
+	</form>
+	<br><br>
+	
+	<form action="<?php echo htmlentities($_SERVER['PHP_SELF']);?>" method="post">
+	<input type="hidden" name="current_css_file" value="<?php echo $current_css_file;?>"/>
+	<b>Edit Existing CSS File:</b><select name="edit_css">
+	<?php
+	for ($i = 0; $i < $number_files; $i++)
+	{
+		echo "<option value=\"" . $files_array[$i] . "\">" . $files_array[$i] . "</option>\n\t";
+	}
+	?>
+	</select>
+	<input type="submit" value="Edit CSS File"/>
+	</form>
+	<br><br>
+	
+	<form action="<?php echo htmlentities($_SERVER['PHP_SELF']);?>" method="post"> 
+	<b>Create New CSS File (e.g. mycssfile.css):</b>
+	<input type="text" size="40" name="create_css"/>
+	<input type="submit" value="Create New CSS File"/>
+	</form>
+	<?php
+}
+?>
+
+<br>
+<br>
+<a href="admin.php"><img src="images/admin.png" border="0" class="icon" alt="Admin Page" title="Admin Page" /></a><a href="admin.php">Return to Admin Page</a>
+</body>
+</html>
+

--- /dev/null
+++ b/css/blue_black.css
@@ -1,1 +1,101 @@
-
+th { /* Table Header */
+	background-color: #3366CC;
+	padding: 4px;
+	border-bottom: 2px solid #000000;
+}
+th.subheader { /* Sub-Table Header */
+	background-color: #306EFF
+	border-bottom: 2px solid #660000;
+}
+tr.selected {
+        background-color: #FFCC00;
+        color: #FFFFFF;
+}
+tr.row0 {
+	background-color: #C9C2CC;
+	color: #000000;
+}
+tr.row0 a:link {color: #000066}
+tr.row0 a:visited {color: #000066}
+tr.row0 a:active  {color: #000066}
+tr.row0 a:hover   {color: #000066}
+tr.row1 {
+	background-color: #CCCCFF;
+	color: #000000;
+}
+tr.row1 a:link {color: #000066}
+tr.row1 a:visited {color: #000066}
+tr.row1 a:active  {color: #000066}
+tr.row1 a:hover   {color: #000066}
+td.percent {
+	background-color: #33CC33;
+}
+td.percentleft {
+	background-color: #CCCCCC;
+}
+img.icon { 
+	vertical-align: middle;
+	padding: 4px;
+}
+body {
+	font: 12pt sans-serif;
+	background-color: #000060;
+	color: #FFFFFF;
+}
+h1 { /* Tracker Header/Title */
+	font: 24px verdana, sans-serif;
+	text-align: center;
+}
+h2 { /* Smaller Page Headers */
+	font: 18px verdana, sans-serif;
+}
+a {
+	color: #FF33FF;
+	text-decoration: none;
+	background-color: transparent;
+}
+a:hover {
+	text-decoration: underline;
+}
+a:link    {color: #CCCCFF}
+a:visited {color: #CCCCFF}
+a:active  {color: #CCCCFF}
+a:hover   {color: #CCCCFF}
+table.percentages {
+	width: 200px;
+}
+table.torrentlist td {
+	padding: 4px;
+}
+table.nopadding td {
+	padding: 0px;
+}
+.details {
+	font: 12px verdana, sans-serif;
+	height: 0px;
+}
+p.error {
+	color: yellow;
+	text-align: center;
+	font-weight: bold;
+}
+p.success {
+	color: green;
+	text-align: center;
+	font-weight: bold;
+}	
+table {
+	width: 100%;
+}
+.center {	
+	text-align: center; 
+}
+.left {
+	text-align: left;
+}
+.right {
+	text-align: right;
+}
+span.notice {
+	color: #FF0000;
+}

file:b/css/index.php (new)
--- /dev/null
+++ b/css/index.php
@@ -1,1 +1,5 @@
+<?php
 
+header("Location: ../index.php");
+
+?>

--- /dev/null
+++ b/css/light_blue.css
@@ -1,1 +1,95 @@
-
+th { /* Table Header */
+	background-color: #CCCC99;
+	padding: 4px;
+	border-bottom: 2px solid #664D33;
+}
+th.subheader { /* Sub-Table Header */
+	background-color: #99FF99;
+	border-bottom: 2px solid #336633;
+}
+tr.selected {
+        background-color: #FFCC00;
+        color: #000000;
+}
+tr.row0 {
+	background-color: #A7BCD3;
+}
+tr.row0 a:link {color: #000770}
+tr.row0 a:visited {color: #000770}
+tr.row0 a:active  {color: #000770}
+tr.row0 a:hover   {color: #000770}
+tr.row1 {
+	background-color: #8AA9C6;
+}
+tr.row1 a:link {color: #000770}
+tr.row1 a:visited {color: #000770}
+tr.row1 a:active  {color: #000770}
+tr.row1 a:hover   {color: #000770}
+td.percent {
+	background-color: #33CC33;
+}
+td.percentleft {
+	background-color: #CCCCCC;
+}
+img.icon { 
+	vertical-align: middle;
+	padding: 4px;
+}
+body {
+	font: 12pt sans-serif;
+	background-color: #99B2CC;
+	color: #000000;
+}
+h1 { /* Tracker Header/Title */
+	font: 24px verdana, sans-serif;
+	text-align: center;
+}
+h2 { /* Smaller Page Headers */
+	font: 18px verdana, sans-serif;
+}
+a {
+	color: #000770;
+	text-decoration: none;
+	background-color: transparent;
+}
+a:hover {
+	text-decoration: underline;
+}
+table.percentages {
+	width: 200px;
+}
+table.torrentlist td {
+	padding: 4px;
+}
+table.nopadding td {
+	padding: 0px;
+}
+.details {
+	font: 12px verdana, sans-serif;
+	height: 0px;
+}
+p.error {
+	color: red;
+	text-align: center;
+	font-weight: bold;
+}
+p.success {
+	color: green;
+	text-align: center;
+	font-weight: bold;
+}	
+table {
+	width: 100%;
+}
+.center {	
+	text-align: center; 
+}
+.left {
+	text-align: left;
+}
+.right {
+	text-align: right;
+}
+span.notice {
+	color: #FF0000;
+}

file:b/css/style.css (new)
--- /dev/null
+++ b/css/style.css
@@ -1,1 +1,95 @@
-
+th { /* Table Header */
+	background-color: #CCCC99;
+	padding: 4px;
+	border-bottom: 2px solid #664D33;
+}
+th.subheader { /* Sub-Table Header */
+	background-color: #99FF99;
+	border-bottom: 2px solid #336633;
+}
+tr.selected {
+        background-color: #FFCC00;
+        color: #000000;
+}
+tr.row0 {
+	background-color: #A7BCD3;
+}
+tr.row0 a:link {color: #000770}
+tr.row0 a:visited {color: #000770}
+tr.row0 a:active  {color: #000770}
+tr.row0 a:hover   {color: #000770}
+tr.row1 {
+	background-color: #8AA9C6;
+}
+tr.row1 a:link {color: #000770}
+tr.row1 a:visited {color: #000770}
+tr.row1 a:active  {color: #000770}
+tr.row1 a:hover   {color: #000770}
+td.percent {
+	background-color: #33CC33;
+}
+td.percentleft {
+	background-color: #CCCCCC;
+}
+img.icon { 
+	vertical-align: middle;
+	padding: 4px;
+}
+body {
+	font: 12pt sans-serif;
+	background-color: #99B2CC;
+	color: #000000;
+}
+h1 { /* Tracker Header/Title */
+	font: 24px verdana, sans-serif;
+	text-align: center;
+}
+h2 { /* Smaller Page Headers */
+	font: 18px verdana, sans-serif;
+}
+a {
+	color: #000770;
+	text-decoration: none;
+	background-color: transparent;
+}
+a:hover {
+	text-decoration: underline;
+}
+table.percentages {
+	width: 200px;
+}
+table.torrentlist td {
+	padding: 4px;
+}
+table.nopadding td {
+	padding: 0px;
+}
+.details {
+	font: 12px verdana, sans-serif;
+	height: 0px;
+}
+p.error {
+	color: red;
+	text-align: center;
+	font-weight: bold;
+}
+p.success {
+	color: green;
+	text-align: center;
+	font-weight: bold;
+}	
+table {
+	width: 100%;
+}
+.center {	
+	text-align: center; 
+}
+.left {
+	text-align: left;
+}
+.right {
+	text-align: right;
+}
+span.notice {
+	color: #FF0000;
+}

file:b/deleter.php (new)
--- /dev/null
+++ b/deleter.php
@@ -1,1 +1,133 @@
+<?php
+require ("config.php");
+//Check session
+session_start();
 
+if (!$_SESSION['admin_logged_in'])
+{
+	//check fails
+	header("Location: authenticate.php?status=session");
+	exit();
+}
+?>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+	<title>Delete Torrent(s) From Database</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+	<link rel="stylesheet" type="text/css" href="./css/style.css" />
+	<script language="javascript">
+	function selectRow(checkBox)
+	{
+		if (checkBox.value % 2 == 1) //odd
+			var Style = "row1";
+		else //even
+			var Style = "row0";
+		if (checkBox.checked == true)
+			var Style = "selected";
+		var el = checkBox.parentNode;
+		while(el.tagName.toLowerCase() != "tr")
+		{
+			el = el.parentNode;
+    		}
+    		el.className = Style;
+	}
+	</script>
+</head>
+<body>
+<form action="<?php echo htmlentities($_SERVER['PHP_SELF']);?>"  method="POST">
+<?php
+require_once("funcsv2.php");
+
+// check database user
+if (isset($dbuser) && isset($dbpass))
+{
+	$db = mysql_connect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Cannot connect to database. Check your username and password in the config file.</p>");
+	mysql_select_db($database) or die(errorMessage() . "Error selecting database.</p>");
+
+	foreach ($_POST as $left => $right)
+	{
+		if (strlen($left) == 41)
+		{
+			if (!is_numeric($right) || !verifyHash(substr($left, 1)))
+				continue;
+			$hash = substr($left, 1);
+			//delete torrent file
+			$query = "SELECT filename FROM ".$prefix."namemap WHERE info_hash =\"$hash\"";
+			$delete_file = mysql_query($query) or die(errorMessage() . "Can't do SQL query - " . mysql_error() . "</p>");
+			$delete = mysql_fetch_row($delete_file);
+			unlink("torrents/" . $delete[0] . ".torrent");
+			//continue deleting information in database
+			@mysql_query("DELETE FROM " . $prefix . "summary WHERE info_hash=\"$hash\"");
+			@mysql_query("DELETE FROM " . $prefix . "namemap WHERE info_hash=\"$hash\""); 
+			@mysql_query("DELETE FROM " . $prefix . "timestamps WHERE info_hash=\"$hash\"");
+			@mysql_query("DELETE FROM " . $prefix . "webseedfiles WHERE info_hash=\"$hash\"");
+			@mysql_query("DROP TABLE " . $prefix . "y$hash");
+			@mysql_query("DROP TABLE " . $prefix . "x$hash");
+			//optimize tables, good after major changes have been made to database
+			@mysql_query("OPTIMIZE TABLE " . $prefix . "summary");
+			@mysql_query("OPTIMIZE TABLE " . $prefix . "namemap");
+			@mysql_query("OPTIMIZE TABLE " . $prefix . "timestamps");
+			//run RSS generator
+			require_once("rss_generator.php");
+		}
+	}
+}
+else
+{
+	$db = mysql_connect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - " . mysql_error() . "</p>");
+	mysql_select_db($database) or die(errorMessage() . "Tracker error: can't open database $database - " . mysql_error() . "</p>");
+	$GLOBALS["maydelete"] = false;
+}
+
+?>
+<h1>Delete Torrent(s) From Database</h1>
+<table class="torrentlist" cellspacing="1">
+<tr>
+	<th>Name/Info Hash</th>
+	<th>File Size</th>
+	<th>Seeders</th>
+	<th>Leechers</th>
+	<th>Completed D/Ls</th>
+	<th>Bytes Transfered</th>
+	<th>Delete?</th>
+</tr>
+<?php
+
+if ($GLOBALS["customtitle"] != "true")
+$results = mysql_query("SELECT ".$prefix."summary.info_hash, ".$prefix."namemap.size, ".$prefix."summary.seeds, ".$prefix."summary.leechers, format(".$prefix."summary.finished,0), format(".$prefix."summary.dlbytes/1073741824,3), ".$prefix."namemap.filename FROM ".$prefix."summary LEFT JOIN ".$prefix."namemap ON ".$prefix."summary.info_hash = ".$prefix."namemap.info_hash ORDER BY ".$prefix."namemap.filename") or die(errorMessage() . "" . mysql_error() . "</p>");
+else $results = mysql_query("SELECT ".$prefix."summary.info_hash, ".$prefix."namemap.size, ".$prefix."summary.seeds, ".$prefix."summary.leechers, format(".$prefix."summary.finished,0), format(".$prefix."summary.dlbytes/1073741824,3), ".$prefix."namemap.title FROM ".$prefix."summary LEFT JOIN ".$prefix."namemap ON ".$prefix."summary.info_hash = ".$prefix."namemap.info_hash ORDER BY ".$prefix."namemap.title") or die(errorMessage() . "" . mysql_error() . "</p>");
+
+
+$i = 0;
+
+while ($data = mysql_fetch_row($results)) {
+	$writeout = "row" . $i % 2;
+	$hash = $data[0];
+	if (is_null($data[6]))
+		$data[6] = $data[0];
+	if (strlen($data[6]) == 0)
+		$data[6] = $data[0];
+		
+	echo "<tr class=\"$writeout\">\n";
+	echo "\t<td>".$data[6]."</td>\n";
+	echo "\t<td>".bytesToString($data[1])."</td>\n";
+	for ($j=2; $j < 5; $j++)
+		echo "\t<td class=\"center\">$data[$j]</td>\n";
+	echo "\t<td class=\"center\">$data[5] GB</td>\n";
+	
+	echo "\t<td class=\"center\"><input type=\"checkbox\" name=\"x$hash\" value=\"$i\" onclick=\"selectRow(this);\"/></td>\n";
+	echo "</tr>\n";
+	$i++;
+}
+
+?>
+</table>
+<p class="error">Warning: there is no confirmation for deleting files. Clicking this button is final.</p>
+<p class="center"><input type="submit" value="Delete" /></p>
+</form>
+<a href="index.php"><img src="images/stats.png" border="0" class="icon" alt="Tracker Statistics" title="Tracker Statistics" /></a><a href="index.php">Return to Statistics Page</a><br>
+<a href="admin.php"><img src="images/admin.png" border="0" class="icon" alt="Admin Page" title="Admin Page" /></a><a href="admin.php">Return to Admin Page</a>
+</body></html>
+

file:b/dltorrent.php (new)
--- /dev/null
+++ b/dltorrent.php
@@ -1,1 +1,77 @@
+<?php
 
+require_once ("config.php");
+
+//Check session only if hiddentracker is TRUE
+if ($hiddentracker == true)
+{
+	session_start();
+	
+	if (!$_SESSION['admin_logged_in'] && !$_SESSION['upload_logged_in'])
+	{
+		//check fails
+		header("Location: authenticate.php?status=error");
+		exit();
+	}
+}
+else
+{
+	//don't run
+	exit();
+}
+
+
+//if hash isn't of length 40, don't even bother connecting to database
+if (strlen($_GET['hash']) != 40)
+{
+	header("index.php"); 	
+  	exit();
+}
+
+require_once ("funcsv2.php"); //required for errorMessage()
+
+//connect to database and turn hash value into a filename
+if ($GLOBALS["persist"])
+	$db = mysql_pconnect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - " . mysql_error() . "</p>");
+else
+	$db = mysql_connect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - " . mysql_error() . "</p>");
+mysql_select_db($database) or die(errorMessage() . "Tracker error: can't open database $database - " . mysql_error() . "</p>");
+$query = "SELECT filename FROM ".$prefix."namemap WHERE info_hash = '" . $_GET['hash'] . "'";
+$results = mysql_query($query) or die(errorMessage() . "Can't do SQL query - " . mysql_error() . "</p>");
+$row = mysql_fetch_row($results);
+
+if ($row[0] == null)
+{
+	//hash doesn't exist in database, error out
+	header("Location: index.php");
+  	exit();
+}
+else
+	$filename = $row[0];
+
+if (!file_exists("./torrents/" . $filename . ".torrent"))
+{
+  	header("Location: index.php");
+  	exit();
+}
+
+//you have be referred from the main website URL then you can download
+if (strpos($_SERVER['HTTP_REFERER'], $website_url . "/") === 0 && strpos($_SERVER['HTTP_REFERER'], "http") === 0)
+{
+  	$stat = stat("./torrents/" . $filename . ".torrent");
+  	header("Content-Type: application/x-bittorrent");
+  	header("Content-Length: " . $stat[7]);
+  	header("Last-Modified: " . gmdate("D, d M Y H:i:s", $stat[9]) . " GMT");
+  	header("Content-Disposition: attachment; filename=\"" . $filename . ".torrent\"");
+  	readfile("./torrents/" . $filename . ".torrent");
+  	exit();
+}
+else
+{
+	header("Location: index.php");
+	exit();
+}
+
+header('Pragma: no-cache');
+header('Cache-Control: no-cache, no-store, must-revalidate');
+?>

--- /dev/null
+++ b/docs/BEncode-API.txt
@@ -1,1 +1,83 @@
+API Interfaces
 
+
+BDecode($string)
+---------------
+
+Takes input as a single string. This string should be the whole
+.torrent file or whatever encoded stream you want to decode.
+
+Returns the array of the original encoded data. For example, to
+get the URL of the tracker used by a .torrent, use
+
+	$fd = fopen("myfile.torrent", "rb");
+	$stream = fread($fd, filesize("myfile.torrent"));
+	fclose($fd);
+	
+	$array = BDecode($stream);
+	
+	echo "Url: ".$array["announce"]."\n";
+
+
+
+
+BEncode($array)
+---------------
+
+Pretty much the opposite of the decoder. It takes an array and
+outputs the encoded data as one large string. Assuming there
+are no bugs in the code, BEncode(BDecode($stream) should give
+the exact same string back.
+
+
+
+
+
+$array
+------
+
+My first impression of the whole BEncode system is that Python
+makes a distinction between lists and dictionaries. I'm sure that's
+a good thing for the Python programmers but we have a different
+problem.
+
+PHP doesn't really make a difference between lists and
+dictionaries. They're all arrays. As such, the difference
+between a dictionary and an array is simple: lists are numerically
+indexed only. If (isset($array[0])) is true, you may assume the
+array is a "list" and treat it as such. Iterate until !isset($array[$i]);
+
+In the event of a list that has zero entries ("le"), it will be represented
+as array() (is_array() && empty()). Dictionaries ("de") will be represented
+as the boolean type true, not an array.
+
+This should hold as long as Bram doesn't do something cruel in the
+near future. :)
+
+
+Notes
+-----
+
+The return value will always be an array if the response is one of the
+normal responses of BitTorrent, which are always dictionaries. But it will
+also accept non-dictionaries as input.
+
+For exmaple, BDecode("i15e") === (int) 15
+
+Finally, the decoder is a little more tolerant of bencoding errors than
+the Python becode library. Things like sorted dictionaries when decoding
+are not enforced.
+
+
+
+Dictionaries
+------------
+One last thing about dictionaries. If you were to do something like this:
+
+foreach ($array as $left => $right) { .. }
+ or similarly
+$array[$left] = $right;
+
+Then beware: $left has had addslashes applied to it. This is to work
+around a small quirk in PHP. Null bytes ("\0") would cause the value
+of $left to be truncated at the null byte.

file:b/docs/License.txt (new)
--- /dev/null
+++ b/docs/License.txt
@@ -1,1 +1,341 @@
-
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+

--- /dev/null
+++ b/docs/changelog.txt
@@ -1,1 +1,160 @@
+RivetTracker is a modified version of PHPBTTracker Version 1.5rc3, written by "DeHackEd".
 
+Changes:
+
+---Version 1.03---
+
+-Prevented XSS attack on index.php page with htmlspecialchars (Thanks to report on forums)
+-Show message about folder permissions for 'torrents' and 'rss' when logged into admin.php main menu page
+-Changed install.php text at the end explaining folder permissions
+-Fixed rss_generator.php bug where torrents were not being ordered according to date (newest should be first)
+
+---Version 1.02---
+
+-Fixed dltorrent.php bug where if the tracker was set to hidden, user was unable to download the torrent because of OR instead of AND in conditional check (Thanks to bug report on forums)
+
+---Version 1.01---
+
+-Fixed index.php page where authenticate.php link didn't have PHP file extension (Thanks to bug report from Johannes)
+-Fixed MySQL formatting finished downloads with thousands and PHP not being able to recognize this correctly (Thanks to bug report on forums)
+
+---Version 1.0---
+
+-Changed database engine from default MyISAM to InnoDB, hopefully this will prevent table crashes
+-Changed CSS files
+-Changed session authentication to more secure method, does not store username or MD5 anymore
+-Passwords are now no longer stored in cleartext in the config.php file, they are computed as md5(username.password)
+
+---Version 0.9991---
+
+-Added information on upgrading in help file
+-Fixed install bug
+
+---Version 0.999---
+
+-Fixed bug where RSS feed was being displayed in header when it was disabled
+-Fixed rounding error in statistics.php where user was being shown as 100% done when they are only almost done (99.6%)
+-Changed display of bytes transferred on index.php page to correct units, before it defaulted to GB
+-Added check for stalled download in runSpeed() function
+-Fixed error where speed was set to 0 if seeders == 0, not always the case, can still be downloading even if there are no seeders (partial d/l)
+-took out repair statement in sanity.php and sanity_no_output.php
+-Added CSS page where you can change/swap/create CSS files and examine colors with the color picker
+-Added batch upload of torrents via ZIP file
+-Added help link to index.php
+-Used htmlspecialchars on inputs in order to prevent code injection
+-Added javascript row select in delete page
+-Fixed delete bug, URL bug
+-Added MySQL table prefix option
+-Sanitized some inputs (still more?), this way if someone gets your admin password they won't be able to execute malicious code
+
+---Version 0.995---
+
+-Fixed bug in namemap table where MySQL size variable INT type was being used, changed it to BIGINT
+-Fixed bug where single quotes were not being checked in torrent file, filenames, title, RSS description, and RSS title
+-Changed funcsv2.php and added in the clean() and addquotes() functions
+-Added REPAIR MySQL command to sanity.php and sanity_no_output.php (to fix table crashes, sometimes it happens, dunno why)
+-Fixed bug where null entry for filename search caused error
+-Added scrape option in config.php file, changed tracker to check for this before doling out scrape information to client
+-Changed location of announce URL to announce.php in order to enable support for scraping
+-Added display of files inside torrent via [+] button on index.php page
+-Added ability to disable RSS feed
+-Peercaching is now on by default, slightly more diskspace needed for this but it's worth it because of the lessened strain on database
+-Added ability to have a hiddentracker, this is not a private tracker, but hidden enough so that it requires a login, .htaccess or something
+similar will be needed to secure the "torrents" folder, also all BT clients can connect to the tracker still, there is no username authentication there
+-Added dltorrent.php that is used when in hiddentracker mode, no direct linking to .torrent file on main page
+-Removed updatePeer(), the function was emtpy so not a big deal...
+-Added ability to have legal terms and a policy agreement before logging in, if you want this create a file called legalterms.txt with the info in it
+-Changed edit database script so that you click on a file to edit it instead of displaying too much information on one screen
+-Various minor display improvements
+-Checked IE and Firefox for display issues
+-Updated documentation with some minor additions
+
+---Version 0.99---
+
+-Changed fonts in CSS file so they were easier to read/view in IE
+-Limit results on index.php page, can now switch between pages
+-Limit results on statistics.php page, can now switch between pages
+-Fixed bug install.php and editconfig.php where RSS information was not required, now it is
+-Torrent URL checked in newtorrents.php file, error message if it doesn't start with http://
+-Tracker announce URL checked in newtorrents.php file, if it doesn't match the tracker, user is asked to re-create torrent and re-upload
+-Added display of private torrent variable in DumpTorrentCGI.php
+-Fixed bug where uploaded torrent was being used even if there was an error
+-Displays torrent information after successful torrent added to database
+-Split functions used by DumpTorrentCGI.php into torrent_functions.php, now it can be used by any file to display torrent info
+-Changed index.php page to point to admin.php not authenticate.php login page
+-Fixed bug where in install or editing the config file, maxpeers could be set to negative number or zero
+-Added size to list of items displayed when removing a torrent
+-Fixed bug where in install or editing the config file, max reannounce interval and min reannounce interval could be negative or zero
+-Made speed estimate slightly more accurate, if no leechers, sets speed to zero
+-Added an aggregate total at the top of the index.php page
+-Added search functionality to statistics.php page using REGEXP in MySQL
+-Added sanity_no_output.php, a stripped down version of sanity.php that gets run by the index.php page every once in awhile
+-Added uploadstats.php in admin section that shows upload rates for HTTP seeding and regular bittorrent
+-Added support for GetRight HTTP seeding and Bittornado HTTP seeding
+-Changed doctype on all pages to <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+-Added help.html file in docs folder that consolidates all information into one file, help.pdf is the PDF equivalent
+
+---Version 0.9---
+
+-Added alt and title properties for all image files
+-added automatic creation of valid RSS 2.0 file (listed on main page, right side)
+-added pubDate (for RSS feed) to table namemap in MySQL
+-added index.php redirect file in rss folder
+-modified install.php and editconfig.php for additional RSS variables, timezone, and more
+-check at beginning of index.php if there is no config.php file, error out and display message
+-check at beginning of install.php, if there is a config.php file, this is an indication of an already
+ existing installation and thus the user should be warned and unable to continue
+-various minor display improvements
+-added RSS variables, timezone, and others to config.php file
+-changed 'info' in MySQL namemap table to 'size' and make it store total size of all file(s) in torrent in bytes
+This is related to an error where the filesize was reported as higher than actual when the file(s) in the torrent were very small
+-display total file(s) size in DumpTorrentCGI.php
+-added bytesToString() function in funcsv2.php
+-make sure install.php and editconfig.php check for blank entries for required variables so config.php isn't populated with null values
+-added check for install.php file in admin.php, if so, display strong warning message
+-added critical message icon to most class="error" areas
+-added page in admin section where user can edit torrents and values already in database
+-added errorMessage() in funcsv2.php that shows error message and icon, most errors that are displayed with die() now use this function
+-made installer easier to read and walkthrough
+-converted all uppercase HTML to lowercase
+-run optimize MySQL command after deleter.php runs
+-removed dynamic_torrents variable that allowed torrents to be added without authentication
+-fixed division by zero error in statistics.php
+
+---Version 0.8---
+
+-Adding a torrent file saves the file in the "torrents" folder and is displayed on the main statistics page.
+-Restructured files into more folders
+-Added icons from the Tango Project:
+http://tango.freedesktop.org/
+(creative commons license)
+http://creativecommons.org/licenses/by-sa/2.5/
+-Show tracker URL in newtorrents.php
+-Delete torrent from database will also delete the saved torrent file
+-Added index.php redirect file in images, docs, and torrents folders
+-Fixed DumpTorrentCGI.php MAX_FILE_SIZE error
+-Consolidated authentication to one script
+-Password protect newtorrents.php page to prevent people uploading items who don't actually have an account
+
+---Version 0.1---
+
+-minor formatting issues, addition of links to admin page and create torrent in index.php
+-each statistics column is totalled and displayed in the last row
+-admin page added with links to relevent scripts, each script except add torrent requires session authentication
+-added admin username and password in config.php
+-fixed index.php $GLOBALS bug for <title>
+-added title variable in config.php
+-upload user in config.php is able to add torrents but not access admin resources, this requires the separate admin user
+-admin user is able to access any page
+-if the number of leechers is zero, then the speed is zero
+-if the number of leechers is zero and the number of seeders is zero, then the speed is zero
+-mystats.php renamed to index.php
+-changed speed units to KB, MB, and GB
+-fixed installer.php writing to config.php to account for additional variables
+-added statistics.php script, admin resource that shows detailed information on each user the tracker has saved
+-removed "short description" in add torrent, now it just defaults to the size all the time
+-heavily modified install.php file to allow for a more robust and easier installation
+-allow config.php file to be saved to server or downloaded in install.php
+-added page in admin section where user can change config.php values right from webpage
+
+

file:b/docs/help.html (new)
--- /dev/null
+++ b/docs/help.html
@@ -1,1 +1,217 @@
-
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<title>RivetTracker Help</title>
+</head>
+<body>
+<center>
+<h3>RivetTracker Help</h3>
+</center>
+<hr>
+<table border="0">
+<tr bgcolor="#CCCCCC">
+<td>
+<ul>
+<li><a href="#about">About</a></li>
+<li><a href="#bittorrent">What is BitTorrent?</a></li>
+<li><a href="#tracker">What is a Tracker?</a></li>
+<li><a href="#requirements">Requirements</a></li>
+<li><a href="#installation">RivetTracker Installation</a></li>
+<li><a href="#upgrading">Upgrading</a></li>
+<li><a href="#httpseeding">HTTP Seeding</a></li>
+<li><a href="#help">Support/Help</a></li>
+<li><a href="#contribute">Contribute</a></li>
+<li><a href="#thanksto">Thanks To</a></li>
+</ul>
+</td>
+</tr>
+</table>
+
+<a name="about"></a>
+<h3>About</h3>
+
+<p>RivetTracker is a modified version of <a href="http://dehacked.2y.net/BT/">PHPBTTracker Version 1.5rc3</a>,
+written by "DeHackEd".  This program provides the same functionality as most other BitTorrent trackers and uses MySQL as the database backend.
+It provides an RSS feed, optional support for HTTP seeding, detailed connection statistics, and much more.</p>
+<p>PHPBTTracker was released under the <a href="http://www.fsf.org/licensing/licenses/info/GPLv2.html">GPLv2 license</a>
+as is this program.</p>
+<p>Some of the images used were provided by the <a href="http://tango.freedesktop.org/">Tango Desktop Project</a>.
+These images are licensed under the
+<a href="http://creativecommons.org/licenses/by-sa/2.5/">Creative Commons Attribution-ShareAlike 2.5 License</a>.</p>
+
+<a name="bittorrent"></a>
+<h3>What is BitTorrent?</h3>
+
+<p>BitTorrent is a Peer to Peer (P2P) communication protocol for sharing files.  A client downloads a small
+.torrent file from a website that contains the necessary information to put the whole file or files together.
+This torrent file contains a link to a tracker or trackers that provide information on who else is downloading or
+seeding the file.  The term seeder refers to someone who has downloaded the entire file and is uploading parts of
+it to others called leechers.  Leechers are people who have started the download and are downloading the file but
+have not finished yet.  The beauty of BitTorrent is that it allows people to share files (especially large ones) easily
+without incuring huge hosting costs because of bandwidth limitations.  Since all seeders and leechers are constantly uploading
+whatever data they have available, this speeds up the overall distribution of the file.  You can start using BitTorrent
+right now by downloading and installing a <a href="http://en.wikipedia.org/wiki/BitTorrent_client">BitTorrent client</a>.
+<p>If you are new to BitTorrent and have not used it before please be careful when initially using it.  Sadly, BitTorrent
+has become used heavily for distributing pirated movies, music, and games.  That being said, many Linux distributions use
+BitTorrent legally to efficiently release their distribution on a global scale.  Many websites use BitTorrent
+for purposes which may be illegal in the country that you live in.  Just be careful and watch what you are downloading!</p>
+<p>For more information on the specifics of the BitTorrent protocol you can visit the following websites:
+<ul>
+<li><a href="http://wiki.theory.org/Main_Page">http://wiki.theory.org/Main_Page</a></li>
+<li><a href="http://en.wikipedia.org/wiki/BitTorrent">http://en.wikipedia.org/wiki/BitTorrent</a></li>
+</ul>
+
+<a name="tracker"></a>
+<h3>What is a Tracker?</h3>
+
+<p>A <a href="http://en.wikipedia.org/wiki/BitTorrent_tracker">BitTorrent tracker</a> is a piece of software that BitTorrent
+clients communicate with in order to receive information about other people downloading the file.  You can think of a tracker
+as a mediator between clients.  It doesn't actually transmit the file it just provides information about other people that have it.</p>
+<p>RivetTracker is special because it is built using PHP and MySQL.  This means you can easily create a website to share files
+and have a tracker that people around the world are able to connect to.  The web-interface makes it easy to navigate and administer.</p>
+
+<a name="requirements"></a>
+<h3>Requirements</h3>
+
+
+<ul>
+<li>A webserver, <a href="http://www.apache.org">Apache</a> is a great one.</li>
+<li>A recent version of <a href="http://www.php.net">PHP.</a></li>
+<li>The <a href="http://www.mysql.org">MySQL Database.</a></li>
+</ul>
+
+<p>RivetTracker has been tested under <a href="http://www.ubuntu.com">Ubuntu Linux</a>,
+support under Windows is unknown at this time.</p>
+
+<a name="installation"></a>
+<h3>RivetTracker Installation</h3>
+
+<p>Installation is very easy, just copy the folder and all the files to your webserver.
+Next, run install.php to create the MySQL database and setup the configuration.</p>
+<a href="./imgs/1.gif"><img src="./imgs/1.gif" border="0" alt="Installation" /></a>
+<p>In this case, I want to create a new database and user that only has access rights to that database.
+For security reasons, it is recommended that you go with the second option.</p>
+<a href="./imgs/2.gif"><img src="./imgs/2.gif" border="0" alt="Database Setup"/></a>
+<p>In this step I provided the installer with a user that can create other users.  You will also be required to specify
+the hostname of the MySQL server, user that will be created, and the database name.  As you can see in the image
+I have provided all the necessary information.  Click install and the script will go through the process of connecting
+to the MySQL server and running the appropriate setup commands.</p>
+<a href="./imgs/3.gif"><img src="./imgs/3.gif" border="0" alt="Configuration File"/></a>
+<p>This section lets you setup the configuration file that stores all your settings about the tracker.  Make sure you
+read the directions carefully about each item.  It's fairly self-explanatory, just take your time.  When you are
+ready click on the create config file button to continue.</p>
+<p>An important note about the hidden tracker feature is that it requires a login by either the admin or upload
+account in order to even view the main torrents page and download them.  However, the /torrents folder is NOT
+protected in any way.  You will have to go in and create a .htaccess file or something else to protect that folder.
+Also, having the hidden tracker on does not mean this is a private tracker.  People will still be able to connect to
+your torrents and use the tracker system if they can get a copy of the torrent.  Also, it may be possible if you
+also have scrape support enabled that a client could connect and get information about what files are on your tracker
+through the scrape.  Unfortunately, I do not know all the details of how scrape works.  If you need a very secure
+tracker, I would suggest checking out the other programs that are available.</p>
+<p>At this point, your installation is finished.</p>
+<p><font color="red"><big>***MAKE SURE YOU DELETE install.php AFTER YOU ARE FINISHED INSTALLING!***</big></font></p>
+<p>Also, make sure that the "torrents" and "rss" folders are writeable by your webserver.</p>
+<p>Click on the link to go to your main statistics page.</p>
+<a href="./imgs/4.gif"><img src="./imgs/4.gif" border="0" alt="Main Page"/></a>
+<p>There are no torrents yet because you have not added them to the database.</p>
+<p>At this point you can start adding torrents to the database by logging in as either the upload user
+or as the administrator.  When you get to the add torrent page you should see something like this:</p>
+<a href="./imgs/5.gif"><img src="./imgs/5.gif" border="0" alt="Add Torrent"/></a>
+<p>Simply provide a .torrent file that you have created and specify whether you want one, both, or neither of the
+web seeding features.  Most information can be automatically gathered from the torrent file but if you wish, you can
+provide a specific filename and URL for the database to use.</p>
+<p>Click add and you have just added your first torrent to the database.  If you go back to the statistics page
+you should see it listed as well as a link to download the torrent file.</p>
+<p>If you want <a href="http://www.azureuswiki.com/index.php/Scrape">scrape</a> functionality, 
+check the appropriate box in the configuration settings.  It is generally safe to leave this enabled, however,
+there is the possiblity that BitTorrent clients could use this abusively and request this information too much.
+For trackers that serve large numbers of torrents with many users, this will also increase bandwidth usage.
+Scraping is used in order to figure out if a request for additional peers is warranted.  This request for peers
+eats up a lot of bandwidth and it is usually better to try to gauge this by asking the tracker via a scrape.</p>
+<p>The speed information is a rough estimate and only gets updated when a client
+connects to the tracker.</p>
+<p>You can now utilize a short announce URL for your tracker.  If you enable this feature, you will need the provided
+htaccess file and URL rewriting capabilities enabled and set properly on your server.  More info to set it up will be
+located <a href="./htaccess-readme.txt">here.</a></p>
+<p>If you don't like the color scheme you can change it by editing the provided CSS file.
+Go into the admin page and click on "Change CSS File".  From there you will be able to create new
+CSS files or edit existing ones.</p>
+<p>The RSS feed that is available is great for people who publish files on a regular basis, for example audio or
+video podcasts.</p>
+<p>If you want to have legal information like a use policy, create a file called "legalterms.txt" in the main directory
+where the index.php file is.  Inside the legalterms text file, put your information.  Now, when people go to login
+they will have to agree to the terms before it will let them on.</p>
+
+<a name="upgrading"></a>
+<h3>Upgrading</h3>
+
+<p>If you are upgrading from a previous installation of RivetTracker there is an easy way to save your
+torrents.  Because there might be changes to the database or changes to the configuration file, it is
+much easier just to delete the existing database and installation and start from scratch.  Before you do this
+however, go into your current 'torrents' folder and ZIP them all up into one file.  Next, delete your current
+database and do a complete reinstall of RivetTracker.  After this, go into the admin page and use the batch
+upload system to upload the ZIP file you created.  This will load each torrent file in the ZIP one by one.
+It makes it much easier to upload large quantities of torrent files into the database.</p>
+
+<a name="httpseeding"></a>
+<h3>HTTP Seeding</h3>
+
+<p>There are two standards for providing <a href="http://wiki.theory.org/BitTorrentSpecification#WebSeeding">web seeding</a>
+in BitTorrent.  Web seeding allows an HTTP or FTP server to act as a seeder and provide files to the client.  Most of the major
+BitTorrent clients support one standard or the other, or both.</p>
+<p>The first standard was created by BitTornado and you can find the detailed specification
+<a href="http://bittornado.com/docs/webseed-spec.txt">here</a>.  It requires that the .torrent file be created with
+an additional list that holds the location or locations of a URL script that provides an interface to the BitTorrent client.
+The benefit of this is that the script is able to limit bandwidth usage and the number of connections to the server requesting the file.
+It also prevents hotlinking directly to the file and abuse.  The downside is that only some BitTorrent client support this
+standard.</p>
+<p>The other standard for web seeding is detailed by the GetRight creator <a href="http://getright.com/seedtorrent.html">here</a>.
+This solution also requires that .torrent files include an additional list of direct links to the file or a link to a
+directory where the heirarchy can be re-created.  The benefit of this standard is that it does not require any additional
+script be setup or communication with the server that the BitTornado standard does.  The downside is that this standard is
+open to abuse from clients potentially hotlinking directly to the file.</p>
+<p>RivetTracker includes support for both standards as well as the scripts required by BitTornado.  When you go to add
+a torrent to the database, there is an option to add web seeding support.  Simply fill in the required information and when
+the torrent is uploaded this information is added into the .torrent file.  This makes migration of old torrent files a snap.</p>
+
+<a name="help"></a>
+<h3>Support/Help</h3>
+
+<p>If this document was unable to answer your question or you're stuck on something please visit the RivetCode
+<a href="http://forums.rivetcode.com">forums</a> or <a href="http://www.rivetcode.com/contact/">contact me</a>.
+
+<a name="contribute"></a>
+<h3>Contribute</h3>
+
+<p>Want to contribute to future RivetTracker development and releases?  There are a couple ways
+in which you can help.  First of all, try to find bugs and submit <a href="http://www.rivetcode.com/contact/">bug reports</a>.
+You can also submit suggestions for future versions <a href="http://www.rivetcode.com/contact/">here</a>.
+If you know PHP and are willing to dive into the code, adding features and improving on the project would also be immensely
+helpful.  There is a <a href="http://sourceforge.net/projects/rivettracker/">Sourceforge project</a> page that has the bug tracker,
+git repository, and download links.  Public git access is available via:
+<pre>git clone git://rivettracker.git.sourceforge.net/gitroot/rivettracker/rivettracker</pre><p>
+
+
+<p>Finally, if you want to consider donating a few dollars for further development of this software and future projects that
+would be fantastic.  Every little bit helps, thanks!</p>
+<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
+<input type="hidden" name="cmd" value="_s-xclick">
+<input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-but04.gif" border="0" name="submit" alt="Make a donation and help support further RivetCode software!" title="Make a donation and help support further RivetCode software!">
+<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1">
+<input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----MIIHmAYJKoZIhvcNAQcEoIIHiTCCB4UCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYCrOE3S7LR5OwDimRfELsv9ZG7z14706zRg+x8jBQLiltSecWAKIKf+v2MKwsjwpD2wFvtyclEWIADoAuNXJZsxI0Myt+vz49udx+pamfVtSBGjRwQoRbfEr5aw2IzbtKSGcSWB4x3vVd9M/sq1MqTbcUDFbkwdidLcwzCBuPgjFzELMAkGBSsOAwIaBQAwggEUBgkqhkiG9w0BBwEwFAYIKoZIhvcNAwcECJEgkx1sSxmOgIHw+yFB2f0GyCQgCSywBGzq2fypvBo9keMNl9O/i43T+dK4rfLLuoKN+mpD6wpi6jwNNiowGb38XwzqiCy6+PrwiNtLKkUSKSMF+sWxT763cbFGehPNA99i4YkEojQ5by/5hpiPxb9N+G3ud//ygOYw7K6F19oiornqjkqZSAr6i8ADpHoJyWN37xO3O1j/j5xViRL8TFOQFQdEEn4SyA0fDUg/DWDsGye8jtAsrtxOXYyKzkUAC5kmcDux6ecMuS4flH1030H5tkkjpz/oJhV63prg5oehTEwmZy81j2ubtH+RIJZ9WL7SO//OCM77fYMgoIIDhzCCA4MwggLsoAMCAQICAQAwDQYJKoZIhvcNAQEFBQAwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMB4XDTA0MDIxMzEwMTMxNVoXDTM1MDIxMzEwMTMxNVowgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBR07d/ETMS1ycjtkpkvjXZe9k+6CieLuLsPumsJ7QC1odNz3sJiCbs2wC0nLE0uLGaEtXynIgRqIddYCHx88pb5HTXv4SZeuv0Rqq4+axW9PLAAATU8w04qqjaSXgbGLP3NmohqM6bV9kZZwZLR/klDaQGo1u9uDb9lr4Yn+rBQIDAQABo4HuMIHrMB0GA1UdDgQWBBSWn3y7xm8XvVk/UtcKG+wQ1mSUazCBuwYDVR0jBIGzMIGwgBSWn3y7xm8XvVk/UtcKG+wQ1mSUa6GBlKSBkTCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb22CAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCBXzpWmoBa5e9fo6ujionW1hUhPkOBakTr3YCDjbYfvJEiv/2P+IobhOGJr85+XHhN0v4gUkEDI8r2/rNk1m0GA8HKddvTjyGw/XqXa+LSTlDYkqI8OwR8GEYj4efEtcRpRYBxV8KxAW93YDWzFGvruKnnLbDAF6VR5w/cCMn5hzGCAZowggGWAgEBMIGUMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbQIBADAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMDcwODI5MDMyNjExWjAjBgkqhkiG9w0BCQQxFgQUFcAEoXVkNQqmb7Dx3Htz7+AccEcwDQYJKoZIhvcNAQEBBQAEgYAsc18izZga46hGipu+C9xiR89ucTahC2T1vL8XJa1dqKxg0ii0c75dWJ29/baxATr3VNiQ7KES6d55M4q9cGFzm1lvJjDybVhIjahiED0PygiOqM/B/reCPb79y0DZ07AJaDf0RESyX+SsplTVq3bWFw5VtMryECZkZe8BkF6q4Q==-----END PKCS7-----">
+</form>
+
+<a name="thanksto"></a>
+<h3>Thanks To</h3>
+
+<ul>
+<li>DeHackEd, author of PHPBTTracker</li>
+<li>Bram Cohen, author of BitTorrent</li>
+<li>Everyone on #bittorrent who answered my questions</li>
+<li>The Tango Desktop Project for the excellent icons</li>
+<li>All the testers who reported bugs and gave suggestions</li>
+</ul>
+
+</body>
+</html>
+

--- /dev/null
+++ b/docs/htaccess-readme.txt
@@ -1,1 +1,37 @@
+This is for Apache users to utilize the given .htaccess file in RivetTracker.
 
+[1] Make sure you have the mod_rewrite module enabled.
+[2] For /local/path/to/rivettracker, change this to your absolute local path
+    to your RivetTracker install.
+    [a] Unix servers: /local/path/to/rivettracker
+    [b] Windows servers: <drive letter>:\local\path\to\rivettracker
+
+
+For Apache 2.0.x and 2.2.x installs, put this in your httpd.conf file:
+
+<Directory "/local/path/to/rivettracker">
+Options -Indexes +FollowSymLinks
+RewriteEngine On
+AllowOverride All
+Order allow,deny
+Allow from all
+</Directory>
+
+
+For Apache 2.4.x installs, put this in your httpd.conf file:
+
+<Directory "/local/path/to/rivettracker">
+Options -Indexes +FollowSymLinks
+RewriteEngine On
+AllowOverride All
+Require all granted
+</Directory>
+
+
+You could also change AllowOverride None to AllowOverride All in the main
+httpd.conf and uncomment the first two lines in the htaccess file, but that
+also searches for .htaccess in all other subdirectories as well.
+
+
+For nginx users, it turns out to be a bit different. Read this:
+http://wiki.nginx.org/HttpRewriteModule

file:b/docs/imgs/1.gif (new)
 Binary files /dev/null and b/docs/imgs/1.gif differ
file:b/docs/imgs/2.gif (new)
 Binary files /dev/null and b/docs/imgs/2.gif differ
file:b/docs/imgs/3.gif (new)
 Binary files /dev/null and b/docs/imgs/3.gif differ
file:b/docs/imgs/4.gif (new)
 Binary files /dev/null and b/docs/imgs/4.gif differ
file:b/docs/imgs/5.gif (new)
 Binary files /dev/null and b/docs/imgs/5.gif differ
file:b/docs/index.php (new)
--- /dev/null
+++ b/docs/index.php
@@ -1,1 +1,5 @@
+<?php
 
+header("Location: ../index.php");
+
+?>

--- /dev/null
+++ b/docs/old_phpbttracker_readme.txt
@@ -1,1 +1,253 @@
-
+Welcome to my BitTorrent Tracker written in PHP.
+
+Highlights:
++ Provides the same functionality as the offical tracker
++ Runs using MySQL as a database backend
++ Built-in statistics collection with sample summary script
++ Customiztion is pretty easy to implement
+
+Pitfalls
+- PHP has some limitations, so this tracker is not optimal.
+
+This is my first PHP project, and I'm rather happy with the
+result.
+
+UPGRADING
+---------
+
+If you are upgrading from a previous version, then you may be in trouble.
+The database structure was slightly modified to accomidate a change in the
+latest MySQL. The word "hash" became a keyword, and cannot be used as a
+column name. Furthermore, the addition of the "speed" code requires
+table additions and a new table entirely.
+
+The script upgrade.php is provided to carry out these modifications. You do
+not need to run it if you are installing from scratch, and if only needs
+to be done once regardless. Also, running it will not cause any problems
+even if you have the latest version of the database.
+
+
+** New in version 1.5: Peer caching. If you want to use this feature,
+you must execute the makecache.php script to generate the tables from
+your current database.
+
+
+INSTALLATION
+------------
+
+Requirements:
+- Working PHP environment (ideally Apache with PHP built-in or
+  working via module)
+- Working MySQL server
+
+
+Upload tracker.php, funcsv2.php, newtorrents.php, BDecode.php, BEncode.php
+ and install.php to the web site which will be hosting the tracker. Uploading 
+index.php is recommended if you want a home page for the tracker. Feel free
+to re-theme it.
+
+Access the install.php script from your web browser. It will
+guide you through the creation of the SQL database. All you need
+is the database's username and password. You may want to let your
+webmaster run through this phase.
+
+If install.php has write access to the installation directory, it will
+write its own config.php file with the database configuration and some
+default settings. If install.php cannot do this, you must modify
+config-sample.php yourself and upload it to the same directory as
+tracker.php and rename it to config.php.
+
+*************************
+************************* Set up config.php !!!
+
+There are two variables named $upload_username and $upload_password.
+These are the values that will be used by the newtorrents.php script
+to authorize submission of new torrents. You must set these, or your
+tracker will not accept new files, making it rather useless.
+
+
+
+OTHER FILES
+-----------
+
+The tracker package also includes some other scripts. Here is a list and a
+description of what they do.
+
+- DumpTorrentCGI.php
+ Originally intended as a demo of the BEncode library, but it became popular
+ pretty quickly. This script allows users to upload a .torrent file to the
+ server (or specify a URL to download) and the script will decode it and
+ display the file's contents to the user in a (hopefully) friendly manner.
+ It also supports other bencoded data, such as /announce and /scrape data,
+ although it is not reliable enough to do /scrape due to a strange quirk
+ in PHP.
+- BEncode.php
+ Used by DumpTorrentCGI.php and newtorrents.php to make bencoded data
+ streams. The primary reason for doing this is calculating info_hash values.
+- BDecode.php
+ The decoding compliment to BEncode.php
+- sanity.php
+ When run, this script will do some simple consistency checks on the
+ tracker's summary page and will forcibly expire peers who have not reported
+ in within double the configured re-announce interval. If it doesn't seem to
+ work, try running it as sanity.php?nolock=on
+- sha1lib.php
+ An SHA1 implementation entirely in PHP. It's not perfect and it's slow, but
+ in a pinch, it works fine. Ignored if PHP version is at least 4.3.0 or if
+ the mhash extension is installed.
+
+
+The rest is documentation and other text documents.
+
+FILE RENAMING AND MOVING
+------------------------
+All PHP files will function properly if renamed, except
+funcsv2.php and config.php. Renaming these files require
+modifying most other .php files.
+
+
+USAGE
+-----
+Create your torrent files as usual. Specify the url to 
+tracker.php (or its new name if you renamed it) as the announce
+URL.
+
+***********************************
+If you want /scrape functionality, target announce.php
+instead of tracker.php.
+***********************************
+
+Call up the newtorrents.php URL. Specify all the data you want to
+show up on the statistics page. You must specify at least the
+username, password, and either upload the .torrent or copy the
+info_hash into the indicated field.
+
+The checkbox, when checked (defaults to yes) will cause the script
+to fill in the file's name and a short description. The description is
+the file's size (roughly calculated) and the comment field if present
+in the torrent file.
+
+*New: a PHP implemention of the SHA1 algorithm is included. All users
+can upload directly to the newtorrents.php script now. Note however
+that is may produce wrong hashes and generally run slowly.
+
+
+DELETING TORRENTS
+-----------------
+The script deleter.php allows you to delete torrents from the database.
+The username and password are NOT the same as newtorrnets.php uses.
+Use the login and password that the SQL database itself uses. Of course,
+there's nothing preventing you from making these identical.
+
+Be warned: there is no confirmation of deletion and a torrent
+need not be abandoned to be erased. Changes take effect immediately.
+
+
+
+TORRENTSPY COMPATABILITY (and other /scrape functions)
+------------------------
+Starting with version 1.5, since an official statement has
+been made on /scrape conventions, announce.php and scrape.php
+are provided. announce.php simply executes tracker.php,
+while scrape.php causes tracker.php to output scrape data.
+
+The old style of using http://www.site.com/tracker.php/announce
+is still included (in fact, this is how scrape.php works) but
+is now discouraged since this convention caused more problems
+than it ever really should have.
+
+Any program not capable of figuring out the scrape.php script
+name from announce.php is broken and needs to be fixed.
+
+
+
+STATISTIC COLLECTING (or, "Database Structure")
+--------------------
+
+I tried to make the database information as easy to understand
+as possible. "SELECT * FROM summary" should provide you with
+all the programming information you need, but here is a brief
+rundown of what the fields mean.
+
+Summary:
+	*info_hash - The 40 character hex representation of
+	 the file. It is unique to every torrent.
+
+	*dlbytes - The approximate sum of all the bytes downloaded
+	 by everyone.
+
+	*seeds - The number of connected users who have the
+	 whole file and are uploading.
+
+	*leechers - The number of connected users who are still
+	 downloading the file.
+
+	*finished - The number of users who have fully downloaded
+	 the file. Use this as a measure of how many people
+	 have the file.
+
+	*lastcycle - Used by the trash collector to decide if
+	 it should try to purge users who have timed out.
+
+	*lastSpeedCycle - Used by the speec calculator to decide
+	 if the speed should be updated.
+
+	*speed - in bytes per second. Consider it to be extremely
+	rough.
+
+Namemap:
+  Note that all fields (except hash) are optional and may be "" (but not NULL,
+  those are annoying). A torrent need not have an entry here at all.
+  This is used only by the index.php script.
+	*info_hash - The file's unique 40 character hash.
+
+	*filename - The file that this torrent represents
+
+	*url - A link to where the .torrent file may be grabbed.
+
+	*info - A short text description added after the previous
+	 information is shown. Default is the file size.
+
+timestamps:
+  Used by the speed calculator to contain the sliding window average
+  download rate. This is of little interest to external users, so 
+  I'll skip it.
+
+x<hexadecimal string>:
+  Each torrent's user list is stored in a table whose name is the
+  info_hash of the torrent prefixed by an x.  
+  
+	*peer_id - A 40 character hash that is unique to each client
+
+	*bytes - The number of bytes this peer still needs to download
+	 to have the complete file. Seeders have this set to 0.
+
+	*ip - The client's IP address
+
+	*port - The port the client is listening on (usually 6881)
+
+	*status - Either "seeder" or "leecher" (see above). It's a bit
+	 redundent right now since "bytes==0" is the same as a seeder	 
+
+	*lastupdate - Unix time of when the client last reported in.
+	 Clients whose time is 2 * report_interval will be deleted.
+
+y<hexadecimal string>:
+  The couterpart to the "x" table, only with the peer caching data.
+  I won't describe it here.
+
+
+CREDITS
+-------
+
+People besides me who deserve credit.
+
+Bram Cohen - Author of BT, and really patient guy.
+KktoMx     - Figured out the "stripslashes" problem.
+bideomex   - Found the dumb thing I did with stripslashes.
+Gottaname  - First real load test.
+"daan" (?) - SHA1 in PHP code. See http://www.php.net/manual/en/function.sha1.php
+             user comments.
+Bak4San    - Provider of torrents with ten thousand peers. On a weekly
+             basis.
+

--- /dev/null
+++ b/docs/webseed-spec.txt
@@ -1,1 +1,107 @@
+                   HTTP-BASED SEEDING SPECIFICATION
+                   ================================
 
+This specification is for John Hoffman's and DeHackEd's proposed
+extension to the BitTorrent metadata format, and for an alternate
+protocol for retrieving torrent data from a web server.  This
+extension is not official as of this writing.
+
+
+METADATA EXTENSION:
+
+* "httpseeds"
+
+In the main area of the metadata file and not part of the "info"
+section, will be a new key, "httpseeds".  This key will refer to a
+list of URLs, and will contain a list of web addresses where torrent
+data can be retrieved.  This key may be safely ignored if the client
+is not capable of using it.
+
+* examples.
+
+d['httpseeds'] = [ 'http://www.whatever.com/seed.php' ]
+  This specifies the client can retrieve data by accessing the given
+  URL with the parameters supplied in the protocol specification
+  below.
+
+d['httpseeds'] = [ 'http://www.site1.com/source1.php',
+                   'http://www.site2.com/source2.php'  ]
+  More than one URL may be specified; if so, the client will attempt
+  to access both URLs to download seed data.
+
+
+PROTOCOL:
+
+The client calls the URL given, in the following format:
+<url>?info_hash=[hash]&piece=[piece]{&ranges=[start]-[end]{,[start]-[end]}...}
+
+Examples:
+http://www.whatever.com/seed.php?info_hash=%9C%D9i%8A%F5Uu%1A%91%86%AE%06lW%EA%21W%235%E0&piece=3
+http://www.whatever.com/seed.php?info_hash=%9C%D9i%8A%F5Uu%1A%91%86%AE%06lW%EA%21W%235%E0&piece=8&ranges=49152-131071,180224-262143
+
+The URL would be for a script which has access to the files
+contained in the torrent, and to the metadata (.torrent) file
+itself, so that it may calculate what byte ranges to pull from
+what files.  One such script has been written by DeHackEd, and
+is available at http://bt.degreez.net .
+
+The script should return, if everything is okay, either a status
+of 200 (OK) and a block of data (either the entire piece if no
+ranges were given, or the ranges of data requested for that piece
+appended together), in binary format, or 503 (Service Temporarily
+Unavailable), with the body of the return being an ASCII integer
+value specifying how long the client should wait before retrying.
+The client should consider any other return code as an error.
+In the case of an error, the client should retry, but should
+retry less often if the failure to contact the seed continues.
+
+
+* server-side implementation notes.
+
+The purpose of the http seed script is to limit access to the
+data being downloaded so that the web server isn't overwhelmed
+by clients asking for the data.  If it weren't for this limiting,
+there would be no way to prevent someone from coding a client
+to try to download continuously or multiply, resulting in a
+heavy load on the server.  Limiting the download rate also
+allows an http seed script to be run on a web account where
+the total amount of data downloaded is restricted or may result
+in extra service charges.
+
+The script must provide three major functions:
+
+1. Limit its average upload to a reasonable level. 
+
+2. Intelligently tell peers how long they should wait before
+   retrying.
+
+3. translate from an info-hash and piece number to a byte range
+   within a file or set of files, and return those bytes.
+
+Another highly desirable function is to check whether peers are
+retrying too often, and to automatically ban those peers.
+
+Other desirable features include a way of monitoring the tracker
+the torrent is using and to stop uploading data if sufficient
+P2P seeds exist, and a way to feed back to the tracker to show
+a seed is present.
+
+
+
+* client-side implementation notes.
+
+The prototype code base has a default retry time of 30 seconds;
+after 3 retries with errors, the time is lengthened with each
+cycle.
+
+The prototype code will not display any errors with contacting
+http seeds (unless the URL given in the .torrent is incorrect)
+until it has received data from that seed.  (The prototype code
+also won't display any errors for any http reply that was
+actually received.)
+
+Current behavior is:  Request the rarest piece you're missing
+in entirety that you can locate.  If you have no pieces that
+aren't partially downloaded, skip one retry cycle, then start
+requesting partials.  If you receive a 503 response, set the
+retry time equal to the integer value received in the response.

file:b/edit_database.php (new)
--- /dev/null
+++ b/edit_database.php
@@ -1,1 +1,127 @@
+<?php
+require ("config.php");
+require_once ("funcsv2.php");
+//Check session
+session_start();
 
+if (!$_SESSION['admin_logged_in'])
+{
+	//check fails
+	header("Location: authenticate.php?status=session");
+	exit();
+}
+?>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+	<title>Edit Torrent in Database</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+	<link rel="stylesheet" href="./css/style.css" type="text/css" />
+</head>
+<body>
+<h1>Edit Torrent in Database</h1>
+<h2>This page allows you to edit torrents that are already in the database.  If you need to change other things about
+the torrent please <a href="deleter.php">delete it</a> and add it again.</h2>
+	
+<?php
+
+//connect to database
+if ($GLOBALS["persist"])
+	$db = mysql_pconnect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - " . mysql_error() . "</p>");
+else
+	$db = mysql_connect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - " . mysql_error() . "</p>");
+mysql_select_db($database) or die(errorMessage() . "Error selecting database.</p>");
+
+//get filename from URL string
+if (isset($_GET['filename'])) {
+	$filename = htmlentities($_GET['filename']);
+}
+
+//if not edit database or filename set, display all torrents as links
+if (!isset($_POST["editdatabase"]) && !isset($filename))
+{
+	?>
+	<p><strong>Click on a file to edit it:</strong></p>
+	<table border="0">
+	<?php
+	if ($GLOBALS["customtitle"] == "true")
+	$query = "SELECT title, filename FROM ".$prefix."namemap ORDER BY title ASC";	
+	else $query = "SELECT filename FROM ".$prefix."namemap ORDER BY filename ASC";
+	$rows = mysql_query($query) or die(errorMessage() . "Can't do SQL query - " . mysql_error() . "</p>");
+	
+	while ($data = mysql_fetch_row($rows))
+	{
+		if ($GLOBALS["customtitle"] == "true")
+		echo "<tr><td><a href=\"" . htmlentities($_SERVER['PHP_SELF']) . "?filename=" . rawurlencode($data[1]) . "\">" . $data[0] . "</a></td></tr>\n";
+		else echo "<tr><td><a href=\"" . htmlentities($_SERVER['PHP_SELF']) . "?filename=" . rawurlencode($data[0]) . "\">" . $data[0] . "</a></td></tr>\n";
+	}
+	?>
+	</table>
+	<?php
+}
+
+if (isset($filename) && !isset($_POST["editdatabase"]))
+{
+	$query = "SELECT info_hash,title,filename,url,pubDate FROM ".$prefix."namemap WHERE filename = '" . mysql_real_escape_string($filename) . "'";
+	$rows = mysql_query($query) or die(errorMessage() . "Can't do SQL query - " . mysql_error() . "</p>");
+	
+	$data = mysql_fetch_row($rows); //should be only one entry...
+	?>
+	<form action="<?php echo htmlentities($_SERVER['PHP_SELF']);?>" method="POST">
+	<input type="hidden" name="editdatabase" value="1">
+	<input type="hidden" name="<?php echo $data[0];?>" value="<?php echo $data[0];?>">
+	<input type="hidden" name="<?php echo $data[0] . "_old_filename";?>" value="<?php echo $data[2];?>">
+	<table border="0">
+	<tr><td><b>Info Hash: </b></td><td><?php echo $data[0];?></td></tr>
+	<tr><td><b>Title:</b></td><td><input type="text" name="<?php echo $data[0] . "_title";?>" size="60" value="<?php echo $data[1];?>"></td></tr>
+	<tr><td><b>Filename:</b></td><td><input type="text" name="<?php echo $data[0] . "_filename";?>" size="60" value="<?php echo $data[2];?>"></td></tr>
+	<tr><td><b>URL:</b></td><td><input type="text" name="<?php echo $data[0] . "_url";?>" size="60" value="<?php echo $data[3];?>"></td></tr>
+	<tr><td><b>Publication Date:</b></td><td><input type="text" name="<?php echo $data[0] . "_pubDate";?>" size="60" value="<?php echo $data[4];?>"></td></tr>
+	<tr><td><hr></td><td><hr></td></tr>		
+	
+	</table>
+	<br>
+	<input type="submit" value="Edit Entry">
+	</form>
+	
+	<?php
+}
+
+//write data to database
+if (isset($_POST["editdatabase"]))
+{
+	$temp_counter = (count($_POST)-1)/5;
+	array_shift($_POST);
+	
+	for ($i = 0; $i < $temp_counter; $i++)
+	{
+		$temp_hash = htmlspecialchars(array_shift($_POST));
+		$old_filename = htmlspecialchars(array_shift($_POST));
+		$temp_title = htmlspecialchars(array_shift($_POST));
+		$temp_filename = array_shift($_POST);
+		$temp_filename = Ltrim($temp_filename);
+		$temp_filename = htmlspecialchars(rtrim($temp_filename));
+		$temp_url = htmlspecialchars(array_shift($_POST));
+		$temp_pubDate = htmlspecialchars(array_shift($_POST));
+		$query = "UPDATE ".$prefix."namemap SET title=\"$temp_title\", filename=\"$temp_filename\", url=\"$temp_url\", pubDate=\"$temp_pubDate\" WHERE info_hash=\"$temp_hash\"";
+		mysql_query($query) or die(errorMessage() . "Can't do SQL query - " . mysql_error() . "</p>");
+		//if filename changes, rename .torrent
+		if ($old_filename != $temp_filename)
+			rename("torrents/" . $old_filename . ".torrent", "torrents/" . $temp_filename . ".torrent");
+	}
+	
+	//run RSS generator
+	require_once("rss_generator.php");
+	
+	echo "<br><p class=\"success\">The database was edited successfully!</p>\n";
+}
+
+?>
+<br>
+<br>
+<a href="admin.php"><img src="images/admin.png" border="0" class="icon" alt="Admin Page" title="Admin Page" /></a><a href="admin.php">Return to Admin Page</a>
+</body>
+</html>
+

file:b/editconfig.php (new)
--- /dev/null
+++ b/editconfig.php
@@ -1,1 +1,549 @@
-
+<?php
+require ("config.php");
+require_once ("funcsv2.php");
+//Check session
+session_start();
+
+if (!$_SESSION['admin_logged_in'])
+{
+	//check fails
+	header("Location: authenticate.php?status=session");
+	exit();
+}
+?>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html><head><title>Edit Config File</title>
+	<meta http-equiv="Content-type" content="text/html; charset=iso-8859-1" />
+	<link rel="stylesheet" href="./css/style.css" type="text/css" />
+</head><body>
+
+<?php
+//open up config file and display for editing
+if (!isset($_POST["saveconfig"]))
+{
+	?>
+	<h1>Edit Config File</h1>
+	<form action="<?php echo htmlentities($_SERVER['PHP_SELF']);?>" method="POST">
+	<input type="hidden" name="saveconfig" value="1">
+	<h2>This page allows you to configure the "config.php" settings.  This file stores all the necessary
+	settings for your tracker.  Please do NOT edit the "config.php" file directly, 
+	use this admin page for any changes.</h2>
+	<h2><span class="notice">*</span> - required value</h2>
+	<table border="1" cellpadding="3">
+	<?php
+	//open up config file
+	$fr = fopen("config.php", "r") or die(errorMessage() . "Error: couldn't read config.php!</p>");
+	$temp = fgets($fr);
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+2, -2);
+	?>
+	<tr><td>Make tracker hidden: This will require a login by either the admin or upload user in order to
+	see the torrents available on the main statistics page.  This does not mean it's a private tracker.  If you
+	need a private tracker, there are many other trackers out there.  Also, you will need to secure the "torrents"
+	folder with an .htaccess file for Apache or some other method.  The tracker will still accept all valid
+	connections by clients.  There is no user checking in that regard.</td>
+	<td><input type="checkbox" value="<?php if($temp == "true") echo "on"; else echo "off"?>" name="hiddentracker"<?php if ($temp == "true") echo " checked";?>></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+2, -2);
+	?>
+	<tr><td>Enable or disable scraping by clients.  Generally it is safe to leave this on unless
+	you have a large number of torrents or users which can lead to increased bandwidth usage.  Also, scraping
+	can possibily be used maliciously by abusive clients.</td>
+	<td><input type="checkbox" value="<?php if($temp == "true") echo "on"; else echo "off"?>" name="scrape"<?php if ($temp == "true") echo " checked";?>></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+2, -2);
+	?>
+	<tr><td>Displays custom titles on the main torrent statistics page instead of the filename. This is because the uploader script will auto-rename your uploaded filename to exactly what you specified initally or by automatically using the data from the uploaded torrent. Check this if you want the titles to be different from the filename.</td>
+	<td><input type="checkbox" value="<?php if($temp == "true") echo "on"; else echo "off"?>" name="customtitle"<?php if ($temp == "true") echo " checked";?>></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+3, -3);
+	?>
+	<tr><td>Short Announce URL: You can turn on the short announce feature, making the URL end in /announce for your tracker. You should not utilize both tracker URL forms in one torrent at the same time, or you will get inconsistent results. Note: You will need the provided htaccess file, have URL rewrite capabilities, and have it properly set up to use this feature. Otherwise, leave it disabled.</td>
+	<td><select name="announceurl" id="announceurl">
+	<option title="disabled" value="announce.php"<?php if($temp == "announce.php") echo " selected=\"selected\"";?>>disabled</option>
+	<option title="enabled" value="announce"<?php if($temp == "announce") echo " selected=\"selected\"";?>>enabled</option>
+	</select>
+	</td>
+	</tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+2, -2);
+	?>
+	<tr><td><span class="notice">*</span> Lists the number of torrents on each page on your torrent tracker list. Default is 10.</td>
+	<td><input type="text" name="indexpagelimitspecify" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+2, -2);
+	?>
+	<tr><td><span class="notice">*</span> Lists the number of torrents on each page on the detailed statistics page. Default is 5.</td>
+	<td><input type="text" name="statspagelimitspecify" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+2, -2);
+	?>
+	<tr><td><span class="notice">*</span> Maximum reannounce interval (in seconds) 1800 == 30 minutes</td>
+	<td><input type="text" name="report_interval" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+2, -2);
+	?>
+	<tr><td><span class="notice">*</span> Minimum reannounce interval (also in seconds) 300 == 5 minutes</td>
+	<td><input type="text" name="min_interval" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+2, -2);
+	?>
+	<tr><td><span class="notice">*</span> Number of peers to send in one request.  Some logic will break if you set this to more than 300,
+	so please don't do that. 100 is the most you should set anyway.</td>
+	<td><input type="text" name="maxpeers" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+2, -2);
+	?>	
+	<tr><td>If set, NAT checking will be performed.
+	This may cause trouble with some providers, so it's
+	off by default.</td>
+	<td><input type="checkbox" value="<?php if($temp == "true") echo "on"; else echo "off"?>" name="NAT"<?php if ($temp == "true") echo " checked";?>></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+2, -2);
+	?>
+	<tr><td>Persistent MySQL connections:
+	Check with your webmaster to see if you're allowed to use these.
+	Highly recommended, especially for higher loads, but generally
+	not allowed unless it's a dedicated machine.</td>
+	<td><input type="checkbox" value="<?php if($temp == "true") echo "on"; else echo "off"?>" name="persist"<?php if ($temp == "true") echo " checked";?>></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+2, -2);
+	?>
+	<tr><td>Allow users to override ip address.
+	Enable this if you know people have a legit reason to use
+	this function. Leave disabled otherwise.</td>
+	<td><input type="checkbox" value="<?php if($temp == "true") echo "on"; else echo "off"?>" name="ip_override"<?php if ($temp == "true") echo " checked";?>></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+2, -2);
+	?>
+	<tr><td>For heavily loaded trackers, uncheck this. It will stop count the number
+	of downloaded bytes and the speed of the torrent, but will significantly reduce
+	the load.</td>
+	<td><input type="checkbox" value="<?php if($temp == "true") echo "on"; else echo "off"?>" name="countbytes"<?php if ($temp == "true") echo " checked";?>></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+3, -3);
+	?>
+	<tr><td><span class="notice">*</span> Username for individual who can add torrents to tracker database.
+	This user is only able to create, and not delete torrents to the tracker.
+	For full privileges, see the admin user.</td>
+	<td><input type="text" name="upload_username" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+3, -3);
+	?>
+	<tr><td><span class="notice">*</span> Password for individual who can add torrents to tracker database.
+	Again, this user is only able to create, and not delete torrents to the tracker.
+	For full privileges, see the admin user.<br><br>
+	<input type="hidden" name="old_upload_password" value="<?php echo $temp;?>">
+	<b>Current MD5 hashed username+password: <?php echo $temp;?></b></td>
+	<td><input type="password" name="upload_password" size="40" value=""></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+3, -3);
+	?>
+	<tr><td><span class="notice">*</span> Admin username. The admin is able to go to the admin page and show detailed 
+	information about the tracker as well as access a few other important tools.
+	The admin is also able to upload torrents to the database
+	just like the previous account.</td>
+	<td><input type="text" name="admin_username" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+3, -3);
+	?>
+	<tr><td><span class="notice">*</span> Password for admin.  Again, The admin is able to go to the admin page and show detailed 
+	information about the tracker as well as access a few other important tools.
+	The admin is also able to upload torrents to the database.<br><br>
+	<input type="hidden" name="old_admin_password" value="<?php echo $temp;?>">
+	<b>Current MD5 hashed username+password: <?php echo $temp;?></b></td>
+	<td><input type="password" name="admin_password" size="40" value=""></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = clean(substr($temp, strpos($temp, "=")+3, -3));
+	?>
+	<tr><td>Title on index.php statistics page, if not set, defaults to "Tracker Statistics"</td>
+	<td><input type="text" name="title" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+3, -3);
+	?>
+	<tr><td><span class="notice">*</span> Database Hostname: This is the MySQL database hostname, if it is the local machine, it should
+	be set to localhost.</td>
+	<td><input type="text" name="dbhost" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+3, -3);
+	?>
+	<tr><td><span class="notice">*</span> Database Username: This is the user who has access to the database table.  If you are unsure,
+	check with your system administrator.</td>
+	<td><input type="text" name="dbuser" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+3, -3);
+	?>
+	<tr><td><span class="notice">*</span> Database Password: This is the password for the user who has access to the database table.
+	If you are unsure, check with your system administrator.</td>
+	<td><input type="text" name="dbpass" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+3, -3);
+	?>
+	<tr><td><span class="notice">*</span> Database name: This is the name of the database.  If you are unsure, check with
+	your system administrator.</td>
+	<td><input type="text" name="database" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+2, -2);
+	?>
+	<tr><td>Enable RSS feed:
+	If you do not want the RSS feed to be created for privacy reasons or do not need it disable this checkbox.</td>
+	<td><input type="checkbox" value="<?php if($temp == "true") echo "on"; else echo "off"?>" name="enablerss"<?php if ($temp == "true") echo " checked";?>></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = clean(substr($temp, strpos($temp, "=")+3, -3));
+	?>
+	<tr><td>RSS Title: In the rss.xml file, this is the main <pre>&lt;title&gt;</pre> tag.</td>
+	<td><input type="text" name="rss_title" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+3, -3);
+	?>
+	<tr><td>RSS link to main website: In the rss.xml file, this is the main <pre>&lt;link&gt;</pre> tag.</td>
+	<td><input type="text" name="rss_link" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = clean(substr($temp, strpos($temp, "=")+3, -3));
+	?>
+	<tr><td>RSS description: In the rss.xml file, this is the main <pre>&lt;description&gt;</pre> tag.</td>
+	<td><input type="text" name="rss_description" size="60" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+3, -3);
+	?>
+	<tr><td><span class="notice">*</span> Main website url that the tracker runs on, example: http://www.mywebsite.com</td>
+	<td><input type="text" name="website_url" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+2, -2);
+	?>
+	<tr><td><span class="notice">*</span> For HTTP seeding, this is the maximum total upload rate per second in kilobytes, for example 100 would be 100 KB/s</td>
+	<td><input type="text" name="max_upload_rate" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+2, -2);
+	?>
+	<tr><td><span class="notice">*</span> For HTTP seeding, this is the maximum number of uploads to run at a time</td>
+	<td><input type="text" name="max_uploads" size="40" value="<?php echo $temp;?>"></td></tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+3, -3);
+	?>
+	<tr><td><span class="notice">*</span> Date format of the torrent publication date. It shows on statistics.php. If you change this setting, you will have to change it for every other existing torrent!</td>
+	<td>
+	<select name="dateformat" id="dateformat">
+	<option title="Mon, 4 Jan, 1999 01:15:40 PM" value="D, j M, Y h:i:s A"<?php if($temp == "D, j M, Y h:i:s A") echo " selected=\"selected\"";?>>Mon, 4 Jan, 1999 01:15:40 PM</option>
+	<option title="Monday, 4 Jan, 1999 01:15:40 PM" value="l, j M, Y h:i:s A"<?php if($temp == "l, j M, Y h:i:s A") echo " selected=\"selected\"";?>>Monday, 4 Jan, 1999 01:15:40 PM</option>
+	<option title="Mon, 4 January, 1999 01:15:40 PM" value="D, j F, Y h:i:s A"<?php if($temp == "D, j F, Y h:i:s A") echo " selected=\"selected\"";?>>Mon, 4 January, 1999 01:15:40 PM</option>
+	<option title="Monday, 4 January, 1999 01:15:40 PM" value="l, j M, Y h:i:s A"<?php if($temp == "l, j F, Y h:i:s A") echo " selected=\"selected\"";?>>Monday, 4 January, 1999 01:15:40 PM</option>
+	<option title="Mon, 4 Jan, 1999 13:15:40" value="D, j M, Y H:i:s"<?php if($temp == "D, j M, Y H:i:s") echo " selected=\"selected\"";?>>Mon, 4 Jan, 1999 13:15:40</option>
+	<option title="Monday, 4 Jan, 1999 13:15:40" value="l, j M, Y H:i:s"<?php if($temp == "l, j M, Y H:i:s") echo " selected=\"selected\"";?>>Monday, 4 Jan, 1999 13:15:40</option>
+	<option title="Mon, 4 January, 1999 13:15:40" value="D, j F, Y H:i:s"<?php if($temp == "D, j F, Y H:i:s") echo " selected=\"selected\"";?>>Mon, January 4, 1999 13:15:40</option>
+	<option title="Monday, 4 January, 1999 13:15:40" value="l, j F, Y H:i:s"<?php if($temp == "l, j F, Y H:i:s") echo " selected=\"selected\"";?>>Monday, 4 January, 1999 13:15:40</option>
+	<option title="Mon, Jan 4, 1999 01:15:40 PM" value="D, M j, Y h:i:s A"<?php if($temp == "D, M j, Y h:i:s A") echo " selected=\"selected\"";?>>Mon, Jan 4, 1999 01:15:40 PM</option>
+	<option title="Monday, Jan 4, 1999 01:15:40 PM" value="l, M j, Y h:i:s A"<?php if($temp == "l, M j, Y h:i:s A") echo " selected=\"selected\"";?>>Monday, Jan 4, 1999 01:15:40 PM</option>
+	<option title="Mon, January 4, 1999 01:15:40 PM" value="D, F j, Y h:i:s A"<?php if($temp == "D, F j, Y h:i:s A") echo " selected=\"selected\"";?>>Mon, January 4, 1999 01:15:40 PM</option>
+	<option title="Monday, January 4, 1999 01:15:40 PM" value="l, F j, Y h:i:s A"<?php if($temp == "l, F j, Y h:i:s A") echo " selected=\"selected\"";?>>Monday, January 4, 1999 01:15:40 PM</option>
+	<option title="Mon, Jan 4, 1999 13:15:40" value="D, M j, Y H:i:s"<?php if($temp == "D, M j, Y H:i:s") echo " selected=\"selected\"";?>>Mon, Jan 4, 1999 13:15:40</option>
+	<option title="Monday, Jan 4, 1999 13:15:40" value="l, M j, Y H:i:s"<?php if($temp == "l, M j, Y H:i:s") echo " selected=\"selected\"";?>>Monday, Jan 4, 1999 13:15:40</option>
+	<option title="Mon, January 4, 1999 13:15:40" value="D, F j, Y H:i:s"<?php if($temp == "D, F j, Y H:i:s") echo " selected=\"selected\"";?>>Mon, January 4, 1999 13:15:40</option>
+	<option title="Monday, January 4, 1999 13:15:40" value="l, F j, Y H:i:s"<?php if($temp == "l, F j, Y H:i:s") echo " selected=\"selected\"";?>>Monday, January 4, 1999 13:15:40</option>
+	</select>
+	</td>
+	</tr>
+	<?php
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+3, -3);
+	?>
+	<tr><td><span class="notice">*</span> Timezone that the server runs on</td>
+	<td>
+	<select name="timezone" id="timezone">
+	<option title="[UTC - 12] Baker Island Time" value="-1200"<?php if($temp == "-1200") echo " selected=\"selected\"";?>>[UTC - 12] Baker Island Time</option>
+	<option title="[UTC - 11] Niue Time, Samoa Standard Time" value="-1100"<?php if($temp == "-1100") echo " selected=\"selected\"";?>>[UTC - 11] Niue Time, Samoa Standard Time</option>
+	<option title="[UTC - 10] Hawaii-Aleutian Standard Time, Cook Island Time" value="-1000"<?php if($temp == "-1000") echo " selected=\"selected\"";?>>[UTC - 10] Hawaii-Aleutian Standard Time, Cook Isl...</option>
+	<option title="[UTC - 9:30] Marquesas Islands Time" value="-0930"<?php if($temp == "-0930") echo " selected=\"selected\"";?>>[UTC - 9:30] Marquesas Islands Time</option>
+	<option title="[UTC - 9] Alaska Standard Time, Gambier Island Time" value="-0900"<?php if($temp == "-0900") echo " selected=\"selected\"";?>>[UTC - 9] Alaska Standard Time, Gambier Island Tim...</option>
+	<option title="[UTC - 8] Pacific Standard Time" value="-0800"<?php if($temp == "-0800") echo " selected=\"selected\"";?>>[UTC - 8] Pacific Standard Time</option>
+	<option title="[UTC - 7] Mountain Standard Time" value="-0700"<?php if($temp == "-0700") echo " selected=\"selected\"";?>>[UTC - 7] Mountain Standard Time</option>
+	<option title="[UTC - 6] Central Standard Time" value="-0600"<?php if($temp == "-0600") echo " selected=\"selected\"";?>>[UTC - 6] Central Standard Time</option>
+	<option title="[UTC - 5] Eastern Standard Time" value="-0500"<?php if($temp == "-0500") echo " selected=\"selected\"";?>>[UTC - 5] Eastern Standard Time</option>
+	<option title="[UTC - 4] Atlantic Standard Time" value="-0400"<?php if($temp == "-0400") echo " selected=\"selected\"";?>>[UTC - 4] Atlantic Standard Time</option>
+	<option title="[UTC - 3:30] Newfoundland Standard Time" value="-0330"<?php if($temp == "-0330") echo " selected=\"selected\"";?>>[UTC - 3:30] Newfoundland Standard Time</option>
+	<option title="[UTC - 3] Amazon Standard Time, Central Greenland Time" value="-0300"<?php if($temp == "-0300") echo " selected=\"selected\"";?>>[UTC - 3] Amazon Standard Time, Central Greenland ...</option>
+	<option title="[UTC - 2] Fernando de Noronha Time, South Georgia &amp; the South Sandwich Islands Time" value="-0200"<?php if($temp == "-0200") echo " selected=\"selected\"";?>>[UTC - 2] Fernando de Noronha Time, South Georgia ...</option>
+	<option title="[UTC - 1] Azores Standard Time, Cape Verde Time, Eastern Greenland Time" value="-0100"<?php if($temp == "-0100") echo " selected=\"selected\"";?>>[UTC - 1] Azores Standard Time, Cape Verde Time, E...</option>
+	<option title="[UTC] Western European Time, Greenwich Mean Time" value="+0000"<?php if($temp == "+0000") echo " selected=\"selected\"";?>>[UTC] Western European Time, Greenwich Mean Time</option>
+	<option title="[UTC + 1] Central European Time, West African Time" value="+0100"<?php if($temp == "+0100") echo " selected=\"selected\"";?>>[UTC + 1] Central European Time, West African Time</option>
+	<option title="[UTC + 2] Eastern European Time, Central African Time" value="+0200"<?php if($temp == "+0200") echo " selected=\"selected\"";?>>[UTC + 2] Eastern European Time, Central African T...</option>
+	<option title="[UTC + 3] Moscow Standard Time, Eastern African Time" value="+0300"<?php if($temp == "+0300") echo " selected=\"selected\"";?>>[UTC + 3] Moscow Standard Time, Eastern African Ti...</option>
+	<option title="[UTC + 3:30] Iran Standard Time" value="+0330"<?php if($temp == "+0330") echo " selected=\"selected\"";?>>[UTC + 3:30] Iran Standard Time</option>
+	<option title="[UTC + 4] Gulf Standard Time, Samara Standard Time" value="+0400"<?php if($temp == "+0400") echo " selected=\"selected\"";?>>[UTC + 4] Gulf Standard Time, Samara Standard Time</option>
+	<option title="[UTC + 4:30] Afghanistan Time" value="+0430"<?php if($temp == "+0430") echo " selected=\"selected\"";?>>[UTC + 4:30] Afghanistan Time</option>
+	<option title="[UTC + 5] Pakistan Standard Time, Yekaterinburg Standard Time" value="+0500"<?php if($temp == "+0500") echo " selected=\"selected\"";?>>[UTC + 5] Pakistan Standard Time, Yekaterinburg St...</option>
+	<option title="[UTC + 5:30] Indian Standard Time, Sri Lanka Time" value="+0530"<?php if($temp == "+0530") echo " selected=\"selected\"";?>>[UTC + 5:30] Indian Standard Time, Sri Lanka Time</option>
+	<option title="[UTC + 6] Bangladesh Time, Bhutan Time, Novosibirsk Standard Time" value="+0600"<?php if($temp == "+0600") echo " selected=\"selected\"";?>>[UTC + 6] Bangladesh Time, Bhutan Time, Novosibirs...</option>
+	<option title="[UTC + 6:30] Cocos Islands Time, Myanmar Time" value="+0630"<?php if($temp == "+0630") echo " selected=\"selected\"";?>>[UTC + 6:30] Cocos Islands Time, Myanmar Time</option>
+	<option title="[UTC + 7] Indochina Time, Krasnoyarsk Standard Time" value="+0700"<?php if($temp == "+0700") echo " selected=\"selected\"";?>>[UTC + 7] Indochina Time, Krasnoyarsk Standard Tim...</option>
+	<option title="[UTC + 8] Chinese Standard Time, Australian Western Standard Time, Irkutsk Standard Time" value="+0800"<?php if($temp == "+0800") echo " selected=\"selected\"";?>>[UTC + 8] Chinese Standard Time, Australian Wester...</option>
+	<option title="[UTC + 9] Japan Standard Time, Korea Standard Time, Chita Standard Time" value="+0900"<?php if($temp == "+0900") echo " selected=\"selected\"";?>>[UTC + 9] Japan Standard Time, Korea Standard Time...</option>
+	<option title="[UTC + 9:30] Australian Central Standard Time" value="+0930"<?php if($temp == "+0930") echo " selected=\"selected\"";?>>[UTC + 9:30] Australian Central Standard Time</option>
+	<option title="[UTC + 10] Australian Eastern Standard Time, Vladivostok Standard Time" value="+1000"<?php if($temp == "+1000") echo " selected=\"selected\"";?>>[UTC + 10] Australian Eastern Standard Time, Vladi...</option>
+	<option title="[UTC + 10:30] Lord Howe Standard Time" value="+1030"<?php if($temp == "+1030") echo " selected=\"selected\"";?>>[UTC + 10:30] Lord Howe Standard Time</option>
+	<option title="[UTC + 11] Solomon Island Time, Magadan Standard Time" value="+1100"<?php if($temp == "+1100") echo " selected=\"selected\"";?>>[UTC + 11] Solomon Island Time, Magadan Standard T...</option>
+	<option title="[UTC + 11:30] Norfolk Island Time" value="+1130"<?php if($temp == "+1130") echo " selected=\"selected\"";?>>[UTC + 11:30] Norfolk Island Time</option>
+	<option title="[UTC + 12] New Zealand Time, Fiji Time, Kamchatka Standard Time" value="+1200"<?php if($temp == "+1200") echo " selected=\"selected\"";?>>[UTC + 12] New Zealand Time, Fiji Time, Kamchatka ...</option>
+	<option title="[UTC + 13] Tonga Time, Phoenix Islands Time" value="+1300"<?php if($temp == "+1300") echo " selected=\"selected\"";?>>[UTC + 13] Tonga Time, Phoenix Islands Time</option>
+	<option title="[UTC + 14] Line Island Time" value="+1400"<?php if($temp == "+1400") echo " selected=\"selected\"";?>>[UTC + 14] Line Island Time</option>
+	</select>
+	</td>
+	</tr>
+	<?php
+	
+	//get MySQL table prefix, store in hidden form field
+	$temp = fgets($fr);
+	$temp = substr($temp, strpos($temp, "=")+3, -3);
+	?>
+	<input type="hidden" name="prefix" value="<?php echo $temp;?>" />	
+
+	<?php
+	fclose($fr);
+
+	?>		
+	</table>
+	<input type="submit" value="Save Config">
+	</form>
+	
+	<?php
+}
+
+
+if (isset($_POST["saveconfig"]))
+{
+	//check required entries for values, if blank: error out
+	if ($_POST["announceurl"] == "")
+	{
+		echo errorMessage() . "Error: The announce URL is blank.</p>";
+		exit();
+	}
+	if (!is_numeric($_POST["indexpagelimitspecify"]) || $_POST["indexpagelimitspecify"] == "" || $_POST["indexpagelimitspecify"] <= 0)
+	{
+		echo errorMessage() . "Error: The index page limit is not an integer, a negative number, or is blank.</p>";
+		exit();
+	}	
+	if (!is_numeric($_POST["statspagelimitspecify"]) || $_POST["statspagelimitspecify"] == "" || $_POST["statspagelimitspecify"] <= 0)
+	{
+		echo errorMessage() . "Error: The statistics page limit is not an integer, a negative number, or is blank.</p>";
+		exit();
+	}
+	if (!is_numeric($_POST["report_interval"]) || $_POST["report_interval"] == "" || $_POST["report_interval"] <= 0)
+	{
+		echo errorMessage() . "Error: The maximum reannounce interval is not an integer, a negative number, or is blank.</p>";
+		exit();
+	}
+	if (!is_numeric($_POST["min_interval"]) || $_POST["min_interval"] == "" || $_POST["min_interval"] <= 0)
+	{
+		echo errorMessage() . "Error: The minimum reannounce interval is not an integer, a negative number, or is blank.</p>";
+		exit();
+	}
+	if (!is_numeric($_POST["maxpeers"]) || $_POST["maxpeers"] == "" || $_POST["maxpeers"] > 300 || $_POST["maxpeers"] <= 0)
+	{
+		echo errorMessage() . "Error: The number of peers to send in one request is not an integer, over 300, a negative number, zero, or blank.</p>";
+		exit();
+	}
+	if ($_POST["upload_username"] == "")
+	{
+		echo errorMessage() . "Error: The upload username is blank.</p>";
+		exit();
+	}
+	if ($_POST["admin_username"] == "")
+	{
+		echo errorMessage() . "Error: The admin username is blank.</p>";
+		exit();
+	}
+	if ($_POST["dbhost"] == "")
+	{
+		echo errorMessage() . "Error: The database hostname is blank.</p>";
+		exit();
+	}
+	if ($_POST["dbuser"] == "")
+	{
+		echo errorMessage() . "Error: The database username is blank.</p>";
+		exit();
+	}
+	if ($_POST["dbpass"] == "")
+	{
+		echo errorMessage() . "Error: The database password is blank.</p>";
+		exit();
+	}
+	if ($_POST["database"] == "")
+	{
+		echo errorMessage() . "Error: The database name is blank.</p>";
+		exit();
+	}
+	if ($_POST["rss_link"] != "" && Substr($_POST["rss_link"], 0, 7) != "http://")
+	{
+		echo errorMessage() . "Error: The RSS website URL does not start with http://</p>";
+		exit();
+	}
+	if ($_POST["website_url"] == "" || Substr($_POST["website_url"], 0, 7) != "http://")
+	{
+		echo errorMessage() . "Error: The website URL does not start with http:// or is blank.</p>";
+		exit();
+	}
+	if (!is_numeric($_POST["max_upload_rate"]) || $_POST["max_upload_rate"] == "" || $_POST["max_upload_rate"] <= 0)
+	{
+		echo errorMessage() . "Error: The maximum upload rate is not an integer, a negative number, or is blank.</p>";
+		exit();
+	}
+	if (!is_numeric($_POST["max_uploads"]) || $_POST["max_uploads"] == "" || $_POST["max_uploads"] <= 0)
+	{
+		echo errorMessage() . "Error: The maximum uploads is not an integer, a negative number, or is blank.</p>";
+		exit();
+	}
+	if ($_POST["dateformat"] == "")
+	{
+		echo errorMessage() . "Error: The date format is blank.</p>";
+		exit();
+	}
+	if ($_POST["timezone"] == "")
+	{
+		echo errorMessage() . "Error: The timezone is blank.</p>";
+		exit();
+	}
+	if ($_POST["upload_username"] == $_POST["admin_username"])
+	{
+		echo errorMessage() . "Error: The admin username cannot be the same as the upload username.</p>";
+		exit();
+	}
+	
+	//calculate new MD5 password if needed
+	if ($_POST["upload_password"] != "")
+	{
+		$_POST["upload_password"] = md5($_POST["upload_username"].$_POST["upload_password"]);
+	}
+	else
+		$_POST["upload_password"] = $_POST["old_upload_password"];
+	if ($_POST["admin_password"] != "")
+	{
+		$_POST["admin_password"] = md5($_POST["admin_username"].$_POST["admin_password"]);
+	}
+	else
+		$_POST["admin_password"] = $_POST["old_admin_password"];
+		
+	//check if config.php has write access
+	if (is_writable("config.php"))
+	{
+		//go through checkboxes and change "on" to "true"
+		if (isset($_POST["hiddentracker"]))
+			$hiddentracker = "true";
+		else
+			$hiddentracker = "false";
+		if (isset($_POST["enablerss"]))
+			$enablerss = "true";
+		else
+			$enablerss = "false";
+		if (isset($_POST["scrape"]))
+			$scrape = "true";
+		else
+			$scrape = "false";
+		if (isset($_POST["customtitle"]))
+			$customtitle = "true";
+		else
+			$customtitle = "false";
+		if (isset($_POST["NAT"]))
+			$NAT = "true";
+		else
+			$NAT = "false";
+		if (isset($_POST["persist"]))
+			$persist = "true";
+		else
+			$persist = "false";
+		if (isset($_POST["ip_override"]))
+			$ip_override = "true";
+		else
+			$ip_override = "false";
+		if (isset($_POST["countbytes"]))
+			$countbytes = "true";
+		else
+			$countbytes = "false";
+
+		//write config.php file
+		$fd = fopen("config.php", "w") or die(errorMessage() . "Warning: write to config.php!</p>");
+		fwrite($fd, 
+		"<?php //Please do NOT edit this file, use the admin page for changes.\n" .
+		"\$GLOBALS['hiddentracker'] = " . $hiddentracker . ";\n" .
+		"\$GLOBALS['scrape'] = " . $scrape . ";\n" .
+		"\$GLOBALS['customtitle'] = " . $customtitle . ";\n" .
+		"\$announceurl = '" . htmlspecialchars($_POST["announceurl"]) . "';\n" .
+		"\$GLOBALS['indexpagelimitspecify'] = " . htmlspecialchars($_POST["indexpagelimitspecify"]) . ";\n" .
+		"\$GLOBALS['statspagelimitspecify'] = " . htmlspecialchars($_POST["statspagelimitspecify"]) . ";\n" .
+		"\$GLOBALS['report_interval'] = " . htmlspecialchars($_POST["report_interval"]) . ";\n" .
+		"\$GLOBALS['min_interval'] = " . htmlspecialchars($_POST["min_interval"]) . ";\n" .
+		"\$GLOBALS['maxpeers'] = " . htmlspecialchars($_POST["maxpeers"]) . ";\n" .
+		"\$GLOBALS['NAT'] = " . $NAT . ";\n" .
+		"\$GLOBALS['persist'] = " . $persist . ";\n" .
+		"\$GLOBALS['ip_override'] = " . $ip_override . ";\n" .
+		"\$GLOBALS['countbytes'] = " . $countbytes . ";\n" .
+		"\$upload_username = '" . htmlspecialchars($_POST["upload_username"]) . "';\n" .
+		"\$upload_password = '" . htmlspecialchars($_POST["upload_password"]) . "';\n" .
+		"\$admin_username = '" . htmlspecialchars($_POST["admin_username"]) . "';\n" .
+		"\$admin_password = '" . htmlspecialchars($_POST["admin_password"]) . "';\n" .
+		"\$GLOBALS['title'] = '" . htmlspecialchars(addquotes($_POST["title"])) . "';\n" .
+		"\$dbhost = '" . htmlspecialchars($_POST["dbhost"]) . "';\n" .
+		"\$dbuser = '" . htmlspecialchars($_POST["dbuser"]) . "';\n" .
+		"\$dbpass = '" . htmlspecialchars($_POST["dbpass"]) . "';\n" .
+		"\$database = '" . htmlspecialchars($_POST["database"]) . "';\n" .
+		"\$enablerss = " . $enablerss . ";\n" .
+		"\$rss_title = '" . htmlspecialchars(addquotes($_POST["rss_title"])) . "';\n" .
+		"\$rss_link = '" . htmlspecialchars($_POST["rss_link"]) . "';\n" .
+		"\$rss_description = '" . htmlspecialchars(addquotes($_POST["rss_description"])) . "';\n" .
+		"\$website_url = '" . htmlspecialchars($_POST["website_url"]) . "';\n" .
+		"\$GLOBALS['max_upload_rate'] = " . htmlspecialchars($_POST['max_upload_rate']) . ";\n" .
+		"\$GLOBALS['max_uploads'] = " . htmlspecialchars($_POST['max_uploads']) . ";\n" .
+		"\$dateformat = '" . htmlspecialchars($_POST["dateformat"]) . "';\n" .
+		"\$timezone = '" . htmlspecialchars($_POST["timezone"]) . "';\n" .
+		"\$prefix = '" . htmlspecialchars($_POST["prefix"]) . "';\n" .
+		"?>"
+		);
+
+		fclose($fd);
+		echo "<br><p class=\"success\">config.php file was edited successfully!</p>\n";
+		
+		//run RSS generator
+		require_once("rss_generator.php");
+	}
+	else
+	{
+		echo errorMessage() . "config.php was not able to be written.  Please check the permissions and try again.</p>\n";
+	}
+}
+
+?>
+<br>
+<br>
+<a href="admin.php"><img src="images/admin.png" border="0" class="icon" alt="Admin Page" title="Admin Page" /></a><a href="admin.php">Return to Admin Page</a>
+</body>
+</html>
+

file:b/funcsv2.php (new)
--- /dev/null
+++ b/funcsv2.php
@@ -1,1 +1,678 @@
-
+<?php
+
+
+//////////////////////////////////////////////////////////////////
+// Worker functions
+
+if (function_exists("bcadd"))
+{
+	function sqlAdd($left, $right)
+	{
+		return bcadd($left, $right,0);
+	}
+	function sqlSubtract($left, $right)
+	{
+		return bcsub($left, $right,0);
+	}
+	function sqlMultiply($left, $right)
+	{
+		return bcmul($left, $right,0);
+	}
+	function sqlDivide($left, $right)
+	{
+		return bcdiv($left, $right,0);
+	}
+}
+else // BC vs SQL math
+{
+
+// Uses the mysql database connection to perform string math. :)
+// Used by byte counting functions
+// No error handling as we assume nothing can go wrong. :|
+function sqlAdd($left, $right)
+{
+	$query = 'SELECT '.$left.'+'.$right;
+	$results = mysql_query($query) or showError("Database error.");
+	return mysql_result($results,0,0);
+}
+
+// Ditto
+function sqlSubtract($left, $right)
+{
+	$query = 'SELECT '.$left.'-'.$right;
+	$results = mysql_query($query) or showError("Database error");
+	return mysql_result($results,0,0);
+}
+
+function sqlDivide($left, $right)
+{
+	$query = 'SELECT '.$left.'/'.$right;
+	$results = mysql_query($query) or showError("Database error");
+	return mysql_result($results,0,0);
+}
+
+function sqlMultiply($left, $right)
+{
+	$query = 'SELECT '.$left.'*'.$right;
+	$results = mysql_query($query) or showError("Database error");
+	return mysql_result($results,0,0);
+}
+
+
+} // End of BC vs SQL
+
+// Runs a query with no regard for the result
+function quickQuery($query)
+{
+	$results = @mysql_query($query);
+	if (!is_bool($results))
+		mysql_free_result($results);
+	else
+		return $results;
+	return true;
+}
+
+if(!function_exists('hex2bin'))
+{
+	function hex2bin ($input, $assume_safe=true)
+	{
+		if ($assume_safe !== true && ! ((strlen($input) % 2) === 0 || preg_match ('/^[0-9a-f]+$/i', $input)))
+			return "";
+		return pack('H*', $input );
+	}
+}
+
+// Reports an error to the client in $message.
+// Any other output will confuse the client, so please don't do that.
+function showError($message, $log=false)
+{
+  if ($log)
+	  error_log("RivetTracker: Sent error ($message)");
+  echo "d14:failure reason".strlen($message).":$message"."e";
+  exit(0);
+}
+
+
+function errorMessage()
+{
+	echo "<center><img src='images/important.png' border='0' class='icon' alt='Critical Message' title='Critical Message' /></center>\n<p class='error'>";
+}
+
+
+
+// Used by newtorrents.php
+// Returns true/false, depending on if there were errors.
+function makeTorrent($hash, $tolerate = false)
+{
+	require("config.php"); //necessary to get the prefix value, require_once() doesn't seem to work :/
+	if (strlen($hash) != 40)
+		showError("makeTorrent: Received an invalid hash");
+	$result = true;
+	$query = "CREATE TABLE ".$prefix."x$hash (peer_id char(40) NOT NULL default '', bytes bigint NOT NULL default 0, ip char(50) NOT NULL default 'error.x', port smallint UNSIGNED NOT NULL default '0', status enum('leecher','seeder') NOT NULL, lastupdate int unsigned NOT NULL default 0, sequence int unsigned AUTO_INCREMENT NOT NULL, natuser enum('N', 'Y') not null default 'N', primary key(sequence), unique(peer_id)) DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci ENGINE = innodb";
+	if (!@mysql_query($query))
+		$result = false;
+	if (!$result && !$tolerate)
+		return false;
+	//peercaching is ALWAYS on
+	$query = "CREATE TABLE ".$prefix."y$hash (sequence int unsigned NOT NULL default 0, with_peerid char(101) NOT NULL default '', without_peerid char(40) NOT NULL default '', compact char(6) NOT NULL DEFAULT '', unique k (sequence)) DELAY_KEY_WRITE=1 CHECKSUM=0 DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci ENGINE = innodb";
+	mysql_query($query);
+		
+	$query = "INSERT INTO ".$prefix."summary set info_hash='".$hash."', lastSpeedCycle=UNIX_TIMESTAMP()";
+	if (!@mysql_query($query))
+		$result = false;
+	return $result;
+}
+
+// Returns true if the torrent exists.
+// Currently checks by locating the row in "summary"
+function verifyTorrent($hash)
+{
+	require("config.php"); //need prefix value...
+	$query = "SELECT COUNT(*) FROM ".$prefix."summary where info_hash='$hash'";
+	$results = mysql_query($query);
+	
+	$res = mysql_result($results,0,0);
+	
+	if ($res == 1)
+		return true;
+
+	return false;
+}
+
+function verifyHash($input)
+{
+	if (strlen($input) === 40 && preg_match('/^[0-9a-f]+$/', $input))
+		return true;
+	else
+		return false;
+}
+
+
+
+
+// Returns info on one peer
+function getPeerInfo($user, $hash)
+{
+	require("config.php");
+	// If "trackerid" is set, let's try that
+	if (isset($GLOBALS["trackerid"]))
+	{
+		$query = "SELECT peer_id,bytes,ip,port,status,lastupdate,sequence FROM ".$prefix."x$hash WHERE sequence=${GLOBALS["trackerid"]}";
+		$results = mysql_query($query) or showError("Tracker error: invalid torrent");
+		$data = mysql_fetch_assoc($results);
+		if (!$data || $data["peer_id"] != $user)
+		{
+			// Damn, but don't crash just yet.
+			$query = "SELECT peer_id,bytes,ip,port,status,lastupdate,sequence FROM ".$prefix."x$hash WHERE peer_id='$user'";
+			$results = mysql_query($query) or showError("Tracker error: invalid torrent"); 
+			$data = mysql_fetch_assoc($results);
+			$GLOBALS["trackerid"] = $data["sequence"];
+		}
+	}
+	else
+	{
+		$query = "SELECT peer_id,bytes,ip,port,status,lastupdate,sequence FROM ".$prefix."x$hash WHERE peer_id='$user'";
+		$results = mysql_query($query) or showError("Tracker error: invalid torrent");
+		$data = mysql_fetch_assoc($results);
+		$GLOBALS["trackerid"] = $data["sequence"];
+
+	}
+	
+	if (!($data))
+		return false;
+	
+	return $data;
+}
+
+// Slight redesign of loadPeers
+function getRandomPeers($hash, $where="")
+{
+	require("config.php");
+
+	// Don't want to send a bad "num peers" for new seeds
+	if ($GLOBALS["NAT"])
+		$results = mysql_query("SELECT COUNT(*) FROM ".$prefix."x$hash WHERE natuser = 'N'");
+	else
+		$results = mysql_query("SELECT COUNT(*) FROM ".$prefix."x$hash");
+
+	$peercount = mysql_result($results, 0,0);
+
+	// ORDER BY RAND() is expensive. Don't do it when the load gets too high
+	if ($peercount < 500)
+		$query = "SELECT ".((isset($_GET["no_peer_id"]) && $_GET["no_peer_id"] == 1) ? "" : "peer_id,")."ip, port, status FROM ".$prefix."x$hash ".$where." ORDER BY RAND() LIMIT ${GLOBALS['maxpeers']}";
+	else
+		$query = "SELECT ".((isset($_GET["no_peer_id"]) && $_GET["no_peer_id"] == 1) ? "" : "peer_id,")."ip, port, status FROM ".$prefix."x$hash LIMIT ".@mt_rand(0, $peercount - $GLOBALS["maxpeers"]).", ${GLOBALS['maxpeers']}";
+
+	$results = mysql_query($query);
+	if (!$results)
+		return false;
+
+	$peerno = 0;
+	while ($return[] = mysql_fetch_assoc($results))
+		$peerno++;
+
+	array_pop ($return);
+	mysql_free_result($results);
+	$return['size'] = $peerno;
+ 
+	return $return;
+}
+	
+//  Deletes a peer from the system and performs all cleaning up
+//
+//  $assumepeer contains the result of getPeerInfo, or false
+//  if we should grab it ourselves.
+function killPeer($userid, $hash, $left, $assumepeer = false)
+{
+	require("config.php");
+	if (!$assumepeer)
+	{
+		$peer = getPeerInfo($userid, $hash);
+		if (!$peer)
+			return;
+		if ($left != $peer["bytes"])
+			$bytes = sqlSubtract($peer["bytes"], $left);
+		else
+			$bytes = 0;
+	}
+	else
+	{
+		$bytes = 0;
+		$peer = $assumepeer;
+	}
+
+	quickQuery("DELETE FROM ".$prefix."x$hash WHERE peer_id='$userid'");
+	if (mysql_affected_rows() == 1)
+	{
+		//peercaching ALWAYS on
+		quickQuery("DELETE FROM ".$prefix."y$hash WHERE sequence=" . $peer["sequence"]);
+		if ($peer["status"] == "leecher")
+			summaryAdd("leechers", -1);
+		else
+			summaryAdd("seeds", -1);
+		if ($GLOBALS["countbytes"] && ((float)$bytes) > 0)
+			summaryAdd("dlbytes",$bytes);
+		if ($peer["bytes"] != 0 && $left == 0)
+			summaryAdd("finished", 1);
+	}
+}
+
+// Transfers bytes from "left" to "dlbytes" when a peer reports in.
+function collectBytes($peer, $hash, $left)
+{
+	require("config.php");
+	$peerid=$peer["peer_id"];
+
+	if (!$GLOBALS["countbytes"])
+	{
+		quickQuery("UPDATE ".$prefix."x$hash SET lastupdate=UNIX_TIMESTAMP() where " . (isset($GLOBALS["trackerid"]) ? "sequence='${GLOBALS["trackerid"]}'" : "peer_id='$peerid'"));
+		return;
+	}
+	$diff = sqlSubtract($peer["bytes"], $left);
+	quickQuery("UPDATE ".$prefix."x$hash set " . (($diff != 0) ? "bytes='$left'," : ""). " lastupdate=UNIX_TIMESTAMP() where " . (isset($GLOBALS["trackerid"]) ? "sequence='${GLOBALS["trackerid"]}'" : "peer_id='$peerid'"));
+
+
+	// Anti-negative clause
+	if (((float)$diff) > 0)
+		summaryAdd("dlbytes", $diff);
+}
+
+// Transmits the actual data to the peer. No other output is permitted if
+// this function is called, as that would break BEncoding.
+// I don't use the bencode library, so watch out! If you add data,
+// rules such as dictionary sorting are enforced by the remote side.
+function sendPeerList($peers)
+{
+	echo "d";
+  	echo "8:intervali".$GLOBALS["report_interval"]."e";
+	if (isset($GLOBALS["min_interval"]))
+		echo "12:min intervali".$GLOBALS["min_interval"]."e";
+	echo "5:peers";
+	$size=$peers["size"];
+	if (isset($_GET["compact"]) && $_GET["compact"] == '1')
+	{
+		$p = '';
+		for ($i=0; $i < $size; $i++)
+			$p .= pack("Nn", ip2long($peers[$i]['ip']), $peers[$i]['port']);
+		echo strlen($p).':'.$p;
+	}
+	else // no_peer_id or no feature supported
+	{
+		echo 'l';
+		for ($i=0; $i < $size; $i++)
+		{
+			echo "d2:ip".strlen($peers[$i]["ip"]).":".$peers[$i]["ip"];
+			if (isset($peers[$i]["peer_id"]))
+				echo "7:peer id20:".hex2bin($peers[$i]["peer_id"]);
+			echo "4:porti".$peers[$i]["port"]."ee";
+		}
+		echo "e";
+	}
+	if (isset($GLOBALS["trackerid"]))
+	{
+		// Now it gets annoying. trackerid is a string
+		echo "10:tracker id".strlen($GLOBALS["trackerid"]).":".$GLOBALS["trackerid"];
+	}
+
+	echo "e";
+}
+
+
+// Faster pass-through version of getRandompeers => sendPeerList
+// It's the only way to use cache tables. In fact, it only uses it.
+function sendRandomPeers($info_hash)
+{
+	require("config.php");
+	$result = mysql_query("SELECT COUNT(*) FROM ".$prefix."y$info_hash");
+	$count = mysql_result($result, 0, 0);
+	
+	if (isset($_GET["compact"]) && $_GET["compact"] == '1')
+		$column = "compact";
+	else if (isset($_GET["no_peer_id"]) && $_GET["no_peer_id"] == '1')
+		$column = "without_peerid";
+	else
+		$column = "with_peerid";
+	
+	if ($count < $GLOBALS["maxpeers"])
+		$query = "SELECT $column FROM ".$prefix."y$info_hash";
+	else if ($count > 500)
+	{
+		do
+		{
+			$rand1 = mt_rand(0, $count-$GLOBALS["maxpeers"]);
+			$rand2 = mt_rand(0, $count-$GLOBALS["maxpeers"]);
+		} while (abs($rand1 - $rand2) < $GLOBALS["maxpeers"]/2);
+		$query = "(SELECT $column FROM ".$prefix."y$info_hash LIMIT $rand1, ".($GLOBALS["maxpeers"]/2). ") UNION (SELECT $column FROM ".$prefix."y$info_hash LIMIT $rand2, ".($GLOBALS["maxpeers"]/2). ")";
+	}
+	else
+		$query = "SELECT $column FROM ".$prefix."y$info_hash ORDER BY RAND() LIMIT ".$GLOBALS["maxpeers"];
+
+	
+
+	echo "d";
+  	echo "8:intervali".$GLOBALS["report_interval"]."e";
+	if (isset($GLOBALS["min_interval"]))
+		echo "12:min intervali".$GLOBALS["min_interval"]."e";
+	echo "5:peers";
+
+	$result = mysql_query($query);
+	if ($column == "compact")
+	{
+		echo (mysql_num_rows($result) * 6) . ":";
+		while ($row = mysql_fetch_row($result))
+			echo str_pad($row[0], 6, chr(32));
+	}
+	else
+	{
+		echo "l";
+		while ($row = mysql_fetch_row($result))
+			echo "d".$row[0]."e";
+		echo "e";
+	}
+	if (isset($GLOBALS["trackerid"]))
+		echo "10:tracker id".strlen($GLOBALS["trackerid"]).":".$GLOBALS["trackerid"];
+	echo "e";
+}
+
+
+// Returns a $peers array of all peers that have timed out (2* report interval seems fair
+// for any reasonable report interval (900 or larger))
+function loadLostPeers($hash, $timeout)
+{
+	require("config.php"); //necessary for getting prefix value
+	$results = mysql_query("SELECT peer_id,bytes,ip,port,status,lastupdate,sequence from ".$prefix."x$hash where lastupdate < (UNIX_TIMESTAMP() - 2 * $timeout)");
+	$peerno = 0;
+	if (!$results)
+		return false;
+	
+	while ($return[] = mysql_fetch_assoc($results))
+		$peerno++;	
+	array_pop($return);
+	$return["size"] = $peerno;
+	mysql_free_result($results);
+	return $return;
+}
+
+function trashCollector($hash, $timeout)
+{
+	require("config.php"); //need to grab prefix value...
+	if (isset($GLOBALS["trackerid"]))
+		unset($GLOBALS["trackerid"]);
+
+	if (!Lock($hash))
+		return;
+	
+	$results = mysql_query("SELECT lastcycle FROM ".$prefix."summary WHERE info_hash='$hash'");
+	$lastcheck = (mysql_fetch_row($results));
+	
+	// Check once every re-announce cycle
+	if (($lastcheck[0] + $timeout) < time())
+	{
+		$peers = loadLostPeers($hash, $timeout);
+		for ($i=0; $i < $peers["size"]; $i++)
+			killPeer($peers[$i]["peer_id"], $hash, $peers[$i]["bytes"]);
+		summaryAdd("lastcycle", "UNIX_TIMESTAMP()", true);
+	}
+	Unlock($hash);
+}
+
+// Attempts to aquire a lock by name.
+// Returns true on success, false on failure
+function Lock($hash, $time = 0)
+{
+	$results = mysql_query("SELECT GET_LOCK('$hash', $time)");
+	$string = mysql_fetch_row($results);
+	if (strcmp($string[0], "1") == 0)
+		return true;
+	return false;
+
+}
+
+// Releases a lock. Ignores errors.
+function Unlock($hash)
+{
+	quickQuery("SELECT RELEASE_LOCK('$hash')");
+}
+
+// Returns true if the lock is available
+function isFreeLock($lock)
+{
+	if (Lock($lock, 0))
+	{
+		Unlock($lock);
+		return true;
+	}
+	return false;
+}
+
+
+/* Returns true if the user is firewalled, NAT'd, or whatever.
+ * The original tracker had its --nat_check parameter, so
+ * here is my version.
+ *
+ * This code has proven itself to be sufficiently correct,
+ * but will consume system resources when a lot of httpd processes
+ * are lingering around trying to connect to remote hosts.
+ * Consider disabling it under higher loads.
+ */
+function isFireWalled($hash, $peerid, $ip, $port)
+{
+
+	// NAT checking off?
+	if (!$GLOBALS["NAT"])
+		return false;
+
+	$protocol_name = 'BitTorrent protocol';
+	$theError = "";
+	// Hoping 10 seconds will be enough
+	$fd = fsockopen($ip, $port, $errno, $theError, 10);
+	if (!$fd)
+		return true;
+
+	stream_set_timeout($fd, 5, 0);
+	fwrite($fd, chr(strlen($protocol_name)).$protocol_name.hex2bin("0000000000000000").
+		hex2bin($hash));
+	
+	$data = fread($fd, strlen($protocol_name)+1+20+20+8); // ideally...
+
+	fclose($fd);
+	$offset = 0;
+
+	// First byte: strlen($protocol_name), then the protocol string itself
+	if (ord($data[$offset]) != strlen($protocol_name))
+		return true;
+
+	$offset++;
+	if (substr($data, $offset, strlen($protocol_name)) != $protocol_name)
+		return true;
+
+	$offset += strlen($protocol_name);
+	// 8 bytes reserved, ignore
+	$offset += 8;
+	
+	// Download ID (hash)
+	if (substr($data, $offset, 20) != hex2bin($hash))
+		return true;
+
+	$offset+=20;
+	
+	// Peer ID
+	if (substr($data, $offset, 20) != hex2bin($peerid))
+		return true;
+
+	
+	return false;
+}
+
+
+// It's cruel, but if people abuse my tracker, I just might do it.
+// It pretends to accept the torrent, and reports that you are the
+// only person connected.
+function evilReject($ip, $peer_id, $port)
+{
+
+	// For those of you who are feeling evil, comment out this line.
+	showError("Torrent is not authorized for use on this tracker.");
+
+	$peers[0]["peer_id"] = $peer_id;
+	$peers[0]["ip"] = $ip;
+	$peers[0]["port"] = $port;
+	$peers["size"] = 1;
+	$GLOBALS["report_interval"] = 86400;
+	$GLOBALS["min_interval"] = 86000;
+	sendPeerList($peers);
+	exit(0);
+}
+
+
+function runSpeed($info_hash, $delta)
+{
+	require("config.php");
+	//stick in our latest data before we calc it out
+	quickQuery("INSERT IGNORE INTO ".$prefix."timestamps (info_hash, bytes, delta, sequence) SELECT '$info_hash' AS info_hash, dlbytes, UNIX_TIMESTAMP() - lastSpeedCycle, NULL FROM ".$prefix."summary WHERE info_hash='$info_hash'");
+
+	// mysql blows sometimes so we have to read the data into php before updating it
+	$results = mysql_query('SELECT (MAX(bytes)-MIN(bytes))/SUM(delta), COUNT(*), MIN(sequence) FROM '.$prefix.'timestamps WHERE info_hash="'.$info_hash.'"' );
+	$data = mysql_fetch_row($results);
+	
+	$results2 = mysql_query('SELECT '.$prefix.'summary.leechers FROM '.$prefix.'summary WHERE info_hash="'.$info_hash.'"');
+	$data2 = mysql_fetch_row($results2);
+	if ($data2[0] == 0) //if no leechers, speed is zero
+		$data[0] = 0;
+		
+	$results3 = mysql_query("SELECT MIN(d1.bytes), MAX(d1.bytes) FROM (SELECT bytes FROM ".$prefix."timestamps WHERE info_hash='".$info_hash."' ORDER BY sequence DESC LIMIT 5) AS d1");
+	$data3 = mysql_fetch_row($results3);
+	//if the last 5 updates from clients show the same bytes, it's probably stalled, set speed to zero
+	if ($data3[0] == $data3[1])
+		$data[0] = 0;
+	
+	summaryAdd("speed", $data[0], true);
+	summaryAdd("lastSpeedCycle", "UNIX_TIMESTAMP()", true);
+
+	// if we have more than 20 drop the rest
+	//if ($data[1] == 21)
+		//quickQuery("DELETE FROM timestamps WHERE info_hash='$info_hash' AND sequence=${data[2]}");
+	if ($data[1] > 21)
+		// This query requires MySQL 4.0.x, but should rarely be used.
+		quickQuery ('DELETE FROM '.$prefix.'timestamps WHERE info_hash="'.$info_hash.'" ORDER BY sequence LIMIT '.($data['1'] - 20));
+}
+
+// Schedules an update to the summary table. It gets so much traffic
+// that we do all our changes at once.
+// When called, the column $column for the current info_hash is incremented
+// by $value, or set to exactly $value if $abs is true.
+function summaryAdd($column, $value, $abs = false)
+{
+	if (isset($GLOBALS["summaryupdate"][$column]))
+	{
+		if (!$abs)
+			$GLOBALS["summaryupdate"][$column][0] += $value;
+		else
+			showError("Tracker bug calling summaryAdd");
+	}
+	else
+	{
+		$GLOBALS["summaryupdate"][$column][0] = $value;
+		$GLOBALS["summaryupdate"][$column][1] = $abs;
+	}
+}
+
+
+//converts byte size to string format for display
+function bytesToString($total_size)
+{
+	if ($total_size < 1024) //dealing with bytes
+		return $total_size . " bytes";
+	elseif ($total_size < 1048576) //dealing with kilobytes
+		return round($total_size/1024, 2) . " KB";
+	elseif ($total_size < 1073741824) //dealing with megabytes
+		return round($total_size/1048576, 2) . " MB";
+	elseif ($total_size >= 1073741824) //dealing with gigabytes
+		return round($total_size/1073741824, 2) . " GB";
+}
+
+
+// Even if you're missing PHP 4.3.0, the MHASH extension might be of use.
+// Someone was kind enought to email this code snippit in.
+if (function_exists('mhash') && (!function_exists('sha1')) && 
+defined('MHASH_SHA1'))
+{
+	function sha1($str)
+	{
+		return bin2hex(mhash(MHASH_SHA1,$str));
+	}
+}
+
+//If magic quotes are on, returns the cleaned (no single quotes) output string
+function clean($input)
+{
+	if (get_magic_quotes_gpc())
+		return stripslashes($input);
+	return $input;
+}
+
+//If magic quotes are off, returns the added (single quotes) output string
+function addquotes($input)
+{
+	if (!get_magic_quotes_gpc())
+		return addslashes($input);
+	return $input;
+}
+
+//generic filter function for cleaning data
+function filterData($data)
+{
+	$data = trim(htmlentities(strip_tags($data)));
+
+	if (get_magic_quotes_gpc()) {
+		return stripslashes($data);
+	}
+
+	$data = mysql_real_escape_string($data);
+
+	return $data;
+}
+
+//generic filter function for character cleaning data
+function filterChar($data)
+{
+	$data = filter_var($data, FILTER_SANITIZE_FULL_SPECIAL_CHARS);
+
+	if (get_magic_quotes_gpc()) {
+		return stripslashes($data);
+	}
+
+	$data = mysql_real_escape_string($data);
+
+	return $data;
+}
+
+//generic filter function for validating integer and hex data
+function filterInt($data)
+{
+	$data = filter_var($data, FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX);
+
+	if (get_magic_quotes_gpc()) {
+		return stripslashes($data);
+	}
+
+	$data = mysql_real_escape_string($data);
+
+	return $data;
+}
+
+//generic filter function for number cleaning data
+function filterFloat($data)
+{
+	$data = filter_var($data, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
+
+	if (get_magic_quotes_gpc()) {
+		return stripslashes($data);
+	}
+
+	$data = mysql_real_escape_string($data);
+
+	return $data;
+}
+
+?>

file:b/images/add.png (new)
 Binary files /dev/null and b/images/add.png differ
file:b/images/admin.png (new)
 Binary files /dev/null and b/images/admin.png differ
 Binary files /dev/null and b/images/batch_upload.png differ
file:b/images/check.png (new)
 Binary files /dev/null and b/images/check.png differ
file:b/images/color.png (new)
 Binary files /dev/null and b/images/color.png differ
 Binary files /dev/null and b/images/database.png differ
file:b/images/delete.png (new)
 Binary files /dev/null and b/images/delete.png differ
 Binary files /dev/null and b/images/download.png differ
file:b/images/edit.png (new)
 Binary files /dev/null and b/images/edit.png differ
file:b/images/help.png (new)
 Binary files /dev/null and b/images/help.png differ
 Binary files /dev/null and b/images/important.png differ
file:b/images/index.php (new)
--- /dev/null
+++ b/images/index.php
@@ -1,1 +1,5 @@
+<?php
 
+header("Location: ../index.php");
+
+?>

 Binary files /dev/null and b/images/install.png differ
file:b/images/lock.png (new)
 Binary files /dev/null and b/images/lock.png differ
file:b/images/logout.png (new)
 Binary files /dev/null and b/images/logout.png differ
 Binary files /dev/null and b/images/magnet-icon.gif differ
file:b/images/no.png (new)
 Binary files /dev/null and b/images/no.png differ
 Binary files /dev/null and b/images/rss-logo.png differ
file:b/images/stats.png (new)
 Binary files /dev/null and b/images/stats.png differ
 Binary files /dev/null and b/images/torrent.png differ
 Binary files /dev/null and b/images/userstats.png differ
file:b/images/yes.png (new)
 Binary files /dev/null and b/images/yes.png differ
file:b/index.php (new)
--- /dev/null
+++ b/index.php
@@ -1,1 +1,425 @@
-
+<?php
+//if config.php file not available, error out
+if (!file_exists("config.php"))
+{
+	echo "<font color=red><strong>Error: config.php file is not available.  Did you forget to upload it?" .
+	" If you haven't run the installer yet, please do so <a href=\"install.php\">here.</a></strong></font>";
+	exit();
+}
+
+require_once ("config.php");
+require_once ("funcsv2.php");
+
+//Check session only if hiddentracker is TRUE
+if ($hiddentracker == true)
+{
+	session_start();
+	
+	if (!$_SESSION['admin_logged_in'] && !$_SESSION['upload_logged_in'])
+	{
+		//check fails
+		header("Location: authenticate.php?status=indexlogin");
+		exit();
+	}
+}
+?>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<?php
+//variables for column totals
+$total_disk_usage = 0;
+$total_seeders = 0;
+$total_leechers = 0;
+$total_downloads = 0;
+$total_bytes_transferred = 0;
+$total_speed = 0;
+
+$scriptname = $_SERVER["PHP_SELF"] . "?";
+if (!isset($GLOBALS["countbytes"]))
+	$GLOBALS["countbytes"] = true;
+?>
+<html>
+<head>
+	<title><?php if ($GLOBALS["title"] != "") echo $GLOBALS["title"]; else echo "Tracker Statistics";?></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+	<link rel="stylesheet" href="./css/style.css" type="text/css" />
+	<?php
+	if ($enablerss == true)
+		echo "<link rel=\"alternate\" title=\"" . $rss_title . "\" href=\"rss/rss.xml\" type=\"application/rss+xml\">";
+	?>
+</head>
+<body>
+<?php
+//display total stats as header on page
+if ($GLOBALS["persist"])
+	$db = mysql_pconnect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - " . mysql_error() . "</p>");
+else
+	$db = mysql_connect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - " . mysql_error() . "</p>");
+mysql_select_db($database) or die(errorMessage() . "Tracker error: can't open database $database - " . mysql_error() . "</p>");
+
+$query = "SELECT SUM(".$prefix."namemap.size), SUM(".$prefix."summary.seeds), SUM(".$prefix."summary.leechers), SUM(".$prefix."summary.finished), SUM(".$prefix."summary.dlbytes), SUM(".$prefix."summary.speed) FROM ".$prefix."summary LEFT JOIN ".$prefix."namemap ON ".$prefix."summary.info_hash = ".$prefix."namemap.info_hash";
+$results = mysql_query($query) or die(errorMessage() . "Can't do SQL query - " . mysql_error() . "</p>");
+$data = mysql_fetch_row($results);
+?>
+
+<center>
+<table>
+<tr>
+<th class="subheader">Total Space Used</th>
+<th class="subheader">Seeders</th>
+<th class="subheader">Leechers</th>
+<th class="subheader">Completed D/Ls</th>
+<th class="subheader">Bytes Transferred</th>
+<th class="subheader">Speed (rough estimate)</th>
+</tr>
+<tr>
+<?php
+if ($data[0] != null) //if there are no torrents in database, don't show anything
+{
+	echo "<td align=\"center\">" . bytesToString($data[0]) . "</td>\n";
+	echo "<td align=\"center\">" . $data[1] . "</td>\n";
+	echo "<td align=\"center\">" . $data[2] . "</td>\n";
+	echo "<td align=\"center\">" . $data[3] . "</td>\n";
+	echo "<td align=\"center\">" . bytesToString($data[4]) . "</td>\n";
+	if ($GLOBALS["countbytes"]) //stop count bytes OFF, OK to do speed calculation
+	{
+		if ($data[5] > 2097152)
+			echo "<td align=\"center\">" . round($data[5] / 1048576, 2) . " MB/sec</td>\n";
+		else
+			echo "<td align=\"center\">" . round($data[5] / 1024, 2) . " KB/sec</td>\n";
+	}
+	else
+		echo "<td align=\"center\">No Info Available</td>\n";
+}
+?>
+</tr>
+</table>
+</center>
+<br>
+
+<h1><?php if ($GLOBALS["title"] != "") echo $GLOBALS["title"]; else echo "Tracker Statistics";?></h1>
+<table width="100%">
+<tr>
+<td width="25%">
+<?php
+//Display logout option if logged in
+if ($hiddentracker == true)
+{
+	echo "Hello, <i>" . $_SESSION["username"] . "</i><br>";
+	echo "<a href=\"authenticate.php?status=logout\"><img src=\"images/logout.png\" border=\"0\" class=\"icon\" alt=\"Logout\" title=\"Logout\" /></a><a href=\"authenticate.php?status=logout\">Logout</a>";
+}
+?>
+</td>
+<td align="center">
+<a href="newtorrents.php"><img src="images/add.png" border="0" class="icon" alt="Add Torrent" title="Add Torrent" /></a><a href="newtorrents.php">Add Torrent to Tracker Database</a>
+<a href="admin.php"><img src="images/admin.png" border="0" class="icon" alt="Admin Page" title="Admin Page" /></a><a href="admin.php">Admin Page</a>
+</td>
+<td align="right" width="25%">
+
+<?php
+if (file_exists("rss/rss.xml"))
+{
+	echo "<a href='rss/rss.xml'><img src='images/rss-logo.png' border='0' class='icon' alt='RSS 2.0 Feed' title='RSS 2.0 Feed' /></a><a href='rss/rss.xml'>RSS 2.0 Feed</a>";
+}
+?>
+</td>
+</tr>
+</table>
+
+
+<table>
+<tr>
+	<?php
+	//Cleanup page number to prevent XSS
+	if (isset($_GET["page_number"])) {
+		$_GET["page_number"] = htmlspecialchars($_GET["page_number"]);
+	} else {
+		$_GET["page_number"] = "";
+	}
+	$scriptname = htmlspecialchars($scriptname);
+	
+	if (!isset($_GET["activeonly"]))
+		$scriptname = $scriptname . "activeonly=	yes&amp;";
+	if (isset($_GET["seededonly"]) && !isset($_GET["activeonly"]))
+	{
+		$scriptname = $scriptname . "seededonly=yes&";
+		$_GET["page_number"] = 1;
+	}
+	if (isset($_GET["page_number"]))
+		$scriptname = $scriptname . "page_number=" . $_GET["page_number"] . "&amp;";
+		
+	if (isset($_GET["activeonly"]))
+		echo "<td><a href=\"$scriptname\">Show all torrents</a></td>\n";
+	else
+		echo "<td><a href=\"$scriptname\">Show only active torrents</a></td>\n";
+		
+	$scriptname = $_SERVER["PHP_SELF"] . "?";
+	$scriptname = htmlspecialchars($scriptname);
+	
+	if (!isset($_GET["seededonly"]))
+		$scriptname = $scriptname . "seededonly=yes&amp;";
+	if (isset($_GET["activeonly"]) && !isset($_GET["seededonly"]))
+	{
+		$scriptname = $scriptname . "activeonly=yes&";
+		$_GET["page_number"] = 1;
+	}
+	if (isset($_GET["page_number"]))
+		$scriptname = $scriptname . "page_number=" . $_GET["page_number"] . "&amp;";
+		
+	if (isset($_GET["seededonly"]))
+		echo "<td align=\"right\"><a href=\"$scriptname\">Show all torrents</a></td>\n";
+	else
+		echo "<td align=\"right\"><a href=\"$scriptname\">Show only seeded torrents</a></td>\n";
+		
+	$scriptname = $_SERVER["PHP_SELF"] . "?";
+	$scriptname = htmlspecialchars($scriptname);
+	
+	?>
+</tr>
+</table>
+
+<?php
+if ($GLOBALS["persist"])
+	$db = mysql_pconnect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - " . mysql_error() . "</p>");
+else
+	$db = mysql_connect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - " . mysql_error() . "</p>");
+mysql_select_db($database) or die(errorMessage() . "Tracker error: can't open database $database - " . mysql_error() . "</p>");
+
+if (isset($_GET["seededonly"]))
+	$where = " WHERE seeds > 0";
+else if (isset($_GET["activeonly"]))
+	$where = " WHERE leechers+seeds > 0";
+else
+	$where = " ";
+
+$query = "SELECT COUNT(*) FROM ".$prefix."summary $where";
+$results = mysql_query($query);
+$res = mysql_result($results,0,0);
+
+if (isset($_GET["activeonly"]))
+	$scriptname = $scriptname . "activeonly=yes&";
+if (isset($_GET["seededonly"]))
+	$scriptname = $scriptname . "seededonly=yes&";
+
+echo "<p align='center'>Page: \n";
+$count = 0;
+$page = 1;
+while($count < $res)
+{
+	if (isset($_GET["page_number"]) && $page == $_GET["page_number"])
+		echo "<b><a href=\"$scriptname" . "page_number=$page\">($page)</a></b>-\n";
+	else if (!isset($_GET["page_number"]) && $page == 1)
+		echo "<b><a href=\"$scriptname" . "page_number=$page\">($page)</a></b>-\n";
+	else
+		echo "<a href=\"$scriptname" . "page_number=$page\">$page</a>-\n";
+	$page++;
+	$count = $count + ($GLOBALS['indexpagelimitspecify']);
+}
+echo "</p>\n";
+?>
+
+<table>
+<tr>
+	<td>
+	<table class="torrentlist">
+
+	<!-- Column Headers -->
+	<tr>
+		<th>Name/Info Hash</th>
+		<th>Seeders</th>
+		<th>Leechers</th>
+		<th>Completed D/Ls</th>
+		<?php
+		// Bytes mode off? Ignore the columns
+		if ($GLOBALS["countbytes"])
+			echo '<th>Bytes Transferred</th><th>Speed (rough estimate)</th>';
+		?>
+	</tr>
+	
+<?php
+if ($GLOBALS["customtitle"] != "true")
+{
+	if (!isset($_GET["page_number"]))
+	$query = "SELECT ".$prefix."summary.info_hash, ".$prefix."summary.seeds, ".$prefix."summary.leechers, ".$prefix."summary.finished, ".$prefix."summary.dlbytes, ".$prefix."namemap.filename, ".$prefix."namemap.url, ".$prefix."namemap.size, ".$prefix."summary.speed FROM ".$prefix."summary LEFT JOIN ".$prefix."namemap ON ".$prefix."summary.info_hash = ".$prefix."namemap.info_hash $where ORDER BY ".$prefix."namemap.filename LIMIT 0,${GLOBALS['indexpagelimitspecify']}";
+	else
+	{
+		if ($_GET["page_number"] <= 0) //account for possible negative number entry by user
+			$_GET["page_number"] = 1;
+		
+		$page_limit = ($_GET["page_number"] - 1) * ($GLOBALS['indexpagelimitspecify']);
+		$query = "SELECT ".$prefix."summary.info_hash, ".$prefix."summary.seeds, ".$prefix."summary.leechers, ".$prefix."summary.finished, ".$prefix."summary.dlbytes, ".$prefix."namemap.filename, ".$prefix."namemap.url, ".$prefix."namemap.size, ".$prefix."summary.speed FROM ".$prefix."summary LEFT JOIN ".$prefix."namemap ON ".$prefix."summary.info_hash = ".$prefix."namemap.info_hash $where ORDER BY ".$prefix."namemap.filename LIMIT $page_limit,${GLOBALS['indexpagelimitspecify']}";
+	}
+}
+
+if ($GLOBALS["customtitle"] == "true")
+{
+	if (!isset($_GET["page_number"]))
+	$query = "SELECT ".$prefix."summary.info_hash, ".$prefix."summary.seeds, ".$prefix."summary.leechers, ".$prefix."summary.finished, ".$prefix."summary.dlbytes, ".$prefix."namemap.title, ".$prefix."namemap.url, ".$prefix."namemap.size, ".$prefix."summary.speed, ".$prefix."namemap.filename FROM ".$prefix."summary LEFT JOIN ".$prefix."namemap ON ".$prefix."summary.info_hash = ".$prefix."namemap.info_hash $where ORDER BY ".$prefix."namemap.title LIMIT 0,${GLOBALS['indexpagelimitspecify']}";
+	else
+	{
+		if ($_GET["page_number"] <= 0) //account for possible negative number entry by user
+			$_GET["page_number"] = 1;
+		
+		$page_limit = ($_GET["page_number"] - 1) * ($GLOBALS["indexpagelimitspecify"]);
+		$query = "SELECT ".$prefix."summary.info_hash, ".$prefix."summary.seeds, ".$prefix."summary.leechers, ".$prefix."summary.finished, ".$prefix."summary.dlbytes, ".$prefix."namemap.title, ".$prefix."namemap.url, ".$prefix."namemap.size, ".$prefix."summary.speed, ".$prefix."namemap.filename  FROM ".$prefix."summary LEFT JOIN ".$prefix."namemap ON ".$prefix."summary.info_hash = ".$prefix."namemap.info_hash $where ORDER BY ".$prefix."namemap.title LIMIT $page_limit,${GLOBALS['indexpagelimitspecify']}";
+	}
+}
+
+$results = mysql_query($query) or die(errorMessage() . "Can't do SQL query - " . mysql_error() . "</p>");
+$i = 0;
+
+while ($data = mysql_fetch_row($results)) {
+	// NULLs are such a pain at times. isset($nullvar) == false
+	if (is_null($data[5]))
+		$data[5] = $data[0];
+	if (is_null($data[6]))
+	$data[6] = "";
+	if (is_null($data[7]))
+		$data[7] = "";
+	if (strlen($data[5]) == 0)
+		$data[5] = $data[0];
+	$myhash = $data[0];
+	$writeout = "row" . $i % 2;
+	echo "<tr class=\"$writeout\">\n";
+	echo "\t<td>";
+	echo "\t<table class=\"nopadding\" border=\"0\"><tr><td valign=\"top\" align=\"left\" width=\"10%\">\n";
+	echo "\t<form method='post' action='torrent_functions.php'>\n";
+	echo "\t<input type='hidden' name='hash' value='" . $data[0] . "'/>\n";
+	echo "\t<input type='submit' value=' + '/></form>\n";
+	echo "\t</td><td valign=\"top\" align=\"left\">\n";
+	if (strlen($data[6]) > 0)
+		echo "<a href=\"${data[6]}\">${data[5]}</a> - ";
+	else
+		echo $data[5] . " - ";
+
+if ($GLOBALS["customtitle"] == "true")
+{
+	if ($hiddentracker == true) //obscure direct link to torrent, use dltorrent.php script
+		echo "<a href=\"dltorrent.php?hash=" . $myhash . "\">  (Download Torrent)</a>";
+	else //just display ordinary direct link
+		echo "<a href=\"torrents/" . rawurlencode($data[9]) . ".torrent\">  (Download Torrent)</a>";
+}
+
+if ($GLOBALS["customtitle"] != "true")
+{
+	if ($hiddentracker == true) //obscure direct link to torrent, use dltorrent.php script
+		echo "<a href=\"dltorrent.php?hash=" . $myhash . "\">  (Download Torrent)</a>";
+	else //just display ordinary direct link
+		echo "<a href=\"torrents/" . rawurlencode($data[5]) . ".torrent\">  (Download Torrent)</a>";
+}
+
+	//Magnet link
+	echo "&nbsp;<a href='";
+		//https://en.wikipedia.org/wiki/Magnet_URI_scheme
+		//Base-32 encoded SHA1 hash sum
+		echo "magnet:?xt=urn:btih:".$data[0];
+		//Size in bytes
+		echo "&xl=".$data[7];
+		//name
+		if ($GLOBALS["customtitle"] == "true")
+		echo "&dn=".rawurlencode($data[9]);
+		else echo "&dn=".rawurlencode($data[5]);
+		//tracker url
+		echo "&tr=".$website_url . substr($_SERVER['PHP_SELF'], 0, -9) . $announceurl;
+	echo "'>(Magnet";
+	echo "<img src='images/magnet-icon.gif' border='0' class='icon' alt='Magnet Link' title='Magnet Link' />";
+	echo ")</a>";
+
+	echo "</td></tr>";
+
+
+	if (strlen($data[7]) > 0) //show file size
+	{
+		echo "<tr><td>&nbsp;</td><td>" . bytesToString($data[7]) . "</td>";
+		$total_disk_usage = $total_disk_usage + $data[7]; //total up file sizes
+	}
+	echo "</tr></table></td>\n";
+	for ($j=1; $j < 4; $j++) //show seeders, leechers, and completed downloads
+	{
+		echo "\t<td class=\"center\">$data[$j]</td>\n";
+		if ($j == 1) //add to total seeders
+			$total_seeders = $total_seeders + $data[1];
+		if ($j == 2) //add to total leechers
+			$total_leechers = $total_leechers + $data[2];
+		if ($j == 3) //add to completed downloads
+			$total_downloads = $total_downloads + $data[3];
+	}
+
+	if ($GLOBALS["countbytes"])
+	{
+		echo "\t<td class=\"center\">" . bytestoString($data[4]) . "</td>\n";
+		$total_bytes_transferred = $total_bytes_transferred + $data[4]; //add to total GB transferred
+
+		// The SPEED column calculations.
+		if ($data[8] <= 0)
+		{
+			$speed = "0";
+			$total_speed = $total_speed - $data[8]; //for total speed column
+		}
+		else if ($data[8] > 2097152)
+			$speed = round($data[8] / 1048576, 2) . " MB/sec";
+		else
+			$speed = round($data[8] / 1024, 2) . " KB/sec";
+		echo "\t<td class=\"center\">$speed</td>\n";
+		$total_speed = $total_speed + $data[8]; //add to total speed, in bytes
+	}
+	echo "</tr>\n";
+	$i++;
+}
+
+if ($i == 0)
+	echo "<tr class=\"row0\"><td style=\"text-align: center;\" colspan=\"6\">No torrents</td></tr>";
+
+//show totals in last row
+echo "<tr>";
+echo "<th>Space Used: " . bytesToString($total_disk_usage) . "</th>";
+echo "<th>" . $total_seeders . "</th>";
+echo "<th>" . $total_leechers . "</th>";
+echo "<th>" . $total_downloads . "</th>";
+if ($GLOBALS["countbytes"]) //stop count bytes variable
+{
+	echo "<th>" . bytestoString($total_bytes_transferred) . "</th>";
+	if ($total_speed > 2097152)
+		echo "<th>" . round($total_speed / 1048576, 2) . " MB/sec</th>";
+	else
+		echo "<th>" . round($total_speed / 1024, 2) . " KB/sec</th>";
+}
+
+?>
+	</tr></table></td></tr>
+<table>
+	<tr class="details">
+		<td align="left"><a href="http://www.rivetcode.com">RivetTracker</a>
+		<?php
+		require("version.php");
+		print($version);
+		?>
+		</td>
+		<td align="right">
+		<?php
+		if (file_exists("legalterms.txt"))
+			echo "<td align=\"right\"><a href=\"legalterms.txt\">Use Policy and Terms of Service</a>";
+		?>
+		</td>
+	</tr>
+</table>
+<a href="./docs/help.html"><img src="images/help.png" border="0" class="icon" alt="Help" title="Help" /></a><a href="./docs/help.html">Help</a>
+<h3>Notes</h3>
+<?php
+if ($GLOBALS["NAT"])
+	echo "<ul><li>This tracker does NAT checking when users connect. If you receive a probe to port 6881, it's probably this tracker.</li></ul>\n";
+else
+	echo "<ul><li>NAT checking has been disabled on this tracker.</li></ul>\n";
+
+echo "<ul><li>Even if there are no seeders, the download may still work because of HTTP seeding.</li></ul>\n";
+	
+if (rand(1, 10) == 1)
+{
+	//10% of the time, run sanity_no_output.php to prune database and keep users fresh
+	include("sanity_no_output.php");
+}
+
+?>
+</body></html>
+

file:b/install.php (new)
--- /dev/null
+++ b/install.php
@@ -1,1 +1,754 @@
-
+<?php
+require_once ("funcsv2.php");
+
+//check if config.php file already exists, if so, this could be an already existing installation
+if (file_exists("config.php"))
+{
+	echo "<font color=red><strong>The config.php file already exists.  This is an indication of an already existing installation" .
+	" of RivetTracker.  If you are sure you are installing for the first time, please try recopying the files/folders." .
+	" This is also a security feature to prevent malicious attempts to run the installer if you forgot to delete it." .
+	" This installer will now abort.</strong></font>";
+	exit();
+}
+
+if (isset($_POST["download"]))
+{
+	//download config.php using header()
+	header('content-type: application/octet-stream');
+	header("Content-Disposition: attachment; filename=\"config.php\"");
+
+	print "<?php //Please do NOT edit this file, use the admin page for changes.\n";
+	print "\$GLOBALS['hiddentracker'] = " . htmlspecialchars($_POST["hiddentracker"]) . ";\n";
+	print "\$GLOBALS['scrape'] = " . htmlspecialchars($_POST["scrape"]) . ";\n";
+	print "\$GLOBALS['customtitle'] = " . htmlspecialchars($_POST["customtitle"]) . ";\n";
+	print "\$announceurl = '" . htmlspecialchars($_POST["announceurl"]) . "';\n";
+	print "\$GLOBALS['indexpagelimitspecify'] = " . htmlspecialchars($_POST["indexpagelimitspecify"]) . ";\n";
+	print "\$GLOBALS['statspagelimitspecify'] = " . htmlspecialchars($_POST["statspagelimitspecify"]) . ";\n";
+	print "\$GLOBALS['report_interval'] = " . htmlspecialchars($_POST["report_interval"]) . ";\n";
+	print "\$GLOBALS['min_interval'] = " . htmlspecialchars($_POST["min_interval"]) . ";\n";
+	print "\$GLOBALS['maxpeers'] = " . htmlspecialchars($_POST["maxpeers"]) . ";\n";
+	print "\$GLOBALS['NAT'] = " . htmlspecialchars($_POST["NAT"]) . ";\n";
+	print "\$GLOBALS['persist'] = " . htmlspecialchars($_POST["persist"]) . ";\n";
+	print "\$GLOBALS['ip_override'] = " . htmlspecialchars($_POST["ip_override"]) . ";\n";
+	print "\$GLOBALS['countbytes'] = " . htmlspecialchars($_POST["countbytes"]) . ";\n";
+	print "\$upload_username = '" . htmlspecialchars($_POST["upload_username"]) . "';\n";
+	print "\$upload_password = '" . htmlspecialchars($_POST["upload_password"]) . "';\n";
+	print "\$admin_username = '" . htmlspecialchars($_POST["admin_username"]) . "';\n";
+	print "\$admin_password = '" . htmlspecialchars($_POST["admin_password"]) . "';\n";
+	print "\$GLOBALS['title'] = '" . htmlspecialchars(addquotes($_POST["title"])) . "';\n";
+	print "\$dbhost = '" . htmlspecialchars($_POST["dbhost"]) . "';\n";
+	print "\$dbuser = '" . htmlspecialchars($_POST["dbuser"]) . "';\n";
+	print "\$dbpass = '" . htmlspecialchars($_POST["dbpass"]) . "';\n";
+	print "\$database = '" . htmlspecialchars($_POST["database"]) . "';\n";
+	print "\$enablerss = " . htmlspecialchars($_POST['enablerss']) . ";\n";
+	print "\$rss_title = '" . htmlspecialchars(addquotes($_POST["rss_title"])) . "';\n";
+	print "\$rss_link = '" . htmlspecialchars($_POST["rss_link"]) . "';\n";
+	print "\$rss_description = '" . htmlspecialchars($_POST["rss_description"]) . "';\n";
+	print "\$website_url = '" . htmlspecialchars($_POST['website_url']) . "';\n";
+	print "\$GLOBALS['max_upload_rate'] = " . htmlspecialchars($_POST['max_upload_rate']) . ";\n";
+	print "\$GLOBALS['max_uploads'] = " . htmlspecialchars($_POST['max_uploads']) . ";\n";
+	print "\$dateformat = '" . htmlspecialchars($_POST['dateformat']) . "';\n";
+	print "\$timezone = '" . htmlspecialchars($_POST['timezone']) . "';\n";
+	print "\$prefix = '" . htmlspecialchars($_POST['prefix']) . "';\n";
+	print "?>";
+exit;
+}
+
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+	<title>RivetTracker Installer</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+	<link rel="stylesheet" href="./css/style.css" type="text/css" />
+</head>
+<body>
+<p align="right"><a href="./docs/help.html"><img src="images/help.png" border="0" class="icon" alt="Help" title="Help" /></a><a href="./docs/help.html">Help</a></p>
+
+<?php
+
+	if (!isset($_POST["started"]))
+	{
+		?>
+		<center>
+		<h1>RivetTracker Installer</h1>
+		<img src="images/install.png" border="0" class="icon" alt="RivetTracker Installation" title="RivetTracker Installation" />
+		<br>
+		<br>
+		<br>
+		<h2>Check for PHP and MySQL</h2>
+		</center>
+		<form method="post" action="<?php echo htmlentities($_SERVER['PHP_SELF']);?>">
+		<input type="hidden" name="started" value="1">
+<?php
+
+echo <<<HTML
+<style>table {width: 650px; margin: auto;} th {background: transparent; border: none; align: center;} td.a {width: 225px; text-align: center;} td.c {width: 50px; height: 42px; margin: auto;}</style>
+HTML;
+
+// PHP Version
+$_GET['php_version'] = PHP_VERSION;
+	
+// Check 5.3
+if (version_compare(PHP_VERSION, '5.3.0', '>='))
+	{
+echo <<<HTML
+<font face="Verdana" size="3"><br><table><tr><th>PHP Version:</th></tr><tr><td class="a">{$_GET['php_version']}</td><td class="b">Your server supports PHP 5.3+.</td><td class="c"><img src="./images/yes.png" class="icon" alt="Supported" /></td></tr></table></font>
+HTML;
+	}
+	// Check 5.0
+	else if (version_compare(PHP_VERSION, '5.0.0', '>='))
+	{
+echo <<<HTML
+<font face="Verdana" size="3"><br><table><tr><th>PHP Version:</th></tr><tr><td class="a">{$_GET['php_version']}</td><td class="b">Your server supports PHP 5.0+. Update to PHP 5.3 or higher when possible. </td><td class="c"><img src="./images/yes.png" class="icon" alt="Supported" /></td></tr></font>
+HTML;
+	}
+	// Does not support PHP 5
+	else if (version_compare(PHP_VERSION, '4.4.9', '<='))
+	{
+echo <<<HTML
+<font face="Verdana" size="3"><br><table><tr><th>PHP Version:</th></tr><tr><td class="a">{$_GET['php_version']}</td><td class="b">Your server does not support PHP 5. You may have issues running this tracker.</td><td class="c">&nbsp;<img src="./images/no.png" alt="Not Supported" </tr></table></font>
+HTML;
+	}
+
+echo <<<HTML
+<br>
+HTML;
+
+//MySQL check
+if (class_exists('mysqli') OR function_exists('mysql_connect'))
+{
+echo <<<HTML
+<font face="Verdana" size="3"><table><tr><th>MySQL Support:</th></tr><tr><td class="a">Yes</td><td class="b">Your server supports MySQL.</td><td class="c"><img src="./images/yes.png" class="icon" alt="Supported" /></td></tr></table></font>
+HTML;
+	}
+	// No MySQL
+	else
+	{
+echo <<<HTML
+<font face="Verdana" size="3"><table><tr><th>MySQL Support:</tr></th><tr><td class="a">No</td><td class="b">Your server does not support MySQL.</td><td class="c">&nbsp;<img src="./images/no.png" alt="Not Supported" /></td></tr></table></font>
+HTML;
+	}
+
+echo ("<br><br>");
+		if (version_compare(PHP_VERSION, '5.0.0', '>=') && class_exists('mysqli') || version_compare(PHP_VERSION, '5.0.0', '>=') && function_exists('mysql_connect')) echo "<center><font face=\"Verdana\">Fully supported. You may continue.</font></center>";
+		else if (version_compare(PHP_VERSION, '5.0.0', '>=') && !class_exists('mysqli') || version_compare(PHP_VERSION, '5.0.0', '>=') && !function_exists('mysql_connect')) die ("<center><font face=\"Verdana\">Fully supported, but cannot connect to database. You may not continue.</font></center>");
+
+		if (version_compare(PHP_VERSION, '4.4.9', '<=') && class_exists('mysqli') || version_compare(PHP_VERSION, '4.4.9', '<=') && function_exists('mysql_connect')) echo "<center><font face=\"Verdana\">Not fully supported, but you may try.</font></center>";
+		else if (version_compare(PHP_VERSION, '4.4.9', '<=') && !class_exists('mysqli') || version_compare(PHP_VERSION, '4.4.9', '<=') && !function_exists('mysql_connect')) die("<center><font face=\"Verdana\">Not fully supported, also cannot connect to database. You may not continue.</font></center>");
+		?>
+		<br>
+		<br>
+		<center>
+		<input type="submit" name="checkpassed" value="Continue">
+		</form>
+		</center>
+		<br>
+		</body></html><?php exit;
+	}
+	if (isset($_POST["checkpassed"]))
+	{
+		?>
+		<center>
+		<h1>RivetTracker Installer</h1>
+		<img src="images/install.png" border="0" class="icon" alt="RivetTracker Installation" title="RivetTracker Installation" />
+		</center>
+		<br>
+		<br>
+		<form method="post" action="<?php echo htmlentities($_SERVER['PHP_SELF']);?>">
+		<input type="hidden" name="started" value="1">
+		<h2>The MySQL database needs to be prepared for the tracker. This script will help
+		you do that.</h2>
+		<h2>You have two choices:</h2>
+		<br>
+		<ul>
+		<li><h2>If you have a username, password, and database for the tracker already
+		created:</h2></li>
+		</ul>
+		<input type="submit" name="preexisting" value="Click Here">
+		<br>
+		<br>
+		<ul>
+		<li><h2>If you need to create the account and database, and you have the username and password
+		of a user who can create user accounts and databases:</h2></li>
+		</ul>
+		<input type="submit" name="makeaccount" value="Click Here">
+		</form>
+		<br>
+		</body></html><?php exit;
+	}
+	if (isset($_POST["preexisting"]))
+	{
+		?>
+		<form method="post" action="<?php echo htmlentities($_SERVER['PHP_SELF']);?>">
+		<input type="hidden" name="maketables" value="1">
+		<input type="hidden" name="started" value="1">
+		<h1>RivetTracker Installation</h1>
+		<center>
+		<img src="images/install.png" border="0" class="icon" alt="RivetTracker Installation" title="RivetTracker Installation" />
+		</center>
+		<br><br>
+		<table border=0 cellpadding=5>
+		<tr><td align="right">Database hostname:<br>(in MySQL format, example: localhost)</td><td align="left"><input type="text" name="host" value="localhost" size="40"></td></tr>
+		<tr><td align="right">Tracker's database username:</td><td align="left"><input type="text" name="username" size="40"></td></tr>
+		<tr><td align="right">Tracker's database password:</td><td align="left"><input type="password" name="password" size="40"></td></tr>
+		<tr><td align="right">Database name:</td><td align="left"><input type="text" name="database" size="40"></td></tr>
+		<tr><td align="right">Table Prefix:<br> (If you want to use an existing<br> database this will add the tables
+		in<br> with the specified prefix. If you<br> are unsure, leave this blank.)<br>e.g.: rt_</td><td align="left"><input type="text" name="prefix" size="40"></td></tr>
+		</table>
+		<br><br>
+		<center>
+		<input type="submit" value="Install">
+		</center>
+		<br>
+		</form></body></html><?php exit;
+
+	}
+	if (isset($_POST["makeaccount"]))
+	{
+		?>
+		<form method="post" action="<?php echo htmlentities($_SERVER['PHP_SELF']);?>">
+		<input type="hidden" name="domakeaccount" value="1">
+		<input type="hidden" name="started" value="1">
+		<h1>Tracker Installation</h1>
+		<center>
+		<img src="images/install.png" border="0" class="icon" alt="RivetTracker Installation" title="RivetTracker Installation" />
+		</center>
+		<br><br>
+		<table border=0 cellpadding=5>
+		<tr><td align="right">Username of database admin:</td><td align="left"><input type="text" name="adminname" size="40"></td></tr>
+		<tr><td align="right">Password of database admin:</td><td align="left"><input type="password" name="adminpass" size="40"></td></tr>
+		<tr><td align="right">Database hostname:<br>(in MySQL format, example: localhost)</td><td align="left"><input type="text" name="host" size="40" value="localhost"></td></tr>
+		<tr><td align="right">Create user for MySQL:<br>(make sure this user does not already exist)</td><td align="left"><input type="text" name="username" size="40"></td></tr>
+		<tr><td align="right">Password:</td><td align="left"><input type="password" name="password" size="40"></td></tr>
+		<tr><td align="right">Create database (name):</td><td align="left"><input type="text" name="database" size="40"></td></tr>
+		</table>
+		<br><br>
+		<center>		
+		<input type="submit" value="Install">
+		</center>
+		</form></body></html>
+		<?php exit;
+	}
+
+	if (isset($_POST["prefix"])) {
+		$prefix = $_POST["prefix"];
+	} else {
+		$prefix = "";
+	}
+
+	$makenamemap= 'CREATE TABLE ' . $prefix . 'namemap (info_hash char(40) NOT NULL default "", title varchar(250) NOT NULL default "", filename varchar(250) NOT NULL default "", url varchar(250) NOT NULL default "", size bigint(20) unsigned NOT NULL, pubDate varchar(50) NOT NULL default "", PRIMARY KEY(info_hash)) DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci ENGINE = innodb';
+	$makesummary = 'CREATE TABLE ' . $prefix . 'summary (info_hash char(40) NOT NULL default "", dlbytes bigint unsigned NOT NULL default 0, seeds int unsigned NOT NULL default 0, leechers int unsigned NOT NULL default 0, finished int unsigned NOT NULL default 0, lastcycle int unsigned NOT NULL default "0", lastSpeedCycle int unsigned NOT NULL DEFAULT "0", speed bigint unsigned NOT NULL default 0, piecelength int(11) NOT NULL default -1, numpieces int(11) NOT NULL default 0, PRIMARY KEY (info_hash)) DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci ENGINE = innodb';
+	$maketimestamps = 'CREATE TABLE ' . $prefix . 'timestamps (info_hash char(40) not null, sequence int unsigned not null auto_increment, bytes bigint unsigned not null, delta smallint unsigned not null, primary key(sequence), key sorting (info_hash)) DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci ENGINE = innodb';
+	$makespeedlimit = 'CREATE TABLE ' . $prefix . 'speedlimit (uploaded bigint(25) NOT NULL default 0, total_uploaded bigint(30) NOT NULL default 0, started bigint(25) NOT NULL default 0) DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci ENGINE = innodb';
+	$makewebseedfiles = 'CREATE TABLE ' . $prefix . 'webseedfiles (info_hash char(40) default NULL, filename char(250) NOT NULL default "", startpiece int(11) NOT NULL default 0, endpiece int(11) NOT NULL default 0, startpieceoffset int(11) NOT NULL default 0, fileorder int(11) NOT NULL default 0, UNIQUE KEY fileseq (info_hash,fileorder)) DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci ENGINE = innodb';
+	if (isset($_POST["maketables"]))
+	{
+		$username = $_POST["username"] or die(errorMessage() . "No username was given, please try again.</p>");
+		$password = $_POST["password"] or die(errorMessage() . "No username password was given, this is a huge security risk, please try again.</p>");
+		$database = $_POST["database"] or die(errorMessage() . "No database specified, please try again.</p>");
+		$hostname = $_POST["host"] or die(errorMessage() . "No database hostname specified, please try again.</p>");
+
+		$db = mysql_connect($hostname, $username, $password) or die(errorMessage() . "Can't connect to database: " . mysql_error() . "</p>"); 
+		mysql_select_db($database) or die(errorMessage() . "Can't select database: " . mysql_error() . "</p>");
+		mysql_query($makesummary) or die(errorMessage() . "Can't make the summary table: " . mysql_error() . "</p>");
+		mysql_query($makenamemap) or die(errorMessage() . "Can't make the namemap table: " . mysql_error() . "</p>");
+		mysql_query($maketimestamps) or die(errorMessage() . "Can't make the timestamps table: " . mysql_error() . "</p>");
+		mysql_query($makespeedlimit) or die(errorMessage() . "Can't make the speedlimit table: " . mysql_error() . "</p>");
+		mysql_query($makewebseedfiles) or die(errorMessage() . "Can't make the webseedfiles table: " . mysql_error() . "</p>");
+		mysql_query("INSERT INTO ".$prefix."speedlimit values (0,0,0)") or die(errorMessage() . "Can't insert zeros into speedlimit table: " . mysql_error() . "</p>");
+		echo "<p class=\"success\">Database was created successfully!</p><br><br>";
+	}
+
+	if (isset($_POST["domakeaccount"]))
+	{
+		$username = $_POST["username"] or die(errorMessage() . "No username was given, please try again.</p>");
+		$password = $_POST["password"] or die(errorMessage() . "No username password was given, this is a huge security risk, please try again.</p>");
+		$database = $_POST["database"] or die(errorMessage() . "No database specified, please try again.</p>");
+		$hostname = $_POST["host"] or die(errorMessage() . "No database hostname specified, please try again.</p>");
+
+		$dbadmin = $_POST["adminname"] or die(errorMessage() . "No admin username was given, please try again.</p>");
+		$dbpass = $_POST["adminpass"]; // No admin password, OK but huge security risk...
+		
+		// Escaping strings will be ignored for now.
+		$db = mysql_connect($hostname, $dbadmin, $dbpass) or die(errorMessage() . "Error connecting: " . mysql_error() . "</p>");
+		mysql_select_db("mysql") or die(errorMessage() . "Can't select db \"mysql\":" . mysql_error() . "</p>");
+
+		mysql_query("INSERT INTO user SET user=\"$username\", password=PASSWORD(\"$password\"), host=\"\"") or die(errorMessage() . "Can't make user: " . mysql_error() . "</p>");
+		mysql_query("INSERT INTO db SET Host=\"%\", db=\"$database\", user=\"$username\", select_priv='Y', Insert_priv='Y', Update_priv='Y', Delete_priv='Y', Create_priv='Y', Drop_priv='Y', Alter_priv='Y', index_priv='Y'") or die(errorMessage() . "Cannot insert into \"Db\": " . mysql_error() . "</p>");
+		mysql_query("CREATE DATABASE $database") or die(errorMessage() . "Can't make database: " . mysql_error() . "</p>");
+		
+		mysql_query("FLUSH PRIVILEGES") or die(errorMessage() . "Can't flush privileges: " . mysql_error() . "</p>");
+	
+		mysql_select_db($database) or die(errorMessage() . "Can't select database \"$database\":" . mysql_error() . "</p>");
+	
+		mysql_query($makesummary) or die(errorMessage() . "Can't make the summary table: " . mysql_error() . "</p>");
+		mysql_query($makenamemap) or die(errorMessage() . "Can't make the namemap table: " . mysql_error() . "</p>");
+		mysql_query($maketimestamps) or die(errorMessage() . "Can't make the timestamps table: " . mysql_error() . "</p>");
+		mysql_query($makespeedlimit) or die(errorMessage() . "Can't make the speedlimit table: " . mysql_error() . "</p>");
+		mysql_query($makewebseedfiles) or die(errorMessage() . "Can't make the webseedfiles table: " . mysql_error() . "</p>");
+		mysql_query("INSERT INTO ".$prefix."speedlimit values (0,0,0)") or die(errorMessage() . "Can't insert zeros into speedlimit table: " . mysql_error() . "</p>");
+		echo "<p class=\"success\">Database was created successfully!</p><br><br>";
+
+	}
+
+	if (isset($_POST["domakeaccount"]) || isset($_POST["maketables"]))
+	{
+		//have user set values for config.php
+		?>
+		<form method="post" action="<?php echo htmlentities($_SERVER['PHP_SELF']);?>">
+		<input type="hidden" name="config" value="1">
+		<input type="hidden" name="started" value="1">
+		<?php
+		echo "<input type=\"hidden\" name=\"dbhost\" value=\"" . $hostname . "\">\n";
+		echo "<input type=\"hidden\" name=\"dbuser\" value=\"" . $username . "\">\n";
+		echo "<input type=\"hidden\" name=\"dbpass\" value=\"" . $password . "\">\n";
+		echo "<input type=\"hidden\" name=\"database\" value=\"" . $database . "\">\n";
+		echo "<input type=\"hidden\" name=\"prefix\" value=\"" . $prefix . "\">\n";
+		?>
+		<h1>Create Configuration File</h1>
+		<br><br>
+		<h2>This last step allows you to configure the "config.php" file.  This file stores all the necessary
+		settings for your tracker.  You can edit these settings at a later time in the admin page if you need
+		to change them.  Please do NOT edit the "config.php" file directly, use the admin page for any changes.
+		It's usually pretty safe to leave most of the settings to the default unless you know what you're doing.</h2>
+		<h2><span class="notice">*</span> - required value</h2>
+		<table border=1 cellpadding=3>
+		
+		<tr><td>Make tracker hidden: This will require a login by either the admin or upload user in order to
+		see the torrents available on the main statistics page.  This does not mean it's a private tracker.  If you
+		need a private tracker, there are many other trackers out there.  Also, you will need to secure the "torrents"
+		folder with an .htaccess file for Apache or some other method.  The tracker will still accept all valid
+		connections by clients.  There is no user checking in that regard.</td>
+		<td><input type="checkbox" name="hiddentracker"></td></tr>
+		
+		<tr><td>Enable or disable scraping by clients.  Generally it is safe to leave this on unless
+		you have a large number of torrents or users which can lead to increased bandwidth usage.  Also, scraping
+		can possibily be used maliciously by abusive clients.</td>
+		<td><input type="checkbox" name="scrape" checked></td></tr>
+
+		<tr><td>Displays custom titles on the main torrent statistics page instead of the filename.  This is
+		because the uploader script will auto-rename your uploaded filename to exactly what you specified initally
+		or by automatically using the data from the uploaded torrent.  Check this if you want the titles to be
+		different from the filename.</td>
+		<td><input type="checkbox" name="customtitle"></td></tr>
+
+		<tr><td>Short Announce URL: You can enable the short announce feature, making the URL end in /announce for
+		your tracker.  You should not utilize both tracker URL forms in one torrent at the same time, or you will get
+		inconsistent results.  Note: You will need the provided htaccess file, have URL rewrite capabilities, and have
+		it properly set up to use this feature.  Otherwise, leave it disabled.</td>
+		<td><select name="announceurl" id="announceurl">
+		<option title="disabled" value="announce.php"<?php if($temp == "announce.php") echo " selected=\"selected\"";?>>disabled</option>
+		<option title="enabled" value="announce"<?php if($temp == "announce") echo " selected=\"selected\"";?>>enabled</option>
+		</select>
+		</td>
+		</tr>
+
+		<tr><td><span class="notice">* </span>Lists the number of torrents on each page on your torrent tracker list.  Default is 10.</td>
+		<td><input type="text" name="indexpagelimitspecify" size="40" value="10"></td></tr>
+
+		<tr><td><span class="notice">* </span>Lists the number of torrents on each page on the detailed statistics page.  Default is 5.</td>
+		<td><input type="text" name="statspagelimitspecify" size="40" value="5"></td></tr>
+
+		<tr><td><span class="notice">* </span>Maximum reannounce interval (in seconds) 1800 == 30 minutes</td>
+		<td><input type="text" name="report_interval" size="40" value="1800"></td></tr>
+
+		<tr><td><span class="notice">* </span>Minimum reannounce interval (also in seconds) 300 == 5 minutes</td>
+		<td><input type="text" name="min_interval" size="40" value="300"></td></tr>
+
+		<tr><td><span class="notice">* </span>Number of peers to send in one request.  Some logic will break if you set this to more than 300,
+		so please don't do that. 100 is the most you should set anyway.</td>
+		<td><input type="text" name="maxpeers" size="40" value="50"></td></tr>
+
+		<tr><td>If set, NAT checking will be performed.
+		This may cause trouble with some providers, so it's
+		off by default.</td>
+		<td><input type="checkbox" name="NAT"></td></tr>
+
+		<tr><td>Persistent MySQL connections:
+		Check with your webmaster to see if you're allowed to use these.
+		Highly recommended, especially for higher loads, but generally
+		not allowed unless it's a dedicated machine.</td>
+		<td><input type="checkbox" name="persist"></td></tr>
+
+		<tr><td>Allow users to override ip address.
+		Enable this if you know people have a legit reason to use
+		this function. Leave disabled otherwise.</td>
+		<td><input type="checkbox" name="ip_override"></td></tr>
+
+		<tr><td>For heavily loaded trackers, uncheck this. It will stop count the number
+		of downloaded bytes and the speed of the torrent, but will significantly reduce
+		the load.</td>
+		<td><input type="checkbox" name="countbytes" checked></td></tr>
+
+		<tr><td><span class="notice">* </span>Username for individual who can add torrents to tracker database.
+		This user is only able to create, and not delete torrents to the tracker.
+		For full privileges, see the admin user.</td>
+		<td><input type="text" name="upload_username" size="40"></td></tr>
+
+		<tr><td><span class="notice">* </span>Password for individual who can add torrents to tracker database.
+		Again, this user is only able to create, and not delete torrents to the tracker.
+		For full privileges, see the admin user.</td>
+		<td><input type="password" name="upload_password" size="40"></td></tr>
+
+		<tr><td><span class="notice">* </span>Admin username. The admin is able to go to the admin page and show detailed 
+		information about the tracker as well as access a few other important tools.
+		The admin is also able to upload torrents to the database
+		just like the previous account.</td>
+		<td><input type="text" name="admin_username" size="40"></td></tr>
+
+		<tr><td><span class="notice">* </span>Password for admin.  Again, The admin is able to go to the admin page and show detailed 
+		information about the tracker as well as access a few other important tools.
+		The admin is also able to upload torrents to the database.</td>
+		<td><input type="password" name="admin_password" size="40"></td></tr>
+
+		<tr><td>Title on index.php statistics page, if not set, defaults to "Tracker Statistics"</td>
+		<td><input type="text" name="title" size="40"></td></tr>
+		
+		<tr><td>Enable RSS feed: If you do not want the RSS feed to be created for 
+		privacy reasons or do not need it disable this checkbox.</td>
+		<td><input type="checkbox" name="enablerss" checked></td></tr>
+		
+		<tr><td>RSS Title: In the rss.xml file, this is the main <pre>&lt;title&gt;</pre> tag.</td>
+		<td><input type="text" name="rss_title" size="40"></td></tr>
+		
+		<tr><td>RSS link to main website: In the rss.xml file, this is the main <pre>&lt;link&gt;</pre> tag.</td>
+		<td><input type="text" name="rss_link" size="40"></td></tr>
+		
+		<tr><td>RSS description: In the rss.xml file, this is the main <pre>&lt;description&gt;</pre> tag.</td>
+		<td><input type="text" name="rss_description" size="60"></td></tr>
+		
+		<tr><td><span class="notice">* </span>Main website url that the tracker runs on, example: http://www.mywebsite.com</td>
+		<td><input type="text" name="website_url" size="40"></td></tr>
+		
+		<tr><td><span class="notice">* </span>For HTTP seeding, this is the maximum total upload rate per second in kilobytes, for example 100 would be 100 KB/s</td>
+		<td><input type="text" name="max_upload_rate" size="40" value="100"></td></tr>
+		
+		<tr><td><span class="notice">* </span>For HTTP seeding, this is the maximum number of uploads to run at a time</td>
+		<td><input type="text" name="max_uploads" size="40" value="5"></td></tr>
+		
+		<tr><td><span class="notice">* </span>Date format of the torrent publication date. It shows on statistics.php. If you change this setting, you will have to change it for every other existing torrent!</td>
+		<td>
+		<select name="dateformat" id="dateformat">
+		<option title="Mon, 4 Jan, 1999 01:15:40 PM" value="D, j M, Y h:i:s A" selected="selected">Mon, 4 Jan, 1999 01:15:40 PM</option>
+		<option title="Monday, 4 Jan, 1999 01:15:40 PM" value="l, j M, Y h:i:s A">Monday, 4 Jan, 1999 01:15:40 PM</option>
+		<option title="Mon, 4 January, 1999 01:15:40 PM" value="D, j F, Y h:i:s A">Mon, 4 January, 1999 01:15:40 PM</option>
+		<option title="Monday, 4 January, 1999 01:15:40 PM" value="l, j F, Y h:i:s A">Monday, 4 January, 1999 01:15:40 PM</option>
+		<option title="Mon, 4 Jan, 1999 13:15:40" value="D, j M, Y H:i:s">Mon, 4 Jan, 1999 13:15:40</option>
+		<option title="Monday, 4 Jan, 1999 13:15:40" value="l, j M, Y H:i:s">Monday, 4 Jan, 1999 13:15:40</option>
+		<option title="Mon, 4 January, 1999 13:15:40" value="D, j F, Y H:i:s">Mon, 4 January, 1999 13:15:40</option>
+		<option title="Monday, 4 January, 1999 13:15:40" value="l, j F, Y H:i:s">Monday, 4 January, 1999 13:15:40</option>
+		<option title="Mon, Jan 4, 1999 01:15:40 PM" value="D, M j, Y h:i:s A">Mon, Jan 4, 1999 01:15:40 PM</option>
+		<option title="Monday, Jan 4, 1999 01:15:40 PM" value="l, M j, Y h:i:s A">Monday, Jan 4, 1999 01:15:40 PM</option>
+		<option title="Mon, January 4, 1999 01:15:40 PM" value="D, F j, Y h:i:s A">Mon, January 4, 1999 01:15:40 PM</option>
+		<option title="Monday, January 4, 1999 01:15:40 PM" value="l, F j, Y h:i:s A">Monday, January 4, 1999 01:15:40 PM</option>
+		<option title="Mon, Jan 4, 1999 13:15:40" value="D, M j, Y H:i:s">Mon, Jan 4, 1999 13:15:40</option>
+		<option title="Monday, Jan 4, 1999 13:15:40" value="l, M j, Y H:i:s">Monday, Jan 4, 1999 13:15:40</option>
+		<option title="Mon, January 4, 1999 13:15:40" value="D, F j, Y H:i:s">Mon, January 4, 1999 13:15:40</option>
+		<option title="Monday, January 4, 1999 13:15:40" value="l, F j, Y H:i:s">Monday, January 4, 1999 13:15:40</option>
+		</select>
+		</td>
+		</tr>
+
+		<tr><td><span class="notice">* </span>Timezone that the server runs on</td>
+		<td>
+		<select name="timezone" id="timezone">
+		<option title="[UTC - 12] Baker Island Time" value="-1200">[UTC - 12] Baker Island Time</option>
+		<option title="[UTC - 11] Niue Time, Samoa Standard Time" value="-1100">[UTC - 11] Niue Time, Samoa Standard Time</option>
+		<option title="[UTC - 10] Hawaii-Aleutian Standard Time, Cook Island Time" value="-1000">[UTC - 10] Hawaii-Aleutian Standard Time, Cook Isl...</option>
+		<option title="[UTC - 9:30] Marquesas Islands Time" value="-0930">[UTC - 9:30] Marquesas Islands Time</option>
+		<option title="[UTC - 9] Alaska Standard Time, Gambier Island Time" value="-0900">[UTC - 9] Alaska Standard Time, Gambier Island Tim...</option>
+		<option title="[UTC - 8] Pacific Standard Time" value="-0800">[UTC - 8] Pacific Standard Time</option>
+		<option title="[UTC - 7] Mountain Standard Time" value="-0700">[UTC - 7] Mountain Standard Time</option>
+		<option title="[UTC - 6] Central Standard Time" value="-0600">[UTC - 6] Central Standard Time</option>
+		<option title="[UTC - 5] Eastern Standard Time" value="-0500">[UTC - 5] Eastern Standard Time</option>
+		<option title="[UTC - 4] Atlantic Standard Time" value="-0400">[UTC - 4] Atlantic Standard Time</option>
+		<option title="[UTC - 3:30] Newfoundland Standard Time" value="-0330">[UTC - 3:30] Newfoundland Standard Time</option>
+		<option title="[UTC - 3] Amazon Standard Time, Central Greenland Time" value="-0300">[UTC - 3] Amazon Standard Time, Central Greenland ...</option>
+		<option title="[UTC - 2] Fernando de Noronha Time, South Georgia &amp; the South Sandwich Islands Time" value="-0200">[UTC - 2] Fernando de Noronha Time, South Georgia ...</option>
+		<option title="[UTC - 1] Azores Standard Time, Cape Verde Time, Eastern Greenland Time" value="-0100">[UTC - 1] Azores Standard Time, Cape Verde Time, E...</option>
+		<option title="[UTC] Western European Time, Greenwich Mean Time" value="+0000" selected="selected">[UTC] Western European Time, Greenwich Mean Time</option>
+		<option title="[UTC + 1] Central European Time, West African Time" value="+0100">[UTC + 1] Central European Time, West African Time</option>
+		<option title="[UTC + 2] Eastern European Time, Central African Time" value="+0200">[UTC + 2] Eastern European Time, Central African T...</option>
+		<option title="[UTC + 3] Moscow Standard Time, Eastern African Time" value="+0300">[UTC + 3] Moscow Standard Time, Eastern African Ti...</option>
+		<option title="[UTC + 3:30] Iran Standard Time" value="+0330">[UTC + 3:30] Iran Standard Time</option>
+		<option title="[UTC + 4] Gulf Standard Time, Samara Standard Time" value="+0400">[UTC + 4] Gulf Standard Time, Samara Standard Time</option>
+		<option title="[UTC + 4:30] Afghanistan Time" value="+0430">[UTC + 4:30] Afghanistan Time</option>
+		<option title="[UTC + 5] Pakistan Standard Time, Yekaterinburg Standard Time" value="+0500">[UTC + 5] Pakistan Standard Time, Yekaterinburg St...</option>
+		<option title="[UTC + 5:30] Indian Standard Time, Sri Lanka Time" value="+0530">[UTC + 5:30] Indian Standard Time, Sri Lanka Time</option>
+		<option title="[UTC + 6] Bangladesh Time, Bhutan Time, Novosibirsk Standard Time" value="+0600">[UTC + 6] Bangladesh Time, Bhutan Time, Novosibirs...</option>
+		<option title="[UTC + 6:30] Cocos Islands Time, Myanmar Time" value="+0630">[UTC + 6:30] Cocos Islands Time, Myanmar Time</option>
+		<option title="[UTC + 7] Indochina Time, Krasnoyarsk Standard Time" value="+0700">[UTC + 7] Indochina Time, Krasnoyarsk Standard Tim...</option>
+		<option title="[UTC + 8] Chinese Standard Time, Australian Western Standard Time, Irkutsk Standard Time" value="+0800">[UTC + 8] Chinese Standard Time, Australian Wester...</option>
+		<option title="[UTC + 9] Japan Standard Time, Korea Standard Time, Chita Standard Time" value="+0900">[UTC + 9] Japan Standard Time, Korea Standard Time...</option>
+		<option title="[UTC + 9:30] Australian Central Standard Time" value="+0930">[UTC + 9:30] Australian Central Standard Time</option>
+		<option title="[UTC + 10] Australian Eastern Standard Time, Vladivostok Standard Time" value="+1000">[UTC + 10] Australian Eastern Standard Time, Vladi...</option>
+		<option title="[UTC + 10:30] Lord Howe Standard Time" value="+1030">[UTC + 10:30] Lord Howe Standard Time</option>
+		<option title="[UTC + 11] Solomon Island Time, Magadan Standard Time" value="+1100">[UTC + 11] Solomon Island Time, Magadan Standard T...</option>
+		<option title="[UTC + 11:30] Norfolk Island Time" value="+1130">[UTC + 11:30] Norfolk Island Time</option>
+		<option title="[UTC + 12] New Zealand Time, Fiji Time, Kamchatka Standard Time" value="+1200">[UTC + 12] New Zealand Time, Fiji Time, Kamchatka ...</option>
+		<option title="[UTC + 13] Tonga Time, Phoenix Islands Time" value="+1300">[UTC + 13] Tonga Time, Phoenix Islands Time</option>
+		<option title="[UTC + 14] Line Island Time" value="+1400">[UTC + 14] Line Island Time</option>
+		</select>
+		</td>
+		</tr>
+		
+		</table>
+		<br>
+		<center>
+		<input type="submit" value="Create Config File">
+		</center>
+		<br><br><br>
+		</form>
+		</body>
+		</html>
+		<?php
+	}
+
+	if (isset($_POST["config"]))
+	{
+		//check required entries for values, if blank: error out
+		if ($_POST["announceurl"] == "")
+		{
+			echo errorMessage() . "Error: The announce URL is blank.</p>";
+			exit();
+		}
+		if (!is_numeric($_POST["indexpagelimitspecify"]) || $_POST["indexpagelimitspecify"] == "" || $_POST["indexpagelimitspecify"] <= 0)
+		{
+			echo errorMessage() . "Error: The index page limit is not an integer, a negative number, or is blank.</p>";
+			exit();
+		}	
+		if (!is_numeric($_POST["statspagelimitspecify"]) || $_POST["statspagelimitspecify"] == "" || $_POST["statspagelimitspecify"] <= 0)
+		{
+			echo errorMessage() . "Error: The statistics page limit is not an integer, a negative number, or is blank.</p>";
+			exit();
+		}
+		if (!is_numeric($_POST["report_interval"]) || $_POST["report_interval"] == "" || $_POST["report_interval"] <= 0)
+		{
+			echo errorMessage() . "Error: The maximum reannounce interval is not an integer, a negative number, or is blank.</p>";
+			exit();
+		}
+		if (!is_numeric($_POST["min_interval"]) || $_POST["min_interval"] == "" || $_POST["min_interval"] <= 0)
+		{
+			echo errorMessage() . "Error: The minimum reannounce interval is not an integer, a negative number, or is blank.</p>";
+			exit();
+		}
+		if (!is_numeric($_POST["maxpeers"]) || $_POST["maxpeers"] == "" || $_POST["maxpeers"] > 300 || $_POST["maxpeers"] <= 0)
+		{
+			echo errorMessage() . "Error: The number of peers to send in one request is not an integer, over 300, a negative number, zero, or blank.</p>";
+			exit();
+		}
+		if ($_POST["upload_username"] == "")
+		{
+			echo errorMessage() . "Error: The upload username is blank.</p>";
+			exit();
+		}
+		if ($_POST["upload_password"] == "")
+		{
+			echo errorMessage() . "Error: The upload user password is blank. This is considered a security risk.</p>";
+			exit();
+		}
+		if ($_POST["admin_username"] == "")
+		{
+			echo errorMessage() . "Error: The admin username is blank.</p>";
+			exit();
+		}
+		if ($_POST["admin_password"] == "")
+		{
+			echo errorMessage() . "Error: The admin user password is blank. This is considered a LARGE security risk.</p>";
+			exit();
+		}
+		if ($_POST["dbhost"] == "")
+		{
+			echo errorMessage() . "Error: The database hostname is blank.</p>";
+			exit();
+		}
+		if ($_POST["dbuser"] == "")
+		{
+			echo errorMessage() . "Error: The database username is blank.</p>";
+			exit();
+		}
+		if ($_POST["dbpass"] == "")
+		{
+			echo errorMessage() . "Error: The database password is blank.</p>";
+			exit();
+		}
+		if ($_POST["database"] == "")
+		{
+			echo errorMessage() . "Error: The database name is blank.</p>";
+			exit();
+		}
+		if ($_POST["rss_link"] != "" && Substr($_POST["rss_link"], 0, 7) != "http://")
+		{
+			echo errorMessage() . "Error: The RSS website URL does not start with http://</p>";
+			exit();
+		}
+		if ($_POST["website_url"] == "" || Substr($_POST["website_url"], 0, 7) != "http://")
+		{
+			echo errorMessage() . "Error: The website URL does not start with http:// or is blank.</p>";
+			exit();
+		}
+		if (!is_numeric($_POST["max_upload_rate"]) || $_POST["max_upload_rate"] == "" || $_POST["max_upload_rate"] <= 0)
+		{
+			echo errorMessage() . "Error: The maximum upload rate is not an integer, a negative number, or is blank.</p>";
+			exit();
+		}
+		if (!is_numeric($_POST["max_uploads"]) || $_POST["max_uploads"] == "" || $_POST["max_uploads"] <= 0)
+		{
+			echo errorMessage() . "Error: The maximum uploads is not an integer, a negative number, or is blank.</p>";
+			exit();
+		}
+		if ($_POST["dateformat"] == "")
+		{
+			echo errorMessage() . "Error: The date format is blank.</p>";
+			exit();
+		}
+		if ($_POST["timezone"] == "")
+		{
+			echo errorMessage() . "Error: The timezone is blank.</p>";
+			exit();
+		}
+		if ($_POST["upload_username"] == $_POST["admin_username"])
+		{
+			echo errorMessage() . "Error: The admin username cannot be the same as the upload username.</p>";
+			exit();
+		}
+	
+		//create config.php based on user input
+		//first try creating it on the server
+		if (is_writable("./"))
+		{
+			//go through checkboxes and change "on" to "true"
+			if ($_POST["hiddentracker"] == "on")
+				$hiddentracker = "true";
+			else
+				$hiddentracker = "false";
+			if ($_POST["enablerss"] == "on")
+				$enablerss = "true";
+			else
+				$enablerss = "false";
+			if ($_POST["scrape"] == "on")
+				$scrape = "true";
+			else
+				$scrape = "false";
+			if ($_POST["customtitle"] == "on")
+				$customtitle = "true";
+			else
+				$customtitle = "false";
+			if ($_POST["NAT"] == "on")
+				$NAT = "true";
+			else
+				$NAT = "false";
+			if ($_POST["persist"] == "on")
+				$persist = "true";
+			else
+				$persist = "false";
+			if ($_POST["ip_override"] == "on")
+				$ip_override = "true";
+			else
+				$ip_override = "false";
+			if ($_POST["countbytes"] == "on")
+				$countbytes = "true";
+			else
+				$countbytes = "false";
+
+			//write config.php file
+			$fd = fopen("config.php", "w") or die(errorMessage() . "Error: couldn't make config.php!</p>");
+			fwrite($fd, 
+			"<?php //Please do NOT edit this file, use the admin page for changes.\n" .
+			"\$GLOBALS['hiddentracker'] = " . $hiddentracker . ";\n" .
+			"\$GLOBALS['scrape'] = " . $scrape . ";\n" .
+			"\$GLOBALS['customtitle'] = " . $customtitle . ";\n" .
+			"\$announceurl = " . htmlspecialchars($_POST["announceurl"]) . ";\n" .
+			"\$GLOBALS['indexpagelimitspecify'] = " . htmlspecialchars($_POST["indexpagelimitspecify"]) . ";\n" .
+			"\$GLOBALS['statspagelimitspecify'] = " . htmlspecialchars($_POST["statspagelimitspecify"]) . ";\n" .
+			"\$GLOBALS['report_interval'] = " . htmlspecialchars($_POST["report_interval"]) . ";\n" .
+			"\$GLOBALS['min_interval'] = " . htmlspecialchars($_POST["min_interval"]) . ";\n" .
+			"\$GLOBALS['maxpeers'] = " . htmlspecialchars($_POST["maxpeers"]) . ";\n" .
+			"\$GLOBALS['NAT'] = " . $NAT . ";\n" .
+			"\$GLOBALS['persist'] = " . $persist . ";\n" .
+			"\$GLOBALS['ip_override'] = " . $ip_override . ";\n" .
+			"\$GLOBALS['countbytes'] = " . $countbytes . ";\n" .
+			"\$upload_username = '" . htmlspecialchars($_POST["upload_username"]) . "';\n" .
+			"\$upload_password = '" . md5($_POST["upload_username"].$_POST["upload_password"]) . "';\n" .
+			"\$admin_username = '" . htmlspecialchars($_POST["admin_username"]) . "';\n" .
+			"\$admin_password = '" . md5($_POST["admin_username"].$_POST["admin_password"]) . "';\n" .
+			"\$GLOBALS['title'] = '" . htmlspecialchars(addquotes($_POST["title"])) . "';\n" .
+			"\$dbhost = '" . htmlspecialchars($_POST["dbhost"]) . "';\n" .
+			"\$dbuser = '" . htmlspecialchars($_POST["dbuser"]) . "';\n" .
+			"\$dbpass = '" . htmlspecialchars($_POST["dbpass"]) . "';\n" .
+			"\$database = '" . htmlspecialchars($_POST["database"]) . "';\n" .
+			"\$enablerss = " . $enablerss . ";\n" .
+			"\$rss_title = '" . htmlspecialchars(addquotes($_POST["rss_title"])) . "';\n" .
+			"\$rss_link = '" . htmlspecialchars($_POST["rss_link"]) . "';\n" .
+			"\$rss_description = '" . htmlspecialchars(addquotes($_POST["rss_description"])) . "';\n" .
+			"\$website_url = '" . htmlspecialchars($_POST["website_url"]) . "';\n" .
+			"\$GLOBALS['max_upload_rate'] = " . htmlspecialchars($_POST['max_upload_rate']) . ";\n" .
+			"\$GLOBALS['max_uploads'] = " . htmlspecialchars($_POST['max_uploads']) . ";\n" .
+			"\$dateformat = '" . htmlspecialchars($_POST["dateformat"]) . "';\n" .
+			"\$timezone = '" . htmlspecialchars($_POST["timezone"]) . "';\n" .
+			"\$prefix = '" . htmlspecialchars($_POST["prefix"]) . "';\n" .
+			"?>"
+			);
+
+			fclose($fd);
+			echo "<br><p class=\"success\">config.php file was created successfully!</p>";
+		}
+
+		//if unable to create on server, user downloads config.php file for future upload
+		if (!is_writable("./"))
+		{
+			?>
+			<h2>"config.php" was unable to be created on the server, 
+			you will have to download the file and upload it manually.</h2>
+			<br>
+			<form method="post" action="<?php echo htmlentities($_SERVER['PHP_SELF']);?>">
+			<input type="hidden" name="download" value="1">
+			<input type="hidden" name="hiddentracker" value="<?php if (isset($_POST['hiddentracker']) AND $_POST['hiddentracker'] == 'on') echo 'true'; else echo 'false';?>">
+			<input type="hidden" name="scrape" value="<?php if (isset($_POST['scrape']) AND $_POST['scrape'] == 'on') echo 'true'; else echo 'false';?>">
+			<input type="hidden" name="customtitle" value="<?php if (isset($_POST['customtitle']) AND $_POST['customtitle'] == 'on') echo 'true'; else echo 'false';?>">
+			<input type="hidden" name="announceurl" value="<?php echo $_POST['announceurl'];?>">
+			<input type="hidden" name="indexpagelimitspecify" value="<?php echo $_POST['indexpagelimitspecify'];?>">
+			<input type="hidden" name="statspagelimitspecify" value="<?php echo $_POST['statspagelimitspecify'];?>">
+			<input type="hidden" name="report_interval" value="<?php echo $_POST['report_interval'];?>">
+			<input type="hidden" name="min_interval" value="<?php echo $_POST['min_interval'];?>">
+			<input type="hidden" name="maxpeers" value="<?php echo $_POST['maxpeers'];?>">
+			<input type="hidden" name="NAT" value="<?php if (isset($_POST['NAT']) AND $_POST['NAT'] == 'on') echo 'true'; else echo 'false';?>">
+			<input type="hidden" name="persist" value="<?php if (isset($_POST['persist']) AND $_POST['persist'] == 'on') echo 'true'; else echo 'false';?>">
+			<input type="hidden" name="ip_override" value="<?php if (isset($_POST['ip_override']) AND $_POST['ip_override'] == 'on') echo 'true'; else echo 'false';?>">
+			<input type="hidden" name="countbytes" value="<?php if (isset($_POST['countbytes']) AND $_POST['countbytes'] == 'on') echo 'true'; else echo 'false';?>">
+			<input type="hidden" name="upload_username" value="<?php echo $_POST['upload_username'];?>">
+			<input type="hidden" name="upload_password" value="<?php echo md5($_POST["upload_username"].$_POST["upload_password"]);?>">
+			<input type="hidden" name="admin_username" value="<?php echo $_POST['admin_username'];?>">
+			<input type="hidden" name="admin_password" value="<?php echo md5($_POST["admin_username"].$_POST["admin_password"]);?>">
+			<input type="hidden" name="title" value="<?php echo $_POST['title'];?>">
+			<input type="hidden" name="dbhost" value="<?php echo $_POST['dbhost'];?>">
+			<input type="hidden" name="dbuser" value="<?php echo $_POST['dbuser'];?>">
+			<input type="hidden" name="dbpass" value="<?php echo $_POST['dbpass'];?>">
+			<input type="hidden" name="database" value="<?php echo $_POST['database'];?>">
+			<input type="hidden" name="enablerss" value="<?php if (isset($_POST['enablerss']) AND $_POST['enablerss'] == 'on') echo 'true'; else echo 'false';?>">
+			<input type="hidden" name="rss_title" value="<?php echo $_POST['rss_title'];?>">
+			<input type="hidden" name="rss_link" value="<?php echo $_POST['rss_link'];?>">
+			<input type="hidden" name="rss_description" value="<?php echo $_POST['rss_description'];?>">
+			<input type="hidden" name="website_url" value="<?php echo $_POST['website_url'];?>">
+			<input type="hidden" name="max_upload_rate" value="<?php echo $_POST['max_upload_rate'];?>">
+			<input type="hidden" name="max_uploads" value="<?php echo $_POST['max_uploads'];?>">
+			<input type="hidden" name="dateformat" value="<?php echo $_POST['dateformat'];?>">
+			<input type="hidden" name="timezone" value="<?php echo $_POST['timezone'];?>">
+			<input type="hidden" name="prefix" value="<?php echo $_POST['prefix'];?>">
+			<input type="submit" value="Download config.php File">
+			</form>
+			<br>
+			<?php
+		}
+
+		//display message to delete install.php file
+		echo "<p class=\"error\">Make sure you go and delete this installer script when you are done! (install.php)</p><br><br>\n";
+		echo "<p class=\"error\">Also, check the permissions and make sure the 'torrents' and 'rss' folders are able to be written to by the server.</p><br><br>\n";
+		echo "<br><center><a href=\"index.php\">Main Statistics Page</a></center>\n";		
+		echo "</body></html>\n";
+	}
+
+?>
+

file:b/login.php (new)
--- /dev/null
+++ b/login.php
@@ -1,1 +1,36 @@
+<?php
+//Login Script
+//Validates Username and Password
+require_once ("config.php");
 
+if ($_POST['legalterms'] != "on")
+{
+	//did not agree to legal terms, go back
+	header("Location: authenticate.php?status=legalterms");
+	exit();
+}
+
+if (md5($_POST['f_user'].$_POST['f_pass']) == $admin_password && $_POST['f_user'] == $admin_username)
+{
+	//successful admin login
+	session_start();
+	$_SESSION['admin_logged_in'] = true;
+	header("Location: admin.php");
+	exit();
+}
+
+if (md5($_POST['f_user'].$_POST['f_pass']) == $upload_password && $_POST['f_user'] == $upload_username)
+{
+	//successful upload login
+	session_start();
+	$_SESSION['upload_logged_in'] = true;
+	header("Location: index.php");
+	exit();
+}
+
+//Username or password was incorrect at this point!
+header("Location: authenticate.php?status=error");
+exit();
+
+?>
+

file:b/newtorrents.php (new)
--- /dev/null
+++ b/newtorrents.php
@@ -1,1 +1,311 @@
-
+<?php
+require_once("config.php");
+require_once("funcsv2.php");
+//Check session
+session_start();
+
+if (!$_SESSION['admin_logged_in'] && !$_SESSION['upload_logged_in'])
+{
+	//check fails
+	header("Location: authenticate.php?status=error");
+	exit();
+}
+?>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+	<title>Add Torrent to Tracker</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+	<link rel="stylesheet" type="text/css" href="./css/style.css" />
+</head>
+<body>
+
+<?php
+$tracker_url = $website_url . substr($_SERVER['PHP_SELF'], 0, -15) . $announceurl;
+
+if (isset($_FILES["torrent"]))
+	addTorrent();
+
+
+endOutput();
+
+	
+function addTorrent()
+{
+	require ("config.php");
+	$tracker_url = $website_url . substr($_SERVER['PHP_SELF'], 0, -15) . $announceurl;
+	
+	$hash = strtolower($_POST["hash"]);
+
+	$db = mysql_connect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Couldn't connect to the database, contact the administrator</p>");
+	mysql_select_db($database) or die(errorMessage() . "Can't open the database.</p>");
+	
+	require_once ("funcsv2.php");
+	require_once ("BDecode.php");
+	require_once ("BEncode.php");
+	
+	if ($_FILES["torrent"]["error"] != 4)	
+	{
+		$fd = fopen($_FILES["torrent"]["tmp_name"], "rb") or die(errorMessage() . "File upload error 1</p>\n");
+		is_uploaded_file($_FILES["torrent"]["tmp_name"]) or die(errorMessage() . "File upload error 2</p>\n");
+		$alltorrent = fread($fd, filesize($_FILES["torrent"]["tmp_name"]));
+
+		$array = BDecode($alltorrent);
+		if (!$array)
+		{
+			echo errorMessage() . "Error: The parser was unable to load your torrent.  Please re-create and re-upload the torrent.</p>\n";
+			endOutput();
+			exit;
+		}		
+
+		if (isset($array["announce-list"])) {
+			//multiple trackers are listed
+			$found_tracker = false;
+			for ($i = 0; $i < count($array["announce-list"]); $i++) {
+				if (strtolower($array["announce-list"][$i][0]) == $tracker_url) {
+					$found_tracker = true;
+					break;
+				}
+			}
+			if ($found_tracker == false)
+			{
+				echo errorMessage() . "Error: Multiple trackers were found but none of them match the
+					announce URL:<br>$tracker_url<br>Please re-create and re-upload the torrent.</p>\n";
+				endOutput();
+				exit;
+			}
+		} else {
+			//a single tracker is listed
+			if (strtolower($array["announce"]) != $tracker_url) {
+				echo errorMessage() . "Error: The tracker announce URL does not match this:<br>$tracker_url<br>Please re-create and re-upload the torrent.</p>\n";
+				endOutput();
+				exit;
+			}
+		}
+		
+		if (isset($_POST["httpseed"]) && $_POST["httpseed"] == "enabled" && $_POST["relative_path"] == "")
+		{
+			echo errorMessage() . "Error: HTTP seeding was checked however no relative path was given.</p>\n";
+			endOutput();
+			exit;
+		}
+		if (isset($_POST["httpseed"]) && $_POST["httpseed"] == "enabled" && $_POST["relative_path"] != "")
+		{
+			if (Substr($_POST["relative_path"], -1) == "/")
+			{
+				if (!is_dir($_POST["relative_path"]))
+				{
+					echo errorMessage() . "Error: HTTP seeding relative path ends in / but is not a valid directory.</p>\n";
+					endOutput();
+					exit;
+				}
+			}
+			else
+			{
+				if (!is_file($_POST["relative_path"]))
+				{
+					echo errorMessage() . "Error: HTTP seeding relative path is not a valid file.</p>\n";
+					endOutput();
+					exit;
+				}
+			}
+		}
+		if (isset($_POST["getrightseed"]) && $_POST["getrightseed"] == "enabled" && $_POST["httpftplocation"] == "")
+		{
+			echo errorMessage() . "Error: GetRight HTTP seeding was checked however no URL was given.</p>\n";
+			endOutput();
+			exit;
+		}
+		if (isset($_POST["getrightseed"]) && $_POST["getrightseed"] == "enabled" &&
+			(Substr($_POST["httpftplocation"], 0, 7) != "http://" && Substr($_POST["httpftplocation"], 0, 6) != "ftp://")
+		)
+		{
+			echo errorMessage() . "Error: GetRight HTTP seeding URL must start with http:// or ftp://</p>\n";
+			endOutput();
+			exit;
+		}
+		$hash = @sha1(BEncode($array["info"]));
+		fclose($fd);
+		
+		$target_path = "torrents/";
+		$target_path = $target_path . basename( clean($_FILES['torrent']['name'])); 
+		$move_torrent = move_uploaded_file($_FILES["torrent"]["tmp_name"], $target_path);
+		if ($move_torrent == false)
+		{
+			echo errorMessage() . "Unable to move " . $_FILES["torrent"]["tmp_name"] . " to torrents/</p>\n";
+		}	
+	}
+	
+
+	if (isset($_POST["title"]))
+		$title = clean($_POST["title"]);
+	else
+		$title = "";
+		
+	if (isset($_POST["filename"]))
+		$filename = clean($_POST["filename"]);
+	else
+		$filename = "";
+	
+	if (isset($_POST["url"]))
+		$url = clean($_POST["url"]);
+	else
+		$url = "";
+
+	if (isset($_POST["autoset"]))
+	if (strcmp($_POST["autoset"], "enabled") == 0)
+	{
+		if (strlen($filename) == 0 && isset($array["info"]["name"]))
+			$filename = $array["info"]["name"];
+	}
+	
+
+	//figure out total size of all files in torrent
+	$info = $array["info"];
+	$total_size = 0;
+	if (isset($info["files"]))
+	{
+		foreach ($info["files"] as $file)
+		{
+			$total_size = $total_size + $file["length"];
+		}
+	}
+	else
+	{
+		$total_size = $info["length"];
+	}
+	
+	//Validate torrent file, make sure everything is correct
+	
+	$filename = mysql_real_escape_string($filename);
+	$filename = stripslashes($filename);
+	$filename = htmlspecialchars(clean($filename));
+	$url = htmlspecialchars(mysql_real_escape_string($url));
+
+	if ((strlen($hash) != 40) || !verifyHash($hash))
+	{
+		echo errorMessage() . "Error: Info hash must be exactly 40 hex bytes.</p>\n";
+		endOutput();
+	}
+
+	if (Substr($url, 0, 7) != "http://" && $url != "")
+	{
+		echo errorMessage() . "Error: The Torrent URL does not start with http:// Make sure you entered a correct URL.</p>\n";
+		endOutput();
+	}
+
+	if ($GLOBALS["customtitle"] == "true")
+	$query = "INSERT INTO ".$prefix."namemap (info_hash, title, filename, url, size, pubDate) VALUES (\"$hash\", \"$title\", \"$filename\", \"$url\", \"$total_size\", \"" . date("$dateformat") . "\")";
+	else $query = "INSERT INTO ".$prefix."namemap (info_hash, title, filename, url, size, pubDate) VALUES (\"$hash\", \"$filename\", \"$filename\", \"$url\", \"$total_size\", \"" . date("$dateformat") . "\")";
+	$status = makeTorrent($hash, true);
+	quickQuery($query);
+	if ($status)
+	{
+		echo "<p class=\"success\">Torrent was added successfully.</p>\n";
+		echo "<a href=\"newtorrents.php\"><img src=\"images/add.png\" border=\"0\" class=\"icon\" alt=\"Add Torrent\" title=\"Add Torrent\" /></a><a href=\"newtorrents.php\">Add Another Torrent</a><br>\n";
+		//rename torrent file to match filename
+		rename("torrents/" . clean($_FILES['torrent']['name']), "torrents/" . $filename . ".torrent");
+		//make torrent file readable by all
+		chmod("torrents/" . $filename . ".torrent", 0644);
+	
+		//run RSS generator
+		require_once("rss_generator.php");
+		//Display information from DumpTorrentCGI.php
+		require_once("torrent_functions.php");
+	}
+	else
+	{
+		echo errorMessage() . "There were some errors. Check if this torrent has been added previously.</p>\n";
+		//delete torrent file if it doesn't exist in database
+		$query = "SELECT COUNT(*) FROM ".$prefix."summary WHERE info_hash = '$hash'";
+		$results = mysql_query($query) or die(errorMessage() . "Can't do SQL query - " . mysql_error() . "</p>");
+		$data = mysql_fetch_row($results);
+		if ($data[0] == 0)
+		{
+			if (file_exists("torrents/" . $_FILES['torrent']['name']))
+				unlink("torrents/" . $_FILES['torrent']['name']);
+		}
+		//make torrent file readable by all
+		chmod("torrents/" . $filename . ".torrent", 0644);
+		endOutput();
+	}
+}
+
+function endOutput() 
+{
+	require ("config.php");
+	$tracker_url = $website_url . substr($_SERVER['PHP_SELF'], 0, -15) . $announceurl;
+	?>
+	<p align="right"><a href="./docs/help.html"><img src="images/help.png" border="0" class="icon" alt="Help" title="Help" /></a><a href="./docs/help.html">Help</a></p>
+	<div class="center">
+	<h1>Add Torrent to Tracker Database</h1>
+	<h3>Tracker URL: <?php echo $tracker_url;?></h3>
+	<form enctype="multipart/form-data" method="post" action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>">
+	<table>
+	<tr>
+		<?php
+		if ($GLOBALS["customtitle"] == "true")
+		echo "<td class=\"right\">Title:</td>
+		<td class=\"left\"><input type=\"title\" name=\"title\" size=\"50\"/></td>";
+		else ($GLOBALS["customtitle"] != "true");
+		?>
+	</tr>
+	<tr>
+		<td class="right">Torrent file:</td>
+		<td class="left"><?php
+		if (function_exists("sha1"))
+			echo "<input type=\"file\" name=\"torrent\" size=\"50\"/>";
+		else
+			echo '<i>File uploading not available - no SHA1 function.</i>';
+		?></td>
+	</tr>
+	<tr><td colspan="2"><hr></td></tr>
+	<tr>	
+	<td class="center" colspan="2"><input type="checkbox" name="httpseed" value="enabled">Use BitTornado HTTP seeding specification (optional)</td>
+	</tr>
+	<tr>
+	<td class="right">Relative location of file or directory:<br>e.g. ../../files/file.zip</td>
+	<td class="left"><input type="text" name="relative_path" size="70"/></td>
+	</tr>
+	<tr><td colspan="2"><hr></td></tr>
+	<tr>
+	<td class="center" colspan="2"><input type="checkbox" name="getrightseed" value="enabled">Use GetRight HTTP seeding specification (optional)</td>
+	</tr>
+	<tr>
+	<td class="right">FTP/HTTP URL of file or directory:<br>e.g. http://yourwebsite.com/file.zip</td>
+	<td class="left"><input type="text" name="httpftplocation" size="70"/></td>
+	</tr>
+	<tr><td colspan="2"><hr></td></tr>
+	<?php if (function_exists("sha1")) 
+		echo "<tr><td class=\"center\" colspan=\"2\"><input type=\"checkbox\" name=\"autoset\" value=\"enabled\" checked=\"checked\" /> Fill in fields below automatically using data from the torrent file.</td></tr>\n";
+	?>
+	<tr>
+		<td class="right">Info Hash:</td>
+		<td class="left"><input type="text" name="hash" size="40"/></td>
+	</tr>
+	<tr>
+		<td class="right">File name (optional): </td>
+		<td class="left"><input type="text" name="filename" size="60" maxlength="200"/></td>
+	</tr>
+	<tr>
+		<td class="right">Torrent's URL (optional): </td>
+		<td class="left"><input type="text" name="url" size="60" maxlength="200"/></td>
+	</tr>
+	<tr><td colspan="2"><hr></td></tr>
+	<tr>
+		<td class="center" colspan="2"><input type="submit" value="Add Torrent to Database"/> - <input type="reset" value="Clear Settings"/></td>
+	</tr>
+	</table>
+	<br>
+	<input type="hidden" name="username" value="<?php echo $_POST['username']; ?>"/>
+	<input type="hidden" name="password" value="<?php echo $_POST['password']; ?>"/>
+	</form>
+	<a href="index.php"><img src="images/stats.png" border="0" class="icon" alt="Tracker Statistics" title="Tracker Statistics" /></a><a href="index.php">Return to Statistics Page</a><br>
+	</div>
+	</body></html>
+	<?php 	
+	// Still in function endOutput()
+	exit;
+}
+?>

file:b/rss/index.php (new)
--- /dev/null
+++ b/rss/index.php
@@ -1,1 +1,5 @@
+<?php
 
+header("Location: ../index.php");
+
+?>

file:b/rss_generator.php (new)
--- /dev/null
+++ b/rss_generator.php
@@ -1,1 +1,68 @@
+<?php
+//re-read config.php file after it has been written
+include ("config.php");
+require_once ("funcsv2.php");
 
+//This script runs whenever:
+//1) a torrent is added to the database
+//2) a torrent is deleted from the database
+//3) the config.php file is edited
+//This is to ensure that the correct rss.xml file is available and generated
+
+//connect to database
+$db = mysql_connect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Cannot connect to database. Check your username and password in the config file.</p>");
+mysql_select_db($database) or die(errorMessage() . "Error selecting database.</p>");
+$query = "SELECT filename,url,size,pubDate FROM ".$prefix."namemap ORDER BY pubDate DESC";
+$results = mysql_query($query) or die(errorMessage() . "Can't do SQL query - " . mysql_error() . "</p>");
+
+//if there are no entries in database or RSS feed is disabled in config.php file, delete rss.xml file
+if (mysql_num_rows($results) == 0 || $enablerss == false)
+{
+	if (file_exists("rss/rss.xml")) //make sure file exists before trying to delete
+		unlink("rss/rss.xml") or die ("Can't delete rss.xml file using unlink().  Are you running the server under Windows?");
+}
+else //otherwise, generate new rss.xml file
+{
+	$fd = fopen("rss/rss.xml", "w") or die(errorMessage() . "Error: Unable to write to rss.xml file!</p>");
+	$start_text = 
+	"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" .
+	"<rss version=\"2.0\">\n" .
+	"<channel>\n" .
+	"<title>" . clean($rss_title) . "</title>\n" .
+	"<link>" . $rss_link . "</link>\n" .
+	"<description>" . clean($rss_description) . "</description>\n" .
+	"<lastBuildDate>" . date('D, j M Y h:i:s') . " " . $timezone . "</lastBuildDate>\n";
+	
+	$middle_text = "";
+	while ($row = mysql_fetch_row($results))
+	{
+		//figure out full torrent URL
+		$url = $website_url . $_SERVER['REQUEST_URI'];
+		$url = str_replace("newtorrents.php", "", $url);
+		$url = str_replace("editconfig.php", "", $url);
+		$url = str_replace("deleter.php", "", $url);
+		$url = $url . "torrents/" . $row[0] . ".torrent";
+		$url = str_replace(" ", "%20", $url);
+		
+		//figure out file(s) size
+		$file_size = bytesToString($row[2]);
+		
+		//go through each entry in database
+		$middle_text = $middle_text . "<item>\n" .
+		"<title>" . $row[0] . " (" . $file_size . ")</title>\n" .
+		"<description>" . $row[0] . " (" . $file_size . ") " . $row[1] . "</description>\n" .
+		"<pubDate>" . $row[3] . " " . $timezone . "</pubDate>\n" .
+		"<guid>" . $url . "</guid>\n" .
+		"<link>" . $url . "</link>\n" .
+		"<enclosure url=\"" . $url . "\" length=\"" . filesize("torrents/" . $row[0] . ".torrent") . "\" type=\"application/x-bittorrent\" />\n" .
+		"</item>\n";
+	}
+	
+	$end_text = "</channel>\n</rss>";
+	
+	fwrite($fd, $start_text . $middle_text . $end_text);
+	fclose($fd);
+	
+}
+
+?>

file:b/sanity.php (new)
--- /dev/null
+++ b/sanity.php
@@ -1,1 +1,219 @@
-
+<?php
+require ("config.php");
+require_once("funcsv2.php");
+//Check session
+session_start();
+
+if (!$_SESSION['admin_logged_in'])
+{
+	//check fails
+	header("Location: authenticate.php?status=session");
+	exit();
+}
+?>
+
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+	<title>Check Tracker for Expired Peers</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+	<link rel="stylesheet" type="text/css" href="./css/style.css" />
+</head>
+<body>
+<h1>Check Tracker for Expired Peers</h1>
+<?php
+
+
+error_reporting(E_ALL);
+//header("Content-Type: text/plain");
+
+//require_once("config.php");
+//require_once("funcsv2.php");
+
+$summaryupdate = array();
+
+// Non-persistant: we lock tables!
+$db = mysql_connect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - ".mysql_error() . "</p>");
+mysql_select_db($database) or die(errorMessage() . "Tracker error: can't open database $database - ".mysql_error() . "</p>");
+
+if (isset($_GET["nolock"]))
+	$locking = false;
+else
+	$locking = true;
+
+// Assumes success
+if ($locking)
+	quickQuery("LOCK TABLES ".$prefix."summary WRITE, ".$prefix."namemap READ");
+
+?>
+<table class="torrentlist" cellspacing="1">
+<!-- Column Headers -->
+<tr>
+	<th>Name/Info Hash</th>
+	<th>Seeders</th>
+	<th>Leechers</th>
+	<th>Bytes Transfered</th>
+	<th>Stale Clients</th>
+	<th>Peer Cache</th>
+</tr>
+<?php
+
+$results = mysql_query("SELECT ".$prefix."summary.info_hash, seeds, leechers, dlbytes, ".$prefix."namemap.filename FROM ".$prefix."summary LEFT JOIN ".$prefix."namemap ON ".$prefix."summary.info_hash = ".$prefix."namemap.info_hash");
+
+$i = 0;
+
+while ($row = mysql_fetch_row($results))
+{
+	$writeout = "row" . $i % 2;
+	list($hash, $seeders, $leechers, $bytes, $filename) = $row;
+	if ($locking)
+	{
+		//peercaching ALWAYS on
+		quickQuery("LOCK TABLES ".$prefix."x$hash WRITE, ".$prefix."y$hash WRITE, ".$prefix."summary WRITE");
+	}
+	$results2 = mysql_query("SELECT status, COUNT(status) from ".$prefix."x$hash GROUP BY status");
+	echo "<tr class=\"$writeout\"><td>";
+	if (!is_null($filename))
+		echo $filename;
+	else
+		echo $hash;
+	echo "</td>";
+	if (!$results2)
+	{
+		echo "<td colspan=\"4\">Unable to process: ".mysql_error()."</td></tr>";
+		continue;
+	}
+
+	$counts = array();
+	while ($row = mysql_fetch_row($results2))
+		$counts[$row[0]] = $row[1];	
+	if (!isset($counts["leecher"]))
+		$counts["leecher"] = 0;
+	if (!isset($counts["seeder"]))
+		$counts["seeder"] = 0;
+
+	if ($counts["seeder"] != $seeders)
+	{
+		quickQuery("UPDATE ".$prefix."summary SET seeds=".$counts["seeder"]." WHERE info_hash=\"$hash\"");
+		echo "<td class=\"center\">$seeders -> ".$counts["seeder"]."</td>";
+	}
+	else
+		echo "<td class=\"center\">$seeders</td>";
+		
+	if ($counts["leecher"] != $leechers)
+	{
+		quickQuery("UPDATE ".$prefix."summary SET leechers=".$counts["leecher"]." WHERE info_hash=\"$hash\"");
+		echo "<td class=\"center\">$leechers -> ".$counts["leecher"]."</td>";
+	}
+	else
+		echo "<td class=\"center\">$leechers</td>";
+		
+	if ($counts["leecher"] == 0)
+	{
+		//If there are no leechers, set the speed to zero
+		quickQuery("UPDATE ".$prefix."summary set speed=0 WHERE info_hash=\"$hash\"");
+	}
+
+	if ($bytes < 0)
+	{
+		quickQuery("UPDATE ".$prefix."summary SET dlbytes=0 WHERE info_hash=\"$hash\"");
+		echo "<td class=\"center\">$bytes -> Zero</td>";
+	}
+	else
+		echo "<td class=\"center\">". round($bytes/1048576/1024,3) ." GB</td>";
+
+	myTrashCollector($hash, $report_interval, time(), $writeout);
+	echo "<td class=\"center\">";
+	
+	$result = mysql_query("SELECT ".$prefix."x$hash.sequence FROM ".$prefix."x$hash LEFT JOIN ".$prefix."y$hash ON ".$prefix."x$hash.sequence = ".$prefix."y$hash.sequence WHERE ".$prefix."y$hash.sequence IS NULL") or die(errorMessage() . "" . mysql_error() . "</p>");
+	if (mysql_num_rows($result) > 0)
+	{
+		echo "Added ", mysql_num_rows($result);
+		$row = array();
+		
+		while ($data = mysql_fetch_row($result))
+				$row[] = "sequence=\"${data[0]}\"";
+		$where = implode(" OR ", $row);
+		$query = mysql_query("SELECT * FROM ".$prefix."x$hash WHERE $where");
+		
+		while ($row = mysql_fetch_assoc($query))
+		{
+			$compact = mysql_real_escape_string(pack('Nn', ip2long($row["ip"]), $row["port"]));
+			$peerid = mysql_real_escape_string('2:ip' . strlen($row["ip"]) . ':' . $row["ip"] . '7:peer id20:' . hex2bin($row["peer_id"]) . "4:porti{$row["port"]}e");
+			$no_peerid = mysql_real_escape_string('2:ip' . strlen($row["ip"]) . ':' . $row["ip"] . "4:porti{$row["port"]}e");
+			mysql_query("INSERT INTO ".$prefix."y$hash SET sequence='{$row["sequence"]}', compact='$compact', with_peerid='$peerid', without_peerid='$no_peerid'");
+		}
+	}	
+	else
+		echo "Added none";
+
+	$result = mysql_query("SELECT ".$prefix."y$hash.sequence FROM ".$prefix."y$hash LEFT JOIN ".$prefix."x$hash ON ".$prefix."y$hash.sequence = ".$prefix."x$hash.sequence WHERE ".$prefix."x$hash.sequence IS NULL");
+	if (mysql_num_rows($result) > 0)
+	{
+		echo ", Deleted ",mysql_num_rows($result);
+
+		$row = array();
+		
+		while ($data = mysql_fetch_row($result))
+			$row[] = "sequence=\"${data[0]}\"";
+		$where = implode(" OR ", $row);
+		$query = mysql_query("DELETE FROM ".$prefix."y$hash WHERE $where");
+	}
+	else
+		echo ", Deleted none";
+
+	echo "</td>";
+	
+	echo "</tr>\n";
+	$i ++;
+
+
+	if ($locking)
+		quickQuery("UNLOCK TABLES");
+		
+	//Repair tables, is this necessary?  Sometimes the tables crash...
+	//Can't repair table if locked?
+	//quickQuery("REPAIR Table x$hash");
+	//quickQuery("REPAIR Table y$hash");
+
+	// Finally, it's time to do stuff to the summary table.
+	if (!empty($summaryupdate))
+	{
+		$stuff = "";
+		foreach ($summaryupdate as $column => $value)
+		{
+			$stuff .= ', '.$column. ($value[1] ? "=" : "=$column+") . $value[0];
+		}
+		mysql_query("UPDATE ".$prefix."summary SET ".substr($stuff, 1)." WHERE info_hash=\"$hash\"");
+		$summaryupdate = array();
+	}
+
+
+}
+
+function myTrashCollector($hash, $timeout, $now, $writeout)
+{
+//	error_log("Trash collector working on $hash");
+ 	require("config.php");
+ 	$peers = loadLostPeers($hash, $timeout);
+ 	for ($i=0; $i < $peers["size"]; $i++)
+	        killPeer($peers[$i]["peer_id"], $hash, $peers[$i]["bytes"], $peers[$i]);
+	if ($i != 0)
+		echo "<td class=\"center\">Removed $i</td>";
+	else
+		echo "<td class=\"center\">Removed 0</td>";
+ 	quickQuery("UPDATE ".$prefix."summary SET lastcycle='$now' WHERE info_hash='$hash'");
+}
+
+
+
+
+?>
+</table>
+<p><a href="sanity.php?nolock=on">Not working? Try running this.</a></p>
+<a href="index.php"><img src="images/stats.png" border="0" class="icon" alt="Tracker Statistics" title="Tracker Statistics" /></a><a href="index.php">Return to Statistics Page</a><br>
+<a href="admin.php"><img src="images/admin.png" border="0" class="icon" alt="Admin Page" title="Admin Page" /></a><a href="admin.php">Return to Admin Page</a>
+</body>
+</html>
+

--- /dev/null
+++ b/sanity_no_output.php
@@ -1,1 +1,126 @@
+<?php
+require_once("config.php");
+require_once("funcsv2.php");
 
+$summaryupdate = array();
+
+// Non-persistant: we lock tables!
+$db = mysql_connect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - ".mysql_error() . "</p>");
+mysql_select_db($database) or die(errorMessage() . "Tracker error: can't open database $database - ".mysql_error() . "</p>");
+
+
+quickQuery("LOCK TABLES ".$prefix."summary WRITE, ".$prefix."namemap READ");
+
+$results = mysql_query("SELECT ".$prefix."summary.info_hash, seeds, leechers, dlbytes, ".$prefix."namemap.filename FROM ".$prefix."summary LEFT JOIN ".$prefix."namemap ON ".$prefix."summary.info_hash = ".$prefix."namemap.info_hash");
+
+$i = 0;
+
+while ($row = mysql_fetch_row($results))
+{
+	$writeout = "row" . $i % 2;
+	list($hash, $seeders, $leechers, $bytes, $filename) = $row;
+	if (isset($locking) && $locking)
+	{
+		//peercaching ALWAYS on
+		quickQuery("LOCK TABLES ".$prefix."x$hash WRITE, ".$prefix."y$hash WRITE, ".$prefix."summary WRITE");
+	}
+	$results2 = mysql_query("SELECT status, COUNT(status) FROM ".$prefix."x$hash GROUP BY status");
+
+	if (!$results2)
+	{
+		//unable to process
+		continue;
+	}
+
+	$counts = array();
+	while ($row = mysql_fetch_row($results2))
+		$counts[$row[0]] = $row[1];	
+	if (!isset($counts["leecher"]))
+		$counts["leecher"] = 0;
+	if (!isset($counts["seeder"]))
+		$counts["seeder"] = 0;
+
+	if ($counts["leecher"] != $leechers)
+		quickQuery("UPDATE ".$prefix."summary SET leechers=".$counts["leecher"]." WHERE info_hash=\"$hash\"");
+
+	if ($counts["seeder"] != $seeders)
+		quickQuery("UPDATE ".$prefix."summary SET seeds=".$counts["seeder"]." WHERE info_hash=\"$hash\"");
+		
+	if ($counts["leecher"] == 0)
+	{
+		//If there are no leechers, set the speed to zero
+		quickQuery("UPDATE ".$prefix."summary set speed=0 WHERE info_hash='$hash'");
+	}
+	
+
+	if ($bytes < 0)
+		quickQuery("UPDATE ".$prefix."summary SET dlbytes=0 WHERE info_hash='$hash'");
+
+	myTrashCollector($hash, $report_interval, time(), $writeout);
+
+	$result = mysql_query("SELECT ".$prefix."x$hash.sequence FROM ".$prefix."x$hash LEFT JOIN ".$prefix."y$hash ON ".$prefix."x$hash.sequence = ".$prefix."y$hash.sequence WHERE ".$prefix."y$hash.sequence IS NULL") or die(errorMessage() . "" . mysql_error() . "</p>");
+	if (mysql_num_rows($result) > 0)
+	{
+		$row = array();
+		
+		while ($data = mysql_fetch_row($result))
+				$row[] = "sequence=\"${data[0]}\"";
+		$where = implode(" OR ", $row);
+		$query = mysql_query("SELECT * FROM ".$prefix."x$hash WHERE $where");
+		
+		while ($row = mysql_fetch_assoc($query))
+		{
+			$compact = mysql_real_escape_string(pack('Nn', ip2long($row["ip"]), $row["port"]));
+			$peerid = mysql_real_escape_string('2:ip' . strlen($row["ip"]) . ':' . $row["ip"] . '7:peer id20:' . hex2bin($row["peer_id"]) . "4:porti{$row["port"]}e");
+			$no_peerid = mysql_real_escape_string('2:ip' . strlen($row["ip"]) . ':' . $row["ip"] . "4:porti{$row["port"]}e");
+			mysql_query("INSERT INTO ".$prefix."y$hash SET sequence='{$row["sequence"]}', compact='$compact', with_peerid='$peerid', without_peerid='$no_peerid'");
+		}
+	}	
+
+	$result = mysql_query("SELECT ".$prefix."y$hash.sequence FROM ".$prefix."y$hash LEFT JOIN ".$prefix."x$hash ON ".$prefix."y$hash.sequence = ".$prefix."x$hash.sequence WHERE ".$prefix."x$hash.sequence IS NULL");
+	if (mysql_num_rows($result) > 0)
+	{
+		$row = array();
+		
+		while ($data = mysql_fetch_row($result))
+			$row[] = "sequence=\"${data[0]}\"";
+		$where = implode(" OR ", $row);
+		$query = mysql_query("DELETE FROM ".$prefix."y$hash WHERE $where");
+	}
+
+
+	$i ++;
+
+	quickQuery("UNLOCK TABLES");
+	
+	//Repair tables, is this necessary?  Sometimes the tables crash...
+	//Can't repair table if locked?
+	//quickQuery("REPAIR Table x$hash");
+	//quickQuery("REPAIR Table y$hash");
+
+	// Finally, it's time to do stuff to the summary table.
+	if (!empty($summaryupdate))
+	{
+		$stuff = "";
+		foreach ($summaryupdate as $column => $value)
+		{
+			$stuff .= ', '.$column. ($value[1] ? "=" : "=$column+") . $value[0];
+		}
+		mysql_query("UPDATE ".$prefix."summary SET ".substr($stuff, 1)." WHERE info_hash=\"$hash\"");
+		$summaryupdate = array();
+	}
+		
+}
+
+
+function myTrashCollector($hash, $timeout, $now, $writeout)
+{
+	require("config.php");
+	$peers = loadLostPeers($hash, $timeout);
+	for ($i=0; $i < $peers["size"]; $i++) {
+	        killPeer($peers[$i]["peer_id"], $hash, $peers[$i]["bytes"], $peers[$i]);
+	}
+ 	quickQuery("UPDATE ".$prefix."summary SET lastcycle='$now' WHERE info_hash='$hash'");
+}
+
+?>

file:b/saveconfig.php (new)
--- /dev/null
+++ b/saveconfig.php
@@ -1,1 +1,13 @@
+<?php
+//takes information from installer.php and creates config.php file
+//allows user to save config.php
 
+header('content-type: application/octet-stream');
+header("Content-Disposition: attachment; filename=\"config.php\"");
+
+print "<?php $config = " . var_export($config, true)  . ";"
+
+
+
+?>
+

file:b/scrape.php (new)
--- /dev/null
+++ b/scrape.php
@@ -1,1 +1,7 @@
+<?php
 
+$_SERVER["PATH_INFO"] = "/scrape";
+require("tracker.php");
+exit;
+
+?>

file:b/seed.php (new)
--- /dev/null
+++ b/seed.php
@@ -1,1 +1,180 @@
+<?php
+//Used for HTTP seeding
+//Requires information in torrent file for client to use
 
+header("Content-Type: text/plain");
+
+//error_log("One");
+if (!isset($_GET["info_hash"]) || !isset($_GET["piece"]))
+	reject("400 Bad Request");
+
+if (get_magic_quotes_gpc())
+	$info_hash=stripslashes($_GET["info_hash"]);
+else
+	$info_hash=$_GET["info_hash"];
+
+$piece = $_GET["piece"];
+//error_log("Two");
+
+if (!is_numeric($piece) || strlen($info_hash) != 20)
+	reject("400 Bad Request");
+
+$info_hash = bin2hex($info_hash);
+
+//error_log("Info hash=$info_hash, piece numnber=$piece");
+
+require_once("config.php");
+
+//change from KB to bytes
+$max_upload_rate = $GLOBALS["max_upload_rate"] * 1024;
+
+function Lock($hash, $time = 0)
+{
+	$results = mysql_query("SELECT GET_LOCK('$hash', $time)");
+   $string = mysql_fetch_row($results);
+   if (strcmp($string[0], "1") == 0)
+   {
+   	//error_log("Got lock $hash");
+   	return true;
+	}
+	//error_log("Failed to lock $hash");
+   return false;
+}
+
+function Unlock($hash)
+{
+        mysql_query("SELECT RELEASE_LOCK('$hash')");
+}
+
+function reject($error = "503 Service Temporarily Unavailable", $message="")
+{
+	header("HTTP/1.0 $error");
+	echo $message;
+	die;
+}
+
+mysql_connect($dbhost, $dbuser, $dbpass) or die;
+mysql_select_db($database) or die;
+
+if (!Lock("WebSeedLock", 2))
+	reject();
+
+$result = mysql_query("SELECT (UNIX_TIMESTAMP() - started) FROM ".$prefix."speedlimit");
+$row = mysql_fetch_row($result);
+
+// If nothing has happened for a little while, do NOT
+// let that average enable massive bursts.
+if ($row[0] > 180)
+	mysql_query("UPDATE ".$prefix."speedlimit SET started=UNIX_TIMESTAMP()-1, total_uploaded=total_uploaded+uploaded, uploaded=0");
+
+$result = mysql_query("SELECT uploaded / (UNIX_TIMESTAMP() - started) FROM ".$prefix."speedlimit");
+$row = mysql_fetch_row($result);
+
+if ((float)($row[0]) > $max_upload_rate)
+{
+	$result = mysql_query("SELECT (uploaded/". $max_upload_rate . "+started) - UNIX_TIMESTAMP() FROM ".$prefix."speedlimit");
+	$row = mysql_fetch_row($result);
+	reject("503 Service Temporarily Unavailable", (int)$row[0] + mt_rand(1,30));
+}
+
+$result = mysql_query("SELECT seeds FROM ".$prefix."summary WHERE info_hash=$info_hash");
+if ($result)
+{
+	//error_log("Doing PHPBT check");
+	$row = mysql_fetch_assoc($result);
+	if ($row["seeds"] > 5) //if there are seeds available, don't use HTTP seeding
+		reject();
+}
+if (mysql_num_rows($result) == 0) //hash isn't even in database!
+{
+	//reject em!
+	reject();
+}
+
+Unlock("WebSeedLock");
+
+// Max uploads check
+for ($lockno=0; $lockno < $GLOBALS["max_uploads"]; $lockno++)
+	if (Lock("WebSeed--$lockno", 0))
+		break;
+//error_log("Lockno=$lockno");
+if ($lockno == $GLOBALS["max_uploads"])
+	reject();
+
+
+// Get to work!
+$result = mysql_query("SELECT ".$prefix."summary.piecelength, ".$prefix."summary.numpieces FROM ".$prefix."summary WHERE info_hash=\"$info_hash\"");
+if (!$result)
+	reject("500 Internal Server Error");
+
+$config = mysql_fetch_assoc($result);
+if (!$config)
+	reject("403 Forbidden");
+
+$result = mysql_query("SELECT * FROM ".$prefix."webseedfiles WHERE info_hash=\"$info_hash\" ORDER BY fileorder");
+
+if ($config["numpieces"] < $piece || $piece < 0)
+	reject("400 Bad Request");
+
+
+// Data to return, and accounting.
+$xmit = "";
+$xmitbytes = 0;
+
+while ($row = mysql_fetch_assoc($result))
+{
+	if (!($piece >= $row["startpiece"] && $piece <= $row["endpiece"]))
+		continue;
+
+	$offset = ($row["startpiece"] == $piece) ? 0 : (($piece - $row["startpiece"])*$config["piecelength"] - $row["startpieceoffset"]);
+	$fd = fopen($row["filename"], "rb") or reject("500 Internal Server Error");
+	if (fseek($fd, $offset) != 0)
+		reject("500 Internal Server Error");
+	$data = fread($fd, $config["piecelength"]-$xmitbytes);
+	if ($data === false)
+		reject("500 Internal Server Error");
+	$xmit .= $data;
+	$xmitbytes += strlen($data);
+	if ($xmitbytes == $config["piecelength"])
+		break;
+	fclose($fd);
+}
+
+
+// Header is most likely already: 200 Ok
+
+//error_log("Send length: $xmitbytes == ".strlen($xmit));
+
+if (isset($_GET["ranges"]))
+{
+	$myxmit = "";
+	$ranges = explode(",", $_GET["ranges"]);
+	foreach ($ranges as $blocks)
+	{
+		$startstop = explode("-", $blocks);
+		if (!is_numeric($startstop[0]) || !is_numeric($startstop[1]))
+			reject("400 Bad Request");
+		if (isset($startstop[2]))
+			reject("400 Bad Request");
+		$start = $startstop[0];
+		$stop = $startstop[1];
+		if ($start > $stop)
+			reject("400 Bad Request");
+		$myxmit .= substr($xmit, $start, $stop-$start+1);
+	}
+	header("Content-Length: ".strlen($myxmit));
+	mysql_query("UPDATE ".$prefix."speedlimit SET uploaded=uploaded+".strlen($myxmit));
+	echo $myxmit;
+}
+else
+{
+	mysql_query("UPDATE ".$prefix."speedlimit SET uploaded=uploaded+$xmitbytes");
+	header("Content-Length: $xmitbytes");
+	echo $xmit;
+}
+
+Unlock("WebSeed--$lockno");
+exit;
+
+?>
+

file:b/sha1lib.php (new)
--- /dev/null
+++ b/sha1lib.php
@@ -1,1 +1,249 @@
+<?php
+/*
+ * A PHP implementation of the Secure Hash Algorithm, SHA-1, as defined
+ * in FIPS PUB 180-1
+ * Adjusted from the Javascript implementation by Joror (daan@parse.nl).
+ *
+ * Javascript Version 2.1 Copyright Paul Johnston 2000 - 2002.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for details.
+ */
 
+class Sha1Lib
+{
+	/*
+	 * Configurable variables. You may need to tweak these to be compatible with
+	 * the server-side, but the defaults work in most cases.
+	 */
+	var $hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
+	var $b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
+	var $chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
+	
+	/*
+	 * These are the functions you'll usually want to call
+	 * They take string arguments and return either hex or base-64 encoded strings
+	 */
+	function hex_sha1($s){return $this->binb2hex($this->core_sha1($this->str2binb($s),strlen($s) * $this->chrsz));}
+	function b64_sha1($s){return $this->binb2b64($this->core_sha1($this->str2binb($s),strlen($s) * $this->chrsz));}
+	function str_sha1($s){return $this->binb2str($this->core_sha1($this->str2binb($s),strlen($s) * $this->chrsz));}
+	function hex_hmac_sha1($key, $data){ return $this->binb2hex($this->core_hmac_sha1($key, $data));}
+	function b64_hmac_sha1($key, $data){ return $this->binb2b64($this->core_hmac_sha1($key, $data));}
+	function str_hmac_sha1($key, $data){ return $this->binb2str($this->core_hmac_sha1($key, $data));}
+	
+	/*
+	 * Perform a simple self-test to see if the VM is working
+	 */
+	function sha1_vm_test()
+	{
+		return $this->hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d";
+	}
+	
+	/*
+	 * Calculate the SHA-1 of an array of big-endian words, and a bit $length
+	 */
+	function core_sha1($x, $len)
+	{
+		/* append padding */
+		$x[$len >> 5] |= 0x80 << (24 - $len % 32);
+		$x[(($len + 64 >> 9) << 4) + 15] = $len;
+	
+		$w = Array();
+		$a =  1732584193;
+		$b = -271733879;
+		$c = -1732584194;
+		$d =  271733878;
+		$e = -1009589776;
+	
+		for($i = 0; $i < sizeof($x); $i += 16)
+		{
+			$olda = $a;
+			$oldb = $b;
+			$oldc = $c;
+			$oldd = $d;
+			$olde = $e;
+	
+			for($j = 0; $j < 80; $j++)
+			{
+				if ($j < 16) 
+					$w[$j] = $x[$i + $j];
+				else 
+					$w[$j] = $this->rol($w[$j-3] ^ $w[$j-8] ^ $w[$j-14] ^ $w[$j-16], 1);
+					
+				$t = $this->safe_add(	$this->safe_add($this->rol($a, 5), $this->sha1_ft($j, $b, $c, $d)), 
+										$this->safe_add($this->safe_add($e, $w[$j]), $this->sha1_kt($j)));
+				$e = $d;
+				$d = $c;
+				$c = $this->rol($b, 30);
+				$b = $a;
+				$a = $t;
+			}
+
+			$a = $this->safe_add($a, $olda);
+			$b = $this->safe_add($b, $oldb);
+			$c = $this->safe_add($c, $oldc);
+			$d = $this->safe_add($d, $oldd);
+			$e = $this->safe_add($e, $olde);
+		}
+		
+		return Array($a, $b, $c, $d, $e);
+	}
+	
+	/*
+	 * Joror: PHP does not have the java(script) >>> operator, so this is a 
+	 * replacement function. Credits to Terium.
+	 */
+	function zerofill_rightshift($a, $b) 
+	{ 
+		$z = hexdec(80000000); 
+		if ($z & $a) 
+		{ 
+			$a >>= 1; 
+			$a &= (~ $z); 
+			$a |= 0x40000000; 
+			$a >>= ($b-1); 
+		} 
+		else 
+		{ 
+			$a >>= $b; 
+		} 
+		return $a; 
+	}
+	
+	/*
+	 * Perform the appropriate triplet combination function for the current
+	 * iteration
+	 */
+	function sha1_ft($t, $b, $c, $d)
+	{
+		if($t < 20) return ($b & $c) | ((~$b) & $d);
+		if($t < 40) return $b ^ $c ^ $d;
+		if($t < 60) return ($b & $c) | ($b & $d) | ($c & $d);
+		return $b ^ $c ^ $d;
+	}
+	
+	/*
+	 * Determine the appropriate additive constant for the current iteration
+	 * Silly php does not understand the inline-if operator well when nested,
+	 * so that's why it's ()ed now.
+	 */
+	function sha1_kt($t)
+	{
+		return ($t < 20) ?  1518500249 : (($t < 40) ?  1859775393 :
+				(($t < 60) ? -1894007588 : -899497514));
+	}  
+	
+	/*
+	 * Calculate the HMAC-SHA1 of a key and some data
+	 */
+	function core_hmac_sha1($key, $data)
+	{
+		$bkey = $this->str2binb($key);
+		if(sizeof($bkey) > 16) $bkey = $this->core_sha1($bkey, sizeof($key) * $this->chrsz);
+	
+		$ipad = Array();
+		$opad = Array();
+		
+		for($i = 0; $i < 16; $i++) 
+		{
+			$ipad[$i] = $bkey[$i] ^ 0x36363636;
+			$opad[$i] = $bkey[$i] ^ 0x5C5C5C5C;
+		}
+	
+		$hash = $this->core_sha1(array_merge($ipad,$this->str2binb($data)), 512 + sizeof($data) * $this->chrsz);
+		return $this->core_sha1(array_merge($opad,$hash), 512 + 160);
+	}
+	
+	/*
+	 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+	 * to work around bugs in some JS interpreters.
+	 */
+	function safe_add($x, $y)
+	{
+		$lsw = ($x & 0xFFFF) + ($y & 0xFFFF);
+		$msw = ($x >> 16) + ($y >> 16) + ($lsw >> 16);
+		return ($msw << 16) | ($lsw & 0xFFFF);
+	}
+	
+	/*
+	 * Bitwise rotate a 32-bit number to the left.
+	 */
+	function rol($num, $cnt)
+	{
+		return ($num << $cnt) | $this->zerofill_rightshift($num, (32 - $cnt));
+	}
+	
+	/*
+	 * Convert an 8-bit or 16-bit string to an array of big-endian words
+	 * In 8-bit function, characters >255 have their hi-byte silently ignored.
+	 */
+	function str2binb($str)
+	{
+		$bin = Array();
+		$mask = (1 << $this->chrsz) - 1;
+		for($i = 0; $i < strlen($str) * $this->chrsz; $i += $this->chrsz)
+			$bin[$i >> 5] |= (ord($str{$i / $this->chrsz}) & $mask) << (24 - $i%32);
+		
+		return $bin;
+	}
+	
+	/*
+	 * Convert an array of big-endian words to a string
+	 */
+	function binb2str($bin)
+	{
+		$str = "";
+		$mask = (1 << $this->chrsz) - 1;
+		for($i = 0; $i < sizeof($bin) * 32; $i += $this->chrsz)
+			$str .= chr($this->zerofill_rightshift($bin[$i>>5], 24 - $i%32) & $mask);
+		return $str;
+	}
+	
+	/*
+	 * Convert an array of big-endian words to a hex string.
+	 */
+	function binb2hex($binarray)
+	{
+		$hex_tab = $this->hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+		$str = "";
+		for($i = 0; $i < sizeof($binarray) * 4; $i++)
+		{
+			$str .= $hex_tab{($binarray[$i>>2] >> ((3 - $i%4)*8+4)) & 0xF} .
+					$hex_tab{($binarray[$i>>2] >> ((3 - $i%4)*8  )) & 0xF};
+		}
+		
+		return $str;
+	}
+	
+	/*
+	 * Convert an array of big-endian words to a base-64 string
+	 */
+	function binb2b64($binarray)
+	{
+		$tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+		$str = "";
+		for($i = 0; i < sizeof($binarray) * 4; $i += 3)
+		{
+			$triplet = 	((($binarray[$i   >> 2] >> 8 * (3 -  $i   %4)) & 0xFF) << 16)
+						| ((($binarray[$i+1 >> 2] >> 8 * (3 - ($i+1)%4)) & 0xFF) << 8 )
+						|  (($binarray[$i+2 >> 2] >> 8 * (3 - ($i+2)%4)) & 0xFF);
+			for($j = 0; $j < 4; $j++)
+			{
+				if($i * 8 + $j * 6 > sizeof($binarray) * 32) $str .= $this->b64pad;
+				else $str .= $tab{($triplet >> 6*(3-j)) & 0x3F};
+			}
+		}
+		return $str;
+	}
+}
+
+if ( !function_exists('sha1') )
+{
+	function sha1( $string, $raw_output = false )
+	{
+		$library = new Sha1Lib();
+		
+		return $raw_output ? $library->str_sha1($string) : $library->hex_sha1($string);
+	}
+}
+?>

file:b/statistics.php (new)
--- /dev/null
+++ b/statistics.php
@@ -1,1 +1,183 @@
+<?php
+require ("config.php");
+require_once ("funcsv2.php");
+//Check session
+session_start();
 
+if (!$_SESSION['admin_logged_in'])
+{
+	//check fails
+	header("Location: authenticate.php?status=session");
+	exit();
+}
+?>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+	<title>Tracker User Statistics</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+	<link rel="stylesheet" href="./css/style.css" type="text/css" />
+</head>
+<body>
+<h1>Tracker User Statistics</h1>
+
+<form action="<?php echo htmlentities($_SERVER['PHP_SELF']);?>" method="POST">
+Filename Search:<input type="text" name="filename_search" size="40"<?php if (isset($_POST["filename_search"]))echo " value=\"" . filterData($_POST["filename_search"]) . "\"";?>>
+<input type="submit" value="Search">
+</form>
+<br>
+
+<?php
+require_once ("config.php");
+require_once ("funcsv2.php");
+
+//connect to database and grab each torrent in database
+if ($GLOBALS["persist"])
+	$db = mysql_pconnect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - " . mysql_error() . "</p>");
+else
+	$db = mysql_connect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - " . mysql_error() . "</p>");
+mysql_select_db($database) or die(errorMessage() . "Tracker error: can't open database $database - " . mysql_error() . "</p>");
+
+//Display search information
+if (isset($_POST["filename_search"]) && $_POST["filename_search"] != "")
+{
+	echo "<h2 align=\"center\">Search Results:</h2>";
+	$query = "SELECT * FROM ".$prefix."summary LEFT JOIN ".$prefix."namemap ON ".$prefix."summary.info_hash = ".$prefix."namemap.info_hash WHERE ".$prefix."namemap.filename REGEXP \"$_POST[filename_search]\" ORDER BY ".$prefix."namemap.filename";
+}
+else //display everything
+{
+	$scriptname = htmlentities($_SERVER['PHP_SELF']) . "?";
+	
+	if (!isset($_GET["activeonly"])) 
+		echo "<a href=\"$scriptname" . "activeonly=yes\">Show only torrents with seeders/leechers</a>\n";
+	else
+	{
+		echo "<a href=\"$scriptname\">Show all torrents</a>\n";
+		$scriptname = $scriptname . "activeonly=yes&";	
+	}
+
+	if (isset($_GET["activeonly"]))
+		$where = " WHERE leechers+seeds > 0";
+	else
+		$where = " ";
+	
+	$query = "SELECT COUNT(*) FROM ".$prefix."summary $where";
+	$results = mysql_query($query);
+	$res = mysql_result($results,0,0);
+	
+	echo "<p align='center'>Page: \n";
+	$count = 0;
+	$page = 1;
+	while($count < $res)
+	{
+		if (isset($_GET["page_number"]) && $page == $_GET["page_number"])
+			echo "<b><a href=\"$scriptname" . "page_number=$page\">($page)</a></b>-\n";
+		else if (!isset($_GET["page_number"]) && $page == 1)
+			echo "<b><a href=\"$scriptname" . "page_number=$page\">($page)</a></b>-\n";
+		else
+			echo "<a href=\"$scriptname" . "page_number=$page\">$page</a>-\n";
+		$page++;
+		$count = $count + ($GLOBALS["statspagelimitspecify"]);
+	}
+	echo "</p>\n";
+	
+	if (!isset($_GET["page_number"]))
+		$query = "SELECT * FROM ".$prefix."summary LEFT JOIN ".$prefix."namemap ON ".$prefix."summary.info_hash = ".$prefix."namemap.info_hash $where ORDER BY ".$prefix."namemap.filename LIMIT 0,${GLOBALS['statspagelimitspecify']}";
+	else
+	{
+		$page_limit = ($_GET["page_number"] - 1) * ($GLOBALS["statspagelimitspecify"]);
+		$query = "SELECT * FROM ".$prefix."summary LEFT JOIN ".$prefix."namemap ON ".$prefix."summary.info_hash = ".$prefix."namemap.info_hash $where ORDER BY ".$prefix."namemap.filename LIMIT $page_limit,${GLOBALS['statspagelimitspecify']}";
+	}
+}
+
+$results = mysql_query($query) or die(errorMessage() . "Can't do SQL query - " . mysql_error() . "</p>");
+
+while ($data = mysql_fetch_row($results))
+{
+	$xhash = "x" . $data[0];
+	$query2 = "SELECT * FROM ".$prefix."$xhash";
+	$results2 = mysql_query($query2) or die(errorMessage() . "Can't do SQL query - " . mysql_error() . "</p>");
+
+	if (mysql_num_rows($results2) == 0 && isset($_GET["activeonly"]))
+		break;
+	else
+	{
+		echo "<hr><table>\n";
+		echo "<tr><th>Info Hash</th><th>Filename</th><th>URL</th><th>File Size</th><th>Publication Date</th></tr>\n";
+		echo "<tr><td>" . $data[0] . "</td><td>" . $data[12] . "</td><td>\n";
+		if (Substr($data[13], 0, 7) == "http://")
+			echo "<a href=\"" . $data[13] . "\">" . $data[13] . "</a>\n";
+		else
+			echo $data[13];
+		echo "</td><td>" . bytesToString($data[14]) . "</td>\n";
+		echo "<td>" . $data[15] . "</td></tr>\n";
+		echo "</table>\n";
+	}
+
+	echo "<table>\n";
+	echo "<tr><th class=\"subheader\">IP Address</th><th class=\"subheader\">Data Left to Download</th><th class=\"subheader\" width=200>Percent Finished</th><th class=\"subheader\">Port</th><th class=\"subheader\">Last Update</th><th class=\"subheader\">NAT User</th></tr>\n";
+	while ($data2 = mysql_fetch_row($results2))
+	{
+		//grab information on each user
+		echo "<tr><td>" . $data2[2] . "</td>\n";
+		echo "<td>" . bytesToString($data2[1]) . "</td>\n";
+
+		//calculate percent done for user
+		$percent_done = 1.00;
+		if ($data2[1] != 0) //only run calculation if they are still downloading
+		{
+			$size_in_bytes = $data[14];
+			if ($size_in_bytes == 0) //thou shalt not divide by zero
+				$percent_done = 0;
+			else
+				$percent_done = round(($size_in_bytes - $data2[1]) / $size_in_bytes, 3);
+		}
+
+		?>
+		<td>
+		<table class="percentages" cellspacing="0">
+		<tr>
+		<td align="right" class="percent" width="<?php echo round($percent_done * 200, 0); ?>" height="15">
+		<?php if ($percent_done > .5) echo $percent_done * 100 . "%"; ?>
+		</td>
+		<td align="left" class="percentleft" width="<?php echo 200 - round($percent_done * 200, 0); ?>" height="15">
+		<?php if ($percent_done <= .5) echo $percent_done * 100 . "%"; ?>		
+		</td>
+		</tr>
+		</table>
+		</td>
+		<?php
+		echo "<td>" . $data2[3] . "</td>\n"; //port
+		echo "<td>" . date('g:ia m-d-Y', $data2[5]) . "</td>\n"; //last time check-in
+		echo "<td>" . $data2[7] . "</td>\n"; //NAT user
+		echo "</tr>\n";
+	}
+	echo "</table><br>\n";
+}
+echo "<hr>";
+if (!isset($_POST["filename_search"]))
+{
+	echo "<p align='center'>Page: \n";
+	$count = 0;
+	$page = 1;
+	while($count < $res)
+	{
+	if (isset($_GET["page_number"]) && $page == $_GET["page_number"])
+		echo "<b><a href=\"$scriptname" . "page_number=$page\">($page)</a></b>-\n";
+	else if (!isset($_GET["page_number"]) && $page == 1)
+		echo "<b><a href=\"$scriptname" . "page_number=$page\">($page)</a></b>-\n";
+	else
+		echo "<a href=\"$scriptname" . "page_number=$page\">$page</a>-\n";
+	$page++;
+	$count = $count + ($GLOBALS["statspagelimitspecify"]);
+	}
+	echo "</p>\n";
+}
+?>
+
+<a href="admin.php"><img src="images/admin.png" border="0" class="icon" alt="Admin Page" title="Admin Page" /></a><a href="admin.php">Return to Admin Page</a>
+</body>
+</html>
+

--- /dev/null
+++ b/torrent_functions.php
@@ -1,1 +1,485 @@
-
+<?php
+
+require_once("BDecode.php");
+require_once("BEncode.php");
+
+function classicoutput($array, $infohash)
+{
+
+	if (isset($array["info"]["pieces"]))
+		$array["info"]["pieces"] = "<i>Checksum data (" . strlen ($array["info"]["pieces"]) / 20 . " pieces)</i>";
+
+	echo "Info hash: <TT>$infohash</TT><br>";
+	echo "<pre>";
+	print_r(cleaner($array));
+	echo "</pre>";
+}
+
+function announceoutput($array)
+{
+	if (!isset($array["peers"][0]))
+	{
+		echo "Not a tracker announce block. Falling back on classic.<br><br>";
+		classicoutput($array, "(Not checked)");
+		exit;
+	}
+	echo "<h2>Client configuration options</h2>";
+	echo "<table border=0 cellpadding=2 cellspacing=2>";
+	foreach ($array as $left => $right)
+	{
+		if ($left == "peers")
+			continue;
+		if (is_array($right))
+			$myright = "<I>Error</I>";
+		else
+			$myright = $right;
+		echo "<tr><td align=right>".$left."</td><td>=</td><td>".$myright."</td></tr>\n";
+	}
+	echo "</table><br><h2>Peers</h2><pre>";
+	foreach ($array["peers"] as $data)
+	{
+		if (!is_array($data)) // special case: [0] == true  means empty list
+		{
+			echo "(Empty results)\n";
+			break;
+		}
+		echo 		bin2hex($data["peer id"])." at ".$data["ip"].":".$data["port"]."\n";
+	}
+	echo "</pre>";
+}
+
+function escapeURL($url)
+{
+	$ret = "";
+	$i=0;
+	while (strlen($url) > $i)
+	{
+		$ret .= "%".$url[$i].$url[$i + 1];
+		$i+=2;
+	}
+	return $ret;
+}
+
+
+function stringcleaner($str)
+{
+	/* WARNING:
+	
+	It appears PHP doesn't handle null bytes in the key portion
+	of string-indexed arrays. $array["abcd\0e"] = $something
+	will find itself with only 4 letters in the key. This may
+	cause some confusion when using /scrape, for example.
+	
+	*/
+
+	$len = strlen($str);
+	for ($i=0; $i < $len; $i ++)
+	{
+		if (ord($str[$i]) < 32 || ord($str[$i]) > 128)
+			return "<B>".bin2hex($str)."</B>";
+	}
+	return $str;
+}
+
+function cleaner($array)
+{
+	if (!is_array($array))
+		return $array;
+	$newarray = array();
+	foreach($array as $left => $right)
+	{
+		if (is_string($left))
+			$newleft = stringcleaner(stripslashes($left));
+		else
+			$newleft = $left;
+
+		if (is_string($right))
+			$newright = stringcleaner($right);
+		else if (is_array($right))
+			$newright = cleaner($right);
+		else
+			$newright = $right;
+
+		$newarray[$newleft] = $newright;
+	}
+	return $newarray;
+}
+
+
+if (isset($_POST["output"]))
+{
+	if (!is_numeric($_POST["output"]))
+		$output = -1;
+	else
+		$output = $_POST["output"];
+	if ($output > 3 || $output < -1)
+		$output = -1;
+
+
+}
+else if (isset($_GET["style"]))
+	$output = $_GET["style"];
+else
+	$output = -1;
+
+//used by index.php to show information on torrent
+if (isset($_POST["hash"]))
+{
+	if (!isset($status)) //not coming from newtorrents page
+	{
+	?>
+	<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+	<html>
+	<head>
+		<title>Torrent Information</title>
+		<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+		<link rel="stylesheet" type="text/css" href="./css/style.css" />
+	</head>
+	<body>
+	<?php
+	}
+	//lookup file
+	require_once("config.php");
+	require_once("funcsv2.php");
+	//connect to DB
+	if ($GLOBALS["persist"])
+		$db = mysql_pconnect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - " . mysql_error() . "</p>");
+	else
+		$db = mysql_connect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - " . mysql_error() . "</p>");
+	mysql_select_db($database) or die(errorMessage() . "Tracker error: can't open database $database - " . mysql_error() . "</p>");
+	$query = "SELECT filename FROM ".$prefix."namemap WHERE info_hash = '" . $_POST["hash"] . "'";
+	$results = mysql_query($query) or die(errorMessage() . "Can't do SQL query - " . mysql_error() . "</p>");
+	$data = mysql_fetch_row($results);
+	//find filename and set it
+	$_FILES["torrent"]["tmp_name"] = "torrents/" . $data[0] . ".torrent";
+	if (!isset($status))
+		echo "<h1>" . $data[0] . "</h1>"; 
+	echo "<a href=\"index.php\"><img src=\"images/stats.png\" border=\"0\" class=\"icon\" alt=\"Tracker Statistics\" title=\"Tracker Statistics\" /></a><a href=\"index.php\">Return to Statistics Page</a><br>\n";
+}
+
+//main displaying and processing
+if (isset($_FILES["torrent"]) || isset($_POST["url"]) || isset($_GET["url"]))
+{
+	if (strlen($_FILES["torrent"]["tmp_name"]) > 0 && file_exists($_FILES["torrent"]["tmp_name"])) //for DumpTorrentCGI.php, and index.php
+	{
+		$fd = fopen($_FILES["torrent"]["tmp_name"], "rb") or die(errorMessage() . "File upload error 1</p>");
+		if (!isset($_POST["hash"]))
+			is_uploaded_file($_FILES["torrent"]["tmp_name"]) or die(errorMessage() . "File upload error 2</p>");
+		$alltorrent = fread($fd, filesize($_FILES["torrent"]["tmp_name"]));
+		fclose($fd);
+	}
+	else if (file_exists("torrents/" . $filename . ".torrent")) //for newtorrents.php
+	{
+		$fd = fopen("torrents/" . $filename . ".torrent", "rb") or die(errorMessage() . "File upload error 1</p>");
+		$alltorrent = fread($fd, filesize("torrents/" . $filename . ".torrent"));
+		fclose($fd);
+	}
+	else if (isset($_POST["url"]))
+	{
+		(strlen($_POST["url"]) > 0) or die(errorMessage() . "Logic error in script.</p>");
+		if (strtolower(substr($_POST["url"], 0, 7)) != "http://")
+			die(errorMessage() . "Error: you must specify \"http://\" as part of the URL.</p>");
+		$fd = fopen($_POST["url"], "rb") or die(errorMessage() . "File download error.</p>");
+		$alltorrent = "";
+		while (!feof($fd))
+		{
+			$alltorrent .= fread($fd, 4096);
+			if (strlen($alltorrent) > 50000)
+				die(errorMessage() . "File too large to download.</p>");
+		}
+		fclose($fd);
+	}
+	else if (isset($_GET["url"]))
+	{
+ 	 	(strlen($_GET["url"]) > 0) or die(errorMessage() . "Logic error in script.</p>");
+ 	 	if (strtolower(substr($_GET["url"], 0, 7)) != "http://")
+ 	 	        die(errorMessage() . "Error: you must specify \"http://\" as part of the URL</p>");
+ 	 	$fd = fopen($_GET["url"], "rb") or die(errorMessage() . "File download error.</p>");
+ 	 	$alltorrent = "";
+ 	 	while (!feof($fd))
+ 	 	{
+ 	 	 	$alltorrent .= fread($fd, 4096);
+ 	 	 	if (strlen($alltorrent) > 50000)
+ 	 	 	        die(errorMessage() . "File too large to download.</p>");
+ 	 	}
+ 	 	fclose($fd);
+
+	}
+	$array = BDecode($alltorrent);
+	if (!isset($array))
+	{
+		echo errorMessage() . "There was an error handling your uploaded torrent. It may be corrupted. Are you sure it's of type .torrent?</p>";
+		exit;
+	}
+
+	if ($array == false)
+	{
+      echo errorMessage() . "There was an error handling your uploaded torrent. It may be corrupted. Are you sure it's of type .torrent?</p>";             
+		exit;
+	}
+
+	// Making torrents look nice: If $array["info"] exists, it is used to calculate
+	// an Info_hash value.
+
+	$infohash = "<I>Not applicable</I>";	
+	if (isset($array["info"]))
+		if (is_array($array["info"]))
+		{
+			if (function_exists("sha1"))
+				$infohash = @sha1(BEncode($array["info"]));
+			else
+				$infohash = "(No SHA1 available to calculate info_hash)</TT><br>";
+			
+			// If the "pieces" section exists, it is replaced by some nice text.
+			// The alternative is pages of garbage.
+		}
+
+	// Auto-detect file type
+	if ($output == -1)
+	{
+		if (isset($array["announce"]) && isset($array["info"]))
+			$output = 1;
+		else if (isset($array["files"]))
+			$output = 2;
+		else if (isset($array["peers"]))
+			$output = 3;
+		else
+			$output = 0;
+	}
+
+	// Output information.
+	if ($output == 0)
+	{
+		classicoutput($array, $infohash);
+	}
+
+	if ($output == 1)
+	{
+		if (!isset($array["info"]))
+		{
+		 	echo "Error: not a torrent file. Falling back on classic.<br><br>";
+
+		 	classicoutput($array, "<I>Not applicable</I>");
+		 	exit;	                
+		}
+
+		echo "<br><h2>Non-file data:</h2>\n";
+		echo "<table border=0 cellpadding=2 cellspacing=2><tr>";
+		echo "<td align=right>Info hash</td><td>=</td><td><TT>$infohash</TT></td></tr>\n";
+		echo "<tr><td align=right>Announce URL(s)</td><td>=</td><td>";
+		if (isset($array["announce-list"])) {
+			for ($i = 0; $i < count($array["announce-list"]); $i++) {
+				echo $array["announce-list"][$i][0] . "<br>";
+			}
+		} else {
+			//single tracker
+			echo $array["announce"];
+		}
+		echo "</td></tr>\n";
+
+		if (isset($array["creation date"]))
+		{
+			echo "<tr><td align=right>Creation date</td><td>=</td><td>";
+			if (is_numeric($array["creation date"]))
+				echo date("F j, Y", $array["creation date"]);
+			else
+				echo $array["creation date"];
+			echo "</td></tr>";
+		}
+		if ($array["info"]["private"] == 1)
+			echo "<tr><td align=right>Private (No DHT Allowed)</td><td>=</td><td>yes</td></tr>\n";
+		else
+			echo "<tr><td align=right>Private (No DHT Allowed)</td><td>=</td><td>no</td></tr>\n";
+
+		foreach ($array as $left => $right)
+		{
+			if ($left == "announce" || $left == "info" || $left == "creation date" || $left == "announce-list")
+				continue; // skip
+			if ($left == "url-list" || $left == "httpseeds")
+			{
+				echo "<tr><td align=right>$left</td><td>=</td><td>";
+				print_r(cleaner($array[$left]));
+				echo "</td></tr>\n";
+				continue;
+			}
+			echo "<tr><td align=right>$left</td><td>=</td><td>".$array[$left]."</td></tr>\n";
+		}
+		
+		echo "</table><br><br><h2>File data:</h2><pre>";
+		$info = $array["info"];
+		
+		$total_size = 0;
+		if (isset($info["files"]))
+		{
+			echo "Directory: ".$info["name"]."\nFiles:\n";
+			foreach ($info["files"] as $file)
+			{
+				if (isset($file["path"][1]))
+				{
+					echo "    " . $file["path"][0];
+					for ($i=1; isset($file["path"][$i]); $i++)
+						echo "/".$file["path"][$i];
+				}
+				else
+					echo "    " . $file["path"][0];
+				echo "  (".$file["length"]." bytes)\n";
+				$total_size = $total_size + $file["length"];
+			}
+			echo "\n";
+		}
+		else
+		{
+			echo "File: ".$info["name"]. " (".$info["length"]." bytes)\n\n";
+			$total_size = $info["length"];
+		}
+		
+		echo "Piece length: ".$info["piece length"]."\nNumber of pieces: ". strlen ($array["info"]["pieces"])/20 . "\n\n";
+		if ($total_size < 1024) //dealing with bytes
+			echo "Total Size: " . $total_size . " bytes</pre>\n";
+		elseif ($total_size < 1048576) //dealing with kilobytes
+			echo "Total Size: " . round($total_size/1024, 2) . " kilobytes</pre>\n";
+		elseif ($total_size < 1073741824) //dealing with megabytes
+			echo "Total Size: " . round($total_size/1048576, 2) . " megabytes</pre>\n";
+		elseif ($total_size >= 1073741824) //dealing with gigabytes
+			echo "Total Size: " . round($total_size/1073741824, 2) . " gigabytes</pre>\n";
+	}
+
+	if ($output == 2)
+	{
+		if (!isset($array["files"]))
+		{
+			echo "Error: not /scrape data. Falling back on classic.<br><br>";
+			classicoutput($array, $infohash);
+			exit;		
+		}
+		$files = $array["files"];
+		
+		// Copy and paste from python tracker output, with some 
+		// formatting changes
+		echo '<table cellpadding=2 cellspacing=2 border=1 summary="files"><tr><th>info hash</th><th align="right">complete</th><th align="right">downloading</th><th>finished downloads</th><th>file name</th></tr>';
+		
+				
+		foreach ($files as $hash => $data)
+		{
+			echo "<tr><td><TT>".bin2hex(stripslashes($hash))."</TT>";
+			echo "</td><td>".$data["complete"]."</td><td>".$data["incomplete"]."</td><td>";
+			if (isset($data["downloaded"]))
+				echo $data["downloaded"];
+			else
+				echo "-";
+			echo "</td><td>";
+			if (isset($data["name"]))
+				echo $data["name"];
+			else
+				echo "(unavailable)";
+			echo "</td></tr>";
+		}
+		echo "</table>";
+	}
+
+	// http://tracker.com:6969/announce
+	if ($output == 3)
+	{
+		announceoutput($array);
+	}
+
+	if (isset($filename) && file_exists("torrents/" . $filename . ".torrent")) //for newtorrents.php
+	{
+		echo "<a href=\"newtorrents.php\"><img src=\"images/add.png\" border=\"0\" class=\"icon\" alt=\"Add Torrent\" title=\"Add Torrent\" /></a><a href=\"newtorrents.php\">Add Another Torrent</a><br>\n";
+		//add in Bittornado HTTP seeding spec
+		if (isset($_POST["httpseed"]) && $_POST["httpseed"] == "enabled")
+		{
+			//add information into database
+			$info = $array["info"] or die("Invalid torrent file.");
+			
+			$fsbase = $_POST["relative_path"];
+			
+			if (isset($info["files"])) // Multi-file
+			{
+				if (substr($fsbase, -1) != '/')
+					$fsbase .= '/';
+				$pieceno = 0;
+				$fileno = 0;
+				$piecelen = 0;
+				
+				// Iterate for each file.
+				while (isset($info["files"][$fileno]))
+				{
+					if ($piecelen == $info["piece length"])
+					{
+						$pieceno++;
+						$piecelen = 0;
+					}
+					$startoffset = $piecelen;
+					$startpiece = $pieceno;
+					$filesize = $info["files"][$fileno]["length"];
+					while (true)
+					{
+						$sub = min($info["piece length"]-$piecelen, $filesize);
+						$piecelen += $sub;
+						$filesize -= $sub;
+						
+						if ($filesize == 0)
+							break;
+						if ($piecelen == $info["piece length"])
+						{
+							$pieceno++;
+							$piecelen = 0;
+						}
+						if ($piecelen > $info["piece length"])
+							die("Logic error in script. Please report to the author.");
+					}
+					$filename = $fsbase;
+					if (isset($info["files"][$fileno]["path"][1]))
+					{
+						$filename .= $file["path"][0];
+						for ($i=1; isset($info["files"][$fileno]["path"][$i]); $i++)
+							$filename .= "/".$info["files"][$fileno]["path"][$i];
+					}
+					else
+						$filename .= $info["files"][$fileno]["path"][0];
+					$filename = mysql_real_escape_string($filename);			
+					mysql_query("INSERT INTO ".$prefix."webseedfiles (info_hash,filename,startpiece,endpiece,startpieceoffset,fileorder) values (\"$hash\", \"$filename\", $startpiece, $pieceno, $startoffset, $fileno)");
+					$fileno++;
+				}
+			} // end of multi-file section
+			else //single file
+				mysql_query("INSERT INTO ".$prefix."webseedfiles (info_hash,filename,startpiece,endpiece,startpieceoffset,fileorder) values (\"$hash\", \"".mysql_real_escape_string($fsbase)."\", 0, ". (strlen($array["info"]["pieces"])/20 - 1).", 0, 0)");
+		}
+		
+		if ((isset($_POST["getrightseed"]) && $_POST["getrightseed"] == "enabled") || (isset($_POST["httpseed"]) && $_POST["httpseed"] == "enabled")) //only do one write
+		{
+			//edit torrent file
+			$read_httpseed = fopen("torrents/" . $filename . ".torrent", "rb");
+			$binary_data = fread($read_httpseed, filesize("torrents/" . $filename . ".torrent"));
+			$data_array = BDecode($binary_data);
+			
+			if ($_POST["httpseed"] == "enabled")
+				$data_array["httpseeds"][0] = $website_url . substr($_SERVER['REQUEST_URI'], 0, -15) . "seed.php";
+			if ($_POST["getrightseed"] == "enabled")
+				$data_array["url-list"][0] = $_POST["httpftplocation"];
+				
+			$to_write = BEncode($data_array);
+			fclose($read_httpseed);
+			//write torrent file
+			$write_httpseed = fopen("torrents/" . $filename . ".torrent", "wb");
+			fwrite($write_httpseed, $to_write);
+			fclose($write_httpseed);
+		}
+		
+		//add in piecelength and number of pieces
+		$query = "UPDATE ".$prefix."summary SET piecelength=\"" . $info["piece length"] . "\", numpieces=\"" . strlen ($array["info"]["pieces"])/20 . "\" WHERE info_hash=\"" . $hash . "\"";
+		quickQuery($query);
+	}
+	
+	if (!isset($_POST["hash"])) //don't display admin link if coming from index.php
+		echo "<a href='admin.php'><img src='images/admin.png' border='0' class='icon' alt='Admin Page' title='Admin Page' /></a><a href='admin.php'>Return to Admin Page</a><br>";
+	?>
+	<a href="index.php"><img src="images/stats.png" border="0" class="icon" alt="Tracker Statistics" title="Tracker Statistics" /></a><a href="index.php">Return to Statistics Page</a><br>
+	</body></html>
+	<?php
+	exit();
+}
+
+?>
+

--- /dev/null
+++ b/torrents/index.php
@@ -1,1 +1,5 @@
+<?php
 
+header("Location: ../index.php");
+
+?>

file:b/tracker.php (new)
--- /dev/null
+++ b/tracker.php
@@ -1,1 +1,381 @@
-
+<?php
+
+header("Content-type: text/plain");
+header("Pragma: no-cache");
+
+ignore_user_abort(1);
+
+$GLOBALS["peer_id"] = "";
+$summaryupdate = array();
+
+require_once("config.php");
+require_once("funcsv2.php");
+
+
+// Prep database
+if ($GLOBALS["persist"])
+	$db = @mysql_pconnect($dbhost, $dbuser, $dbpass) or showError("Tracker error: can't connect to database. Contact the webmaster.");
+else
+	$db = @mysql_connect($dbhost, $dbuser, $dbpass) or showError("Tracker error: can't connect to database. Contact the webmaster.");
+@mysql_select_db($database) or showError("Tracker error: can't open database. Contact the webmaster");
+
+
+if (isset ($_SERVER["PATH_INFO"]) )
+{
+	// Scrape interface
+
+// Error: no web browsers allowed
+	if (!isset($_GET["info_hash"]))
+	{
+		header("HTTP/1.0 400 Bad Request");
+		die("This file is for BitTorrent clients.\n");
+	}
+
+// Deny access made with a browser...
+$agent = mysql_real_escape_string($_SERVER["HTTP_USER_AGENT"]);
+
+if (preg_match("/^Mozilla|^Opera|^Links|^Lynx/i", $agent))
+{
+    header("HTTP/1.0 400 Bad Request");
+    die("This file is for BitTorrent clients.\n");
+}
+
+	if (substr($_SERVER["PATH_INFO"],-7) == '/scrape')
+	{
+		if ($scrape == true)
+		{
+			$usehash = false;
+			if (isset($_GET["info_hash"]))
+			{
+				if (get_magic_quotes_gpc())
+					$info_hash = stripslashes(trim(strip_tags($_GET["info_hash"])));
+				else
+					$info_hash = trim(strip_tags($_GET["info_hash"]));
+				if (strlen($info_hash) == 20)
+					$info_hash = filterChar(bin2hex($info_hash));
+				else if (strlen($info_hash) == 40)
+					filterInt(verifyHash($info_hash)) or showError("Invalid info hash value.");
+				else
+					showError("Invalid info hash value.");
+				$usehash = true;
+			}
+			if ($usehash)
+				$query = mysql_query("SELECT info_hash, filename FROM ".$prefix."namemap WHERE info_hash='$info_hash'");
+			else
+				$query = mysql_query("SELECT info_hash, filename FROM ".$prefix."namemap");
+			$namemap = array();
+			while ($row = mysql_fetch_row($query))
+				$namemap[$row[0]] = $row[1];
+	
+			if ($usehash)
+				$query = mysql_query("SELECT info_hash, seeds, leechers, finished FROM ".$prefix."summary WHERE info_hash='$info_hash'") or showError("Database error. Cannot complete request.");
+			else
+				$query = mysql_query("SELECT info_hash, seeds, leechers, finished FROM ".$prefix."summary ORDER BY info_hash") or showError("Database error. Cannot complete request.");
+
+			echo "d5:filesd";
+
+			while ($row = mysql_fetch_row($query))
+			{
+				$hash = hex2bin($row[0]);
+				echo "20:".$hash."d";
+				echo "8:completei".$row[1]."e";
+				echo "10:downloadedi".$row[3]."e";
+				echo "10:incompletei".$row[2]."e";
+				if (isset($namemap[$row[0]]))
+					echo "4:name".strlen($namemap[$row[0]]).":".$namemap[$row[0]];
+				echo "e";
+			}
+
+			echo "ee";
+			exit();
+		}
+		else
+			//client tried scraping but scraping has been disabled by the tracker
+			showError("Scraping has been disabled by this tracker.");
+	}
+}
+
+
+///////////////////////////////////////////////////////////////////
+// Handling of parameters from the URL and other setup
+
+
+// Error: no web browsers allowed
+if (!isset($_GET["info_hash"]) || !isset($_GET["peer_id"]))
+{
+	header("HTTP/1.0 400 Bad Request");
+	die("This file is for BitTorrent clients.\n");
+}
+$agent = mysql_real_escape_string($_SERVER["HTTP_USER_AGENT"]);
+// Deny access made with a browser...
+
+if (preg_match("/^Mozilla|^Opera|^Links|^Lynx/i", $agent))
+{
+    header("HTTP/1.0 400 Bad Request");
+    die("This file is for BitTorrent clients.\n");
+}
+
+
+$info_hash = bin2hex(clean($_GET["info_hash"]));
+$peer_id = filterChar(bin2hex($_GET["peer_id"]));
+
+
+if (!isset($_GET["port"]) || !isset($_GET["downloaded"]) || !isset($_GET["uploaded"]) || !isset($_GET["left"])) {
+	showError("Invalid information received from BitTorrent client");
+}
+
+$port = filterInt($_GET["port"]);
+$ip = filterFloat(str_replace("::ffff:", "", $_SERVER["REMOTE_ADDR"]));
+$downloaded = filterFloat($_GET["downloaded"]);
+$uploaded = filterFloat($_GET["uploaded"]);
+$left = filterFloat($_GET["left"]);
+
+
+if (isset($_GET["event"]))
+	$event = filterData($_GET["event"]);
+else
+	$event = "";
+
+if (!isset($GLOBALS["ip_override"]))
+	$GLOBALS["ip_override"] = true;
+
+if (isset($_GET["numwant"]))
+	if ($_GET["numwant"] < $GLOBALS["maxpeers"] && $_GET["numwant"] >= 0)
+		$GLOBALS["maxpeers"] = filterFloat($_GET["numwant"]);
+
+if (isset($_GET["trackerid"]))
+{	
+	if (is_numeric($_GET["trackerid"]))
+		$GLOBALS["trackerid"] = filterInt($_GET["trackerid"]);
+}
+if (!is_numeric($port) || !is_numeric($downloaded) || !is_numeric($uploaded) || !is_numeric($left))
+	showError("Invalid numerical field(s) from client");
+
+
+
+/////////////////////////////////////////////////////
+// Any section of code might need to make a new peer, so this is a function here.
+// I don't want to put it into funcsv2, even though it should, just for consistency's sake.
+
+function start($info_hash, $ip, $port, $peer_id, $left)
+{
+	require("config.php"); //need prefix value...
+	if (isset($_SERVER["HTTP_X_FORWARDED_FOR"]))
+	{
+      foreach(explode(",",$_SERVER["HTTP_X_FORWARDED_FOR"]) as $address)
+      {
+		$addr = ip2long(trim($address));
+		if ($addr != -1)
+		{
+			if ($addr >= -1062731776 && $addr <= -1062666241)
+			{
+				// 192.168.x.x
+			}
+			else if ($addr >= -1442971648 && $addr <= -1442906113)
+			{
+				// 169.254.x.x
+			}
+			else if ($addr >= 167772160 && $addr <= 184549375)
+			{
+				// 10.x.x.x
+			}
+			else if ($addr >= 2130706432 && $addr <= 2147483647)
+			{
+				// 127.0.0.1
+			}
+			else if ($addr >= -1408237568 && $addr <= -1407188993)
+			{
+				// 172.[16-31].x.x
+			}
+			else
+			{
+				// Finally, we can accept it as a "real" ip address.
+				$ip = mysql_real_escape_string(trim($address));
+				break;
+			}
+		}
+	  }
+	}
+
+	if (isset($_GET["ip"]) && $GLOBALS["ip_override"])
+	{
+		// compact check: valid IP address:
+		if (ip2long($_GET["ip"]) == -1)
+			showError("Invalid IP address. Must be standard dotted decimal (hostnames not allowed)");
+		$ip = filterFloat($_GET["ip"]);
+	}
+
+	if ($left == 0)
+		$status = "seeder";
+	else
+		$status = "leecher";
+	if (@isFireWalled($info_hash, $peer_id, $ip, $port))
+		$nat = "'Y'";
+	else
+		$nat = "'N'";
+	
+	$results = @mysql_query("INSERT INTO ".$prefix."x$info_hash SET peer_id='$peer_id', port='$port', ip='$ip', lastupdate=UNIX_TIMESTAMP(), bytes='$left', status='$status', natuser=$nat");
+
+	// Special case: duplicated peer_id. 
+	if (!$results)
+	{
+		$error = mysql_error();
+		if (stristr($error, "key"))
+		{
+			// Duplicate peer_id! Check IP address
+			$peer = getPeerInfo($peer_id, $info_hash);
+			if ($ip == $peer["ip"])
+			{
+				// Same IP address. Tolerate this error.
+				return "WHERE natuser='N'";
+			}
+			//showError("Duplicated peer_id or changed IP address. Please restart BitTorrent.");
+			// Different IP address. Assume they were disconnected, and alter the IP address.
+			quickQuery("UPDATE ".$prefix."x$info_hash SET ip='$ip' WHERE peer_id='$peer_id'");
+			return "WHERE natuser='N'";
+		}
+		error_log("RivetTracker: start: ".$error);
+		showError("Tracker/database error. The details are in the error log.");
+	}
+	$GLOBALS["trackerid"] = mysql_insert_id();
+
+	$compact = mysql_real_escape_string(pack('Nn', ip2long($ip), $port));
+	$peerid = mysql_real_escape_string('2:ip' . strlen($ip) . ':' . $ip . '7:peer id20:' . hex2bin($peer_id) . "4:porti{$port}e");
+	$no_peerid = mysql_real_escape_string('2:ip' . strlen($ip) . ':' . $ip . "4:porti{$port}e");
+	@mysql_query("INSERT INTO ".$prefix."y$info_hash SET sequence='{$GLOBALS["trackerid"]}', compact='$compact', with_peerid='$peerid', without_peerid='$no_peerid'");
+
+	if ($left == 0)
+	{
+		summaryAdd("seeds", 1);
+		return "WHERE status='leecher' AND natuser='N'";
+	}
+	else
+	{
+		summaryAdd("leechers", 1);
+		return "WHERE natuser='N'";
+	}
+}
+
+// End of function start
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// Actual work. Depends on value of $event. (Missing event is mapped to '' above)
+
+if ($event == '')
+{
+	verifyTorrent($info_hash) or evilReject($ip, $peer_id,$port);
+	$peer_exists = getPeerInfo($peer_id, $info_hash);
+	$where = "WHERE natuser='N'";
+
+	if (!is_array($peer_exists))
+		$where = start($info_hash, $ip, $port, $peer_id, $left);
+
+	if ($peer_exists["bytes"] != 0 && $left == 0)
+	{
+
+		quickQuery("UPDATE ".$prefix."x$info_hash SET bytes=0, status='seeder' WHERE sequence='${GLOBALS["trackerid"]}'");
+		if (mysql_affected_rows() == 1)
+		{
+			summaryAdd("leechers", -1);
+			summaryAdd("seeds", 1);
+			summaryAdd("finished", 1);
+		}
+	}
+	collectBytes($peer_exists, $info_hash, $left);
+	sendRandomPeers($info_hash);
+}
+else if ($event == "started")
+{
+	verifyTorrent($info_hash) or evilReject($ip, $peer_id,$port);
+
+	$start = start($info_hash, $ip, $port, $peer_id, $left);
+	
+	// Don't send the tracker id for newly started clients. Send it next time. Make sure
+	// they get a good random list of peers to begin with.
+	sendRandomPeers($info_hash);
+}
+else if ($event == "stopped")
+{
+	verifyTorrent($info_hash) or evilReject($ip, $peer_id,$port);
+	killPeer($peer_id, $info_hash, $left);	
+
+	// I don't know why, but the real tracker returns peers on event=stopped
+	// but I'll just send an empty list. On the other hand, 
+	// TheSHADOW asked for this.
+	if (isset($_GET["tracker"]))
+		$peers = getRandomPeers($info_hash);
+	else
+		$peers = array("size" => 0);
+
+	sendPeerList($peers);
+}
+else if ($event == "completed") // now the same as an empty string
+{
+	verifyTorrent($info_hash) or evilReject($ip, $peer_id,$port);
+	$peer_exists = getPeerInfo($peer_id, $info_hash);
+
+	if (!is_array($peer_exists))
+		start($info_hash, $ip, $port, $peer_id, $left);
+	else
+	{
+		quickQuery("UPDATE ".$prefix."x$info_hash SET bytes=0, status='seeder' WHERE sequence='${GLOBALS["trackerid"]}'");
+
+		// Race check
+		if (mysql_affected_rows() == 1)
+		{
+			summaryAdd("leechers", -1);
+			summaryAdd("seeds", 1);
+			summaryAdd("finished", 1);
+		}
+	}
+	collectBytes($peer_exists, $info_hash, $left);
+	$peers=getRandomPeers($info_hash);
+
+	sendPeerList($peers);
+
+}
+else
+	showError("Invalid event= from client.");
+
+
+if ($GLOBALS["countbytes"])
+{
+	// Once every minute or so, we run the speed update checker.
+	// This is still not very accurate... :/
+	//@ symbol suppresses errors
+	$query = @mysql_query("SELECT UNIX_TIMESTAMP() - lastSpeedCycle FROM ".$prefix."summary WHERE info_hash='$info_hash'");
+	$results = mysql_fetch_row($query);
+	if ($results[0] >= 60 || $event == "completed")
+	{
+		if (Lock("SPEED:$info_hash"))
+		{
+			@runSpeed($info_hash, $results[0]);
+			Unlock("SPEED:$info_hash");
+		}
+	}
+}
+
+
+
+/* 
+ * Under heavy loads, this will lighten the load slightly... very slightly...
+ */
+//if (mt_rand(1,10) == 4)
+  trashCollector($info_hash, $report_interval);
+
+
+
+// Finally, it's time to do stuff to the summary table.
+if (!empty($summaryupdate))
+{
+	$stuff = "";
+	foreach ($summaryupdate as $column => $value)
+	{
+		$stuff .= ', '.$column. ($value[1] ? "=" : "=$column+") . $value[0];
+	}
+	mysql_query("UPDATE ".$prefix."summary SET ".substr($stuff, 1)." WHERE info_hash='$info_hash'");
+}
+
+?>

file:b/uploadstats.php (new)
--- /dev/null
+++ b/uploadstats.php
@@ -1,1 +1,94 @@
+<?php
 
+require ("config.php");
+require ("funcsv2.php");
+//Check session
+session_start();
+
+if (!$_SESSION['admin_logged_in'])
+{
+	//check fails
+	header("Location: authenticate.php?status=session");
+	exit();
+}
+?>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+	<title>Upload Statistics</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+	<link rel="stylesheet" href="./css/style.css" type="text/css" />
+</head>
+<body>
+<h1>Upload Statistics</h1>
+<h2>This may be wildly inaccurate because when torrents are deleted, the bittorrent traffic is removed yet the HTTP traffic stays the same.</h2>
+<?php
+if ($GLOBALS["persist"])
+	$db = mysql_pconnect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - " . mysql_error() . "</p>");
+else
+	$db = mysql_connect($dbhost, $dbuser, $dbpass) or die(errorMessage() . "Tracker error: can't connect to database - " . mysql_error() . "</p>");
+mysql_select_db($database) or die(errorMessage() . "Tracker error: can't open database $database - " . mysql_error() . "</p>");
+
+$query = "SELECT SUM(".$prefix."summary.dlbytes) FROM ".$prefix."summary";
+$results = mysql_query($query) or die(errorMessage() . "Can't do SQL query - " . mysql_error() . "</p>");
+$data = mysql_fetch_row($results);
+if ($data[0] == null)
+	$btuploaded = 0;
+else
+	$btuploaded = $data[0];
+	
+$query = "SELECT total_uploaded FROM ".$prefix."speedlimit";
+$results = mysql_query($query) or die(errorMessage() . "Can't do SQL query - " . mysql_error() . "</p>");
+$data = mysql_fetch_row($results);
+$httpuploaded = $data[0];
+?>
+<br>
+<center>
+<table>
+<tr><th>HTTP Seeding Uploaded<span class="notice">*</span></th>
+<th>Bittorrent P2P Seeding Uploaded</th></tr>
+<tr>
+<td align="center">
+<?php
+echo bytesToString($httpuploaded);
+?>
+</td>
+<td align="center">
+<?php
+echo bytesToString($btuploaded);
+?>
+</td>
+</tr>
+<tr>
+<td align="center">
+<?php
+if ($httpuploaded + $btuploaded != 0)
+	echo round(($httpuploaded / ($httpuploaded + $btuploaded))*100, 2) . "%";
+else
+	echo "0%";
+?>
+</td>
+<td align="center">
+<?php
+if ($httpuploaded + $btuploaded != 0)
+	echo round(($btuploaded / ($httpuploaded + $btuploaded))*100, 2) . "%";
+else
+	echo "0%";
+?>
+</td>
+</tr>
+</table>
+</center>
+<p align="center">
+<?php
+echo "Total Uploaded: " . bytesToString($httpuploaded + $btuploaded);
+?>
+</p>
+<br>
+<span class="notice">* - This does not include the GetRight HTTP seeding format which links directly to files.</span>
+<br><br>
+<a href="admin.php"><img src="images/admin.png" border="0" class="icon" alt="Admin Page" title="Admin Page" /></a><a href="admin.php">Return to Admin Page</a>
+</body>
+</html>

file:b/version.php (new)
--- /dev/null
+++ b/version.php
@@ -1,1 +1,3 @@
-
+<?php
+echo "Version: 1.04";
+?>