Source for the Original Post Test Server

After over 10 years, I’m finally out of the post test serving game. But there will be details on that in the next post. This post is a celebration of the original version of Post Test Server!

This was something I did in 2008 while developing the first client library for Localytics. It was a simple PHP script hosted on Dreamhost that allowed me to see what my BlackBerry test device was doing to the headers of our uploads (15 years later I still don’t understand BlackBerry uploading). I left it online thinking maybe another developer would one day find some value out of the service.

Curiously, people did find the service and over the course 10 years I slowly added features if they were easy enough and people asked nicely. This is also when people started asking for the source but I was too embarrassed to share something that was originally started as a 3 line script for dumping some upload headers to a file. But, enough time has gone on that I’m over it, so here are 200 lines of PHP that receive and dump posts.

Warning: I ran this 10 years ago, on a server with nothing of value. It would be ill advised to run this without appreciating the security concerns associated with processing data uploaded from anybody on the internet.

<?php

$basedir = "/home/redacated/www.posttestserver.com/data/";
$filedir = "/home/redacted/www.posttestserver.com/files/";

# Enable CORS
header('Access-Control-Allow-Origin: *');

# mkdir fails if the directory already exists
function makeDir($dir)
{
  if(file_exists($dir) == false) {
    mkdir($dir);
  }
}

# create a directory based on the current date (if it doesn't already exist)
function dir_with_date($startDir)
{
  $dir = $startDir . date("Y");
  makeDir($dir);
  $dir = "$dir/".date("m");
  makeDir($dir);
  $dir = "$dir/".date("d");
  makeDir($dir);

  return $dir;
}

# Allow user to override the default 200 HTTP status code
if (isset($_GET['status_code']))
{
  $status = $_GET['status_code'];
  header("HTTP/1.0 $status Custom Status", true, $status);
}

# wait up to 30 seconds before returning a response (helpful for testing timeouts and the like)
if(isset($_GET['sleep']))
{
  $sleep_count = $_GET['sleep'];
  if($sleep_count > 30) { $sleep_count = 30; }
  sleep($sleep_count);
}

# start dumping the data
$output = "Time: " . date(DATE_RFC822) . "\n";
$output .= "Source ip: " . getenv('REMOTE_ADDR') . "\n";

# loop through all headers and dump them
$output .= "\nHeaders (Some may be inserted by server)\n";
foreach ($_SERVER as $name => $content) {
  # ignore server specific content (confuses people)
  if(preg_match("/^PATH/", $name) ||
     preg_match("/^RAILS/", $name) ||
	 preg_match("/^FCGI/", $name) ||
	 preg_match("/^SCRIPT_URL/", $name) ||
	 preg_match("/^SCRIPT_URI/", $name) ||
	 preg_match("/^dsid/", $name) ||
	 preg_match("/^ds_id/", $name) ||
	 preg_match("/^DH_USER/", $name) ||
	 preg_match("/^DOCUMENT/", $name) ||
	 preg_match("/^SERVER/", $name) ||
	 preg_match("/^SCRIPT/", $name) ||
	 preg_match("/^argv/", $name) ||
	 preg_match("/^argc/", $name) ||
	 preg_match("/^PHP/", $name) ||
	 preg_match("/^SCRIPT/", $name) ) {
    continue;
  }
    # This spammer was so prolific they get their own line
    if(false == preg_match("/islandshangrila.digitalcampaignasia.com/", $content)) {
	$output .= "$name = $content\n";
	}
}

$output .= "\n";

# dump any post params if they exist
if($_POST && count($_POST) > 0 )
{
  $output .= "Post Params:\n";
  foreach ($_POST as $key => $value) {
    if($key == "var1" && $value = "lol") {
      $ignore = true;
	}
    $output .= "key: '$key' value: '$value'\n";
  }
}
else
{
  $output .= "No Post Params.\n";
}

# Dump the post body
if($HTTP_RAW_POST_DATA)
{
  $output .= "\n== Begin post body ==\n";
  $output .= $HTTP_RAW_POST_DATA;
  $output .= "\n== End post body ==\n";
}
else
{
  $output .= "Empty post body.\n";
}

# Handle multipart/form-data
# $_FILES is a hash of hashes, one for each uploaded file
if(isset($_SERVER["CONTENT_TYPE"]) &&
   preg_match("/multipart\/form-data/i", $_SERVER["CONTENT_TYPE"] )
)
{
  $output .= "\n== Multipart File upload. ==\n";
  $output .= "Received " . count($_FILES) . " file(s)\n";
  $count = 0;
  foreach($_FILES as $key => $value)
  {
    $output .= " $count: posted name=$key\n";
	foreach($_FILES[$key] as $key2 => $value2)
	{
	  if(!strcmp($key2, "tmp_name")) {
	    continue;
	  }
	  $output .= "    $key2: $value2\n";
	}

	# move the file from temp storage to the actual destination
	$uploaded = $_FILES[$key]['tmp_name'];
    $target_filename = "f_" . date("H.i.s") . rand();
	$target_path = dir_with_date($filedir) . "/$target_filename";
	$target_url = "/files/".date("Y/m/d")."/$target_filename";

    if(is_uploaded_file($uploaded))
    {
      if(copy($uploaded, $target_path)) {
        $output .= "Uploaded File: http://posttestserver.com$target_url\n";
	  }
	  else {
	    $output .= "File uploaded successfully but could not be copied.\n";
  	  }
    }
    else {
      $output .= "File specified was not uploaded. Possible file upload attack.\n";
    }
  }
}

# dump any HTTP PUT data
$putdata = fopen("php://input", "r");
$didit = false;
while ($data = fread($putdata, 1024)) {
  if(!$didit) {
    $output .= "\nUpload contains PUT data:\n";
	$didit = true;
  }
  $output .= $data;
}
fclose($putdata);

$dir = dir_with_date($basedir);

if(! empty($_GET) && isset($_GET["dir"])) {
  # people get clever with their filenames
  $target = str_replace(".", "", $_GET["dir"]);
  $target = str_replace("/", "", $target);
  $target = str_replace(";", "", $target);

  if(strlen($target) > 1) {
    $dir = "$dir/$target";
	makeDir($dir);
  }
}

if($ignore) {
  exit;
}

$filename = date("H.i.s") . rand();
$file = $dir . "/$filename";
$fh = fopen($file, 'w');
fwrite($fh, $output);
fclose($fh);

if (isset($_GET['response_body']))
{
  echo $_GET['response_body'];
}
else
{
  if (isset($_GET['dump']) == false )
  {
      echo "Successfully dumped " . count($_POST) . " post variables.\n";
      $path = date("Y/m/d") . "/";
	  if(isset($_GET["dir"])) {
        $path .=  $_GET["dir"] . "/";
	  }
	  $path .= "$filename\n";

	  #echo "View it at http://www.posttestserver.com/data/" . date("Y/m/d") . "/$filename\n";
	  echo "View it at http://www.posttestserver.com/data/$path";
      echo "Post body was " . strlen($HTTP_RAW_POST_DATA) . " chars long.";
  }

  if (isset($_GET['dump']))
  {
     if(isset($_GET['html']))
	 {
	   echo '<html><head><title>Post test</title></head><body>';
       echo str_replace("\n", "<br />", $output);
       echo '</body></html>';
	 }
	 else {
       echo $output;
	 }
  }
}
?>

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s