Location: PHPKode > projects > web-cp - Web Hosting Control Panel > webcp/server/webcp-httpd-functions.inc.phps
<?	/*
	// 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;
}
?>
Return current item: web-cp - Web Hosting Control Panel