<? /*
// File: webcp-httpd-functions.inc.phps
// Purpose: isolate functions used by web://cp Web Serving Daemon
// Creation: 2003-01-25
// Author: Jonathan Haskins <hide@address.com>
*/
function start_server() {
/* open a socket to listen for connections on */
global $cfg,$echo;
/* create a listening socket */
if (!$lsocket = @socket_create(AF_INET, SOCK_STREAM, 0)) {
webcp_log(0,0,'webcp-httpd','Failed to create a listening socket because: '.socket_strerror(socket_last_error($lsocket)),0,1);
exit;
}
/* able to reuse our listening socket */
if (!@socket_setopt($lsocket, SOL_SOCKET, SO_REUSEADDR, 1)) {
webcp_log(0,0,'webcp-httpd','Failed to make listening address reusable because: '.socket_strerror(socket_last_error($lsocket)),0,1);
exit;
}
/* make our socket non-blocking */
if (!@socket_set_nonblock($lsocket)) {
webcp_log(0,0,'webcp-httpd','Failed to set listening socket to non-blocking because: '.socket_strerror(socket_last_error($lsocket)),0,1);
exit;
}
/* bind the listening socket to the specified address and port */
if (!@socket_bind($lsocket, $cfg['listen_address'], $cfg['ssl'] ? $cfg['ssl_port'] : $cfg['port'])) {
webcp_log(0,0,'webcp-httpd',"Failed to bind to $cfg[listen_address] on port ".($cfg['ssl'] ? $cfg['ssl_port'] : $cfg['port'])." because: ".socket_strerror(socket_last_error($lsocket)),0,1);
exit;
}
/* start listening on our socket */
if (!@socket_listen($lsocket, $cfg['listen_queue'])) {
webcp_log(0,0,'webcp-httpd','Failed to start listening on socket because: '.socket_strerror(socket_last_error($lsocket)),0,1);
exit;
}
return $lsocket;
}
function error_handler($errno, $errstr, $errfile, $errline) {
global $echo;
// make sure there is no \ sign before the error line
if (error_reporting()) {
switch($errno) {
// Fatal Errors
case 1:
case 256:
webcp_log(0,0,'webcp-httpd',"Fatal Error ($errno) in file $errfile($errline): $errstr",0,$echo);
break;
// Warning & errors
case 2:
case 4:
case 8:
case 512:
case 1024:
webcp_log(1,0,'webcp-httpd',"Error ($errno) in file $errfile($errline): $errstr",0,$echo);
break;
// anything else
default:
webcp_log(1,0,'webcp-httpd',"Error unknown ($errno) in file $errfile($errline): $errstr",0,$echo);
}
}
}
function get_microtime() {
/* used to calculate timeouts, also useful for testing */
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
function db_connect($dbhost, $dbname, $dbuser, $dbpass) {
global $echo;
if (!@mysql_connect($dbhost, $dbuser, $dbpass)) {
webcp_log(0,0,'webcp-httpd',"Error: Could not connect to database server",0,$echo);
return false;
}
elseif (!@mysql_select_db($dbname)) {
webcp_log(0,0,'webcp-httpd',"Error: Could not select database",0,$echo);
return false;
}
}
function stop_server($signo='') {
/* shutdown the listening socket and close all client sockets. */
global $shutdown, $cfg, $lsocket, $csocket, $echo;
// shut down client sockets
foreach ($csocket as $slot => $socket) {
close_client($slot);
}
// shut down listening socket
@socket_shutdown($lsocket, 2);
@socket_close($lsocket);
if (file_exists($cfg['httpd_pid']) && !unlink($cfg['httpd_pid'])) {
webcp_log(1,0,'webcp-httpd',"Unable to remove $cfg[httpd_pid]",0,$echo);
}
/* if using SSL then shut down stunnel */
if ($cfg['ssl']) {
if (file_exists($cfg['httpsd_pid'])) {
$tmp = trim(implode('', file($cfg['httpsd_pid'])));
if (!posix_kill($tmp, 15))
webcp_log(1,0,'webcp-httpd',"stunnel could not be shut down.",0,$echo);
else
webcp_log(2,0,'webcp-httpd',"stunnel has been stopped.",0,$echo);
}
}
webcp_log(2,0,'webcp-httpd',"Webcp-httpd daemon has been stopped.",0,$echo);
exit;
}
function reload_server($signo='') {
/* on SIGHUP we reload the server configuration */
global $cfg, $echo;
/* change back to script dir */
chdir(dirname(__FILE__));
/* reload configuration */
include('../web/config.inc.php');
/* reconnect to database */
db_connect($cfg['dbhost'], $cfg['dbname'], $cfg['dbuser'], $cfg['dbpass']);
webcp_log(2,0,'webcp-httpd',"SIGHUP recieved, server was reloaded.",0,$echo);
}
function open_client($lsocket) {
/* open a connection between the server and a client machine. */
global $cfg, $csocket, $cdata, $httpd_status, $echo;
/* find an open slot and fill it with the new connection. */
for ($slot = 1; $slot <= $cfg['max_clients']; $slot++) {
if (!isset($csocket[$slot])) {
if (!$csocket[$slot] = @socket_accept($lsocket)) {
webcp_log(1,0,'webcp-httpd',"Client failed to connect because: ".socket_strerror(socket_last_error($lsocket)),0,$echo);
return;
}
$httpd_status['connections']++;
/* get ip and port of the client and local server */
if (!socket_getpeername($csocket[$slot], $cdata[$slot]['remote_ip'], $cdata[$slot]['remote_port'])) {
close_client($slot);
return;
}
if (!socket_getsockname($csocket[$slot], $cdata[$slot]['local_ip'], $cdata[$slot]['local_port'])) {
close_client($slot);
return;
}
/* if using ssl we should only accept local connections from stunnel */
if ($cfg['ssl']) {
if ($cdata[$slot]['remote_ip'] != '127.0.0.1') {
close_client($slot);
return;
}
}
/* assign initial client data */
$cdata[$slot]['established'] = get_microtime();
if ($cfg['keep_alive'] && !$cfg['ssl']) {
$cdata[$slot]['keep_alive'] = $cdata[$slot]['established'] + $cfg['keep_alive_timeout'];
}
$cdata[$slot]['buffer'] = '';
webcp_log(3,0,'webcp-httpd',$cdata[$slot]['remote_ip']." connected to ".$cdata[$slot]['local_ip']." on port ".$cdata[$slot]['local_port']." at ".date("g:i:s a", $cdata[$slot]['established']),0,$echo);
break;
}
}
}
function close_client($slot) {
/* close a connection between the server and a client machine. */
global $csocket, $cdata, $echo;
/* if still connected shutdown and close the socket */
if (isset($csocket[$slot]) && is_resource($csocket[$slot])) {
@socket_shutdown($csocket[$slot], 2);
@socket_close($csocket[$slot]);
} else {
webcp_log(1,0,'webcp-httpd',"Socket not found while attempting to close the connection",0,$echo);
}
if (isset($csocket[$slot])) {
unset($GLOBALS['csocket'][$slot]);
}
/* clean out data associated with the socket */
if (isset($cdata[$slot])) {
webcp_log(3,0,'webcp-httpd',$cdata[$slot]['remote_ip']." disconnected from ".$cdata[$slot]['local_ip']." on port ".$cdata[$slot]['local_port']." at ".date("g:i:s a", get_microtime()),0,$echo);
unset($GLOBALS['cdata'][$slot]);
}
}
function parse_request(&$data) {
/******************************************************************
* take the raw request data from a client and return something like:
*
* $request
* (
* [method] => GET
* [protocol] => HTTP/1.1
* [file] => test.php
* [connection_close] => false
* [get]
* (
* [key1] => val1
* [key2] => val2
* )
* )
*******************************************************************/
GLOBAL $cfg;
$data = explode("\r\n\r\n", $data, 2);
$header = explode("\r\n", $data[0]);
/* if using ssl grab the custom SSL_Remote_IP: header from stunnel.
To make sure it isn't faked by the client we only accept the first instance
of this header, directly on 'top'.
*/
if ($cfg['ssl'])
$request['remote_ip'] = trim(array_shift($header));
$request_line = explode(' ', $header[0]);
unset($header[0]);
/* make sure it's a valid HTTP request */
if (!isset($request_line[2]) || strpos($request_line[2], "HTTP/") === false) {
return false;
}
$request['method'] = $request_line[0];
$request['protocol'] = $request_line[2];
$request['cookie'] = array();
$request['connection_close'] = false;
/* Here we're going through the headers one by one
* looking for the few that we actually care about */
foreach ($header as $val) {
if (stristr($val, 'User-Agent:')) {
/* pass user-agent string on to use
* with the $_SERVER variable */
$val = substr($val, 11);
$request['user_agent'] = trim($val);
} elseif (stristr($val, 'Accept-Language:')) {
/* pass accept-language string on to
* use with the $_SERVER variable */
$val = substr($val, 16);
$request['language'] = trim($val);
} elseif (stristr($val, 'Cookie:')) {
/* parse any cookies */
$val = substr($val, 7);
$request['cookie'] = parse_query(trim($val), '; ');
continue;
} elseif (stristr($val, 'Connection: close')) {
/* see if they want us to close the connection */
$request['connection_close'] = true;
continue;
} elseif (stristr($val, 'Content-Type:')) {
/* get content-type */
$val = substr($val, 13);
$request['content_type'] = trim($val);
continue;
}
}
/* save requested URI for $_SERVER */
$request['uri'] = $request_line[1];
/* split any GET querries from the requested path */
$path_query = explode('?', $request_line[1]);
$request['file'] = $path_query[0];
/* save query string for $_SERVER */
$request['query_string'] = '';
if (isset($path_query[1])) {
$request['query_string'] = $path_query[1];
}
// initialize file, post, and get data arrays
$request['get'] = array();
$request['post'] = array();
$request['files'] = array();
/* determine if there are POST or GET queries, and parse them */
if (isset($path_query[1]) && strpos($path_query[1], '=') !== false) {
$request['get'] = parse_query($path_query[1]);
}
// check for included request body
if (isset($data[1]) && isset($request['content_type'])) {
if (strpos($request['content_type'], 'application/x-www-form-urlencoded') !== false) {
// normal form
$request['post'] = parse_query($data[1]);
} elseif (strpos($request['content_type'], 'multipart/form-data') !== false) {
// rfc 2388 covers multipart/form-data
$boundary = explode(';', $request['content_type']);
$boundary = explode('=', $boundary[1]);
$boundary = trim($boundary[1]);
parse_multi_part($data[1], $boundary, $request['post'], $request['files']);
}
}
return $request;
}
function build_response($request) {
/******************************************************************
* build the array that we will base our response on:
*
* $response
* (
* [status] => ok
* [protocol] => HTTP/1.1
* [translated_file] => /home/webcp/web/index.php
* [file_type] => php
* [connection_close] => false
* )
*******************************************************************/
global $cfg;
$response['connection_close'] = false;
/* the http 1.1 protocol uses keep alive connections. We should
* close a socket only when it has timed out, or one of the
* following conditions is met. */
if (!$request) {
/* we recieved a request we don't understand */
$response['connection_close'] = true;
} elseif ($request['protocol'] !== 'HTTP/1.1') {
/* they are using a http 1.0 or unknown protocol */
$response['connection_close'] = true;
} elseif ($request['connection_close']) {
/* they asked us to close it with a Connection: close header */
$response['connection_close'] = true;
} elseif (!$cfg['keep_alive'] || $cfg['ssl']) {
/* keep alive is turned off or ssl turned on in the configuration file */
$response['connection_close'] = true;
}
/* if the request we got is invalid respond as such */
if (!$request) {
$response['protocol'] = 'HTTP/1.1';
$response['status'] = 'malformed';
return $response;
}
if ($request['protocol'] == 'HTTP/1.1') {
/* If they're using HTTP version 1.1 then respond with 1.1 */
$response['protocol'] = 'HTTP/1.1';
} else {
/* If they're using any other version drop to 1.0 compatibility */
$response['protocol'] = 'HTTP/1.0';
}
/* extremely basic security check, remove any ../ from requested file
* so they can't ascend into forbidden directories */
$response['translated_file'] = str_replace('../', '', $request['file']);
/* remove leading/trailing slash(es) */
$response['translated_file'] = trim($response['translated_file'], '/');
/* append default path from config file */
$response['translated_file'] = $cfg['basedir'].'/web/'.$response['translated_file'];
/* load index.php for directories */
if (is_readable($response['translated_file']) && is_dir($response['translated_file'])) {
$response['translated_file'] = rtrim($response['translated_file'], '/').'/index.php';
}
/* Get a file's extension by grabbing the characters
* from after the last period to the last character. */
$response['file_type'] = substr($response['translated_file'], (strrpos($response['translated_file'], '.') + 1));
/* we're working with a lot of files that may change */
clearstatcache();
/* see if requested file exists and can be served */
if (basename($response['translated_file']) == '--status') {
$response['status'] = 'status';
} elseif (!file_exists($response['translated_file'])) {
$response['status'] = 'not found';
} elseif (!is_readable($response['translated_file'])) {
$response['status'] = 'forbidden';
} elseif (!is_dir($response['translated_file'])) {
$response['status'] = 'ok';
/* change to file's working directory */
chdir(dirname($response['translated_file']));
}
return $response;
}
function build_headers($response, $send_headers = false, $replace = false) {
/* compile an appropriate list of headers to send to client */
global $cfg, $http_status_map;
/* Status line */
if (!in_array($response['status'], array_keys($http_status_map))) {
$response['status']='default';
}
$headers = $response['protocol'].' '.$http_status_map[$response['status']]."\r\n";
/* Date line, Sat, 06 Sep 2014 23:50:08 GMT*/
$headers .= 'Date: '.gmdate("D, d M Y H:i:s T")."\r\n";
/* Server line */
$headers .= "Server: webcp-httpd - web://cp $cfg[webcp]\r\n";
$headers .= 'X-Powered-By: PHP/'.phpversion()."\r\n";
/* Add content length */
$headers .= 'Content-length: '.$response['content_length']."\r\n";
/* Connection close header */
if ($response['connection_close'] AND $response['protocol'] == 'HTTP/1.1') {
$headers .= "Connection: close\r\n";
}
/* MIME type */
if ($response['status'] !== 'ok') {
$headers .= "Content-type: text/html\r\n";
} else {
$headers .= 'Content-type: '.mime_type($response['file_type'])."\r\n";
}
/* include any headers sent by the subscript using send_header() */
if ($send_headers) {
foreach($send_headers as $val) {
$headers .= $val;
}
}
if ($replace) {
$headers = explode("\r\n", $headers);
foreach ($headers AS $val) {
$header_name = explode(':', $val, 2);
$header[strtolower($header_name[0])] = $val;
}
$headers = implode("\r\n", $header)."\r\n";
}
$headers .= "\r\n";
return $headers;
}
function send_response($slot, $response, $request) {
/* handle the client's request */
global $cfg, $csocket, $cdata, $httpd_status, $echo;
if ($response['status'] == 'ok' && $response['file_type'] == 'php') {
/* if the requested resource is a php file. */
$content = load_page($cdata[$slot], $response, $request, $header_redirect, $send_headers);
/* re-reference our globals that were reset in load_page() */
global $cfg, $csocket, $cdata, $httpd_status, $echo;
/* If $header_redirect is true then the loaded page used send_header()
* to send a Location: header. We don't send the content, instead we
* only send a redirection header */
if ($header_redirect) {
$response['status'] = 'redirect';
$response['connection_close'] = true;
$content = 'redirect';
}
// add up the content-length
$response['content_length'] = intval(strlen($content));
$headers = build_headers($response, $send_headers);
if (@socket_write($csocket[$slot], $headers.$content) === false) {
webcp_log(1,0,'webcp-httpd',"Failed to send to {$cdata[$slot]['remote_ip']} because: ".socket_strerror(socket_last_error($csocket[$slot])),0,$echo);
close_client($slot);
}
} elseif ($response['status'] == 'ok') {
/* if the requested resource is a normal file and can be returned */
$response['content_length'] = filesize($response['translated_file']);
$headers = build_headers($response);
if (@socket_write($csocket[$slot], $headers) === false) {
webcp_log(1,0,'webcp-httpd',"Failed to send to {$cdata[$slot]['remote_ip']} because: ".socket_strerror(socket_last_error($csocket[$slot])),0,$echo);
close_client($slot);
} else {
socket_write_file($slot, $response['translated_file'], $cdata[$slot]);
}
} elseif ($response['status'] == 'forbidden') {
/* if the requested resource isn't readable */
$content = "<html><head>\r\n";
$content .= "<title>403 Forbidden</title>\r\n";
$content .= "</head><body>\r\n";
$content .= "<h1>Forbidden</h1>\r\n";
$content .= "<p>You do not have permission to access $request[file].</p>\r\n";
$content .= "<p><i>$cfg[sysname]</i></p>\r\n";
$content .= "</body></html>";
$response['content_length'] = strlen($content);
$headers = build_headers($response);
if (@socket_write($csocket[$slot], $headers.$content) === false) {
webcp_log(0,0,'webcp-httpd',"Failed to send to {$cdata[$slot]['remote_ip']} because: ".socket_strerror(socket_last_error($csocket[$slot])),0,$echo);
close_client($slot);
}
} elseif ($response['status'] == 'not found') {
/* if the requested resource doesn't exist */
$content = "<html><head>\r\n";
$content .= "<title>404 Not Found</title>\r\n";
$content .= "</head><body>\r\n";
$content .= "<h1>Not Found</h1>\r\n";
$content .= "<p>The requested URL $request[file] was not found.</p>\r\n";
$content .= "<p><i>$cfg[sysname]</i></p>\r\n";
$content .= "</body></html>";
$response['content_length'] = strlen($content);
$headers = build_headers($response);
if (@socket_write($csocket[$slot], $headers.$content) === false) {
webcp_log(1,0,'webcp-httpd',"Failed to send to {$cdata[$slot]['remote_ip']} because: ".socket_strerror(socket_last_error($csocket[$slot])),0,$echo);
close_client($slot);
}
} elseif ($response['status'] == 'status') {
/* they requested server status */
/* convert uptime into days, hours, minutes, and seconds */
$uptime = get_microtime() - $httpd_status['started'];
$uptime_str = '';
if (($days = floor($uptime / 86400)) > 0) $uptime_str .= "$days days";
if (($hours = floor(($uptime % 86400) / (3600))) > 0) $uptime_str .= " $hours hours";
if (($mins = floor((($uptime % 86400) % 3600) / 60)) > 0) $uptime_str .= " $mins min";
if (($secs = floor((($uptime % 86400) % 3600) % 60)) > 0) $uptime_str .= " $secs sec";
/* convert bytes sent into terra, giga, mega, kilo, or bytes */
$sent_str = convert_bytes($httpd_status['sent'], $metric).' '.$metric;
// Count number of code lines in web://cp
$numlines = 0;
$dir = dirlist($cfg['basedir']."/server", "file");
do {
if (strstr(current($dir),".php") AND !strstr(current($dir),"phpmyadmin") AND !strstr(current($dir),"phppgadmin") AND !strstr(current($dir),"errors"))
$numlines += count(file(current($dir)));
} while (next($dir));
$dir = dirlist($cfg['basedir']."/web", "file");
do {
if (strstr(current($dir),".php") AND !strstr(current($dir),"phpmyadmin") AND !strstr(current($dir),"phppgadmin") AND !strstr(current($dir),"errors"))
$numlines += count(file(current($dir)));
} while (next($dir));
$content = "<html><head>\n";
$content .= "<title>web://cp status</title>\n";
$content .= "</head><body>\n";
$content .= "<h1>web://cp status</h1>\n";
$content .= "<b>Version:</b> $cfg[webcp]<br>\n";
$content .= "<b>Lines of code:</b> $numlines<br>\n";
$content .= "<b>Started:</b> ".date("r", $httpd_status['started'])."<br>\n";
$content .= "<b>Uptime:</b> $uptime_str<br>\n";
$content .= "<b>Data Served:</b> $sent_str<br>\n";
$content .= "<b>Hits:</b> $httpd_status[hits]<br>\n";
$content .= "<b>Connections:</b> $httpd_status[connections]<br>\n";
$content .= "<b>SSL:</b> ".($cfg['ssl']?'Enabled':'Disabled')."<br>\n";
$content .= "<b>Keep-Alive:</b> ".(($cfg['keep_alive'] && !$cfg['ssl'])?'Enabled':'Disabled')."<br>\n";
$content .= "<p><i>$cfg[sysname]</i></p>\n";
$content .= "</body></html>";
$response['content_length'] = strlen($content);
$headers = build_headers($response);
if (@socket_write($csocket[$slot], $headers.$content) === false) {
webcp_log(1,0,'webcp-httpd',"Failed to send to {$cdata[$slot]['remote_ip']} because: ".socket_strerror(socket_last_error($csocket[$slot])),0,$echo);
close_client($slot);
}
} else {
/* if we had an invalid request */
$content = "<html><head>\r\n";
$content .= "<title>400 Bad Request</title>\r\n";
$content .= "</head><body>\r\n";
$content .= "<h1>Bad Request</h1>\r\n";
$content .= "<p>You sent a malformed header. Goodbye.</p>\r\n";
$content .= "<p><i>$cfg[sysname]</i></p>\r\n";
$content .= "</body></html>";
$response['content_length'] = strlen($content);
$headers = build_headers($response);
if (@socket_write($csocket[$slot], $headers.$content) === false) {
webcp_log(0,0,'webcp-httpd',"Failed to send to {$cdata[$slot]['remote_ip']} because: ".socket_strerror(socket_last_error($csocket[$slot])),0,$echo);
close_client($slot);
}
}
/* update internal statistics */
$httpd_status['hits']++;
$httpd_status['sent'] += strlen($headers) + $response['content_length'];
// log this request:
log_http_request($cdata[$slot], $response, $request);
}
function load_page($client, $response, $request, &$header_redirect, &$send_headers) {
/* Interpret a seperate php file and return it in a variable. */
global $cfg, $rx, $userdata, $T, $echo;
/* make sure this script won't crash the server */
if ($cfg['validate_php']) {
if (!validate($response['translated_file'])) {
return "Error: Requested file failed webcp-httpd compatiblity validation.";
}
}
/* copy globals, work around for not being able to use $GLOBALS directly due to recursion */
foreach ($GLOBALS AS $key => $val) {
if ($key !== 'GLOBALS') {
$CLI_GLOBALS[$key] = $val;
}
}
$_SERVER = array();
$_REQUEST = array();
/* pass HTTP_SERVER_VARS array to subscript */
$_SERVER['HTTP_HOST'] = $cfg['sysname'];
$_SERVER['HTTP_USER_AGENT'] = (isset($request['user_agent']) ? $request['user_agent'] : '');
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = (isset($request['language']) ? $request['language'] : '');
$_SERVER['SERVER_SOFTWARE'] = "web://cp $cfg[webcp] php/".phpversion();
$_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST'];
$_SERVER['SERVER_ADDR'] = $client['local_ip'];
$_SERVER['SERVER_PORT'] = $cfg['port'];
if ($cfg['ssl']) {
$_SERVER['HTTPS'] = 'on';
$_SERVER['REMOTE_ADDR'] = $request['remote_ip'];
} else {
$_SERVER['REMOTE_ADDR'] = $client['remote_ip'];
$_SERVER['REMOTE_PORT'] = $client['remote_port'];
}
$_SERVER['SERVER_PROTOCOL'] = $response['protocol'];
$_SERVER['DOCUMENT_ROOT'] = $cfg['basedir'].'/web';
$_SERVER['SCRIPT_FILENAME'] = $response['translated_file'];
$_SERVER['REQUEST_METHOD'] = $request['method'];
$_SERVER['QUERY_STRING'] = $request['query_string'];
$_SERVER['REQUEST_URI'] = $request['uri'];
$_SERVER['SCRIPT_NAME'] = substr($response['translated_file'], strlen($cfg['basedir'].'/web'));
$_SERVER['PHP_SELF'] = $_SERVER['SCRIPT_NAME'];
$HTTP_SERVER_VARS = $_SERVER;
/* pass cookie array to subscript */
$_COOKIE = $request['cookie'];
$HTTP_COOKIE_VARS = $_COOKIE;
$_REQUEST = array_merge($_REQUEST, $_COOKIE);
/* pass post array to subscript */
$_POST = $request['post'];
$HTTP_POST_VARS = $_POST;
$_REQUEST = array_merge($_REQUEST, $_POST);
/* pass get array to subscript */
$_GET = $request['get'];
$HTTP_GET_VARS = $_GET;
$_REQUEST = array_merge($_REQUEST, $_GET);
/* pass HTTP_ENV_VARS array to subscript */
$HTTP_ENV_VARS = $_ENV;
/* pass files array to subscript */
$_FILES = $request['files'];
$HTTP_POST_FILES = $_FILES;
/* emulate register_globals turned on */
extract($_SERVER);
extract($_REQUEST);
extract($_ENV);
extract($_FILES);
/* set error reporting to the php.ini default */
restore_error_handler();
$default_error_level = get_cfg_var('error_reporting');
$error_level = error_reporting($default_error_level);
/* evaluate the requested script and cache it to a variable. */
ob_start();
$result = include($response['translated_file']);
$page = ob_get_contents();
ob_end_clean();
/* restore error reporting */
set_error_handler('error_handler');
error_reporting($error_level);
/* See if there is anything returned from running the script. */
if (strlen($result) > 1) {
/* if so, print it out */
$page .= $result;
}
/* remove temporary files that were uploaded by client */
foreach($_FILES AS $val) {
/* make sure that this script can still access the file */
if (is_writable($val['tmp_name'])) {
unlink($val['tmp_name']);
}
}
/* return header info by reference */
$header_redirect = (isset($GLOBALS['header_redirect']) ? $GLOBALS['header_redirect'] : false);
$send_headers = (isset($GLOBALS['send_headers']) ? $GLOBALS['send_headers'] : false);
/* clear all our global variables */
foreach ($GLOBALS AS $key => $val) {
if ($key !== 'GLOBALS') {
if (is_array($GLOBALS[$key])) {
$GLOBALS[$key] = array();
}
unset($GLOBALS[$key]);
}
}
/* restore all global variables to our pre load_page() state */
foreach ($CLI_GLOBALS AS $key => $val) {
$GLOBALS[$key] = $val;
}
return $page;
}
function socket_write_file($slot, $file, $client) {
/* send a file line by line. */
global $csocket, $echo;
$fp = fopen($file, "rb");
while(!feof($fp)) {
if (@socket_write($csocket[$slot], fread($fp, MAX_LENGTH)) === false) {
webcp_log(0,0,'webcp-httpd',"Failed to send to $client[remote_ip] because: ".socket_strerror(socket_last_error($csocket[$slot])),0,$echo);
close_client($slot);
break;
}
}
fclose($fp);
}
function mime_type($file_type) {
/* return the correct mime type based on the file type. */
switch($file_type) {
case 'php':
case 'html':
return 'text/html';
case 'jpg':
case 'jpeg':
return 'image/jpeg';
case 'png':
return 'image/png';
case 'gif':
return 'image/gif';
default:
return 'text/plain';
}
}
function parse_query($query_line, $delimiter='&') {
/* take a query line from a POST, GET or COOKIE and return an
* array holding each query name and value */
if ($delimiter !== '&') {
$query_line = str_replace($delimiter, '&', $query_line);
}
parse_str(trim($query_line), $result);
return $result;
}
function parse_multi_part(&$body, $boundary, &$post, &$files) {
/* This function will parse posted multipart form data
* into temporary files or post queries which will be
* passed back by reference */
// split the multiple parts into an array
$body = preg_split("/(\r\n)?--$boundary(--)?(\r\n)?/", $body, -1, PREG_SPLIT_NO_EMPTY);
// cycle through the parts and parse each one
foreach($body AS $part) {
$part = explode("\r\n\r\n", $part);
if (count($part) < 2) {
$part[1] = '';
}
// separate the part's headers into an array
$part[0] = explode("\r\n", $part[0]);
foreach($part[0] AS $val) {
$arr = explode(':', $val, 2);
if (count($arr) > 1) {
$mime_header[strtolower($arr[0])] = trim($arr[1]);
}
}
// separate all attributes of the content disposition into an array
if (isset($mime_header['content-disposition'])) {
$attribute = explode(';', $mime_header['content-disposition']);
foreach ($attribute AS $val) {
$arr = explode('=', trim($val));
if (count($arr) > 1) {
$cont_disp[strtolower($arr[0])] = trim($arr[1], '"');
}
}
}
// check and see if this part is a file
if (!empty($cont_disp['filename'])) {
// generate a temporary name and get the size
$tmp_name = '/tmp/php'.uniqid(getmypid());
$size = strlen($part[1]);
// add it and it's associated data to the files array that we're going to need
$files[$cont_disp['name']]['name'] = $cont_disp['filename'];
$files[$cont_disp['name']]['tmp_name'] = $tmp_name;
$files[$cont_disp['name']]['size'] = $size;
if (isset($mime_header['content-type'])) {
$files[$cont_disp['name']]['type'] = $mime_header['content-type'];
}
// save the uploaded file to the tmp directory
// we will delete the file when execution of this
// request is finished.
if ($fp = fopen($tmp_name, 'w')) {
fwrite($fp, $part[1], $size);
fclose($fp);
}
// if it's not a file it must be a posted query
} elseif (!empty($part[1])) {
if (isset($cont_disp['name'])) {
$post[$cont_disp['name']] = $part[1];
}
}
}
}
function set_pid_file($pid) {
/* handle pid file details */
global $cfg, $user, $echo;
/* check for an existing pid file */
if (file_exists($cfg['httpd_pid']) && !is_dir($cfg['httpd_pid'])) {
$fp = fopen($cfg['httpd_pid'], "r");
$old_pid = fgets($fp, MAX_LENGTH);
fclose($fp);
/* if there is a corresponding process, cancel execution. */
if ($old_pid > 0 && posix_kill($old_pid, 0)) {
webcp_log(0,0,'webcp-httpd',"Server already running with PID: $old_pid",0,$echo);
exit;
}
}
/* create a pid file */
if ($fp = fopen($cfg['httpd_pid'], "w")) {
fputs($fp, $pid);
fclose($fp);
/* make sure we can delete the pid file later */
chown($cfg['httpd_pid'], $user['uid']);
chgrp($cfg['httpd_pid'], $user['gid']);
chmod($cfg['httpd_pid'], 0644);
} else {
webcp_log(0,0,'webcp-httpd',"Unable to write to $cfg[httpd_pid]",0,$echo);
exit;
}
}
function validate($file) {
/* This function will parse a php file line by line looking for incompatible
* statements. I think this check will go faster if we load the whole file into
* memory at once rather than reading it line by line. Since this will only be
* done once per file the memory use is acceptible. */
global $validated, $echo, $cfg;
/* if this file was already tested return the results */
if (isset($validated[$file]) ) {
return $validated[$file];
}
$content = file($file);
/* check for contraband */
/* This needs to be much improved. Some regex/pcre guru should
* fix these to actually take all cases into account and not
* cause false positives, like we should ignore if within blocked
* comments */
foreach ($content as $num => $line) {
$num++;
/* functions are declared more than once causing a fatal error. */
if (preg_match("/(?!\/\/).*?function\s+.*?\(/i", $line)) {
$validated[$file] = false;
webcp_log(0,0,'webcp-httpd',"Error: $file has a function declaration on line ".$num,0,$echo);
break;
}
/* require will kill the server if the file isn't found */
if (preg_match("/(?!\/\/).*?require(_once)?[\s|\(]/i", $line)) {
$validated[$file] = false;
webcp_log(0,0,'webcp-httpd',"Error: $file has a require() on line ".$num,0,$echo);
break;
}
/* exit kills the server */
if (preg_match("/(?!\/\/).*?exit[\s|\(|;]/i", $line)) {
$validated[$file] = false;
webcp_log(0,0,'webcp-httpd',"Error: $file has an exit on line ".$num,0,$echo);
break;
}
/* die kills the server */
if (preg_match("/(?!\/\/).*?die[\s|\(|;]/i", $line)) {
$validated[$file] = false;
webcp_log(0,0,'webcp-httpd',"Error: $file has a die on line ".$num,0,$echo);
break;
}
/* set_time_limit kills the server after x many seconds */
if (preg_match("/(?!\/\/).*?set_time_limit[\s|\(]/i", $line)) {
$validated[$file] = false;
webcp_log(0,0,'webcp-httpd',"Error: $file has a set_time_limit() call on line ".$num,0,$echo);
break;
}
/* see if they've included any other files and parse them. if they used a
* variable in the path we ignore it. we should catch at least some
* includes and chances are we'll find something somewhere down the line
* if the program isn't compatible. If not, oh well. Kaboom! */
if (ereg("include(_once)?.*\((.+)\)", $line, $match)) {
if (strpos($match[2], '$') === false) {
if ($include = realpath(trim($match[2], "\"\'"))) {
if (!validate($include)) {
$validated[$file] = false;
break;
}
}
}
}
}
/* if we made it this far without an error then the file is validated */
if (!isset($validated[$file])) {
$validated[$file] = true;
}
return $validated[$file];
}
function log_http_request($client, $response, $request){
global
$cfg,
$http_status_map,
$echo;
/*
Log this hit using the Common Log Format:
Equivalent to LogFormat:
LogFormat "%h %l %u %t \"%r\" %>s %b" common
which puts out log lines like:
127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326
see: http://httpd.apache.org/docs/mod/mod_log_config.html#formats for details
*/
$tmp=split(" ", $http_status_map[$response['status']]);
$status_code=$tmp[0];
// Leave the user field for now. Could be done?
$clf_log= "$client[remote_ip] - - ";
$clf_log.= "[".gmdate("d/M/Y H:i:s T")."] ";
$clf_log.= "\"$request[method] $request[uri] $request[protocol]\" $status_code $response[content_length]\n";
if( isset($echo) && $echo == 1 ) {
print $clf_log."\r\n";
}
$fp=fopen($cfg["accesslog"], "a");
if(!$fp){
webcp_log(0,0,'webcp-httpd',"Non-Fatal Error: could not open $cfg[accesslog] for writing",0,$echo);
} else {
fwrite($fp, $clf_log);
fclose($fp);
}
return 1;
}
?>