RPC.php 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099
  1. <?php
  2. // by Edd Dumbill (C) 1999-2001
  3. // <edd@usefulinc.com>
  4. // $Id: RPC.php,v 1.11 2002/10/02 21:10:19 ssb Exp $
  5. // License is granted to use or modify this software ("XML-RPC for PHP")
  6. // for commercial or non-commercial use provided the copyright of the author
  7. // is preserved in any distributed or derivative work.
  8. // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR
  9. // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  10. // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  11. // IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  12. // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  13. // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  14. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  15. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  16. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  17. // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  18. // Adapted to PEAR standards by Stig Sæther Bakken <stig@php.net> and
  19. // Martin Jansen <mj@php.net>
  20. require_once "PEAR.php";
  21. if (!function_exists('xml_parser_create')) {
  22. // Win 32 fix. From: "Leo West" <lwest@imaginet.fr>
  23. if($WINDIR) {
  24. dl("php3_xml.dll");
  25. } else {
  26. dl("xml.so");
  27. }
  28. }
  29. $GLOBALS['XML_RPC_I4']="i4";
  30. $GLOBALS['XML_RPC_Int']="int";
  31. $GLOBALS['XML_RPC_Boolean']="boolean";
  32. $GLOBALS['XML_RPC_Double']="double";
  33. $GLOBALS['XML_RPC_String']="string";
  34. $GLOBALS['XML_RPC_DateTime']="dateTime.iso8601";
  35. $GLOBALS['XML_RPC_Base64']="base64";
  36. $GLOBALS['XML_RPC_Array']="array";
  37. $GLOBALS['XML_RPC_Struct']="struct";
  38. $GLOBALS['XML_RPC_Types']=array($GLOBALS['XML_RPC_I4'] => 1,
  39. $GLOBALS['XML_RPC_Int'] => 1,
  40. $GLOBALS['XML_RPC_Boolean'] => 1,
  41. $GLOBALS['XML_RPC_String'] => 1,
  42. $GLOBALS['XML_RPC_Double'] => 1,
  43. $GLOBALS['XML_RPC_DateTime'] => 1,
  44. $GLOBALS['XML_RPC_Base64'] => 1,
  45. $GLOBALS['XML_RPC_Array'] => 2,
  46. $GLOBALS['XML_RPC_Struct'] => 3);
  47. $GLOBALS['XML_RPC_entities']=array("quot" => '"',
  48. "amp" => "&",
  49. "lt" => "<",
  50. "gt" => ">",
  51. "apos" => "'");
  52. $GLOBALS['XML_RPC_err']["unknown_method"]=1;
  53. $GLOBALS['XML_RPC_str']["unknown_method"]="Unknown method";
  54. $GLOBALS['XML_RPC_err']["invalid_return"]=2;
  55. $GLOBALS['XML_RPC_str']["invalid_return"]="Invalid return payload: enabling debugging to examine incoming payload";
  56. $GLOBALS['XML_RPC_err']["incorrect_params"]=3;
  57. $GLOBALS['XML_RPC_str']["incorrect_params"]="Incorrect parameters passed to method";
  58. $GLOBALS['XML_RPC_err']["introspect_unknown"]=4;
  59. $GLOBALS['XML_RPC_str']["introspect_unknown"]="Can't introspect: method unknown";
  60. $GLOBALS['XML_RPC_err']["http_error"]=5;
  61. $GLOBALS['XML_RPC_str']["http_error"]="Didn't receive 200 OK from remote server.";
  62. $GLOBALS['XML_RPC_defencoding']="UTF-8";
  63. // let user errors start at 800
  64. $GLOBALS['XML_RPC_erruser']=800;
  65. // let XML parse errors start at 100
  66. $GLOBALS['XML_RPC_errxml']=100;
  67. // formulate backslashes for escaping regexp
  68. $GLOBALS['XML_RPC_backslash']=chr(92).chr(92);
  69. $GLOBALS['XML_RPC_twoslash']=$GLOBALS['XML_RPC_backslash'] . $GLOBALS['XML_RPC_backslash'];
  70. $GLOBALS['XML_RPC_twoslash']="2SLS";
  71. // used to store state during parsing
  72. // quick explanation of components:
  73. // st - used to build up a string for evaluation
  74. // ac - used to accumulate values
  75. // qt - used to decide if quotes are needed for evaluation
  76. // cm - used to denote struct or array (comma needed)
  77. // isf - used to indicate a fault
  78. // lv - used to indicate "looking for a value": implements
  79. // the logic to allow values with no types to be strings
  80. // params - used to store parameters in method calls
  81. // method - used to store method name
  82. $GLOBALS['XML_RPC_xh']=array();
  83. function XML_RPC_entity_decode($string)
  84. {
  85. $top=split("&", $string);
  86. $op="";
  87. $i=0;
  88. while($i<sizeof($top)) {
  89. if (ereg("^([#a-zA-Z0-9]+);", $top[$i], $regs)) {
  90. $op.=ereg_replace("^[#a-zA-Z0-9]+;",
  91. XML_RPC_lookup_entity($regs[1]),
  92. $top[$i]);
  93. } else {
  94. if ($i==0)
  95. $op=$top[$i];
  96. else
  97. $op.="&" . $top[$i];
  98. }
  99. $i++;
  100. }
  101. return $op;
  102. }
  103. function XML_RPC_lookup_entity($ent)
  104. {
  105. global $XML_RPC_entities;
  106. if ($XML_RPC_entities[strtolower($ent)])
  107. return $XML_RPC_entities[strtolower($ent)];
  108. if (ereg("^#([0-9]+)$", $ent, $regs))
  109. return chr($regs[1]);
  110. return "?";
  111. }
  112. function XML_RPC_se($parser, $name, $attrs)
  113. {
  114. global $XML_RPC_xh, $XML_RPC_DateTime, $XML_RPC_String;
  115. switch($name) {
  116. case "STRUCT":
  117. case "ARRAY":
  118. $XML_RPC_xh[$parser]['st'].="array(";
  119. $XML_RPC_xh[$parser]['cm']++;
  120. // this last line turns quoting off
  121. // this means if we get an empty array we'll
  122. // simply get a bit of whitespace in the eval
  123. $XML_RPC_xh[$parser]['qt']=0;
  124. break;
  125. case "NAME":
  126. $XML_RPC_xh[$parser]['st'].="'"; $XML_RPC_xh[$parser]['ac']="";
  127. break;
  128. case "FAULT":
  129. $XML_RPC_xh[$parser]['isf']=1;
  130. break;
  131. case "PARAM":
  132. $XML_RPC_xh[$parser]['st']="";
  133. break;
  134. case "VALUE":
  135. $XML_RPC_xh[$parser]['st'].="new XML_RPC_Value(";
  136. $XML_RPC_xh[$parser]['lv']=1;
  137. $XML_RPC_xh[$parser]['vt']=$XML_RPC_String;
  138. $XML_RPC_xh[$parser]['ac']="";
  139. $XML_RPC_xh[$parser]['qt']=0;
  140. // look for a value: if this is still 1 by the
  141. // time we reach the first data segment then the type is string
  142. // by implication and we need to add in a quote
  143. break;
  144. case "I4":
  145. case "INT":
  146. case "STRING":
  147. case "BOOLEAN":
  148. case "DOUBLE":
  149. case "DATETIME.ISO8601":
  150. case "BASE64":
  151. $XML_RPC_xh[$parser]['ac']=""; // reset the accumulator
  152. if ($name=="DATETIME.ISO8601" || $name=="STRING") {
  153. $XML_RPC_xh[$parser]['qt']=1;
  154. if ($name=="DATETIME.ISO8601")
  155. $XML_RPC_xh[$parser]['vt']=$XML_RPC_DateTime;
  156. } else if ($name=="BASE64") {
  157. $XML_RPC_xh[$parser]['qt']=2;
  158. } else {
  159. // No quoting is required here -- but
  160. // at the end of the element we must check
  161. // for data format errors.
  162. $XML_RPC_xh[$parser]['qt']=0;
  163. }
  164. break;
  165. case "MEMBER":
  166. $XML_RPC_xh[$parser]['ac']="";
  167. break;
  168. default:
  169. break;
  170. }
  171. if ($name!="VALUE") $XML_RPC_xh[$parser]['lv']=0;
  172. }
  173. function XML_RPC_ee($parser, $name)
  174. {
  175. global $XML_RPC_xh,$XML_RPC_Types,$XML_RPC_String;
  176. switch($name) {
  177. case "STRUCT":
  178. case "ARRAY":
  179. if ($XML_RPC_xh[$parser]['cm'] && substr($XML_RPC_xh[$parser]['st'], -1) ==',') {
  180. $XML_RPC_xh[$parser]['st']=substr($XML_RPC_xh[$parser]['st'],0,-1);
  181. }
  182. $XML_RPC_xh[$parser]['st'].=")";
  183. $XML_RPC_xh[$parser]['vt']=strtolower($name);
  184. $XML_RPC_xh[$parser]['cm']--;
  185. break;
  186. case "NAME":
  187. $XML_RPC_xh[$parser]['st'].= $XML_RPC_xh[$parser]['ac'] . "' => ";
  188. break;
  189. case "BOOLEAN":
  190. // special case here: we translate boolean 1 or 0 into PHP
  191. // constants true or false
  192. if ($XML_RPC_xh[$parser]['ac']=='1')
  193. $XML_RPC_xh[$parser]['ac']="true";
  194. else
  195. $XML_RPC_xh[$parser]['ac']="false";
  196. $XML_RPC_xh[$parser]['vt']=strtolower($name);
  197. // Drop through intentionally.
  198. case "I4":
  199. case "INT":
  200. case "STRING":
  201. case "DOUBLE":
  202. case "DATETIME.ISO8601":
  203. case "BASE64":
  204. if ($XML_RPC_xh[$parser]['qt']==1) {
  205. // we use double quotes rather than single so backslashification works OK
  206. $XML_RPC_xh[$parser]['st'].="\"". $XML_RPC_xh[$parser]['ac'] . "\"";
  207. } else if ($XML_RPC_xh[$parser]['qt']==2) {
  208. $XML_RPC_xh[$parser]['st'].="base64_decode('". $XML_RPC_xh[$parser]['ac'] . "')";
  209. } else if ($name=="BOOLEAN") {
  210. $XML_RPC_xh[$parser]['st'].=$XML_RPC_xh[$parser]['ac'];
  211. } else {
  212. // we have an I4, INT or a DOUBLE
  213. // we must check that only 0123456789-.<space> are characters here
  214. if (!ereg("^\-?[0123456789 \t\.]+$", $XML_RPC_xh[$parser]['ac'])) {
  215. // TODO: find a better way of throwing an error
  216. // than this!
  217. error_log("XML-RPC: non numeric value received in INT or DOUBLE");
  218. $XML_RPC_xh[$parser]['st'].="ERROR_NON_NUMERIC_FOUND";
  219. } else {
  220. // it's ok, add it on
  221. $XML_RPC_xh[$parser]['st'].=$XML_RPC_xh[$parser]['ac'];
  222. }
  223. }
  224. $XML_RPC_xh[$parser]['ac']=""; $XML_RPC_xh[$parser]['qt']=0;
  225. $XML_RPC_xh[$parser]['lv']=3; // indicate we've found a value
  226. break;
  227. case "VALUE":
  228. // deal with a string value
  229. if (strlen($XML_RPC_xh[$parser]['ac'])>0 &&
  230. $XML_RPC_xh[$parser]['vt']==$XML_RPC_String) {
  231. $XML_RPC_xh[$parser]['st'].="\"". $XML_RPC_xh[$parser]['ac'] . "\"";
  232. }
  233. // This if() detects if no scalar was inside <VALUE></VALUE>
  234. // and pads an empty "".
  235. if($XML_RPC_xh[$parser]['st'][strlen($XML_RPC_xh[$parser]['st'])-1] == '(') {
  236. $XML_RPC_xh[$parser]['st'].= '""';
  237. }
  238. $XML_RPC_xh[$parser]['st'].=", '" . $XML_RPC_xh[$parser]['vt'] . "')";
  239. if ($XML_RPC_xh[$parser]['cm']) $XML_RPC_xh[$parser]['st'].=",";
  240. break;
  241. case "MEMBER":
  242. $XML_RPC_xh[$parser]['ac']=""; $XML_RPC_xh[$parser]['qt']=0;
  243. break;
  244. case "DATA":
  245. $XML_RPC_xh[$parser]['ac']=""; $XML_RPC_xh[$parser]['qt']=0;
  246. break;
  247. case "PARAM":
  248. $XML_RPC_xh[$parser]['params'][]=$XML_RPC_xh[$parser]['st'];
  249. break;
  250. case "METHODNAME":
  251. $XML_RPC_xh[$parser]['method']=ereg_replace("^[\n\r\t ]+", "", $XML_RPC_xh[$parser]['ac']);
  252. break;
  253. case "BOOLEAN":
  254. // special case here: we translate boolean 1 or 0 into PHP
  255. // constants true or false
  256. if ($XML_RPC_xh[$parser]['ac']=='1')
  257. $XML_RPC_xh[$parser]['ac']="true";
  258. else
  259. $XML_RPC_xh[$parser]['ac']="false";
  260. $XML_RPC_xh[$parser]['vt']=strtolower($name);
  261. break;
  262. default:
  263. break;
  264. }
  265. // if it's a valid type name, set the type
  266. if (isset($XML_RPC_Types[strtolower($name)])) {
  267. $XML_RPC_xh[$parser]['vt']=strtolower($name);
  268. }
  269. }
  270. function XML_RPC_cd($parser, $data)
  271. {
  272. global $XML_RPC_xh, $XML_RPC_backslash;
  273. //if (ereg("^[\n\r \t]+$", $data)) return;
  274. // print "adding [${data}]\n";
  275. if ($XML_RPC_xh[$parser]['lv']!=3) {
  276. // "lookforvalue==3" means that we've found an entire value
  277. // and should discard any further character data
  278. if ($XML_RPC_xh[$parser]['lv']==1) {
  279. // if we've found text and we're just in a <value> then
  280. // turn quoting on, as this will be a string
  281. $XML_RPC_xh[$parser]['qt']=1;
  282. // and say we've found a value
  283. $XML_RPC_xh[$parser]['lv']=2;
  284. }
  285. // replace characters that eval would
  286. // do special things with
  287. @$XML_RPC_xh[$parser]['ac'].=str_replace('$', '\$',
  288. str_replace('"', '\"', str_replace(chr(92),
  289. $XML_RPC_backslash, $data)));
  290. }
  291. }
  292. function XML_RPC_dh($parser, $data)
  293. {
  294. global $XML_RPC_xh;
  295. if (substr($data, 0, 1) == "&" && substr($data, -1, 1) == ";") {
  296. if ($XML_RPC_xh[$parser]['lv']==1) {
  297. $XML_RPC_xh[$parser]['qt']=1;
  298. $XML_RPC_xh[$parser]['lv']=2;
  299. }
  300. $XML_RPC_xh[$parser]['ac'].=str_replace('$', '\$',
  301. str_replace('"', '\"', str_replace(chr(92),
  302. $XML_RPC_backslash, $data)));
  303. }
  304. }
  305. class XML_RPC_Client
  306. {
  307. var $path;
  308. var $server;
  309. var $port;
  310. var $errno;
  311. var $errstring;
  312. var $debug=0;
  313. var $username="";
  314. var $password="";
  315. function XML_RPC_Client($path, $server, $port = 80,
  316. $proxy = '', $proxy_port = 8080,
  317. $proxy_user = '', $proxy_pass = '')
  318. {
  319. $this->port=$port;
  320. $this->server=$server;
  321. $this->path=$path;
  322. $this->proxy = $proxy;
  323. $this->proxy_port = $proxy_port;
  324. $this->proxy_user = $proxy_user;
  325. $this->proxy_pass = $proxy_pass;
  326. }
  327. function setDebug($in)
  328. {
  329. if ($in) {
  330. $this->debug=1;
  331. } else {
  332. $this->debug=0;
  333. }
  334. }
  335. function setCredentials($u, $p)
  336. {
  337. $this->username=$u;
  338. $this->password=$p;
  339. }
  340. function send($msg, $timeout=0)
  341. {
  342. // where msg is an xmlrpcmsg
  343. $msg->debug=$this->debug;
  344. return $this->sendPayloadHTTP10($msg, $this->server, $this->port,
  345. $timeout, $this->username,
  346. $this->password);
  347. }
  348. function sendPayloadHTTP10($msg, $server, $port, $timeout=0,
  349. $username="", $password="")
  350. {
  351. // If we're using a proxy open a socket to the proxy server instead to the xml-rpc server
  352. if($this->proxy){
  353. if($timeout > 0) {
  354. $fp=fsockopen($this->proxy, $this->proxy_port, $this->errno, $this->errstr, $timeout);
  355. }
  356. else {
  357. $fp=fsockopen($this->proxy, $this->proxy_port, $this->errno, $this->errstr);
  358. }
  359. }
  360. else {
  361. if($timeout > 0) {
  362. $fp=fsockopen($server, $port, $this->errno, $this->errstr, $timeout);
  363. }
  364. else {
  365. $fp=fsockopen($server, $port, $this->errno, $this->errstr);
  366. }
  367. }
  368. if(!$fp && $this->proxy) {
  369. PEAR::raiseError("Connection to proxy server ".$this->proxy.":".$this->proxy_port." failed");
  370. }
  371. else if(!$fp) {
  372. PEAR::raiseError("Connection to RPC server ".$this->server." failed");
  373. }
  374. // Only create the payload if it was not created previously
  375. if(empty($msg->payload)) $msg->createPayload();
  376. // thanks to Grant Rauscher <grant7@firstworld.net>
  377. // for this
  378. $credentials="";
  379. if ($username!="") {
  380. $credentials="Authorization: Basic " .
  381. base64_encode($username . ":" . $password) . "\r\n";
  382. }
  383. if($this->proxy) {
  384. $op = "POST http://" . $this->server;
  385. if($this->proxy_port) {
  386. $op .= ":" . $this->port;
  387. }
  388. }
  389. else {
  390. $op = "POST ";
  391. }
  392. $op .= $this->path. " HTTP/1.0\r\nUser-Agent: PHP XMLRPC 1.0\r\n" .
  393. "Host: ". $this->server . "\r\n";
  394. if ($this->proxy && $this->proxy_user != '') {
  395. $op .= 'Proxy-Authorization: Basic ' .
  396. base64_encode($this->proxy_user . ':' . $this->proxy_pass) .
  397. "\r\n";
  398. }
  399. $op .= $credentials .
  400. "Content-Type: text/xml\r\nContent-Length: " .
  401. strlen($msg->payload) . "\r\n\r\n" .
  402. $msg->payload;
  403. // print($op);
  404. if (!fputs($fp, $op, strlen($op))) {
  405. $this->errstr="Write error";
  406. return 0;
  407. }
  408. $resp=$msg->parseResponseFile($fp);
  409. fclose($fp);
  410. return $resp;
  411. }
  412. }
  413. class XML_RPC_Response
  414. {
  415. var $xv;
  416. var $fn;
  417. var $fs;
  418. var $hdrs;
  419. function XML_RPC_Response($val, $fcode=0, $fstr="")
  420. {
  421. if ($fcode!=0) {
  422. $this->fn=$fcode;
  423. $this->fs=htmlspecialchars($fstr);
  424. } else {
  425. $this->xv=$val;
  426. }
  427. }
  428. function faultCode()
  429. {
  430. if (isset($this->fn))
  431. return $this->fn;
  432. else
  433. return 0;
  434. }
  435. function faultString() { return $this->fs; }
  436. function value() { return $this->xv; }
  437. function serialize() {
  438. $rs="<methodResponse>\n";
  439. if ($this->fn) {
  440. $rs.="<fault>
  441. <value>
  442. <struct>
  443. <member>
  444. <name>faultCode</name>
  445. <value><int>" . $this->fn . "</int></value>
  446. </member>
  447. <member>
  448. <name>faultString</name>
  449. <value><string>" . $this->fs . "</string></value>
  450. </member>
  451. </struct>
  452. </value>
  453. </fault>";
  454. } else {
  455. $rs.="<params>\n<param>\n" . $this->xv->serialize() .
  456. "</param>\n</params>";
  457. }
  458. $rs.="\n</methodResponse>";
  459. return $rs;
  460. }
  461. }
  462. class XML_RPC_Message
  463. {
  464. var $payload;
  465. var $methodname;
  466. var $params = array();
  467. var $debug=0;
  468. function XML_RPC_Message($meth, $pars=0)
  469. {
  470. $this->methodname=$meth;
  471. if (is_array($pars) && sizeof($pars)>0) {
  472. for($i=0; $i<sizeof($pars); $i++)
  473. $this->addParam($pars[$i]);
  474. }
  475. }
  476. function xml_header()
  477. {
  478. return "<?xml version=\"1.0\"?>\n<methodCall>\n";
  479. }
  480. function xml_footer()
  481. {
  482. return "</methodCall>\n";
  483. }
  484. function createPayload()
  485. {
  486. $this->payload=$this->xml_header();
  487. $this->payload.="<methodName>" . $this->methodname . "</methodName>\n";
  488. // if (sizeof($this->params)) {
  489. $this->payload.="<params>\n";
  490. for($i=0; $i<sizeof($this->params); $i++) {
  491. $p=$this->params[$i];
  492. $this->payload.="<param>\n" . $p->serialize() .
  493. "</param>\n";
  494. }
  495. $this->payload.="</params>\n";
  496. // }
  497. $this->payload.=$this->xml_footer();
  498. $this->payload=str_replace("\n", "\r\n", $this->payload);
  499. }
  500. function method($meth="")
  501. {
  502. if ($meth!="") {
  503. $this->methodname=$meth;
  504. }
  505. return $this->methodname;
  506. }
  507. function serialize()
  508. {
  509. $this->createPayload();
  510. return $this->payload;
  511. }
  512. function addParam($par) { $this->params[]=$par; }
  513. function getParam($i) { return $this->params[$i]; }
  514. function getNumParams() { return sizeof($this->params); }
  515. function parseResponseFile($fp)
  516. {
  517. $ipd="";
  518. while($data=fread($fp, 32768)) {
  519. $ipd.=$data;
  520. }
  521. return $this->parseResponse($ipd);
  522. }
  523. function parseResponse($data="")
  524. {
  525. global $XML_RPC_xh,$XML_RPC_err,$XML_RPC_str;
  526. global $XML_RPC_defencoding;
  527. $parser = xml_parser_create($XML_RPC_defencoding);
  528. $XML_RPC_xh[$parser]=array();
  529. $XML_RPC_xh[$parser]['st']="";
  530. $XML_RPC_xh[$parser]['cm']=0;
  531. $XML_RPC_xh[$parser]['isf']=0;
  532. $XML_RPC_xh[$parser]['ac']="";
  533. $XML_RPC_xh[$parser]['qt']="";
  534. xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
  535. xml_set_element_handler($parser, "XML_RPC_se", "XML_RPC_ee");
  536. xml_set_character_data_handler($parser, "XML_RPC_cd");
  537. xml_set_default_handler($parser, "XML_RPC_dh");
  538. $xmlrpc_value = new XML_RPC_Value;
  539. $hdrfnd=0;
  540. if ($this->debug) {
  541. print "<PRE>---GOT---\n";
  542. print isset($_SERVER['SERVER_PROTOCOL']) ? htmlspecialchars($data) : $data;
  543. print "\n---END---\n</PRE>";
  544. }
  545. // see if we got an HTTP 200 OK, else bomb
  546. // but only do this if we're using the HTTP protocol.
  547. if (ereg("^HTTP",$data) &&
  548. !ereg("^HTTP/[0-9\.]+ 200 ", $data)) {
  549. $errstr= substr($data, 0, strpos($data, "\n")-1);
  550. error_log("HTTP error, got response: " .$errstr);
  551. $r=new XML_RPC_Response(0, $XML_RPC_err["http_error"],
  552. $XML_RPC_str["http_error"]. " (" .
  553. $errstr . ")");
  554. xml_parser_free($parser);
  555. return $r;
  556. }
  557. // gotta get rid of headers here
  558. if ((!$hdrfnd) && ($brpos = strpos($data,"\r\n\r\n"))) {
  559. $XML_RPC_xh[$parser]['ha'] = substr($data,0,$brpos);
  560. $data= substr($data,$brpos+4);
  561. $hdrfnd=1;
  562. }
  563. if (!xml_parse($parser, $data, sizeof($data))) {
  564. // thanks to Peter Kocks <peter.kocks@baygate.com>
  565. if((xml_get_current_line_number($parser)) == 1)
  566. $errstr = "XML error at line 1, check URL";
  567. else
  568. $errstr = sprintf("XML error: %s at line %d",
  569. xml_error_string(xml_get_error_code($parser)),
  570. xml_get_current_line_number($parser));
  571. error_log($errstr);
  572. $r=new XML_RPC_Response(0, $XML_RPC_err["invalid_return"],
  573. $XML_RPC_str["invalid_return"]);
  574. xml_parser_free($parser);
  575. return $r;
  576. }
  577. xml_parser_free($parser);
  578. if ($this->debug) {
  579. print "<PRE>---EVALING---[" .
  580. strlen($XML_RPC_xh[$parser]['st']) . " chars]---\n" .
  581. htmlspecialchars($XML_RPC_xh[$parser]['st']) . ";\n---END---</PRE>";
  582. }
  583. if (strlen($XML_RPC_xh[$parser]['st'])==0) {
  584. // then something odd has happened
  585. // and it's time to generate a client side error
  586. // indicating something odd went on
  587. $r=new XML_RPC_Response(0, $XML_RPC_err["invalid_return"],
  588. $XML_RPC_str["invalid_return"]);
  589. } else {
  590. eval('$v=' . $XML_RPC_xh[$parser]['st'] . '; $allOK=1;');
  591. if ($XML_RPC_xh[$parser]['isf']) {
  592. $f=$v->structmem("faultCode");
  593. $fs=$v->structmem("faultString");
  594. $r=new XML_RPC_Response($v, $f->scalarval(),
  595. $fs->scalarval());
  596. } else {
  597. $r=new XML_RPC_Response($v);
  598. }
  599. }
  600. $r->hdrs=split("\r?\n", $XML_RPC_xh[$parser]['ha'][1]);
  601. return $r;
  602. }
  603. }
  604. class XML_RPC_Value
  605. {
  606. var $me=array();
  607. var $mytype=0;
  608. function XML_RPC_Value($val=-1, $type="")
  609. {
  610. global $XML_RPC_Types;
  611. $this->me=array();
  612. $this->mytype=0;
  613. if ($val!=-1 || $type!="") {
  614. if ($type=="") $type="string";
  615. if ($XML_RPC_Types[$type]==1) {
  616. $this->addScalar($val,$type);
  617. }
  618. else if ($XML_RPC_Types[$type]==2)
  619. $this->addArray($val);
  620. else if ($XML_RPC_Types[$type]==3)
  621. $this->addStruct($val);
  622. }
  623. }
  624. function addScalar($val, $type="string")
  625. {
  626. global $XML_RPC_Types, $XML_RPC_Boolean;
  627. if ($this->mytype==1) {
  628. echo "<B>XML_RPC_Value</B>: scalar can have only one value<BR>";
  629. return 0;
  630. }
  631. $typeof=$XML_RPC_Types[$type];
  632. if ($typeof!=1) {
  633. echo "<B>XML_RPC_Value</B>: not a scalar type (${typeof})<BR>";
  634. return 0;
  635. }
  636. if ($type==$XML_RPC_Boolean) {
  637. if (strcasecmp($val,"true")==0 ||
  638. $val==1 ||
  639. ($val==true &&
  640. strcasecmp($val,"false"))) {
  641. $val=1;
  642. } else {
  643. $val=0;
  644. }
  645. }
  646. if ($this->mytype==2) {
  647. // we're adding to an array here
  648. $ar=$this->me["array"];
  649. $ar[]=new XML_RPC_Value($val, $type);
  650. $this->me["array"]=$ar;
  651. } else {
  652. // a scalar, so set the value and remember we're scalar
  653. $this->me[$type]=$val;
  654. $this->mytype=$typeof;
  655. }
  656. return 1;
  657. }
  658. function addArray($vals)
  659. {
  660. global $XML_RPC_Types;
  661. if ($this->mytype!=0) {
  662. echo "<B>XML_RPC_Value</B>: already initialized as a [" .
  663. $this->kindOf() . "]<BR>";
  664. return 0;
  665. }
  666. $this->mytype=$XML_RPC_Types["array"];
  667. $this->me["array"]=$vals;
  668. return 1;
  669. }
  670. function addStruct($vals)
  671. {
  672. global $XML_RPC_Types;
  673. if ($this->mytype!=0) {
  674. echo "<B>XML_RPC_Value</B>: already initialized as a [" .
  675. $this->kindOf() . "]<BR>";
  676. return 0;
  677. }
  678. $this->mytype=$XML_RPC_Types["struct"];
  679. $this->me["struct"]=$vals;
  680. return 1;
  681. }
  682. function dump($ar)
  683. {
  684. reset($ar);
  685. while ( list( $key, $val ) = each( $ar ) ) {
  686. echo "$key => $val<br>";
  687. if ($key == 'array')
  688. while ( list( $key2, $val2 ) = each( $val ) ) {
  689. echo "-- $key2 => $val2<br>";
  690. }
  691. }
  692. }
  693. function kindOf()
  694. {
  695. switch($this->mytype) {
  696. case 3:
  697. return "struct";
  698. break;
  699. case 2:
  700. return "array";
  701. break;
  702. case 1:
  703. return "scalar";
  704. break;
  705. default:
  706. return "undef";
  707. }
  708. }
  709. function serializedata($typ, $val)
  710. {
  711. $rs="";
  712. global $XML_RPC_Types, $XML_RPC_Base64, $XML_RPC_String, $XML_RPC_Boolean;
  713. switch($XML_RPC_Types[$typ]) {
  714. case 3:
  715. // struct
  716. $rs.="<struct>\n";
  717. reset($val);
  718. while(list($key2, $val2)=each($val)) {
  719. $rs.="<member><name>${key2}</name>\n";
  720. $rs.=$this->serializeval($val2);
  721. $rs.="</member>\n";
  722. }
  723. $rs.="</struct>";
  724. break;
  725. case 2:
  726. // array
  727. $rs.="<array>\n<data>\n";
  728. for($i=0; $i<sizeof($val); $i++) {
  729. $rs.=$this->serializeval($val[$i]);
  730. }
  731. $rs.="</data>\n</array>";
  732. break;
  733. case 1:
  734. switch ($typ) {
  735. case $XML_RPC_Base64:
  736. $rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
  737. break;
  738. case $XML_RPC_Boolean:
  739. $rs.="<${typ}>" . ($val ? "1" : "0") . "</${typ}>";
  740. break;
  741. case $XML_RPC_String:
  742. $rs.="<${typ}>" . htmlspecialchars($val). "</${typ}>";
  743. break;
  744. default:
  745. $rs.="<${typ}>${val}</${typ}>";
  746. }
  747. break;
  748. default:
  749. break;
  750. }
  751. return $rs;
  752. }
  753. function serialize()
  754. {
  755. return $this->serializeval($this);
  756. }
  757. function serializeval($o)
  758. {
  759. global $XML_RPC_Types;
  760. $rs="";
  761. $ar=$o->me;
  762. reset($ar);
  763. list($typ, $val) = each($ar);
  764. $rs.="<value>";
  765. $rs.=$this->serializedata($typ, $val);
  766. $rs.="</value>\n";
  767. return $rs;
  768. }
  769. function structmem($m)
  770. {
  771. $nv=$this->me["struct"][$m];
  772. return $nv;
  773. }
  774. function structreset()
  775. {
  776. reset($this->me["struct"]);
  777. }
  778. function structeach()
  779. {
  780. return each($this->me["struct"]);
  781. }
  782. function getval() {
  783. // UNSTABLE
  784. global $XML_RPC_BOOLEAN, $XML_RPC_Base64;
  785. reset($this->me);
  786. list($a,$b)=each($this->me);
  787. // contributed by I Sofer, 2001-03-24
  788. // add support for nested arrays to scalarval
  789. // i've created a new method here, so as to
  790. // preserve back compatibility
  791. if (is_array($b)) {
  792. foreach ($b as $id => $cont) {
  793. $b[$id] = $cont->scalarval();
  794. }
  795. }
  796. // add support for structures directly encoding php objects
  797. if (is_object($b)) {
  798. $t = get_object_vars($b);
  799. foreach ($t as $id => $cont) {
  800. $t[$id] = $cont->scalarval();
  801. }
  802. foreach ($t as $id => $cont) {
  803. eval('$b->'.$id.' = $cont;');
  804. }
  805. }
  806. // end contrib
  807. return $b;
  808. }
  809. function scalarval()
  810. {
  811. global $XML_RPC_Boolean, $XML_RPC_Base64;
  812. reset($this->me);
  813. list($a,$b)=each($this->me);
  814. return $b;
  815. }
  816. function scalartyp()
  817. {
  818. global $XML_RPC_I4, $XML_RPC_Int;
  819. reset($this->me);
  820. list($a,$b)=each($this->me);
  821. if ($a==$XML_RPC_I4)
  822. $a=$XML_RPC_Int;
  823. return $a;
  824. }
  825. function arraymem($m)
  826. {
  827. $nv=$this->me["array"][$m];
  828. return $nv;
  829. }
  830. function arraysize()
  831. {
  832. reset($this->me);
  833. list($a,$b)=each($this->me);
  834. return sizeof($b);
  835. }
  836. }
  837. /**
  838. * date helpers
  839. */
  840. function XML_RPC_iso8601_encode($timet, $utc=0) {
  841. // return an ISO8601 encoded string
  842. // really, timezones ought to be supported
  843. // but the XML-RPC spec says:
  844. //
  845. // "Don't assume a timezone. It should be specified by the server in its
  846. // documentation what assumptions it makes about timezones."
  847. //
  848. // these routines always assume localtime unless
  849. // $utc is set to 1, in which case UTC is assumed
  850. // and an adjustment for locale is made when encoding
  851. if (!$utc) {
  852. $t=strftime("%Y%m%dT%H:%M:%S", $timet);
  853. } else {
  854. if (function_exists("gmstrftime"))
  855. // gmstrftime doesn't exist in some versions
  856. // of PHP
  857. $t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
  858. else {
  859. $t=strftime("%Y%m%dT%H:%M:%S", $timet-date("Z"));
  860. }
  861. }
  862. return $t;
  863. }
  864. function XML_RPC_iso8601_decode($idate, $utc=0) {
  865. // return a timet in the localtime, or UTC
  866. $t=0;
  867. if (ereg("([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})",$idate, $regs)) {
  868. if ($utc) {
  869. $t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
  870. } else {
  871. $t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
  872. }
  873. }
  874. return $t;
  875. }
  876. /******************************************************************
  877. * XML_RPC_decode takes a message in PHP XML_RPC object format and *
  878. * tranlates it into native PHP types. *
  879. * *
  880. * author: Dan Libby (dan@libby.com) *
  881. ******************************************************************/
  882. function XML_RPC_decode($XML_RPC_val) {
  883. $kind = $XML_RPC_val->kindOf();
  884. if($kind == "scalar") {
  885. return $XML_RPC_val->scalarval();
  886. }
  887. else if($kind == "array") {
  888. $size = $XML_RPC_val->arraysize();
  889. $arr = array();
  890. for($i = 0; $i < $size; $i++) {
  891. $arr[]=XML_RPC_decode($XML_RPC_val->arraymem($i));
  892. }
  893. return $arr;
  894. }
  895. else if($kind == "struct") {
  896. $XML_RPC_val->structreset();
  897. $arr = array();
  898. while(list($key,$value)=$XML_RPC_val->structeach()) {
  899. $arr[$key] = XML_RPC_decode($value);
  900. }
  901. return $arr;
  902. }
  903. }
  904. /*****************************************************************
  905. * XML_RPC_encode takes native php types and encodes them into *
  906. * XML_RPC PHP object format. *
  907. * BUG: All sequential arrays are turned into structs. I don't *
  908. * know of a good way to determine if an array is sequential *
  909. * only. *
  910. * *
  911. * feature creep -- could support more types via optional type *
  912. * argument. *
  913. * *
  914. * author: Dan Libby (dan@libby.com) *
  915. *****************************************************************/
  916. function XML_RPC_encode($php_val) {
  917. global $XML_RPC_Boolean;
  918. global $XML_RPC_Int;
  919. global $XML_RPC_Double;
  920. global $XML_RPC_String;
  921. global $XML_RPC_Array;
  922. global $XML_RPC_Struct;
  923. $type = gettype($php_val);
  924. $XML_RPC_val = new XML_RPC_value;
  925. switch($type) {
  926. case "array":
  927. case "object":
  928. $arr = array();
  929. while (list($k,$v) = each($php_val)) {
  930. $arr[$k] = XML_RPC_encode($v);
  931. }
  932. $XML_RPC_val->addStruct($arr);
  933. break;
  934. case "integer":
  935. $XML_RPC_val->addScalar($php_val, $XML_RPC_Int);
  936. break;
  937. case "double":
  938. $XML_RPC_val->addScalar($php_val, $XML_RPC_Double);
  939. break;
  940. case "string":
  941. case "NULL":
  942. $XML_RPC_val->addScalar($php_val, $XML_RPC_String);
  943. break;
  944. // <G_Giunta_2001-02-29>
  945. // Add support for encoding/decoding of booleans, since they are supported in PHP
  946. case "boolean":
  947. $XML_RPC_val->addScalar($php_val, $XML_RPC_Boolean);
  948. break;
  949. // </G_Giunta_2001-02-29>
  950. case "unknown type":
  951. default:
  952. $XML_RPC_val = false;
  953. break;
  954. }
  955. return $XML_RPC_val;
  956. }
  957. ?>