--- a/webserver.php Tue Jul 01 04:16:04 2008 -0400
+++ b/webserver.php Tue Jul 01 04:17:46 2008 -0400
@@ -33,7 +33,7 @@
* @package Amarok
* @subpackage WebControl
* @author Dan Fuhry
- * @license Public domain
+ * @license GNU General Public License <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
*/
class WebServer
@@ -163,7 +163,7 @@
function __construct($address = '127.0.0.1', $port = 8080, $targetuser = null, $targetgroup = null)
{
@set_time_limit(0);
- @ini_set('memory_limit', '256M');
+ @ini_set('memory_limit', '128M');
// do we have socket functions?
if ( !function_exists('socket_create') )
@@ -406,24 +406,133 @@
// anything on POST?
$postdata = '';
$_POST = array();
+ $_FILES = array();
if ( $method == 'POST' )
{
// read POST data
- if ( isset($_SERVER['HTTP_CONTENT_LENGTH']) )
+ if ( isset($_SERVER['HTTP_CONTENT_TYPE']) && preg_match('#^multipart/form-data; ?boundary=([A-z0-9_-]+)$#i', $_SERVER['HTTP_CONTENT_TYPE'], $match) )
{
- $postdata = socket_read($remote, intval($_SERVER['HTTP_CONTENT_LENGTH']), PHP_BINARY_READ);
+ // this is a multipart request
+ $boundary =& $match[1];
+ $mode = 'data';
+ $last_line = '';
+ $i = 0;
+ while ( $data = socket_read($remote, 8388608, PHP_NORMAL_READ) )
+ {
+ $data_trim = trim($data, "\r\n");
+ if ( $mode != 'data' )
+ {
+ $data = str_replace("\r", '', $data);
+ }
+ if ( ( $data_trim === "--$boundary" || $data_trim === "--$boundary--" ) && $i > 0 )
+ {
+ // trim off the first LF and the last CRLF
+ $currval_data = substr($currval_data, 1, strlen($currval_data)-3);
+
+ // this is the end of a part of the message; parse it into either $_POST or $_FILES
+ if ( is_string($have_a_file) )
+ {
+
+ // write data to a temporary file
+ $errcode = UPLOAD_ERR_OK;
+ $tempfile = tempnam('phpupload', ( function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : '/tmp' ));
+ if ( $fh = @fopen($tempfile, 'w') )
+ {
+ if ( empty($have_a_file) )
+ {
+ $errcode = UPLOAD_ERR_NO_FILE;
+ }
+ else
+ {
+ fwrite($fh, $currval_data);
+ }
+ fclose($fh);
+ }
+ else
+ {
+ $errcode = UPLOAD_ERR_CANT_WRITE;
+ }
+ $_FILES[$currval_name] = array(
+ 'name' => $have_a_file,
+ 'type' => $currval_type,
+ 'size' => filesize($tempfile),
+ 'tmp_name' => $tempfile,
+ 'error' => $errcode
+ );
+ }
+ else
+ {
+ $_POST[$currval_name] = $currval_data;
+ }
+ }
+
+ if ( $data_trim === "--$boundary" )
+ {
+ // switch from "data" mode to "headers" mode
+ $currval_name = '';
+ $currval_data = '';
+ $currval_type = '';
+ $have_a_file = false;
+ $mode = 'headers';
+ }
+ else if ( $data_trim === "--$boundary--" )
+ {
+ // end of request
+ break;
+ }
+ else if ( ( empty($data_trim) && empty($last_line) ) && $mode == 'headers' )
+ {
+ // start of data
+ $mode = 'data';
+ }
+ else if ( $mode == 'headers' )
+ {
+ // read header
+ // we're only looking for Content-Disposition and Content-Type
+ if ( preg_match('#^Content-Disposition: form-data; name="([^"\a\t\r\n]+)"(?:; filename="([^"\a\t\r\n]+)")?#i', $data_trim, $match) )
+ {
+ // content-disposition header, set name and mode.
+ $currval_name = $match[1];
+ if ( isset($match[2]) )
+ {
+ $have_a_file = $match[2];
+ }
+ else
+ {
+ $have_a_file = false;
+ }
+ }
+ else if ( preg_match('#^Content-Type: ([a-z0-9-]+/[a-z0-9/-]+)$#i', $data_trim, $match) )
+ {
+ $currval_type = $match[1];
+ }
+ }
+ else if ( $mode == 'data' )
+ {
+ $currval_data .= $data;
+ }
+ $last_line = $data_trim;
+ $i++;
+ }
}
else
{
- $postdata = socket_read($remote, 8388608, PHP_NORMAL_READ);
- }
- if ( preg_match_all('/(^|&)([a-z0-9_\.\[\]-]+)(=[^ &]+)?/', $postdata, $matches) )
- {
- if ( isset($matches[1]) )
+ if ( isset($_SERVER['HTTP_CONTENT_LENGTH']) )
+ {
+ $postdata = socket_read($remote, intval($_SERVER['HTTP_CONTENT_LENGTH']), PHP_BINARY_READ);
+ }
+ else
{
- foreach ( $matches[0] as $i => $_ )
+ $postdata = socket_read($remote, 8388608, PHP_NORMAL_READ);
+ }
+ if ( preg_match_all('/(^|&)([a-z0-9_\.\[\]-]+)(=[^ &]+)?/', $postdata, $matches) )
+ {
+ if ( isset($matches[1]) )
{
- $_POST[$matches[2][$i]] = ( !empty($matches[3][$i]) ) ? urldecode(substr($matches[3][$i], 1)) : true;
+ foreach ( $matches[0] as $i => $_ )
+ {
+ $_POST[$matches[2][$i]] = ( !empty($matches[3][$i]) ) ? urldecode(substr($matches[3][$i], 1)) : true;
+ }
}
}
}
@@ -456,6 +565,10 @@
}
}
+ $_GET = $this->parse_multi_depth_array($_GET);
+ $_POST = $this->parse_multi_depth_array($_POST);
+ $_FILES = $this->parse_multi_depth_array($_FILES);
+
// init handler
$handler = false;
@@ -514,6 +627,18 @@
$this->send_standard_response($remote, $handler, $uri, $params);
+ // now that we're done sending the response, delete any temporary uploaded files
+ if ( !empty($_FILES) )
+ {
+ foreach ( $_FILES as $file_data )
+ {
+ if ( file_exists($file_data['tmp_name']) )
+ {
+ @unlink($file_data['tmp_name']);
+ }
+ }
+ }
+
if ( !$this->in_keepalive && defined('HTTPD_WS_CHILD') )
{
// if ( defined('HTTPD_WS_CHILD') )
@@ -761,12 +886,21 @@
$output = ob_get_contents();
ob_end_clean();
}
+ // throw an HttpExceptionFatal when you need to break out of an in-progress scriptlet due to an error, use it in place of die() or exit()
+ catch ( HttpExceptionFatal $e )
+ {
+ restore_error_handler();
+ $this->send_http_error($socket, 500, "A handler crashed reporting a fatal exception; see the command line for details.");
+ if ( function_exists('status') )
+ status("fatal exception in handler {$handler['id']}:\n$e");
+ return true;
+ }
catch ( Exception $e )
{
restore_error_handler();
- $this->send_http_error($socket, 500, "A handler crashed with an exception; see the command line for details.");
+ $this->send_http_error($socket, 500, "There was an uncaught exception during the execution of a scripted handler function. See the command line for details.");
if ( function_exists('status') )
- status("caught exception in handler {$handler['id']}:\n$e");
+ status("uncaught exception in handler {$handler['id']}:\n$e");
return true;
}
restore_error_handler();
@@ -1302,6 +1436,40 @@
return false;
}
+ /**
+ * Takes a flat array with keys of format foo[bar] and parses it into multiple depths.
+ * @param array
+ * @return array
+ */
+
+ function parse_multi_depth_array($array)
+ {
+ foreach ( $array as $key => $value )
+ {
+ if ( preg_match('/^([^\[\]]+)\[([^\]]+)\]/', $key, $match) )
+ {
+ $parent =& $match[1];
+ $child =& $match[2];
+ if ( !isset($array[$parent]) || ( isset($array[$parent]) && !is_array($array[$parent]) ) )
+ {
+ $array[$parent] = array();
+ }
+ $array[$parent][$child] = $value;
+ unset($array[$key]);
+ $array[$parent] = $this->parse_multi_depth_array($array[$parent]);
+ }
+ }
+ return $array;
+ }
+
+}
+
+/**
+ * Exception class that allows breaking directly out of a scriptlet.
+ */
+
+class HttpExceptionFatal extends Exception
+{
}
/**