phprpc_client.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. <?php
  2. /**********************************************************\
  3. | |
  4. | The implementation of PHPRPC Protocol 3.0 |
  5. | |
  6. | phprpc_client.php |
  7. | |
  8. | Release 3.0.1 |
  9. | Copyright by Team-PHPRPC |
  10. | |
  11. | WebSite: http://www.phprpc.org/ |
  12. | http://www.phprpc.net/ |
  13. | http://www.phprpc.com/ |
  14. | http://sourceforge.net/projects/php-rpc/ |
  15. | |
  16. | Authors: Ma Bingyao <andot@ujn.edu.cn> |
  17. | |
  18. | This file may be distributed and/or modified under the |
  19. | terms of the GNU General Public License (GPL) version |
  20. | 2.0 as published by the Free Software Foundation and |
  21. | appearing in the included file LICENSE. |
  22. | |
  23. \**********************************************************/
  24. /* PHPRPC Client for PHP.
  25. *
  26. * Copyright: Ma Bingyao <andot@ujn.edu.cn>
  27. * Version: 3.0
  28. * LastModified: Apr 12, 2010
  29. * This library is free. You can redistribute it and/or modify it under GPL.
  30. *
  31. /*
  32. * Interfaces
  33. *
  34. * $rpc_client = new PHPRPC_Client();
  35. * $rpc_client->setProxy(NULL);
  36. * $rpc_client->useService('http://www.phprpc.org/server.php');
  37. * $rpc_client->setKeyLength(1024);
  38. * $rpc_client->setEncryptMode(3);
  39. * $args = array(1, 2);
  40. * echo $rpc_client->invoke('add', &$args);
  41. * echo "<br />";
  42. * $n = 3;
  43. * $args = array(&$n);
  44. * echo $rpc_client->invoke('inc', &$args, true);
  45. * echo "<br />";
  46. * echo $rpc_client->sub(3, 2);
  47. * echo "<br />";
  48. * // error handle
  49. * $result = $rpc_client->mul(1, 2); // no mul function
  50. * if (is_a($result, "PHPRPC_Error")) {
  51. * echo $result->toString();
  52. * }
  53. */
  54. $_PHPRPC_COOKIES = array();
  55. $_PHPRPC_COOKIE = '';
  56. $_PHPRPC_SID = 0;
  57. if (defined('KEEP_PHPRPC_COOKIE_IN_SESSION')) {
  58. if (isset($_SESSION['phprpc_cookies']) and isset($_SESSION['phprpc_cookie'])) {
  59. $_PHPRPC_COOKIES = $_SESSION['phprpc_cookies'];
  60. $_PHPRPC_COOKIE = $_SESSION['phprpc_cookie'];
  61. }
  62. function keep_phprpc_cookie_in_session() {
  63. global $_PHPRPC_COOKIES, $_PHPRPC_COOKIE;
  64. $_SESSION['phprpc_cookies'] = $_PHPRPC_COOKIES;
  65. $_SESSION['phprpc_cookie'] = $_PHPRPC_COOKIE;
  66. }
  67. register_shutdown_function('keep_phprpc_cookie_in_session');
  68. }
  69. class PHPRPC_Error {
  70. var $Number;
  71. var $Message;
  72. function PHPRPC_Error($errno, $errstr) {
  73. $this->Number = $errno;
  74. $this->Message = $errstr;
  75. }
  76. function toString() {
  77. return $this->Number . ":" . $this->Message;
  78. }
  79. function __toString() {
  80. return $this->toString();
  81. }
  82. function getNumber() {
  83. return $this->Number;
  84. }
  85. function getMessage() {
  86. return $this->Message;
  87. }
  88. }
  89. class _PHPRPC_Client {
  90. var $_server;
  91. var $_timeout;
  92. var $_output;
  93. var $_warning;
  94. var $_proxy;
  95. var $_key;
  96. var $_keylen;
  97. var $_encryptMode;
  98. var $_charset;
  99. var $_socket;
  100. var $_clientid;
  101. var $_http_version;
  102. var $_keep_alive;
  103. // Public Methods
  104. function _PHPRPC_Client($serverURL = '') {
  105. global $_PHPRPC_SID;
  106. require_once('compat.php');
  107. //register_shutdown_function(array(&$this, "_disconnect"));
  108. $this->_proxy = NULL;
  109. $this->_timeout = 30;
  110. $this->_clientid = 'php' . rand(1 << 30, 1 << 31) . time() . $_PHPRPC_SID;
  111. $_PHPRPC_SID++;
  112. $this->_socket = false;
  113. if ($serverURL != '') {
  114. $this->useService($serverURL);
  115. }
  116. }
  117. function useService($serverURL, $username = NULL, $password = NULL) {
  118. $this->_disconnect();
  119. $this->_http_version = "1.1";
  120. $this->_keep_alive = true;
  121. $this->_server = array();
  122. $this->_key = NULL;
  123. $this->_keylen = 128;
  124. $this->_encryptMode = 0;
  125. $this->_charset = 'utf-8';
  126. $urlparts = parse_url($serverURL);
  127. if (!isset($urlparts['host'])) {
  128. if (isset($_SERVER["HTTP_HOST"])) {
  129. $urlparts['host'] = $_SERVER["HTTP_HOST"];
  130. }
  131. else if (isset($_SERVER["SERVER_NAME"])) {
  132. $urlparts['host'] = $_SERVER["SERVER_NAME"];
  133. }
  134. else {
  135. $urlparts['host'] = "localhost";
  136. }
  137. if (!isset($_SERVER["HTTPS"]) ||
  138. $_SERVER["HTTPS"] == "off" ||
  139. $_SERVER["HTTPS"] == "") {
  140. $urlparts['scheme'] = "http";
  141. }
  142. else {
  143. $urlparts['scheme'] = "https";
  144. }
  145. $urlparts['port'] = $_SERVER["SERVER_PORT"];
  146. }
  147. if (!isset($urlparts['port'])) {
  148. if ($urlparts['scheme'] == "https") {
  149. $urlparts['port'] = 443;
  150. }
  151. else {
  152. $urlparts['port'] = 80;
  153. }
  154. }
  155. if (!isset($urlparts['path'])) {
  156. $urlparts['path'] = "/";
  157. }
  158. else if (($urlparts['path']{0} != '/') && ($_SERVER["PHP_SELF"]{0} == '/')) {
  159. $urlparts['path'] = substr($_SERVER["PHP_SELF"], 0, strrpos($_SERVER["PHP_SELF"], '/') + 1) . $urlparts['path'];
  160. }
  161. if (isset($urlparts['query'])) {
  162. $urlparts['path'] .= '?' . $urlparts['query'];
  163. }
  164. if (!isset($urlparts['user']) || !is_null($username)) {
  165. $urlparts['user'] = $username;
  166. }
  167. if (!isset($urlparts['pass']) || !is_null($password)) {
  168. $urlparts['pass'] = $password;
  169. }
  170. $this->_server['scheme'] = $urlparts['scheme'];
  171. $this->_server['host'] = $urlparts['host'];
  172. $this->_server['port'] = $urlparts['port'];
  173. $this->_server['path'] = $urlparts['path'];
  174. $this->_server['user'] = $urlparts['user'];
  175. $this->_server['pass'] = $urlparts['pass'];
  176. }
  177. function setProxy($host, $port = NULL, $username = NULL, $password = NULL) {
  178. if (is_null($host)) {
  179. $this->_proxy = NULL;
  180. }
  181. else {
  182. if (is_null($port)) {
  183. $urlparts = parse_url($host);
  184. if (isset($urlparts['host'])) {
  185. $host = $urlparts['host'];
  186. }
  187. if (isset($urlparts['port'])) {
  188. $port = $urlparts['port'];
  189. }
  190. else {
  191. $port = 80;
  192. }
  193. if (isset($urlparts['user']) && is_null($username)) {
  194. $username = $urlparts['user'];
  195. }
  196. if (isset($urlparts['pass']) && is_null($password)) {
  197. $password = $urlparts['pass'];
  198. }
  199. }
  200. $this->_proxy = array();
  201. $this->_proxy['host'] = $host;
  202. $this->_proxy['port'] = $port;
  203. $this->_proxy['user'] = $username;
  204. $this->_proxy['pass'] = $password;
  205. }
  206. }
  207. function setKeyLength($keylen) {
  208. if (!is_null($this->_key)) {
  209. return false;
  210. }
  211. else {
  212. $this->_keylen = $keylen;
  213. return true;
  214. }
  215. }
  216. function getKeyLength() {
  217. return $this->_keylen;
  218. }
  219. function setEncryptMode($encryptMode) {
  220. if (($encryptMode >= 0) && ($encryptMode <= 3)) {
  221. $this->_encryptMode = (int)($encryptMode);
  222. return true;
  223. }
  224. else {
  225. $this->_encryptMode = 0;
  226. return false;
  227. }
  228. }
  229. function getEncryptMode() {
  230. return $this->_encryptMode;
  231. }
  232. function setCharset($charset) {
  233. $this->_charset = $charset;
  234. }
  235. function getCharset() {
  236. return $this->_charset;
  237. }
  238. function setTimeout($timeout) {
  239. $this->_timeout = $timeout;
  240. }
  241. function getTimeout() {
  242. return $this->_timeout;
  243. }
  244. function invoke($funcname, &$args, $byRef = false) {
  245. $result = $this->_key_exchange();
  246. if (is_a($result, 'PHPRPC_Error')) {
  247. return $result;
  248. }
  249. $request = "phprpc_func=$funcname";
  250. if (count($args) > 0) {
  251. $request .= "&phprpc_args=" . base64_encode($this->_encrypt(serialize_fix($args), 1));
  252. }
  253. $request .= "&phprpc_encrypt={$this->_encryptMode}";
  254. if (!$byRef) {
  255. $request .= "&phprpc_ref=false";
  256. }
  257. $request = str_replace('+', '%2B', $request);
  258. $result = $this->_post($request);
  259. if (is_a($result, 'PHPRPC_Error')) {
  260. return $result;
  261. }
  262. $phprpc_errno = 0;
  263. $phprpc_errstr = NULL;
  264. if (isset($result['phprpc_errno'])) {
  265. $phprpc_errno = intval($result['phprpc_errno']);
  266. }
  267. if (isset($result['phprpc_errstr'])) {
  268. $phprpc_errstr = base64_decode($result['phprpc_errstr']);
  269. }
  270. $this->_warning = new PHPRPC_Error($phprpc_errno, $phprpc_errstr);
  271. if (array_key_exists('phprpc_output', $result)) {
  272. $this->_output = base64_decode($result['phprpc_output']);
  273. if ($this->_server['version'] >= 3) {
  274. $this->_output = $this->_decrypt($this->_output, 3);
  275. }
  276. }
  277. else {
  278. $this->_output = '';
  279. }
  280. if (array_key_exists('phprpc_result', $result)) {
  281. if (array_key_exists('phprpc_args', $result)) {
  282. $arguments = unserialize($this->_decrypt(base64_decode($result['phprpc_args']), 1));
  283. for ($i = 0; $i < count($arguments); $i++) {
  284. $args[$i] = $arguments[$i];
  285. }
  286. }
  287. $result = unserialize($this->_decrypt(base64_decode($result['phprpc_result']), 2));
  288. }
  289. else {
  290. $result = $this->_warning;
  291. }
  292. return $result;
  293. }
  294. function getOutput() {
  295. return $this->_output;
  296. }
  297. function getWarning() {
  298. return $this->_warning;
  299. }
  300. function _connect() {
  301. if (is_null($this->_proxy)) {
  302. $host = (($this->_server['scheme'] == "https") ? "ssl://" : "") . $this->_server['host'];
  303. $this->_socket = @pfsockopen($host, $this->_server['port'], $errno, $errstr, $this->_timeout);
  304. }
  305. else {
  306. $host = (($this->_server['scheme'] == "https") ? "ssl://" : "") . $this->_proxy['host'];
  307. $this->_socket = @pfsockopen($host, $this->_proxy['port'], $errno, $errstr, $this->_timeout);
  308. }
  309. if ($this->_socket === false) {
  310. return new PHPRPC_Error($errno, $errstr);
  311. }
  312. stream_set_write_buffer($this->_socket, 0);
  313. socket_set_timeout($this->_socket, $this->_timeout);
  314. return true;
  315. }
  316. function _disconnect() {
  317. if ($this->_socket !== false) {
  318. fclose($this->_socket);
  319. $this->_socket = false;
  320. }
  321. }
  322. function _socket_read($size) {
  323. $content = "";
  324. while (!feof($this->_socket) && ($size > 0)) {
  325. $str = fread($this->_socket, $size);
  326. $content .= $str;
  327. $size -= strlen($str);
  328. }
  329. return $content;
  330. }
  331. function _post($request_body) {
  332. global $_PHPRPC_COOKIE;
  333. $request_body = 'phprpc_id=' . $this->_clientid . '&' . $request_body;
  334. if ($this->_socket === false) {
  335. $error = $this->_connect();
  336. if (is_a($error, 'PHPRPC_Error')) {
  337. return $error;
  338. }
  339. }
  340. if (is_null($this->_proxy)) {
  341. $url = $this->_server['path'];
  342. $connection = "Connection: " . ($this->_keep_alive ? 'Keep-Alive' : 'Close') . "\r\n" .
  343. "Cache-Control: no-cache\r\n";
  344. }
  345. else {
  346. $url = "{$this->_server['scheme']}://{$this->_server['host']}:{$this->_server['port']}{$this->_server['path']}";
  347. $connection = "Proxy-Connection: " . ($this->_keep_alive ? 'keep-alive' : 'close') . "\r\n";
  348. if (!is_null($this->_proxy['user'])) {
  349. $connection .= "Proxy-Authorization: Basic " . base64_encode($this->_proxy['user'] . ":" . $this->_proxy['pass']) . "\r\n";
  350. }
  351. }
  352. $auth = '';
  353. if (!is_null($this->_server['user'])) {
  354. $auth = "Authorization: Basic " . base64_encode($this->_server['user'] . ":" . $this->_server['pass']) . "\r\n";
  355. }
  356. $cookie = '';
  357. if ($_PHPRPC_COOKIE) {
  358. $cookie = "Cookie: " . $_PHPRPC_COOKIE . "\r\n";
  359. }
  360. $content_len = strlen($request_body);
  361. $request =
  362. "POST $url HTTP/{$this->_http_version}\r\n" .
  363. "Host: {$this->_server['host']}:{$this->_server['port']}\r\n" .
  364. "User-Agent: PHPRPC Client 3.0 for PHP\r\n" .
  365. $auth .
  366. $connection .
  367. $cookie .
  368. "Accept: */*\r\n" .
  369. "Accept-Encoding: gzip,deflate\r\n" .
  370. "Content-Type: application/x-www-form-urlencoded; charset={$this->_charset}\r\n" .
  371. "Content-Length: {$content_len}\r\n" .
  372. "\r\n" .
  373. $request_body;
  374. fputs($this->_socket, $request, strlen($request));
  375. while (!feof($this->_socket)) {
  376. $line = fgets($this->_socket);
  377. if (preg_match('/HTTP\/(\d\.\d)\s+(\d+)([^(\r|\n)]*)(\r\n|$)/i', $line, $match)) {
  378. $this->_http_version = $match[1];
  379. $status = (int)$match[2];
  380. $status_message = trim($match[3]);
  381. if ($status != 100 && $status != 200) {
  382. $this->_disconnect();
  383. return new PHPRPC_Error($status, $status_message);
  384. }
  385. }
  386. else {
  387. $this->_disconnect();
  388. return new PHPRPC_Error(E_ERROR, "Illegal HTTP server.");
  389. }
  390. $header = array();
  391. while (!feof($this->_socket) && (($line = fgets($this->_socket)) != "\r\n")) {
  392. $line = explode(':', $line, 2);
  393. $header[strtolower($line[0])][] =trim($line[1]);
  394. }
  395. if ($status == 100) continue;
  396. $response_header = $this->_parseHeader($header);
  397. if (is_a($response_header, 'PHPRPC_Error')) {
  398. $this->_disconnect();
  399. return $response_header;
  400. }
  401. break;
  402. }
  403. $response_body = '';
  404. if (isset($response_header['transfer_encoding']) && (strtolower($response_header['transfer_encoding']) == 'chunked')) {
  405. $s = fgets($this->_socket);
  406. if ($s == "") {
  407. $this->_disconnect();
  408. return array();
  409. }
  410. $chunk_size = (int)hexdec($s);
  411. while ($chunk_size > 0) {
  412. $response_body .= $this->_socket_read($chunk_size);
  413. if (fgets($this->_socket) != "\r\n") {
  414. $this->_disconnect();
  415. return new PHPRPC_Error(1, "Response is incorrect.");
  416. }
  417. $chunk_size = (int)hexdec(fgets($this->_socket));
  418. }
  419. fgets($this->_socket);
  420. }
  421. elseif (isset($response_header['content_length']) && !is_null($response_header['content_length'])) {
  422. $response_body = $this->_socket_read($response_header['content_length']);
  423. }
  424. else {
  425. while (!feof($this->_socket)) {
  426. $response_body .= fread($this->_socket, 4096);
  427. }
  428. $this->_keep_alive = false;
  429. $this->_disconnect();
  430. }
  431. if (isset($response_header['content_encoding']) && (strtolower($response_header['content_encoding']) == 'gzip')) {
  432. $response_body = gzdecode($response_body);
  433. }
  434. if (!$this->_keep_alive) $this->_disconnect();
  435. if ($this->_keep_alive && strtolower($response_header['connection']) == 'close') {
  436. $this->_keep_alive = false;
  437. $this->_disconnect();
  438. }
  439. return $this->_parseBody($response_body);
  440. }
  441. function _parseHeader($header) {
  442. global $_PHPRPC_COOKIE, $_PHPRPC_COOKIES;
  443. if (preg_match('/PHPRPC Server\/([^,]*)(,|$)/i', implode(',', $header['x-powered-by']), $match)) {
  444. $this->_server['version'] = (float)$match[1];
  445. }
  446. else {
  447. return new PHPRPC_Error(E_ERROR, "Illegal PHPRPC server.");
  448. }
  449. if (preg_match('/text\/plain\; charset\=([^,;]*)([,;]|$)/i', $header['content-type'][0], $match)) {
  450. $this->_charset = $match[1];
  451. }
  452. if (isset($header['set-cookie'])) {
  453. foreach ($header['set-cookie'] as $cookie) {
  454. foreach (preg_split('/[;,]\s?/', $cookie) as $c) {
  455. list($name, $value) = explode('=', $c, 2);
  456. if (!in_array($name, array('domain', 'expires', 'path', 'secure'))) {
  457. $_PHPRPC_COOKIES[$name] = $value;
  458. }
  459. }
  460. }
  461. $cookies = array();
  462. foreach ($_PHPRPC_COOKIES as $name => $value) {
  463. $cookies[] = "$name=$value";
  464. }
  465. $_PHPRPC_COOKIE = join('; ', $cookies);
  466. }
  467. if (isset($header['content-length'])) {
  468. $content_length = (int)$header['content-length'][0];
  469. }
  470. else {
  471. $content_length = NULL;
  472. }
  473. $transfer_encoding = isset($header['transfer-encoding']) ? $header['transfer-encoding'][0] : '';
  474. $content_encoding = isset($header['content-encoding']) ? $header['content-encoding'][0] : '';
  475. $connection = isset($header['connection']) ? $header['connection'][0] : 'close';
  476. return array('transfer_encoding' => $transfer_encoding,
  477. 'content_encoding' => $content_encoding,
  478. 'content_length' => $content_length,
  479. 'connection' => $connection);
  480. }
  481. function _parseBody($body) {
  482. $body = explode(";\r\n", $body);
  483. $result = array();
  484. $n = count($body);
  485. for ($i = 0; $i < $n; $i++) {
  486. $p = strpos($body[$i], '=');
  487. if ($p !== false) {
  488. $l = substr($body[$i], 0, $p);
  489. $r = substr($body[$i], $p + 1);
  490. $result[$l] = trim($r, '"');
  491. }
  492. }
  493. return $result;
  494. }
  495. function _key_exchange() {
  496. if (!is_null($this->_key) || ($this->_encryptMode == 0)) return true;
  497. $request = "phprpc_encrypt=true&phprpc_keylen={$this->_keylen}";
  498. $result = $this->_post($request);
  499. if (is_a($result, 'PHPRPC_Error')) {
  500. return $result;
  501. }
  502. if (array_key_exists('phprpc_keylen', $result)) {
  503. $this->_keylen = (int)$result['phprpc_keylen'];
  504. }
  505. else {
  506. $this->_keylen = 128;
  507. }
  508. if (array_key_exists('phprpc_encrypt', $result)) {
  509. $encrypt = unserialize(base64_decode($result['phprpc_encrypt']));
  510. require_once('bigint.php');
  511. require_once('xxtea.php');
  512. $x = bigint_random($this->_keylen - 1, true);
  513. $key = bigint_powmod(bigint_dec2num($encrypt['y']), $x, bigint_dec2num($encrypt['p']));
  514. if ($this->_keylen == 128) {
  515. $key = bigint_num2str($key);
  516. }
  517. else {
  518. $key = pack('H*', md5(bigint_num2dec($key)));
  519. }
  520. $this->_key = str_pad($key, 16, "\0", STR_PAD_LEFT);
  521. $encrypt = bigint_num2dec(bigint_powmod(bigint_dec2num($encrypt['g']), $x, bigint_dec2num($encrypt['p'])));
  522. $request = "phprpc_encrypt=$encrypt";
  523. $result = $this->_post($request);
  524. if (is_a($result, 'PHPRPC_Error')) {
  525. return $result;
  526. }
  527. }
  528. else {
  529. $this->_key = NULL;
  530. $this->_encryptMode = 0;
  531. }
  532. return true;
  533. }
  534. function _encrypt($str, $level) {
  535. if (!is_null($this->_key) && ($this->_encryptMode >= $level)) {
  536. $str = xxtea_encrypt($str, $this->_key);
  537. }
  538. return $str;
  539. }
  540. function _decrypt($str, $level) {
  541. if (!is_null($this->_key) && ($this->_encryptMode >= $level)) {
  542. $str = xxtea_decrypt($str, $this->_key);
  543. }
  544. return $str;
  545. }
  546. }
  547. if (function_exists("overload") && version_compare(phpversion(), "5", "<")) {
  548. eval('
  549. class PHPRPC_Client extends _PHPRPC_Client {
  550. function __call($function, $arguments, &$return) {
  551. $return = $this->invoke($function, $arguments);
  552. return true;
  553. }
  554. }
  555. overload("phprpc_client");
  556. ');
  557. }
  558. else {
  559. class PHPRPC_Client extends _PHPRPC_Client {
  560. function __call($function, $arguments) {
  561. return $this->invoke($function, $arguments);
  562. }
  563. }
  564. }
  565. ?>