<?php
/**
* Serves as a gateway between the browser and the PHP DutchPIPE server
*
* Passes the HTTP request to the PHP server along with its environment and user
* variables, and returns the info retrieved from the PHP server back to the
* user's browser. Used to serve pages, and by the AJAX engine in
* dpclient-js.php. It talks to the PHP server using a fast file socket
* connection.
*
* DutchPIPE version 0.4; PHP version 5
*
* LICENSE: This source file is subject to version 1.0 of the DutchPIPE license.
* If you did not receive a copy of the DutchPIPE license, you can obtain one at
* http://dutchpipe.org/license/1_0.txt or by sending a note to
* hide@address.com, in which case you will be mailed a copy immediately.
*
* @package DutchPIPE
* @subpackage lib
* @author Lennert Stock <hide@address.com>
* @copyright 2006, 2007 Lennert Stock
* @license http://dutchpipe.org/license/1_0.txt DutchPIPE License
* @version Subversion: $Id: dpclient.php 307 2007-09-01 17:16:09Z ls $
* @link http://dutchpipe.org/manual/package/DutchPIPE
* @see dpserver-ini.php, dpserver.php, dpclient.css
*/
/**
* Gets server settings
*/
if (!defined('DPSERVER_HOST_URL')) {
require_once(realpath(dirname($_SERVER['SCRIPT_FILENAME']) . '/..')
. '/config/dpserver-ini.php');
}
/**
* Universe paths used by templates
*/
require_once(DPSERVER_DPUNIVERSE_CONFIG_PATH . 'dpuniverse-ini.php');
/**
* Common functions for templates available to universe objects and dpclient.php
*/
require_once(DPSERVER_LIB_PATH . 'dptemplates.php');
/**
* Provides alternatives to multibyte string functions if not supported
*/
require_once(DPSERVER_LIB_PATH . 'dpmbstring_'
. (!DPSERVER_ENABLE_MBSTRING || !function_exists('mb_strlen')
? 'disabled' : 'enabled') . '.php');
error_reporting(DPSERVER_ERROR_REPORTING);
/**
* Contains a message describing the last error, if any
*/
$gLastErrorMsg = NULL;
/* Deal with AJAX requests and normal page requests */
if ((!isset($_GET['ie6']) || $_GET['ie6'] !== 'yes') && isset($_GET) && (isset($_GET['ajax']) || isset($_GET['method']))) {
/* Output XML or '1' to "keep-alive" */
handle_ajax_request(talk2server());
} else {
/* Output XHTML */
handle_normal_request(talk2server());
}
/**
* Talks to the DutchPIPE server, returns response
*
* @return boolean|string output from the DutchPIPE server, FALSE for error
*/
function talk2server()
{
global $gLastErrorMsg, $header_data;
if (DPSERVER_SOCKET_TYPE === AF_UNIX) {
/* Creates a file socket and connects to it */
if (FALSE === ($socket = socket_create(AF_UNIX, SOCK_STREAM, 0))) {
$gLastErrorMsg = '<h1>' . DPSERVER_SOCKERR_MSG . '</h1>'
. sprintf(dp_text(
"socket_create(): unable to create socket [%u]: %s\n"),
socket_last_error(), socket_strerror(socket_last_error()));
return FALSE;
}
if (FALSE === @socket_connect($socket, DPSERVER_SOCKET_PATH)) {
$gLastErrorMsg = '<h1>' . DPSERVER_SOCKERR_MSG . '</h1>'
. sprintf(dp_text(
"socket_create(): unable to connect [%u]: %s\n"),
socket_last_error(), socket_strerror(socket_last_error()));
return FALSE;
}
} elseif (DPSERVER_SOCKET_TYPE === AF_INET) {
/* Creates a TCP/IP socket and connects to it */
if (FALSE === ($socket = socket_create(AF_INET, SOCK_STREAM,
SOL_TCP))) {
$gLastErrorMsg = '<h1>' . DPSERVER_SOCKERR_MSG . '</h1>'
. sprintf(dp_text(
"socket_create(): unable to create socket [%u]: %s\n"),
socket_last_error(), socket_strerror(socket_last_error()));
return FALSE;
}
if (FALSE === @socket_connect($socket, DPSERVER_SOCKET_ADDRESS,
DPSERVER_SOCKET_PORT)) {
$gLastErrorMsg = '<h1>' . DPSERVER_SOCKERR_MSG . '</h1>'
. sprintf(dp_text(
"socket_create(): unable to connect [%u]: %s\n"),
socket_last_error(), socket_strerror(socket_last_error()));
return FALSE;
}
} else {
$gLastErrorMsg = '<h1>' . DPSERVER_SOCKERR_MSG. '</h1>'
. dp_text('Invalid socket protocol');
return FALSE;
}
/* Talk to the DutchPIPE server and write user variables to it */
if (!isset($_SESSION)) {
$_SESSION = array();
}
if (isset($_FILES['dpuploadimg'])
&& isset($_FILES['dpuploadimg']['tmp_name'])) {
@chmod($_FILES['dpuploadimg']['tmp_name'], 0777);
}
/* Serialize $_SERVER, $_SESSION, ... variables so they can be sent */
$in = serialize(array($_SERVER, $_SESSION, $_COOKIE, $_GET, $_POST,
$_FILES));
if (TRUE === DPSERVER_BASE64_CLIENT2SERVER) {
$in = base64_encode($in);
}
$in = "<vars>$in</vars>\r\nquit\r\n";
socket_write($socket, $in, strlen($in));
/* Read and process server reply, filter header info given by the server,
put the remainder in $output */
$cookie_set = $remove_guest_cookie = $remove_registered_cookie = FALSE;
for ($output = ''; $buf = @socket_read($socket, DPSERVER_CLIENT_CHUNK); ) {
$output .= $buf;
}
$arroutput = explode(chr(0), $output);
$output = '';
foreach ($arroutput as $buf) {
if (!dp_strlen($buf) || isset($newlocation)) {
continue;
}
$bufdec = TRUE === DPSERVER_BASE64_SERVER2CLIENT ? base64_decode($buf)
: $buf;
if (dp_strlen($bufdec) > 11
&& dp_substr($bufdec, 0, 11) == "Set-Login: ") {
$cookie_data = dp_substr($bufdec, 11);
setcookie(DPSERVER_COOKIE_NAME, $cookie_data, FALSE, '/');
$cookie_set = TRUE;
}
elseif (dp_strlen($bufdec) > 17 && FALSE !== ($pos1 = dp_strpos($bufdec,
"<header><![CDATA[")) && FALSE !== ($pos2 = dp_strpos($bufdec,
"]]></header>")) && $pos2 > $pos1 + 12) {
$header_data = dp_substr($bufdec, 0, $pos2);
$header_data = dp_substr($header_data, $pos1 + 17);
header($header_data);
}
elseif (dp_strlen($bufdec) > 17 && FALSE !== ($pos1 = dp_strpos($bufdec,
"<cookie><![CDATA[")) && FALSE !== ($pos2 = dp_strpos($bufdec,
"]]></cookie>")) && $pos2 > $pos1 + 12) {
$cookie_data = dp_substr($bufdec, 0, $pos2);
$cookie_data = dp_substr($cookie_data, $pos1 + 17);
if ($cookie_data == 'removeguest') {
$remove_guest_cookie = TRUE;
}
elseif ($cookie_data == 'removeregistered') {
$remove_registered_cookie = TRUE;
}
else {
setcookie(DPSERVER_COOKIE_NAME, $cookie_data, time()
+ 630720000, '/');
$cookie_set = TRUE;
}
}
elseif (dp_strlen($bufdec) > 19 && FALSE !== ($pos1 = dp_strpos($bufdec,
"<location><![CDATA[")) && FALSE !== ($pos2 = dp_strpos($bufdec,
"]]></location>")) && $pos2 > $pos1 + 14) {
$bufdec = dp_substr($bufdec, 0, $pos2);
$bufdec = dp_substr($bufdec, $pos1 + 19);
$newlocation = $bufdec;
$newlocation = $newlocation == '' ? DPSERVER_CLIENT_DIR
: DPSERVER_CLIENT_URL . "?location=$newlocation";
$output = "<location><![CDATA[$newlocation]]></location>";
continue;
}
else {
$output .= TRUE === DPSERVER_BASE64_SERVER2CLIENT
? base64_decode($buf) : $buf;
}
}
/* Close cocket, return server reply */
socket_close($socket);
if (FALSE === $cookie_set) {
if (FALSE !== $remove_guest_cookie) {
setcookie(DPSERVER_COOKIE_NAME, FALSE, FALSE, '/');
}
if (FALSE !== $remove_registered_cookie) {
setcookie(DPSERVER_COOKIE_NAME, FALSE, time() - 3600, '/');
}
}
if (isset($newlocation)) {
if (!isset($_GET) || !isset($_GET['ajax'])) {
header("Location: $newlocation");
exit;
}
}
return $output;
}
/**
* Handles AJAX requests from dpclient-js.php
*
* @param string $output The output from talk2server()
*/
function handle_ajax_request($output)
{
set_time_limit(round(DPUNIVERSE_LINKDEATH_KICKTIME / 2));
if (FALSE === $output || $output == '1') {
if (is_integer(DPSERVER_APACHE_GZIP_MIN)
&& DPSERVER_APACHE_GZIP_MIN > 1) {
apache_setenv('no-gzip', '1');
}
echo '1';
}
elseif (FALSE === $output || $output == '2') {
if (is_integer(DPSERVER_APACHE_GZIP_MIN)
&& DPSERVER_APACHE_GZIP_MIN > 1) {
apache_setenv('no-gzip', '1');
}
echo '2';
}
else {
if (isset($_GET) && isset($GET['standalone']) && isset($GET['_seq'])
&& $GET['_seq'] == '0') {
$xml2 = simplexml_load_string($str =
'<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>'
. '<dutchpipe>' . $output . '</dutchpipe>');
if (FALSE !== $xml2) {
handle_cookies($xml2);
}
}
if (is_integer(DPSERVER_APACHE_GZIP_MIN)
&& DPSERVER_APACHE_GZIP_MIN > dp_strlen($output)) {
apache_setenv('no-gzip', '1');
}
header('Content-Type: text/xml');
echo "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>
<dutchpipe>$output</dutchpipe>\n";
exit;
}
}
/**
* Sets cookies transmitted with the xml we got from the server
*
* @param string &$xml XML of the output from talk2server()
*/
function handle_cookies(&$xml)
{
foreach ($xml->cookie as $id => $cookie) {
foreach($xml->cookie->attributes() as $attname => $attval) {
switch ($attname) {
case 'name':
$name = $attval;
break;
case 'expire':
$expire = dp_strlen($attval) ? $attval : FALSE;
break;
case 'path':
$path = $attval;
break;
case 'domain':
$domain = $attval;
break;
case 'secure':
$secure = $attval;
break;
default:
break;
}
}
if (!isset($name)) {
break;
}
if (!isset($secure)) {
if (!isset($domain)) {
if (!isset($path)) {
if (!isset($expire)) {
setcookie($name, $cookie, FALSE, '/');
} else {
setcookie($name, $cookie, $expire, '/');
}
} else {
setcookie($name, $cookie,
(!isset($expire) ? FALSE : $expire), $path);
}
} else {
setcookie($name, $cookie, (!isset($expire) ? FALSE : $expire),
(!isset($path) ? '' : $path), $domain);
}
} else {
setcookie($name, $cookie, (!isset($expire) ? FALSE : $expire),
(!isset($path) ? '' : $path), (!isset($domain) ? '' : $domain),
$secure);
}
}
}
/**
* Handles a normal page request
*
* @param string $output The output from talk2server()
*/
function handle_normal_request($output)
{
global $gLastErrorMsg;
if (isset($_GET['ie6']) && 'yes' === $_GET['ie6']) {
return;
}
if (FALSE === $output) {
$body = $gLastErrorMsg;
/* Otherwise serve the page with the retrieved content in it */
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
require_once(DPSERVER_TEMPLATE_PATH . DPSERVER_TEMPLATE_DOWN_FILE);
exit;
}
$xml = simplexml_load_string($str = '<?xml version="1.0" encoding="UTF-8" '
. 'standalone="yes" ?><dutchpipe>' . $output . '</dutchpipe>');
if (FALSE === $xml) {
echo '<pre>' . htmlentities($str) . '</pre>';
exit;
}
$messages = $windows = array();
$body = $dpelements = $scripts = '';
$closetext = dp_text('close');
$inputpersistent = $template_file = FALSE;
handle_cookies($xml);
foreach ($xml->event as $e) {
foreach ($e as $type => $data) {
switch ($type) {
case 'message':
$messages[] = $data;
break;
case 'addDpElement':
$dpelements .= $data->asXML();
break;
case 'removeDpElement':
$dpelements .= $data->asXML();
break;
case 'changeDpElement':
$dpelements .= $data->asXML();
break;
case 'moveDpElement':
$dpelements .= $data->asXML();
break;
case 'stylesheet':
$dpelements .= $data->asXML();
break;
case 'script':
$tmp = $data->asXML();
$pos1 = dp_strpos($tmp, '<![CDATA[');
$pos2 = dp_strpos($tmp, ']]>');
if (FALSE !== $pos1 && FALSE !== $pos2 && $pos1 < $pos2) {
$tmp = dp_substr($tmp, 0, $pos1)
. dp_substr($tmp, $pos1 + 9, $pos2 - $pos1 - 9)
. dp_substr($tmp, $pos2 + 3);
}
$scripts .= $tmp;
break;
case 'div':
foreach ($data->attributes() as $a => $b) {
if ('template' === $a) {
$template_file = $b;
}
}
$body .= str_replace(' template="' . (!$template_file ? ''
: $template_file) . '"', '', $data);
break;
case 'window':
$windows[] = '<div class="dpwindow_default" id="dpwindow">'
. $data . '<p align="right"><a href="javascript:'
. 'close_dpwindow()">' . $closetext . '</a></p></div>';
break;
case 'inputpersistent':
foreach ($data->attributes() as $a => $b) {
if ('persistent' === $a) {
$inputpersistent = $b;
}
}
break;
default:
break;
}
}
}
if (0 === dp_strlen($body)) {
$body = '<h1>' . dp_text('Error fetching page. Invalid page XML.')
. '</h1>';
}
if (dp_strlen($dpelements)) {
$dpelements = " <script type=\"text/javascript\">
function dp_load_xml(text)
{
var xmlDoc = '';
if (window.ActiveXObject) {
xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
xmlDoc.async='false';
xmlDoc.loadXML(text);
}
else if (document.implementation &&
document.implementation.createDocument) {
var parser = new DOMParser();
var xmlDoc=parser.parseFromString(text,'text/xml');
}
else {
/* alert('Your browser cannot handle this script'); */
return false;
}
return xmlDoc;
}
function dp_load_elements()
{
var content = '<?xml version=\"1.0\"?><dutchpipe>"
. "<event count=\"-1\" time=\"-1\">" . addslashes($dpelements)
. "</event></dutchpipe>'; handle_response(dp_load_xml(content));
}
</script>\n";
}
$messages_style = !sizeof($messages) ? ''
: ' style="display: block; padding-top: 12px"';
$windows = implode("\n", $windows);
$messages = implode("\n", $messages);
$subtemplates = dp_get_subtemplates(array('input', 'input_say'),
$template_file);
ob_start();
include($subtemplates['input']);
if ('always' == $inputpersistent) {
$dpinput_say = ob_get_contents();
} else {
$dpinput = ob_get_contents();
}
ob_end_clean();
ob_start();
include($subtemplates['input_say']);
if ('always' == $inputpersistent) {
$dpinput = ob_get_contents();
} else {
$dpinput_say = ob_get_contents();
}
ob_end_clean();
/* Otherwise serve the page with the retrieved content in it */
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
require_once(!$template_file
? DPSERVER_TEMPLATE_PATH . DPSERVER_TEMPLATE_FILE : $template_file);
}
?>