1. Toby Inkster
  2. php-http-mbox

Commits

Toby Inkster  committed adda543

Support for basic blocklists, CORS, and various cleanups.

  • Participants
  • Parent commits d247c78
  • Branches http-mbox

Comments (0)

Files changed (2)

File inc_http-mbox-gateway.php

View file
 <?php
 
-// Only accept POSTed data.
-if ($_SERVER['REQUEST_METHOD'] != 'POST')
-{
-	header("HTTP/1.1 405 Forbidden");
-	header("Content-Type: text/plain");
-	print "Please use HTTP POST.\r\n";
+if (check_cors())
 	exit;
-}
+
+if (!check_http_post())
+	exit;
+
+if (is_blocked($_SERVER['HTTP_ORIGIN'], 'origin'))
+	exit;
+
+if (is_blocked($_SERVER['REMOTE_ADDR'], 'client IP address'))
+	exit;
 
 // Get raw POST data.
 $data = file_get_contents('php://input');
 	}
 }
 
-if ($__webid)
-{
-	$descriptorspec = array(
-		0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
-		1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
-		2 => array("pipe", "w")   // stderr 
-	);
-	$proc = proc_open('./webid.pl', $descriptorspec, $pipes, null, null);
-	if (is_resource($proc))
-	{
-		fwrite($pipes[0], $_SERVER['SSL_CLIENT_CERT']);
-		fclose($pipes[0]);
-		list($_SERVER['WEBID'], $_SERVER['WEBID_NAME'], $_SERVER['WEBID_MAIL']) = explode("\n", stream_get_contents($pipes[1]));
-		fclose($pipes[1]);
-		fclose($pipes[2]);
-		proc_close($proc);
-	}
-}
+process_webid();
 
 if ($__webid === 'force' && !$_SERVER['WEBID'])
 {
 	exit;
 }
 
-// Process HTTP "From" header.
-$from = $__from;
-if ($_SERVER['SSL_CLIENT_S_DN_Email'])
-	$from = $_SERVER['SSL_CLIENT_S_DN_Email'];
-if ($_SERVER['WEBID_MBOX'])
-	$from = preg_replace('/^mailto:/i', '', $_SERVER['WEBID_MBOX']);
-if ($_SERVER['HTTP_FROM'])
-	$from = $_SERVER['HTTP_FROM'];
+if (is_blocked($_SERVER['WEBID'], 'WebID'))
+	exit;
+
+if (is_blocked($_SERVER['WEBID_MBOX'], 'e-mail address (from WebID)'))
+	exit;
+
+$from = detect_from_address($__from);
 if (!$from)
 {
 	header("HTTP/1.1 403 Forbidden");
 	exit;
 }
 
+if (is_blocked($from, 'e-mail address'))
+	exit;
+
 // Process HTTP "Subject" header - not an official RFC 2616 header.
 $subject = isset($_SERVER['HTTP_SUBJECT']) ?
 	$_SERVER['HTTP_SUBJECT'] :
 $messageid = "<http-mbox-gateway." . md5(uniqid(microtime())) . "@" . $_SERVER['SERVER_ADDR'] . ">";
 
 // Assemble message headers.
-$headers = "From: $from\r\n"
-	. "Content-Type: $type\r\n"
-	. "Message-ID: $messageid\r\n"
-	. "Received: from [${_SERVER['REMOTE_ADDR']}] by [${_SERVER['SERVER_ADDR']}] via HTTP; ".date('r')."\r\n";
+$headers  = '';
+$headers .= message_header('Received', "from [${_SERVER['REMOTE_ADDR']}] by ${_SERVER['SERVER_NAME']}:${_SERVER['SERVER_PORT']} via ${_SERVER['SERVER_PROTOCOL']}; ".date('r'));
+$headers .= message_header('From', $from);
+$headers .= message_header('MIME-Version', '1.0');
+$headers .= message_header('Content-Type', $type);
+$headers .= message_header('Message-ID', $messageid);
 if (!empty($_SERVER['HTTP_USER_AGENT']))
-	$headers .= "X-Mailer: ".preg_replace('/[\t\r\n]/', ' ', $_SERVER['HTTP_USER_AGENT']) . "\r\n";
-if ($_SERVER['WEBID'])
-	$headers .= "Link: <${_SERVER[WEBID]}>; rel=\"http://xmlns.com/foaf/0.1/maker\"\r\n";
+	$headers .= message_header('X-Mailer', $_SERVER['HTTP_USER_AGENT']);
+if (!empty($_SERVER['WEBID']))
+	$headers .= message_header('Link', "<${_SERVER[WEBID]}>; rel=\"http://xmlns.com/foaf/0.1/maker\"");
 
 // Send message and respond appropriately.
 if( @mail($__to, $subject, $data, $headers) )
 	exit;
 }
 
