<?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();
	}
}

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($_GET["info_hash"]);
				else
					$info_hash = $_GET["info_hash"];
				if (strlen(htmlentities($_GET['hash'], ENT_QUOTES, UTF-8)) == 20)
					$info_hash = bin2hex($info_hash);
				else if (strlen(htmlentities($_GET['hash'], ENT_QUOTES, UTF-8)) == 40)
					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 = filterChar(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 (preg_match("/^[a-f]+$/i", $ip)) { // Disable compact announce with IPv6
//		$compact = 0;
//	}

	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 = filterChar($_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'");
}

?>