<?php
/**********************************************************\
| |
| The implementation of PHPRPC Protocol 3.0 |
| |
| phprpc_server.php |
| |
| Release 3.0.0 beta 5 |
| Copyright (c) 2005-2007 by Team-PHPRPC |
| |
| WebSite: http://www.phprpc.org/ |
| http://www.phprpc.net/ |
| http://www.phprpc.com/ |
| http://sourceforge.net/projects/php-rpc/ |
| |
| Authors: Ma Bingyao <andot@ujn.edu.cn> |
| |
| This file may be distributed and/or modified under the |
| terms of the GNU Lesser General Public License (LGPL) |
| version 3.0 as published by the Free Software Foundation |
| and appearing in the included file LICENSE. |
| |
\**********************************************************/
/* PHPRPC Server for PHP.
*
* Copyright (C) 2007 Ma Bingyao <andot@ujn.edu.cn>
* Version: 3.0.0 beta 5
* LastModified: May 13, 2007
* This library is free. You can redistribute it and/or modify it.
*
/*
* Interfaces
*
* function add($a, $b) {
* return $a + $b;
* }
* function sub($a, $b) {
* return $a - $b;
* }
* function inc(&$n) {
* return $n++;
* }
* include('phprpc_server.php');:
* $server = new PHPRPC_Server();
* $server->add(array('add', 'sub'));
* $server->add('inc');
* $server->setCharset('UTF-8');
* $server->setDebugMode(true);
* $server->start();
*
*/
class PHPRPC_Server {
var $callback;
var $charset;
var $encode;
var $ref;
var $encrypt;
var $enableGZIP;
var $debug;
var $keylen;
var $key;
var $errno;
var $errstr;
var $functions;
var $cid;
var $buffer;
// Private Methods
function addJsSlashes($str, $flag) {
if ($flag) {
$str = addcslashes($str, "\0..\006\010..\012\014..\037\042\047\134\177..\377");
}
else {
$str = addcslashes($str, "\0..\006\010..\012\014..\037\042\047\134");
}
return str_replace(array(chr(7), chr(11)), array('\007', '\013'), $str);
}
function encodeString($str, $flag = true) {
if ($this->encode) {
return base64_encode($str);
}
else {
return $this->addJsSlashes($str, $flag);
}
}
function encryptString($str, $level) {
if ($this->encrypt >= $level) {
$str = xxtea_encrypt($str, $this->key);
}
return $str;
}
function decryptString($str, $level) {
if ($this->encrypt >= $level) {
$str = xxtea_decrypt($str, $this->key);
}
return $str;
}
function sendHeader() {
header("HTTP/1.1 200 OK");
header("Content-Type: text/plain; charset={$this->charset}");
header("X-Powered-By: PHPRPC Server/3.0");
header('Expires: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
}
function getRequestURL() {
if (!isset($_SERVER['HTTPS']) ||
$_SERVER['HTTPS'] == 'off' ||
$_SERVER['HTTPS'] == '') {
$scheme = 'http';
}
else {
$scheme = 'https';
}
$host = $_SERVER['SERVER_NAME'];
$port = $_SERVER['SERVER_PORT'];
$path = $_SERVER['SCRIPT_NAME'];
return $scheme . '://' . $host . (($port == 80) ? '' : ':' . $port) . $path;
}
function sendURL() {
if (SID != "") {
$url = $this->getRequestURL();
if (count($_GET) > 0) {
$url .= '?' . strip_tags(SID);
foreach ($_GET as $key => $value) {
if (strpos(strtolower($key), 'phprpc_') !== 0) {
$url .= '&' . $key . '=' . urlencode($value);
}
}
}
$this->buffer .= "phprpc_url=\"" . $this->encodeString($url) . "\";\r\n";
}
}
function gzip($buffer) {
$len = strlen($buffer);
if ($this->enableGZIP && strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip,deflate')) {
$gzbuffer = gzencode($buffer);
$gzlen = strlen($gzbuffer);
if ($len > $gzlen) {
header("Content-Length: $gzlen");
header("Content-Encoding: gzip");
return $gzbuffer;
}
}
header("Content-Length: $len");
return $buffer;
}
function sendCallback() {
$this->buffer .= $this->callback;
echo $this->gzip($this->buffer);
ob_end_flush();
restore_error_handler();
if (function_exists('restore_exception_handler')) {
restore_exception_handler();
}
exit();
}
function sendFunctions() {
$this->buffer .= "phprpc_functions=\"" . $this->encodeString(serialize(array_keys($this->functions))) . "\";\r\n";
$this->sendCallback();
}
function sendOutput($output) {
if ($this->encrypt >= 3) {
$this->buffer .= "phprpc_output=\"" . $this->encodeString(xxtea_encrypt($output, $this->key)) . "\";\r\n";
}
else {
$this->buffer .= "phprpc_output=\"" . $this->encodeString($output, false) . "\";\r\n";
}
}
function sendError($output = NULL) {
if (is_null($output)) {
$output = ob_get_clean();
}
$this->buffer .= "phprpc_errno=\"{$this->errno}\";\r\n";
$this->buffer .= "phprpc_errstr=\"" . $this->encodeString($this->errstr, false) . "\";\r\n";
$this->sendOutput($output);
$this->sendCallback();
}
function fatalErrorHandler($buffer) {
if (preg_match('/<b>(.*?) error<\/b>:(.*?)<br/', $buffer, $match)) {
if ($match[1] == 'Fatal') {
$errno = E_ERROR;
}
else {
$errno = E_COMPILE_ERROR;
}
if ($this->debug) {
$errstr = preg_replace('/<.*?>/', '', $match[2]);
}
else {
$errstr = preg_replace('/ in <b>.*<\/b>$/', '', $match[2]);
}
$buffer = "phprpc_errno=\"{$errno}\";\r\n" .
"phprpc_errstr=\"" . $this->encodeString(trim($errstr), false) . "\";\r\n" .
"phprpc_output=\"\";\r\n" .
$this->callback;
$buffer = $this->gzip($buffer);
}
return $buffer;
}
function errorHandler($errno, $errstr, $errfile, $errline) {
if ($this->debug) {
$errstr .= " in $errfile on line $errline";
}
if (($errno == E_ERROR) or ($errno == E_CORE_ERROR) or
($errno == E_COMPILE_ERROR) or ($errno == E_USER_ERROR)) {
$this->errno = $errno;
$this->errstr = $errstr;
$this->sendError();
}
else {
if (($errno == E_NOTICE) or ($errno == E_USER_NOTICE)) {
if ($this->errno == 0) {
$this->errno = $errno;
$this->errstr = $errstr;
}
}
else {
if (($this->errno == 0) or
($this->errno == E_NOTICE) or
($this->errno == E_USER_NOTICE)) {
$this->errno = $errno;
$this->errstr = $errstr;
}
}
}
return true;
}
function exceptionHandler($exception) {
$this->errno = $exception->getCode();
$this->errstr = $exception->getMessage();
if ($this->debug) {
$this->errstr .= "\nfile: " . $exception->getFile() .
"\nline: " . $exception->getLine() .
"\ntrace: " . $exception->getTraceAsString();
}
$this->sendError();
}
function initErrorHandler() {
$this->errno = 0;
$this->errstr = "";
set_error_handler(array(&$this, 'errorHandler'));
if (function_exists('set_exception_handler')) {
set_exception_handler(array(&$this, 'exceptionHandler'));
}
}
function call($function, &$args) {
if ($this->ref) {
$arguments = array();
for ($i = 0; $i < count($args); $i++) {
$arguments[$i] = &$args[$i];
}
}
else {
$arguments = $args;
}
return call_user_func_array($function, $arguments);
}
function getRequest($name) {
$result = $_REQUEST[$name];
if (get_magic_quotes_gpc()) {
$result = stripslashes($result);
}
return $result;
}
function getBooleanRequest($name) {
$var = true;
if (isset($_REQUEST[$name])) {
$var = strtolower($this->getRequest($name));
if ($var == "false") {
$var = false;
}
}
return $var;
}
function initEncode() {
$this->encode = $this->getBooleanRequest('phprpc_encode');
}
function initRef() {
$this->ref = $this->getBooleanRequest('phprpc_ref');
}
function initCallback() {
if (isset($_REQUEST['phprpc_callback'])) {
$this->callback = base64_decode($this->getRequest('phprpc_callback'));
}
else {
$this->callback = "";
}
}
function initKeylen() {
if (isset($_REQUEST['phprpc_keylen'])) {
$this->keylen = (int)$this->getRequest('phprpc_keylen');
}
else if (isset($_SESSION[$this->cid]['keylen'])){
$this->keylen = $_SESSION[$this->cid]['keylen'];
}
else {
$this->keylen = 128;
}
}
function initClientID() {
$this->cid = 0;
if (isset($_REQUEST['phprpc_id'])) {
$this->cid = $this->getRequest('phprpc_id');
}
$this->cid = "phprpc_" . $this->cid;
}
function initEncrypt() {
$this->encrypt = false;
if (isset($_REQUEST['phprpc_encrypt'])) {
$this->encrypt = $this->getRequest('phprpc_encrypt');
if ($this->encrypt === "true") $this->encrypt = true;
if ($this->encrypt === "false") $this->encrypt = false;
}
}
function initKey() {
if (isset($_SESSION[$this->cid]['key'])) {
$this->key = $_SESSION[$this->cid]['key'];
require_once('xxtea.php');
}
else if ($this->encrypt > 0) {
$this->errno = E_ERROR;
$this->errstr = "Can't find the key for decryption.";
$this->encrypt = 0;
$this->sendError();
}
}
function getArguments() {
if (isset($_REQUEST['phprpc_args'])) {
$arguments = unserialize($this->decryptString(base64_decode($this->getRequest('phprpc_args')), 1));
}
else {
$arguments = array();
}
return $arguments;
}
function callFunction() {
$function = strtolower($this->getRequest('phprpc_func'));
if (array_key_exists($function, $this->functions)) {
$this->initKey();
$function = $this->functions[$function];
$arguments = $this->getArguments();
$result = $this->encodeString($this->encryptString(serialize($this->call($function, $arguments)), 2));
$output = ob_get_clean();
$this->buffer .= "phprpc_result=\"$result\";\r\n";
if ($this->ref) {
$arguments = $this->encodeString($this->encryptString(serialize($arguments), 1));
$this->buffer .= "phprpc_args=\"$arguments\";\r\n";
}
}
else {
$this->errno = E_ERROR;
$this->errstr = "Can't find this function $function().";
$output = ob_get_clean();
}
$this->sendError($output);
}
function keyExchange() {
require_once('bigint.php');
$this->initKeylen();
if ($this->encrypt === true) {
require_once('dhparams.php');
$DHParams = new DHParams($this->keylen);
$this->keylen = $DHParams->getL();
$encrypt = $DHParams->getDHParams();
$x = bigint_random($this->keylen - 1, true);
if (!isset($_SESSION[$this->cid])) {
$_SESSION[$this->cid] = array();
}
$_SESSION[$this->cid]['x'] = bigint_num2dec($x);
$_SESSION[$this->cid]['p'] = $encrypt['p'];
$_SESSION[$this->cid]['keylen'] = $this->keylen;
$encrypt['y'] = bigint_num2dec(bigint_powmod(bigint_dec2num($encrypt['g']), $x, bigint_dec2num($encrypt['p'])));
$this->buffer .= "phprpc_encrypt=\"" . $this->encodeString(serialize($encrypt)) . "\";\r\n";
if ($this->keylen != 128) {
$this->buffer .= "phprpc_keylen=\"{$this->keylen}\";\r\n";
}
$this->sendURL();
}
else {
$y = bigint_dec2num($this->encrypt);
$x = bigint_dec2num($_SESSION[$this->cid]['x']);
$p = bigint_dec2num($_SESSION[$this->cid]['p']);
$key = bigint_powmod($y, $x, $p);
if ($this->keylen == 128) {
$key = bigint_num2str($key);
}
else {
$key = pack('H*', md5(bigint_num2dec($key)));
}
$_SESSION[$this->cid]['key'] = str_pad($key, 16, "\0", STR_PAD_LEFT);
}
$this->sendCallback();
}
function initSession() {
@ob_start();
ob_implicit_flush(0);
session_start();
}
function initOutputBuffer() {
@ob_start(array(&$this, "fatalErrorHandler"));
ob_implicit_flush(0);
$this->buffer = "";
}
// Public Methods
function PHPRPC_Server() {
require_once('compat.php');
$this->functions = array();
$this->charset = 'UTF-8';
$this->debug = false;
$this->enableGZIP = false;
}
function add($functions, $obj = NULL, $aliases = NULL) {
if (is_null($functions) || (gettype($functions) != gettype($aliases) && !is_null($aliases))) {
return false;
}
if (is_object($functions)) {
$obj = $functions;
$functions = get_class_methods(get_class($obj));
$aliases = $functions;
}
if (is_null($aliases)) {
$aliases = $functions;
}
if (is_string($functions)) {
if (is_null($obj)) {
$this->functions[strtolower($aliases)] = $functions;
}
else if (is_object($obj)) {
$this->functions[strtolower($aliases)] = array(&$obj, $functions);
}
else if (is_string($obj)) {
$this->functions[strtolower($aliases)] = array($obj, $functions);
}
}
else {
if (count($functions) != count($aliases)) {
return false;
}
foreach ($functions as $key => $function) {
$this->add($function, $obj, $aliases[$key]);
}
}
return true;
}
function setCharset($charset) {
$this->charset = $charset;
}
function setDebugMode($debug) {
$this->debug = $debug;
}
function setEnableGZIP($enableGZIP) {
$this->enableGZIP = $enableGZIP;
}
function start() {
while(ob_get_length() !== false) @ob_end_clean();
$this->initOutputBuffer();
$this->sendHeader();
$this->initErrorHandler();
$this->initEncode();
$this->initCallback();
$this->initRef();
$this->initClientID();
$this->initEncrypt();
if (isset($_REQUEST['phprpc_func'])) {
$this->callFunction();
}
else if ($this->encrypt != false) {
$this->keyExchange();
}
else {
$this->sendFunctions();
}
}
}
PHPRPC_Server::initSession();
?>