+// Handle CORS
+function check_cors ()
+{
+	global $__block;
+
+	if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS' && isset($_SERVER['HTTP_ORIGIN']))
+	{
+		$scram = $_SERVER['ACCESS_CONTROL_REQUEST_METHOD'];
+		
+		header("X-CORS-Preflight: seems to be");
+		
+		if (!in_array($_SERVER['HTTP_ORIGIN'], $__block)
+		&& (empty($scram) || preg_match('/^\s*POST\s*$/i', $scram)))
+		{
+			header("HTTP/1.1 200 OK");
+			header("Content-Type: text/plain");
+			header("Access-Control-Allow-Origin: ".$_SERVER['HTTP_ORIGIN']);
+			header("Access-Control-Allow-Methods: POST, OPTIONS");
+			header("Access-Control-Allow-Headers: CONTENT-TYPE, FROM, SUBJECT");
+			header("Access-Control-Expose-Headers: X-GENERATED-MESSAGE-ID");
+			header("Access-Control-Max-Age: 7200");
+			print "That's totally cool.\r\n";
+		}
+		else
+		{
+			header("HTTP/1.1 405 Forbidden");
+			header("Content-Type: text/plain");
+			print "Cross-origin resource not shared.\r\n";
+		}
+		
+		return true;
+	}
+	
+	return false;
+}
+
+function check_http_post ()
+{
+	// Only accept POSTed data.
+	if ($_SERVER['REQUEST_METHOD'] != 'POST')
+	{
+		header("HTTP/1.1 405 Forbidden");
+		header("Content-Type: text/plain");
+		print "Please use HTTP POST.\r\n";
+		return false;
+	}
+	
+	return true;
+}
+
+function is_blocked ($value, $reason)
+{
+	global $__block;
+	
+	// Basic access control
+	if (in_array($value, $__block))
+	{
+		header("HTTP/1.1 405 Forbidden");
+		header("Content-Type: text/plain");
+		print "You are not welcome here.\r\n";
+		print "Your $reason is '$value', which is disallowed.\r\n";
+		return true;
+	}
+	
+	return false;
+}
+
+// This function sets $_SERVER['WEBID'], $_SERVER['WEBID_NAME'] and $_SERVER['WEBID_MBOX']
+function process_webid ()
+{
+	if ($__webid && file_exists('./webid.pl'))
+	{
+		$descriptorspec = array(
+			0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
+			1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
+			2 => array("pipe", "w")   // stderr 
+		);
+		$proc = proc_open('./webid.pl', $descriptorspec, $pipes, null, null);
+		if (is_resource($proc))
+		{
+			fwrite($pipes[0], $_SERVER['SSL_CLIENT_CERT']);
+			fclose($pipes[0]);
+			list($_SERVER['WEBID'], $_SERVER['WEBID_NAME'], $_SERVER['WEBID_MBOX']) = explode("\n", stream_get_contents($pipes[1]));
+			fclose($pipes[1]);
+			fclose($pipes[2]);
+			proc_close($proc);
+		}
+	}
+}
+
+function detect_from_address ($default=null)
+{
+	if ($_SERVER['HTTP_FROM'])
+		return $_SERVER['HTTP_FROM'];
+	
+	if ($_SERVER['WEBID_MBOX'])
+		return preg_replace('/^mailto:/i', '', $_SERVER['WEBID_MBOX']);
+	
+	if ($_SERVER['SSL_CLIENT_S_DN_Email'])
+		return $_SERVER['SSL_CLIENT_S_DN_Email'];
+	
+	return $default;
+}
+
+function message_header ($header, $value)
+{
+	$value = str_replace(
+		array('\\',   "\n", "\r", "\t"),
+		array('\\\\', '\n', '\r', '\t'),
+		$value);
+	
+	return "${header}: ${value}\r\n";
+}

File mbox.php

View file
 // people to authenticate via WebID.
 $__webid = true;
 
+// Block list. IP addresses and CORS Origin headers to block...
+$__block = array(
+	'666.666.666.666',           # the IP address of the beast (yes, I know)
+	'http://badman.example.com', # an origin to block
+	);
+
 require 'inc_http-mbox-gateway.php';