<?php
require_once("common.php");
if ($_SERVER["REQUEST_METHOD"] !== "POST")
{
	header("Content-Type: text/html; charset=UTF-8");
	die("<h1>water you doing");
}

$data = json_decode(file_get_contents("php://input"), true);

@mkdir("fsum", 0711);
@mkdir("part", 0711);
@mkdir("hist", 0711);

//
// rudimentary scan for bad data

foreach ($data["hash"] as &$hash)
	if (sanitize_hash($hash) !== $hash)
		die('kek');

if (isset($data['wark']))
	if (sanitize_hash($data['wark']) !== $data['wark'])
		die('top kek');

//
// create a salted copy of the data
// and use that to generate a wark, a public session id
// which is reproducible with the same filename/chunksize

$salt = "bhufesbgyrvburegbreguro";
$wark = $salt ."\n". $data["name"] ."\n". $data["size"];
foreach ($data["hash"] as &$hash)
	$wark .= "\n" . $hash;

// print("[[" . $wark . "]]");
// 0JTgJqogVnFhWaxn69r0ckffGSpPOQoiO1Itvt+ovgE
$wark = substr(strtr(base64_encode(hash(
	"sha256", $wark, true)), "+/", "-_"), 0, 32);

$data["wark"] = $wark;
$response = json_decode(json_encode($data), true);  // can't clone

//
// store the upload info, if unique

$data["ip"] = $_SERVER["REMOTE_ADDR"];
$hist_data = json_encode($data);
$suffix = 1;
while (true)
{
	$path = "hist/{$wark}.{$suffix}";
	if (!file_exists($path))
		break;
	
	$text = file_get_contents($path);
	if ($text === $hist_data)
	{
		$suffix = "";
		break;
	}
	
	$suffix++;
}

if ($suffix !== "")
	file_put_contents($path, $hist_data);

//
// check if the merged output already exists

function get_fsum($data)
{
	$hashes = implode("\n", $data["hash"]);
	return substr(strtr(base64_encode(hash(
		"sha256", $hashes, true)), "+/", "-_"), 0, 32);
}

$fsum = get_fsum($data);

if (file_exists("fsum/{$fsum}"))
{
	$response["hash"] = [];
	print json_encode($response);
	exit(0);
}

//
// if this wark has been observed before, consider first sighting
// authoritative for generating the fsum (final hash/id of the file)
// and to check whether all required chunks have been uploaded

if ($suffix !== 1)
	$data = json_decode(file_get_contents(
		"hist/{$wark}.1"), true);

$fsum = get_fsum($data);

//
// create list of segments for client to upload,
// unless we have an fsum entry which means we already have it

$merge_exists = file_exists("fsum/{$fsum}");

$missing = [];
if (!$merge_exists)
	foreach ($data["hash"] as &$hash)
		if (!file_exists("part/{$wark}/{$hash}.ok"))
			$missing[] = $hash;

$response["hash"] = $missing;
print json_encode($response);

//
// if we have all the chunks, merge the upload

// exit if merge has already happened
if ($merge_exists)
	exit(0);

// exit if we need more chunks
if (count($missing) > 0)
	exit(0);

$unsafe_fn = $data['name'];
$fn = sanitize_filename($unsafe_fn, $fsum);
if ($fn === '')
	throw new Exception(
		"could not sanitize filename for wark {$wark} / fsum {$fsum}: [{$unsafe_fn}]");

$ofh = fopen($fn, 'wb');
if (FALSE === $ofh)
	throw new Exception(
		"failed to open output file for wark {$wark} / fsum {$fsum}: [{$fn}]");

foreach ($data["hash"] as &$hash)
{
	$part = "part/{$wark}/{$hash}";
	$ifh = fopen($part, 'rb');
	if (FALSE === $ifh)
		throw new Exception(
			"failed to open input file for wark {$wark} / fsum {$fsum}: [{$part}]");
	
	while (!feof($ifh))
	{
		$buf = fread($ifh, 8192);
		$nwrite = fwrite($ofh, $buf);
		
		if ($nwrite !== strlen($buf))
			throw new Exception(
				"failed to stream chunk for wark {$wark} / fsum {$fsum} [{$part}]; " .
				"wrote {$nwrite} bytes, expected " . strlen($buf));
	}
	fclose($ifh);
}
fclose($ofh);

file_put_contents("fsum/${fsum}", $fn ."\n". $unsafe_fn ."\n". $wark);

//
// and delete the temp files

foreach ($data["hash"] as &$hash)
	@unlink("part/{$wark}/{$hash}");

foreach ($data["hash"] as &$hash)
	@unlink("part/{$wark}/{$hash}.ok");

@rmdir("part/{$wark}");
