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