PageRenderTime 64ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/wishlist-member/extlib/xmlrpc.php

https://bitbucket.org/broderboy/nycendurance-wordpress
PHP | 3725 lines | 3055 code | 147 blank | 523 comment | 304 complexity | 4140686e49251a8b20545031f981156e MD5 | raw file
Possible License(s): AGPL-1.0, GPL-3.0, Apache-2.0, GPL-2.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php // -*-c++-*-
  2. // by Edd Dumbill (C) 1999-2002
  3. // <edd@usefulinc.com>
  4. // $Id: xmlrpc.inc,v 1.2 2006/10/11 22:30:55 eric Exp $
  5. // Copyright (c) 1999,2000,2002 Edd Dumbill.
  6. // All rights reserved.
  7. //
  8. // Redistribution and use in source and binary forms, with or without
  9. // modification, are permitted provided that the following conditions
  10. // are met:
  11. //
  12. // * Redistributions of source code must retain the above copyright
  13. // notice, this list of conditions and the following disclaimer.
  14. //
  15. // * Redistributions in binary form must reproduce the above
  16. // copyright notice, this list of conditions and the following
  17. // disclaimer in the documentation and/or other materials provided
  18. // with the distribution.
  19. //
  20. // * Neither the name of the "XML-RPC for PHP" nor the names of its
  21. // contributors may be used to endorse or promote products derived
  22. // from this software without specific prior written permission.
  23. //
  24. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  25. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  26. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  27. // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  28. // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  29. // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  30. // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  31. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32. // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  33. // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  34. // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  35. // OF THE POSSIBILITY OF SUCH DAMAGE.
  36. if(!defined('ABSPATH'))die(); // added by WishList Member to prevent direct access to this file
  37. if(!function_exists('xml_parser_create'))
  38. {
  39. // For PHP 4 onward, XML functionality is always compiled-in on windows:
  40. // no more need to dl-open it. It might have been compiled out on *nix...
  41. if(strtoupper(substr(PHP_OS, 0, 3) != 'WIN'))
  42. {
  43. dl('xml.so');
  44. }
  45. }
  46. // Try to be backward compat with php < 4.2 (are we not being nice ?)
  47. if(substr(phpversion(), 0, 3) == '4.0' || substr(phpversion(), 0, 3) == '4.1')
  48. {
  49. // give an opportunity to user to specify where to include other files from
  50. if(!defined('PHP_XMLRPC_COMPAT_DIR'))
  51. {
  52. define('PHP_XMLRPC_COMPAT_DIR',dirname(__FILE__).'/compat/');
  53. }
  54. if(substr(phpversion(), 0, 3) == '4.0')
  55. {
  56. include(PHP_XMLRPC_COMPAT_DIR."is_scalar.php");
  57. include(PHP_XMLRPC_COMPAT_DIR."array_key_exists.php");
  58. include(PHP_XMLRPC_COMPAT_DIR."version_compare.php");
  59. }
  60. include(PHP_XMLRPC_COMPAT_DIR."var_export.php");
  61. include(PHP_XMLRPC_COMPAT_DIR."is_a.php");
  62. }
  63. // G. Giunta 2005/01/29: declare global these variables,
  64. // so that xmlrpc.inc will work even if included from within a function
  65. // Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used.
  66. $GLOBALS['xmlrpcI4']='i4';
  67. $GLOBALS['xmlrpcInt']='int';
  68. $GLOBALS['xmlrpcBoolean']='boolean';
  69. $GLOBALS['xmlrpcDouble']='double';
  70. $GLOBALS['xmlrpcString']='string';
  71. $GLOBALS['xmlrpcDateTime']='dateTime.iso8601';
  72. $GLOBALS['xmlrpcBase64']='base64';
  73. $GLOBALS['xmlrpcArray']='array';
  74. $GLOBALS['xmlrpcStruct']='struct';
  75. $GLOBALS['xmlrpcValue']='undefined';
  76. $GLOBALS['xmlrpcTypes']=array(
  77. $GLOBALS['xmlrpcI4'] => 1,
  78. $GLOBALS['xmlrpcInt'] => 1,
  79. $GLOBALS['xmlrpcBoolean'] => 1,
  80. $GLOBALS['xmlrpcString'] => 1,
  81. $GLOBALS['xmlrpcDouble'] => 1,
  82. $GLOBALS['xmlrpcDateTime'] => 1,
  83. $GLOBALS['xmlrpcBase64'] => 1,
  84. $GLOBALS['xmlrpcArray'] => 2,
  85. $GLOBALS['xmlrpcStruct'] => 3
  86. );
  87. $GLOBALS['xmlrpc_valid_parents'] = array(
  88. 'BOOLEAN' => array('VALUE'),
  89. 'I4' => array('VALUE'),
  90. 'INT' => array('VALUE'),
  91. 'STRING' => array('VALUE'),
  92. 'DOUBLE' => array('VALUE'),
  93. 'DATETIME.ISO8601' => array('VALUE'),
  94. 'BASE64' => array('VALUE'),
  95. 'ARRAY' => array('VALUE'),
  96. 'STRUCT' => array('VALUE'),
  97. 'PARAM' => array('PARAMS'),
  98. 'METHODNAME' => array('METHODCALL'),
  99. 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
  100. 'MEMBER' => array('STRUCT'),
  101. 'NAME' => array('MEMBER'),
  102. 'DATA' => array('ARRAY'),
  103. 'FAULT' => array('METHODRESPONSE'),
  104. 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
  105. );
  106. // define extra types for supporting NULL (useful for json or <NIL/>)
  107. $GLOBALS['xmlrpcNull']='null';
  108. $GLOBALS['xmlrpcTypes']['null']=1;
  109. // Not in use anymore since 2.0. Shall we remove it?
  110. /// @deprecated
  111. $GLOBALS['xmlEntities']=array(
  112. 'amp' => '&',
  113. 'quot' => '"',
  114. 'lt' => '<',
  115. 'gt' => '>',
  116. 'apos' => "'"
  117. );
  118. // tables used for transcoding different charsets into us-ascii xml
  119. $GLOBALS['xml_iso88591_Entities']=array();
  120. $GLOBALS['xml_iso88591_Entities']['in'] = array();
  121. $GLOBALS['xml_iso88591_Entities']['out'] = array();
  122. for ($i = 0; $i < 32; $i++)
  123. {
  124. $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
  125. $GLOBALS['xml_iso88591_Entities']['out'][] = "&#$i;";
  126. }
  127. for ($i = 160; $i < 256; $i++)
  128. {
  129. $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
  130. $GLOBALS['xml_iso88591_Entities']['out'][] = "&#$i;";
  131. }
  132. /// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159.
  133. /// These will NOT be present in true ISO-8859-1, but will save the unwary
  134. /// windows user from sending junk.
  135. /*
  136. $cp1252_to_htmlent =
  137. array(
  138. '\x80'=>'&#x20AC;', '\x81'=>'?', '\x82'=>'&#x201A;', '\x83'=>'&#x0192;',
  139. '\x84'=>'&#x201E;', '\x85'=>'&#x2026;', '\x86'=>'&#x2020;', \x87'=>'&#x2021;',
  140. '\x88'=>'&#x02C6;', '\x89'=>'&#x2030;', '\x8A'=>'&#x0160;', '\x8B'=>'&#x2039;',
  141. '\x8C'=>'&#x0152;', '\x8D'=>'?', '\x8E'=>'&#x017D;', '\x8F'=>'?',
  142. '\x90'=>'?', '\x91'=>'&#x2018;', '\x92'=>'&#x2019;', '\x93'=>'&#x201C;',
  143. '\x94'=>'&#x201D;', '\x95'=>'&#x2022;', '\x96'=>'&#x2013;', '\x97'=>'&#x2014;',
  144. '\x98'=>'&#x02DC;', '\x99'=>'&#x2122;', '\x9A'=>'&#x0161;', '\x9B'=>'&#x203A;',
  145. '\x9C'=>'&#x0153;', '\x9D'=>'?', '\x9E'=>'&#x017E;', '\x9F'=>'&#x0178;'
  146. );
  147. */
  148. $GLOBALS['xmlrpcerr']['unknown_method']=1;
  149. $GLOBALS['xmlrpcstr']['unknown_method']='Unknown method';
  150. $GLOBALS['xmlrpcerr']['invalid_return']=2;
  151. $GLOBALS['xmlrpcstr']['invalid_return']='Invalid return payload: enable debugging to examine incoming payload';
  152. $GLOBALS['xmlrpcerr']['incorrect_params']=3;
  153. $GLOBALS['xmlrpcstr']['incorrect_params']='Incorrect parameters passed to method';
  154. $GLOBALS['xmlrpcerr']['introspect_unknown']=4;
  155. $GLOBALS['xmlrpcstr']['introspect_unknown']="Can't introspect: method unknown";
  156. $GLOBALS['xmlrpcerr']['http_error']=5;
  157. $GLOBALS['xmlrpcstr']['http_error']="Didn't receive 200 OK from remote server.";
  158. $GLOBALS['xmlrpcerr']['no_data']=6;
  159. $GLOBALS['xmlrpcstr']['no_data']='No data received from server.';
  160. $GLOBALS['xmlrpcerr']['no_ssl']=7;
  161. $GLOBALS['xmlrpcstr']['no_ssl']='No SSL support compiled in.';
  162. $GLOBALS['xmlrpcerr']['curl_fail']=8;
  163. $GLOBALS['xmlrpcstr']['curl_fail']='CURL error';
  164. $GLOBALS['xmlrpcerr']['invalid_request']=15;
  165. $GLOBALS['xmlrpcstr']['invalid_request']='Invalid request payload';
  166. $GLOBALS['xmlrpcerr']['no_curl']=16;
  167. $GLOBALS['xmlrpcstr']['no_curl']='No CURL support compiled in.';
  168. $GLOBALS['xmlrpcerr']['server_error']=17;
  169. $GLOBALS['xmlrpcstr']['server_error']='Internal server error';
  170. $GLOBALS['xmlrpcerr']['multicall_error']=18;
  171. $GLOBALS['xmlrpcstr']['multicall_error']='Received from server invalid multicall response';
  172. $GLOBALS['xmlrpcerr']['multicall_notstruct'] = 9;
  173. $GLOBALS['xmlrpcstr']['multicall_notstruct'] = 'system.multicall expected struct';
  174. $GLOBALS['xmlrpcerr']['multicall_nomethod'] = 10;
  175. $GLOBALS['xmlrpcstr']['multicall_nomethod'] = 'missing methodName';
  176. $GLOBALS['xmlrpcerr']['multicall_notstring'] = 11;
  177. $GLOBALS['xmlrpcstr']['multicall_notstring'] = 'methodName is not a string';
  178. $GLOBALS['xmlrpcerr']['multicall_recursion'] = 12;
  179. $GLOBALS['xmlrpcstr']['multicall_recursion'] = 'recursive system.multicall forbidden';
  180. $GLOBALS['xmlrpcerr']['multicall_noparams'] = 13;
  181. $GLOBALS['xmlrpcstr']['multicall_noparams'] = 'missing params';
  182. $GLOBALS['xmlrpcerr']['multicall_notarray'] = 14;
  183. $GLOBALS['xmlrpcstr']['multicall_notarray'] = 'params is not an array';
  184. $GLOBALS['xmlrpcerr']['cannot_decompress']=103;
  185. $GLOBALS['xmlrpcstr']['cannot_decompress']='Received from server compressed HTTP and cannot decompress';
  186. $GLOBALS['xmlrpcerr']['decompress_fail']=104;
  187. $GLOBALS['xmlrpcstr']['decompress_fail']='Received from server invalid compressed HTTP';
  188. $GLOBALS['xmlrpcerr']['dechunk_fail']=105;
  189. $GLOBALS['xmlrpcstr']['dechunk_fail']='Received from server invalid chunked HTTP';
  190. $GLOBALS['xmlrpcerr']['server_cannot_decompress']=106;
  191. $GLOBALS['xmlrpcstr']['server_cannot_decompress']='Received from client compressed HTTP request and cannot decompress';
  192. $GLOBALS['xmlrpcerr']['server_decompress_fail']=107;
  193. $GLOBALS['xmlrpcstr']['server_decompress_fail']='Received from client invalid compressed HTTP request';
  194. // The charset encoding used by the server for received messages and
  195. // by the client for received responses when received charset cannot be determined
  196. // or is not supported
  197. $GLOBALS['xmlrpc_defencoding']='UTF-8';
  198. // The encoding used internally by PHP.
  199. // String values received as xml will be converted to this, and php strings will be converted to xml
  200. // as if having been coded with this
  201. $GLOBALS['xmlrpc_internalencoding']='ISO-8859-1';
  202. $GLOBALS['xmlrpcName']='XML-RPC for PHP';
  203. $GLOBALS['xmlrpcVersion']='2.0';
  204. // let user errors start at 800
  205. $GLOBALS['xmlrpcerruser']=800;
  206. // let XML parse errors start at 100
  207. $GLOBALS['xmlrpcerrxml']=100;
  208. // formulate backslashes for escaping regexp
  209. // Not in use anymore since 2.0. Shall we remove it?
  210. /// @deprecated
  211. $GLOBALS['xmlrpc_backslash']=chr(92).chr(92);
  212. // used to store state during parsing
  213. // quick explanation of components:
  214. // ac - used to accumulate values
  215. // isf - used to indicate a fault
  216. // lv - used to indicate "looking for a value": implements
  217. // the logic to allow values with no types to be strings
  218. // params - used to store parameters in method calls
  219. // method - used to store method name
  220. // stack - array with genealogy of xml elements names:
  221. // used to validate nesting of xmlrpc elements
  222. $GLOBALS['_xh']=null;
  223. /**
  224. * Convert a string to the correct XML representation in a target charset
  225. * To help correct communication of non-ascii chars inside strings, regardless
  226. * of the charset used when sending requests, parsing them, sending responses
  227. * and parsing responses, an option is to convert all non-ascii chars present in the message
  228. * into their equivalent 'charset entity'. Charset entities enumerated this way
  229. * are independent of the charset encoding used to transmit them, and all XML
  230. * parsers are bound to understand them.
  231. * Note that in the std case we are not sending a charset encoding mime type
  232. * along with http headers, so we are bound by RFC 3023 to emit strict us-ascii.
  233. *
  234. * @todo do a bit of basic benchmarking (strtr vs. str_replace)
  235. * @todo make usage of iconv() or recode_string() or mb_string() where available
  236. */
  237. function xmlrpc_encode_entitites($data, $src_encoding='', $dest_encoding='')
  238. {
  239. if ($src_encoding == '')
  240. {
  241. // lame, but we know no better...
  242. $src_encoding = $GLOBALS['xmlrpc_internalencoding'];
  243. }
  244. //if ($dest_encoding == '')
  245. //{
  246. // // lame, but we know no better...
  247. // $dest_encoding = 'US-ASCII';
  248. //}
  249. switch(strtoupper($src_encoding.'_'.$dest_encoding))
  250. {
  251. case 'ISO-8859-1_':
  252. case 'ISO-8859-1_US-ASCII':
  253. $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
  254. $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
  255. break;
  256. case 'ISO-8859-1_UTF-8':
  257. $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
  258. $escaped_data = utf8_encode($escaped_data);
  259. break;
  260. case 'ISO-8859-1_ISO-8859-1':
  261. case 'US-ASCII_US-ASCII':
  262. case 'US-ASCII_UTF-8':
  263. case 'US-ASCII_':
  264. case 'US-ASCII_ISO-8859-1':
  265. case 'UTF-8_UTF-8':
  266. $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
  267. break;
  268. case 'UTF-8_':
  269. case 'UTF-8_US-ASCII':
  270. case 'UTF-8_ISO-8859-1':
  271. // NB: this will choke on invalid UTF-8, going most likely beyond EOF
  272. $escaped_data = "";
  273. // be kind to users creating string xmlrpcvals out of different php types
  274. $data = (string) $data;
  275. $ns = strlen ($data);
  276. for ($nn = 0; $nn < $ns; $nn++)
  277. {
  278. $ch = $data[$nn];
  279. $ii = ord($ch);
  280. //1 7 0bbbbbbb (127)
  281. if ($ii < 128)
  282. {
  283. /// @todo shall we replace this with a (supposedly) faster str_replace?
  284. switch($ii){
  285. case 34:
  286. $escaped_data .= '&quot;';
  287. break;
  288. case 38:
  289. $escaped_data .= '&amp;';
  290. break;
  291. case 39:
  292. $escaped_data .= '&apos;';
  293. break;
  294. case 60:
  295. $escaped_data .= '&lt;';
  296. break;
  297. case 62:
  298. $escaped_data .= '&gt;';
  299. break;
  300. default:
  301. $escaped_data .= $ch;
  302. } // switch
  303. }
  304. //2 11 110bbbbb 10bbbbbb (2047)
  305. else if ($ii>>5 == 6)
  306. {
  307. $b1 = ($ii & 31);
  308. $ii = ord($data[$nn+1]);
  309. $b2 = ($ii & 63);
  310. $ii = ($b1 * 64) + $b2;
  311. $ent = sprintf ("&#%d;", $ii);
  312. $escaped_data .= $ent;
  313. }
  314. //3 16 1110bbbb 10bbbbbb 10bbbbbb
  315. else if ($ii>>4 == 14)
  316. {
  317. $b1 = ($ii & 31);
  318. $ii = ord($data[$nn+1]);
  319. $b2 = ($ii & 63);
  320. $ii = ord($data[$nn+2]);
  321. $b3 = ($ii & 63);
  322. $ii = ((($b1 * 64) + $b2) * 64) + $b3;
  323. $ent = sprintf ("&#%d;", $ii);
  324. $escaped_data .= $ent;
  325. }
  326. //4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
  327. else if ($ii>>3 == 30)
  328. {
  329. $b1 = ($ii & 31);
  330. $ii = ord($data[$nn+1]);
  331. $b2 = ($ii & 63);
  332. $ii = ord($data[$nn+2]);
  333. $b3 = ($ii & 63);
  334. $ii = ord($data[$nn+3]);
  335. $b4 = ($ii & 63);
  336. $ii = ((((($b1 * 64) + $b2) * 64) + $b3) * 64) + $b4;
  337. $ent = sprintf ("&#%d;", $ii);
  338. $escaped_data .= $ent;
  339. }
  340. }
  341. break;
  342. default:
  343. $escaped_data = '';
  344. error_log("Converting from $src_encoding to $dest_encoding: not supported...");
  345. }
  346. // } // switch
  347. return $escaped_data;
  348. }
  349. function xmlrpc_se($parser, $name, $attrs)
  350. {
  351. // if invalid xmlrpc already detected, skip all processing
  352. if ($GLOBALS['_xh']['isf'] < 2)
  353. {
  354. // check for correct element nesting
  355. // top level element can only be of 2 types
  356. if (count($GLOBALS['_xh']['stack']) == 0)
  357. {
  358. if ($name != 'METHODRESPONSE' && $name != 'METHODCALL')
  359. {
  360. $GLOBALS['_xh']['isf'] = 2;
  361. $GLOBALS['_xh']['isf_reason'] = 'missing top level xmlrpc element';
  362. return;
  363. }
  364. }
  365. else
  366. {
  367. // not top level element: see if parent is OK
  368. $parent = end($GLOBALS['_xh']['stack']);
  369. if (!array_key_exists($name, $GLOBALS['xmlrpc_valid_parents']) || !in_array($parent, $GLOBALS['xmlrpc_valid_parents'][$name]))
  370. {
  371. $GLOBALS['_xh']['isf'] = 2;
  372. $GLOBALS['_xh']['isf_reason'] = "xmlrpc element $name cannot be child of $parent";
  373. return;
  374. }
  375. }
  376. switch($name)
  377. {
  378. case 'STRUCT':
  379. case 'ARRAY':
  380. // create an empty array to hold child values, and push it onto appropriate stack
  381. $cur_val = array();
  382. $cur_val['values'] = array();
  383. $cur_val['type'] = $name;
  384. // check for out-of-band information to rebuild php objs
  385. // and in case it is found, save it
  386. if (@isset($attrs['PHP_CLASS']))
  387. {
  388. $cur_val['php_class'] = $attrs['PHP_CLASS'];
  389. }
  390. $GLOBALS['_xh']['valuestack'][] = $cur_val;
  391. break;
  392. case 'DATA':
  393. case 'METHODCALL':
  394. case 'METHODRESPONSE':
  395. case 'PARAMS':
  396. // valid elements that add little to processing
  397. break;
  398. case 'METHODNAME':
  399. case 'NAME':
  400. $GLOBALS['_xh']['ac']='';
  401. break;
  402. case 'FAULT':
  403. $GLOBALS['_xh']['isf']=1;
  404. break;
  405. case 'VALUE':
  406. $GLOBALS['_xh']['vt']='value'; // indicator: no value found yet
  407. $GLOBALS['_xh']['ac']='';
  408. $GLOBALS['_xh']['lv']=1;
  409. $GLOBALS['_xh']['php_class']=null;
  410. break;
  411. case 'I4':
  412. case 'INT':
  413. case 'STRING':
  414. case 'BOOLEAN':
  415. case 'DOUBLE':
  416. case 'DATETIME.ISO8601':
  417. case 'BASE64':
  418. if ($GLOBALS['_xh']['vt']!='value')
  419. {
  420. //two data elements inside a value: an error occurred!
  421. $GLOBALS['_xh']['isf'] = 2;
  422. $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
  423. return;
  424. }
  425. $GLOBALS['_xh']['ac']=''; // reset the accumulator
  426. break;
  427. case 'MEMBER':
  428. $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name']=''; // set member name to null, in case we do not find in the xml later on
  429. //$GLOBALS['_xh']['ac']='';
  430. // Drop trough intentionally
  431. case 'PARAM':
  432. // clear value type, so we can check later if no value has been passed for this param/member
  433. $GLOBALS['_xh']['vt']=null;
  434. break;
  435. default:
  436. /// INVALID ELEMENT: RAISE ISF so that it is later recognized!!!
  437. $GLOBALS['_xh']['isf'] = 2;
  438. $GLOBALS['_xh']['isf_reason'] = "found not-xmlrpc xml element $name";
  439. break;
  440. }
  441. // Save current element name to stack, to validate nesting
  442. $GLOBALS['_xh']['stack'][] = $name;
  443. if($name!='VALUE')
  444. {
  445. $GLOBALS['_xh']['lv']=0;
  446. }
  447. }
  448. }
  449. function xmlrpc_ee($parser, $name, $rebuild_xmlrpcvals = true)
  450. {
  451. if ($GLOBALS['_xh']['isf'] < 2)
  452. {
  453. // push this element name from stack
  454. // NB: if XML validates, correct opening/closing is guaranteed and
  455. // we do not have to check for $name == $curr_elem.
  456. // we also checked for proper nesting at start of elements...
  457. $curr_elem = array_pop($GLOBALS['_xh']['stack']);
  458. switch($name)
  459. {
  460. case 'STRUCT':
  461. case 'ARRAY':
  462. // fetch out of stack array of values, and promote it to current value
  463. $curr_val = array_pop($GLOBALS['_xh']['valuestack']);
  464. $GLOBALS['_xh']['value'] = $curr_val['values'];
  465. $GLOBALS['_xh']['vt']=strtolower($name);
  466. if (isset($curr_val['php_class']))
  467. {
  468. $GLOBALS['_xh']['php_class'] = $curr_val['php_class'];
  469. }
  470. break;
  471. case 'NAME':
  472. $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name'] = $GLOBALS['_xh']['ac'];
  473. break;
  474. case 'BOOLEAN':
  475. case 'I4':
  476. case 'INT':
  477. case 'STRING':
  478. case 'DOUBLE':
  479. case 'DATETIME.ISO8601':
  480. case 'BASE64':
  481. $GLOBALS['_xh']['vt']=strtolower($name);
  482. if ($name=='STRING')
  483. {
  484. $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
  485. }
  486. elseif ($name=='DATETIME.ISO8601')
  487. {
  488. /// @todo validate datetime values with a correct format mask?
  489. $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcDateTime'];
  490. $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
  491. }
  492. elseif ($name=='BASE64')
  493. {
  494. /// @todo check for failure of base64 decoding / catch warnings
  495. $GLOBALS['_xh']['value']=base64_decode($GLOBALS['_xh']['ac']);
  496. }
  497. elseif ($name=='BOOLEAN')
  498. {
  499. // special case here: we translate boolean 1 or 0 into PHP
  500. // constants true or false.
  501. // Strings 'true' and 'false' are accepted, even though the
  502. // spec never mentions them (see eg. Blogger api docs)
  503. // NB: this simple checks helps a lot sanitizing input, ie no
  504. // security problems around here
  505. if ($GLOBALS['_xh']['ac']=='1' || strcasecmp($GLOBALS['_xh']['ac'], 'true') == 0)
  506. {
  507. $GLOBALS['_xh']['value']=true;
  508. }
  509. else
  510. {
  511. // log if receiveing something strange, even though we set the value to false anyway
  512. if ($GLOBALS['_xh']['ac']!='0' && strcasecmp($_xh[$parser]['ac'], 'false') != 0)
  513. error_log('XML-RPC: invalid value received in BOOLEAN: '.$GLOBALS['_xh']['ac']);
  514. $GLOBALS['_xh']['value']=false;
  515. }
  516. }
  517. elseif ($name=='DOUBLE')
  518. {
  519. // we have a DOUBLE
  520. // we must check that only 0123456789-.<space> are characters here
  521. if (!ereg("^[+-]?[eE0123456789 \\t.]+$", $GLOBALS['_xh']['ac']))
  522. {
  523. /// @todo: find a better way of throwing an error
  524. // than this!
  525. error_log('XML-RPC: non numeric value received in DOUBLE: '.$GLOBALS['_xh']['ac']);
  526. $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
  527. }
  528. else
  529. {
  530. // it's ok, add it on
  531. $GLOBALS['_xh']['value']=(double)$GLOBALS['_xh']['ac'];
  532. }
  533. }
  534. else
  535. {
  536. // we have an I4/INT
  537. // we must check that only 0123456789-<space> are characters here
  538. if (!ereg("^[+-]?[0123456789 \\t]+$", $GLOBALS['_xh']['ac']))
  539. {
  540. /// @todo find a better way of throwing an error
  541. // than this!
  542. error_log('XML-RPC: non numeric value received in INT: '.$GLOBALS['_xh']['ac']);
  543. $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
  544. }
  545. else
  546. {
  547. // it's ok, add it on
  548. $GLOBALS['_xh']['value']=(int)$GLOBALS['_xh']['ac'];
  549. }
  550. }
  551. $GLOBALS['_xh']['ac']=''; // is this necessary?
  552. $GLOBALS['_xh']['lv']=3; // indicate we've found a value
  553. break;
  554. case 'VALUE':
  555. // This if() detects if no scalar was inside <VALUE></VALUE>
  556. if ($GLOBALS['_xh']['vt']=='value')
  557. {
  558. $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
  559. $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcString'];
  560. }
  561. if ($rebuild_xmlrpcvals)
  562. {
  563. // build the xmlrpc val out of the data received, and substitute it
  564. $temp =& new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']);
  565. // in case we got info about underlying php class, save it
  566. // in the object we're rebuilding
  567. if (isset($GLOBALS['_xh']['php_class']))
  568. $temp->_php_class = $GLOBALS['_xh']['php_class'];
  569. // check if we are inside an array or struct:
  570. // if value just built is inside an array, let's move it into array on the stack
  571. $vscount = count($GLOBALS['_xh']['valuestack']);
  572. if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
  573. {
  574. $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $temp;
  575. }
  576. else
  577. {
  578. $GLOBALS['_xh']['value'] = $temp;
  579. }
  580. }
  581. else
  582. {
  583. /// @todo this needs to treat correctly php-serialized objects,
  584. /// since std deserializing is done by php_xmlrpc_decode,
  585. /// which we will not be calling...
  586. if (isset($GLOBALS['_xh']['php_class']))
  587. {
  588. }
  589. // check if we are inside an array or struct:
  590. // if value just built is inside an array, let's move it into array on the stack
  591. $vscount = count($GLOBALS['_xh']['valuestack']);
  592. if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
  593. {
  594. $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $GLOBALS['_xh']['value'];
  595. }
  596. }
  597. break;
  598. case 'MEMBER':
  599. $GLOBALS['_xh']['ac']=''; // is this necessary?
  600. // add to array in the stack the last element built,
  601. // unless no VALUE was found
  602. if ($GLOBALS['_xh']['vt'])
  603. {
  604. $vscount = count($GLOBALS['_xh']['valuestack']);
  605. $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][$GLOBALS['_xh']['valuestack'][$vscount-1]['name']] = $GLOBALS['_xh']['value'];
  606. } else
  607. error_log('XML-RPC: missing VALUE inside STRUCT in received xml');
  608. break;
  609. case 'DATA':
  610. $GLOBALS['_xh']['ac']=''; // is this necessary?
  611. break;
  612. case 'PARAM':
  613. // add to array of params the current value,
  614. // unless no VALUE was found
  615. if ($GLOBALS['_xh']['vt'])
  616. {
  617. $GLOBALS['_xh']['params'][]=$GLOBALS['_xh']['value'];
  618. $GLOBALS['_xh']['pt'][]=$GLOBALS['_xh']['vt'];
  619. }
  620. else
  621. error_log('XML-RPC: missing VALUE inside PARAM in received xml');
  622. break;
  623. case 'METHODNAME':
  624. $GLOBALS['_xh']['method']=ereg_replace("^[\n\r\t ]+", '', $GLOBALS['_xh']['ac']);
  625. break;
  626. case 'PARAMS':
  627. case 'FAULT':
  628. case 'METHODCALL':
  629. case 'METHORESPONSE':
  630. break;
  631. default:
  632. // End of INVALID ELEMENT!
  633. // shall we add an assert here for unreachable code???
  634. break;
  635. }
  636. }
  637. }
  638. function xmlrpc_ee_fast($parser, $name)
  639. {
  640. xmlrpc_ee($parser, $name, false);
  641. }
  642. function xmlrpc_cd($parser, $data)
  643. {
  644. //if(ereg("^[\n\r \t]+$", $data)) return;
  645. // print "adding [${data}]\n";
  646. // skip processing if xml fault already detected
  647. if ($GLOBALS['_xh']['isf'] < 2)
  648. {
  649. if($GLOBALS['_xh']['lv']!=3)
  650. {
  651. // "lookforvalue==3" means that we've found an entire value
  652. // and should discard any further character data
  653. if($GLOBALS['_xh']['lv']==1)
  654. {
  655. // if we've found text and we're just in a <value> then
  656. // say we've found a value
  657. $GLOBALS['_xh']['lv']=2;
  658. }
  659. if(!@isset($GLOBALS['_xh']['ac']))
  660. {
  661. $GLOBALS['_xh']['ac'] = '';
  662. }
  663. $GLOBALS['_xh']['ac'].=$data;
  664. }
  665. }
  666. }
  667. function xmlrpc_dh($parser, $data)
  668. {
  669. // skip processing if xml fault already detected
  670. if ($GLOBALS['_xh']['isf'] < 2)
  671. {
  672. if(substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';')
  673. {
  674. if($GLOBALS['_xh']['lv']==1)
  675. {
  676. $GLOBALS['_xh']['lv']=2;
  677. }
  678. $GLOBALS['_xh']['ac'].=$data;
  679. }
  680. }
  681. }
  682. class xmlrpc_client
  683. {
  684. var $path;
  685. var $server;
  686. var $port=0;
  687. var $method='http';
  688. var $errno;
  689. var $errstr;
  690. var $debug=0;
  691. var $username='';
  692. var $password='';
  693. var $authtype=1;
  694. var $cert='';
  695. var $certpass='';
  696. var $cacert='';
  697. var $cacertdir='';
  698. var $key='';
  699. var $keypass='';
  700. var $verifypeer=true;
  701. var $verifyhost=1;
  702. var $no_multicall=false;
  703. var $proxy='';
  704. var $proxyport=0;
  705. var $proxy_user='';
  706. var $proxy_pass='';
  707. var $proxy_authtype=1;
  708. var $cookies=array();
  709. /**
  710. * List of http compression methods accepted by the client for responses.
  711. * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
  712. *
  713. * NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since
  714. * in those cases it will be up to CURL to decide the compression methods
  715. * it supports. You might check for the presence of 'zlib' in the output of
  716. * curl_version() to determine wheter compression is supported or not
  717. */
  718. var $accepted_compression = array();
  719. /**
  720. * Name of compression scheme to be used for sending requests.
  721. * Either null, gzip or deflate
  722. */
  723. var $request_compression = '';
  724. /**
  725. * CURL handle: used for keep-alive connections (PHP 4.3.8 up, see:
  726. * http://curl.haxx.se/docs/faq.html#7.3)
  727. */
  728. var $xmlrpc_curl_handle = null;
  729. /// Wheter to use persistent connections for http 1.1 and https
  730. var $keepalive = false;
  731. /// Charset encodings that can be decoded without problems by the client
  732. var $accepted_charset_encodings = array();
  733. /// Charset encoding to be used in serializing request. NULL = use ASCII
  734. var $request_charset_encoding = '';
  735. /**
  736. * Decides the content of xmlrpcresp objects returned by calls to send()
  737. * valid strings are 'xmlrpcvals', 'phpvals' or 'xml'
  738. */
  739. var $return_type = 'xmlrpcvals';
  740. /**
  741. * @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php
  742. * @param string $server the server name / ip address
  743. * @param integer $port the port the server is listening on, defaults to 80 or 443 depending on protocol used
  744. * @param string $method the http protocol variant: defaults to 'http', 'https' and 'http11' can be used if CURL is installed
  745. */
  746. function xmlrpc_client($path, $server='', $port='', $method='')
  747. {
  748. // allow user to specify all params in $path
  749. if($server == '' and $port == '' and $method == '')
  750. {
  751. $parts = parse_url($path);
  752. $server = $parts['host'];
  753. $path = $parts['path'];
  754. if(isset($parts['query']))
  755. {
  756. $path .= '?'.$parts['query'];
  757. }
  758. if(isset($parts['fragment']))
  759. {
  760. $path .= '#'.$parts['fragment'];
  761. }
  762. if(isset($parts['port']))
  763. {
  764. $port = $parts['port'];
  765. }
  766. if(isset($parts['scheme']))
  767. {
  768. $method = $parts['scheme'];
  769. }
  770. if(isset($parts['user']))
  771. {
  772. $this->username = $parts['user'];
  773. }
  774. if(isset($parts['pass']))
  775. {
  776. $this->password = $parts['pass'];
  777. }
  778. }
  779. if($path == '' || $path[0] != '/')
  780. {
  781. $this->path='/'.$path;
  782. }
  783. else
  784. {
  785. $this->path=$path;
  786. }
  787. $this->server=$server;
  788. if($port != '')
  789. {
  790. $this->port=$port;
  791. }
  792. if($method != '')
  793. {
  794. $this->method=$method;
  795. }
  796. // if ZLIB is enabled, let the client by default accept compressed responses
  797. if(function_exists('gzinflate') || (
  798. function_exists('curl_init') && (($info = curl_version()) &&
  799. ((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version'])))
  800. ))
  801. {
  802. $this->accepted_compression = array('gzip', 'deflate');
  803. }
  804. // keepalives: enabled by default ONLY for PHP >= 4.3.8
  805. // (see http://curl.haxx.se/docs/faq.html#7.3)
  806. if(version_compare(phpversion(), '4.3.8') >= 0)
  807. {
  808. $this->keepalive = true;
  809. }
  810. // by default the xml parser can support these 3 charset encodings
  811. $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
  812. }
  813. /*
  814. * Enables/disables the echoing to screen of the xmlrpc responses received
  815. * @param integer $debug values 0, 1 and 2 are supported (2 = echo sent msg too, beside received response)
  816. * @access public
  817. */
  818. function setDebug($in)
  819. {
  820. $this->debug=$in;
  821. }
  822. /*
  823. * Add some http BASIC AUTH credentials, used by the client to authenticate
  824. * @param string $u username
  825. * @param string $p password
  826. * @param integer $t auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC (basic auth)
  827. * @access public
  828. */
  829. function setCredentials($u, $p, $t=1)
  830. {
  831. $this->username=$u;
  832. $this->password=$p;
  833. $this->authtype=$t;
  834. }
  835. /*
  836. * Add a client-side https certificate
  837. * @param string $cert
  838. * @param string $certpass
  839. * @access public
  840. */
  841. function setCertificate($cert, $certpass)
  842. {
  843. $this->cert = $cert;
  844. $this->certpass = $certpass;
  845. }
  846. /*
  847. * Add a CA certificate to verify server with (see man page about
  848. * CURLOPT_CAINFO for more details
  849. * @param string $cacert certificate file name (or dir holding certificates)
  850. * @param bool $is_dir set to true to indicate cacert is a dir. defaults to false
  851. * @access public
  852. */
  853. function setCaCertificate($cacert, $is_dir=false)
  854. {
  855. if ($is_dir)
  856. {
  857. $this->cacert = $cacert;
  858. }
  859. else
  860. {
  861. $this->cacertdir = $cacert;
  862. }
  863. }
  864. /*
  865. * @param string $key The name of a file containing a private SSL key
  866. * @param string $keypass The secret password needed to use the private SSL key
  867. * @access public
  868. * NB: does not work in older php/curl installs
  869. * Thanks to Daniel Convissor
  870. */
  871. function setKey($key, $keypass)
  872. {
  873. $this->key = $key;
  874. $this->keypass = $keypass;
  875. }
  876. /*
  877. * @param bool $i enable/diable verification of peer certificate
  878. * @access public
  879. */
  880. function setSSLVerifyPeer($i)
  881. {
  882. $this->verifypeer = $i;
  883. }
  884. /*
  885. * @access public
  886. */
  887. function setSSLVerifyHost($i)
  888. {
  889. $this->verifyhost = $i;
  890. }
  891. /**
  892. * Set proxy info
  893. *
  894. * @param string $proxyhost
  895. * @param string $proxyport Defaults to 8080 for HTTP and 443 for HTTPS
  896. * @param string $proxyusername Leave blank if proxy has public access
  897. * @param string $proxypassword Leave blank if proxy has public access
  898. * @param int $proxyauthtype set to constant CURLAUTH_MTLM to use NTLM auth with proxy
  899. * @access public
  900. */
  901. function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1)
  902. {
  903. $this->proxy = $proxyhost;
  904. $this->proxyport = $proxyport;
  905. $this->proxy_user = $proxyusername;
  906. $this->proxy_pass = $proxypassword;
  907. $this->proxy_autthtype = $proxyauthtype;
  908. }
  909. /**
  910. * Enables/disables reception of compressed xmlrpc responses.
  911. * Note that enabling reception of compressed responses merely adds some standard
  912. * http headers to xmlrpc requests. It is up to the xmlrpc server to return
  913. * compressed responses when receiving such requests.
  914. * @param string $compmethod either 'gzip', 'deflate', 'any' or ''
  915. * @access public
  916. */
  917. function setAcceptedCompression($compmethod)
  918. {
  919. if ($compmethod == 'any')
  920. $this->accepted_compression = array('gzip', 'deflate');
  921. else
  922. $this->accepted_compression = array($compmethod);
  923. }
  924. /**
  925. * Enables/disables http compression of xmlrpc request.
  926. * Take care when sending compressed requests: servers might not support them
  927. * (and automatic fallback to uncompressed requests is not yet implemented)
  928. * @param string $compmethod either 'gzip', 'deflate' or ''
  929. * @access public
  930. */
  931. function setRequestCompression($compmethod)
  932. {
  933. $this->request_compression = $compmethod;
  934. }
  935. /**
  936. * Adds a cookie to list of cookies that will be sent to server.
  937. * NB: setting any param but name and value will turn the cookie into a 'version 1' cookie:
  938. * do not do it unless you know what you are doing
  939. * @param string $name
  940. * @param string $value
  941. * @param string $path
  942. * @param string $domain
  943. * @param string $port
  944. * @access public
  945. *
  946. * @todo check correctness of urlencoding cookie value (copied from php way of doing it...)
  947. */
  948. function setCookie($name, $value='', $path='', $domain='', $port=null)
  949. {
  950. $this->cookies[$name]['value'] = urlencode($value);
  951. if ($path || $domain || $port)
  952. {
  953. $this->cookies[$name]['path'] = $path;
  954. $this->cookies[$name]['domain'] = $domain;
  955. $this->cookies[$name]['port'] = $port;
  956. $this->cookies[$name]['version'] = 1;
  957. }
  958. else
  959. {
  960. $this->cookies[$name]['version'] = 0;
  961. }
  962. }
  963. /**
  964. * Send an xmlrpc request
  965. * @param mixed $msg The message object, or an array of messages for using multicall, or the complete xml representation of a request
  966. * @param integer $timeout Connection timeout, in seconds, If unspecified, a platform specific timeout will apply
  967. * @param string $method if left unspecified, the http protocol chosen during creation of the object will be used
  968. */
  969. function& send($msg, $timeout=0, $method='')
  970. {
  971. // if user deos not specify http protocol, use native method of this client
  972. // (i.e. method set during call to constructor)
  973. if($method == '')
  974. {
  975. $method = $this->method;
  976. }
  977. if(is_array($msg))
  978. {
  979. // $msg is an array of xmlrpcmsg's
  980. $r = $this->multicall($msg, $timeout, $method);
  981. return $r;
  982. }
  983. elseif(is_string($msg))
  984. {
  985. $n =& new xmlrpcmsg('');
  986. $n->payload = $msg;
  987. $msg = $n;
  988. }
  989. // where msg is an xmlrpcmsg
  990. $msg->debug=$this->debug;
  991. if($method == 'https')
  992. {
  993. $r =& $this->sendPayloadHTTPS(
  994. $msg,
  995. $this->server,
  996. $this->port,
  997. $timeout,
  998. $this->username,
  999. $this->password,
  1000. $this->authtype,
  1001. $this->cert,
  1002. $this->certpass,
  1003. $this->cacert,
  1004. $this->cacertdir,
  1005. $this->proxy,
  1006. $this->proxyport,
  1007. $this->proxy_user,
  1008. $this->proxy_pass,
  1009. $this->proxy_authtype,
  1010. $this->keepalive,
  1011. $this->key,
  1012. $this->keypass
  1013. );
  1014. }
  1015. elseif($method == 'http11')
  1016. {
  1017. $r =& $this->sendPayloadCURL(
  1018. $msg,
  1019. $this->server,
  1020. $this->port,
  1021. $timeout,
  1022. $this->username,
  1023. $this->password,
  1024. $this->authtype,
  1025. null,
  1026. null,
  1027. null,
  1028. null,
  1029. $this->proxy,
  1030. $this->proxyport,
  1031. $this->proxy_user,
  1032. $this->proxy_pass,
  1033. $this->proxy_authtype,
  1034. 'http',
  1035. $this->keepalive
  1036. );
  1037. }
  1038. else
  1039. {
  1040. $r =& $this->sendPayloadHTTP10(
  1041. $msg,
  1042. $this->server,
  1043. $this->port,
  1044. $timeout,
  1045. $this->username,
  1046. $this->password,
  1047. $this->authtype,
  1048. $this->proxy,
  1049. $this->proxyport,
  1050. $this->proxy_user,
  1051. $this->proxy_pass,
  1052. $this->proxy_authtype
  1053. );
  1054. }
  1055. return $r;
  1056. }
  1057. /**
  1058. * @access private
  1059. */
  1060. function &sendPayloadHTTP10($msg, $server, $port, $timeout=0,
  1061. $username='', $password='', $authtype=1, $proxyhost='',
  1062. $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1)
  1063. {
  1064. if($port==0)
  1065. {
  1066. $port=80;
  1067. }
  1068. // Only create the payload if it was not created previously
  1069. if(empty($msg->payload))
  1070. {
  1071. $msg->createPayload($this->request_charset_encoding);
  1072. }
  1073. $payload = $msg->payload;
  1074. // Deflate request body and set appropriate request headers
  1075. if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
  1076. {
  1077. if($this->request_compression == 'gzip')
  1078. {
  1079. $a = @gzencode($msg->payload);
  1080. if($a)
  1081. {
  1082. $payload = $a;
  1083. $encoding_hdr = "Content-Encoding: gzip\r\n";
  1084. }
  1085. }
  1086. else
  1087. {
  1088. $a = @gzdeflate($msg->payload);
  1089. if($a)
  1090. {
  1091. $payload = $a;
  1092. $encoding_hdr = "Content-Encoding: deflate\r\n";
  1093. }
  1094. }
  1095. }
  1096. else
  1097. {
  1098. $encoding_hdr = '';
  1099. }
  1100. // thanks to Grant Rauscher <grant7@firstworld.net>
  1101. // for this
  1102. $credentials='';
  1103. if($username!='')
  1104. {
  1105. $credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
  1106. if ($authtype != 1)
  1107. {
  1108. error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth is supported with HTTP 1.0');
  1109. }
  1110. }
  1111. $accepted_encoding = '';
  1112. if(is_array($this->accepted_compression) && count($this->accepted_compression))
  1113. {
  1114. $accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n";
  1115. }
  1116. $proxy_credentials = '';
  1117. if($proxyhost)
  1118. {
  1119. if($proxyport == 0)
  1120. {
  1121. $proxyport = 8080;
  1122. }
  1123. $connectserver = $proxyhost;
  1124. $connectport = $proxyport;
  1125. $uri = 'http://'.$server.':'.$port.$this->path;
  1126. if($proxyusername != '')
  1127. {
  1128. if ($proxyauthtype != 1)
  1129. {
  1130. error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth to proxy is supported with HTTP 1.0');
  1131. }
  1132. $proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n";
  1133. }
  1134. }
  1135. else
  1136. {
  1137. $connectserver = $server;
  1138. $connectport = $port;
  1139. $uri = $this->path;
  1140. }
  1141. // Cookie generation, as per rfc2965 (version 1 cookies) or
  1142. // netscape's rules (version 0 cookies)
  1143. $cookieheader='';
  1144. foreach ($this->cookies as $name => $cookie)
  1145. {
  1146. if ($cookie['version'])
  1147. {
  1148. $cookieheader .= 'Cookie: $Version="' . $cookie['version'] . '"; ';
  1149. $cookieheader .= $name . '="' . $cookie['value'] . '";';
  1150. if ($cookie['path'])
  1151. $cookieheader .= ' $Path="' . $cookie['path'] . '";';
  1152. if ($cookie['domain'])
  1153. $cookieheader .= ' $Domain="' . $cookie['domain'] . '";';
  1154. if ($cookie['port'])
  1155. $cookieheader .= ' $Port="' . $cookie['domain'] . '";';
  1156. $cookieheader = substr($cookieheader, 0, -1) . "\r\n";
  1157. }
  1158. else
  1159. {
  1160. $cookieheader .= 'Cookie: ' . $name . '=' . $cookie['value'] . "\r\n";
  1161. }
  1162. }
  1163. $op= "POST " . $uri. " HTTP/1.0\r\n" .
  1164. "User-Agent: " . $GLOBALS['xmlrpcName'] . " " . $GLOBALS['xmlrpcVersion'] . "\r\n" .
  1165. "Host: ". $server . "\r\n" .
  1166. $credentials .
  1167. $proxy_credentials .
  1168. $accepted_encoding .
  1169. $encoding_hdr .
  1170. "Accept-Charset: " . implode(',', $this->accepted_charset_encodings) . "\r\n" .
  1171. $cookieheader .
  1172. "Content-Type: " . $msg->content_type . "\r\nContent-Length: " .
  1173. strlen($payload) . "\r\n\r\n" .
  1174. $payload;
  1175. if($this->debug > 1)
  1176. {
  1177. print "<PRE>\n---SENDING---\n" . htmlentities($op) . "\n---END---\n</PRE>";
  1178. // let the client see this now in case http times out...
  1179. flush();
  1180. }
  1181. if($timeout>0)
  1182. {
  1183. $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout);
  1184. }
  1185. else
  1186. {
  1187. $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr);
  1188. }
  1189. if($fp)
  1190. {
  1191. if($timeout>0 && function_exists('stream_set_timeout'))
  1192. {
  1193. stream_set_timeout($fp, $timeout);
  1194. }
  1195. }
  1196. else
  1197. {
  1198. $this->errstr='Connect error: '.$this->errstr;
  1199. $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')');
  1200. return $r;
  1201. }
  1202. if(!fputs($fp, $op, strlen($op)))
  1203. {
  1204. $this->errstr='Write error';
  1205. $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr);
  1206. return $r;
  1207. }
  1208. else
  1209. {
  1210. // reset errno and errstr on succesful socket connection
  1211. $this->errstr = '';
  1212. }
  1213. // G. Giunta 2005/10/24: close socket before parsing.
  1214. // should yeld slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
  1215. //$resp=&$msg->parseResponseFile($fp);
  1216. $ipd='';
  1217. while($data=fread($fp, 32768))
  1218. {
  1219. // shall we check for $data === FALSE?
  1220. // as per the manual, it signals an error
  1221. $ipd.=$data;
  1222. }
  1223. fclose($fp);
  1224. $r =& $msg->parseResponse($ipd, false, $this->return_type);
  1225. return $r;
  1226. }
  1227. /**
  1228. * @access private
  1229. */
  1230. function &sendPayloadHTTPS($msg, $server, $port, $timeout=0, $username='',
  1231. $password='', $authtype=1, $cert='',$certpass='', $cacert='', $cacertdir='',
  1232. $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1,
  1233. $keepalive=false, $key='', $keypass='')
  1234. {
  1235. $r =& $this->sendPayloadCURL($msg, $server, $port, $timeout, $username,
  1236. $password, $authtype, $cert, $certpass, $cacert, $cacertdir, $proxyhost, $proxyport,
  1237. $proxyusername, $proxypassword, $proxyauthtype, 'https', $keepalive, $key, $keypass);
  1238. return $r;
  1239. }
  1240. /**
  1241. * Contributed by Justin Miller <justin@voxel.net>
  1242. * Requires curl to be built into PHP
  1243. * NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
  1244. * @access private
  1245. */
  1246. function &sendPayloadCURL($msg, $server, $port, $timeout=0, $username='',
  1247. $password='', $authtype=1, $cert='', $certpass='', $cacert='', $cacertdir='',
  1248. $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1, $method='https',
  1249. $keepalive=false, $key='', $keypass='')
  1250. {
  1251. if(!function_exists('curl_init'))
  1252. {
  1253. $this->errstr='CURL unavailable on this install';
  1254. $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']);
  1255. return $r;
  1256. }
  1257. if($method == 'https')
  1258. {
  1259. if(($info = curl_version()) &&
  1260. ((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version']))))
  1261. {
  1262. $this->errstr='SSL unavailable on this install';
  1263. $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']);
  1264. return $r;
  1265. }
  1266. }
  1267. if($port == 0)
  1268. {
  1269. if($method == 'http')
  1270. {
  1271. $port = 80;
  1272. }
  1273. else
  1274. {
  1275. $port = 443;
  1276. }
  1277. }
  1278. // Only create the payload if it was not created previously
  1279. if(empty($msg->payload))
  1280. {
  1281. $msg->createPayload($this->request_charset_encoding);
  1282. }
  1283. // Deflate request body and set appropriate request headers
  1284. $payload = $msg->payload;
  1285. if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
  1286. {
  1287. if($this->request_compression == 'gzip')
  1288. {
  1289. $a = @gzencode($msg->payload);
  1290. if($a)
  1291. {
  1292. $payload = $a;
  1293. $encoding_hdr = "Content-Encoding: gzip";
  1294. }
  1295. }
  1296. else
  1297. {
  1298. $a = @gzdeflate($msg->payload);
  1299. if($a)
  1300. {
  1301. $payload = $a;
  1302. $encoding_hdr = "Content-Encoding: deflate";
  1303. }
  1304. }
  1305. }
  1306. else
  1307. {
  1308. $encoding_hdr = '';
  1309. }
  1310. if($this->debug > 1)
  1311. {
  1312. print "<PRE>\n---SENDING---\n" . htmlentities($payload) . "\n---END---\n</PRE>";
  1313. // let the client see this now in case http times out...
  1314. flush();
  1315. }
  1316. if(!$keepalive || !$this->xmlrpc_curl_handle)
  1317. {
  1318. $curl = curl_init($method . '://' . $server . ':' . $port . $this->path);
  1319. if($keepalive)
  1320. {
  1321. $this->xmlrpc_curl_handle = $curl;
  1322. }
  1323. }
  1324. else
  1325. {
  1326. $curl = $this->xmlrpc_curl_handle;
  1327. }
  1328. // results into variable
  1329. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  1330. if($this->debug)
  1331. {
  1332. curl_setopt($curl, CURLOPT_VERBOSE, 1);
  1333. }
  1334. curl_setopt($curl, CURLOPT_USERAGENT, $GLOBALS['xmlrpcName'].' '.$GLOBALS['xmlrpcVersion']);
  1335. // required for XMLRPC: post the data
  1336. curl_setopt($curl, CURLOPT_POST, 1);
  1337. // the data
  1338. curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
  1339. // return the header too
  1340. curl_setopt($curl, CURLOPT_HEADER, 1);
  1341. // will only work with PHP >= 5.0
  1342. // NB: if we set an empty string, CURL will add http header indicating
  1343. // ALL methods it is supporting. This is possibly a better option than
  1344. // letting the user tell what curl can / cannot do...
  1345. if(is_array($this->accepted_compression) && count($this->accepted_compression))
  1346. {
  1347. //curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression));
  1348. // empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
  1349. curl_setopt($curl, CURLOPT_ENCODING, '');
  1350. }
  1351. // extra headers
  1352. $headers = array('Content-Type: ' . $msg->content_type , 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings));
  1353. // if no keepalive is wanted, let the server know it in advance
  1354. if(!$keepalive)
  1355. {
  1356. $headers[] = 'Connection: close';
  1357. }
  1358. // request compression header
  1359. if($encoding_hdr)
  1360. {
  1361. $headers[] = $encoding_hdr;
  1362. }
  1363. curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
  1364. // timeout is borked
  1365. if($timeout)
  1366. {
  1367. curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
  1368. }
  1369. if($username && $password)
  1370. {
  1371. curl_setopt($curl, CURLOPT_USERPWD,"$username:$password");
  1372. if (defined('CURLOPT_HTTPAUTH'))
  1373. {
  1374. curl_setopt($curl, CURLOPT_HTTPAUTH, $authtype);
  1375. }
  1376. else if ($authtype != 1)
  1377. {
  1378. error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth is supported by the current PHP/curl install');
  1379. }
  1380. }
  1381. if($method == 'https')
  1382. {
  1383. // set cert file
  1384. if($cert)
  1385. {
  1386. curl_setopt($curl, CURLOPT_SSLCERT, $cert);
  1387. }
  1388. // set cert password
  1389. if($certpass)
  1390. {
  1391. curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass);
  1392. }
  1393. // whether to verify remote host's cert
  1394. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
  1395. // set ca certificates file/dir
  1396. if($cacert)
  1397. {
  1398. curl_setopt($curl, CURLOPT_CAINFO, $cacert);
  1399. }
  1400. if($cacertdir)
  1401. {
  1402. curl_setopt($curl, CURLOPT_CAPATH, $cacertdir);
  1403. }
  1404. // set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
  1405. if($key)
  1406. {
  1407. curl_setopt($curl, CURLOPT_SSLKEY, $key);
  1408. }
  1409. // set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
  1410. if($keypass)
  1411. {
  1412. curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $keypass);
  1413. }
  1414. // whether to verify cert's common name (CN); 0 for no, 1 to verify that it exists, and 2 to verify that it matches the hostname used
  1415. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
  1416. }
  1417. // proxy info
  1418. if($proxyhost)
  1419. {
  1420. if($proxyport == 0)
  1421. {
  1422. $proxyport = 8080; // NB: even for HTTPS, local connection is on port 8080
  1423. }
  1424. curl_setopt($curl, CURLOPT_PROXY,$proxyhost.':'.$proxyport);
  1425. //curl_setopt($curl, CURLOPT_PROXYPORT,$proxyport);
  1426. if($proxyusername)
  1427. {
  1428. curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyusername.':'.$proxypassword);
  1429. if (defined('CURLOPT_PROXYAUTH'))
  1430. {
  1431. curl_setopt($curl, CURLOPT_PROXYAUTH, $proxyauthtype);
  1432. }
  1433. else if ($proxyauthtype != 1)
  1434. {
  1435. error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth to proxy is supported by the current PHP/curl install');
  1436. }
  1437. }
  1438. }
  1439. // NB: should we build cookie http headers by hand rather than let CURL do it?
  1440. // the following code does not honour 'expires', 'path' and 'domain' cookie attributes
  1441. // set to clint obj the the user...
  1442. if (count($this->cookies))
  1443. {
  1444. $cookieheader = '';
  1445. foreach ($this->cookies as $name => $cookie)
  1446. {
  1447. $cookieheader .= $name . '=' . $cookie['value'] . ', ';
  1448. }
  1449. curl_setopt($curl, CURLOPT_COOKIE, substr($cookieheader, 0, -2));
  1450. }
  1451. $result = curl_exec($curl);
  1452. if(!$result)
  1453. {
  1454. $this->errstr='no response';
  1455. $resp=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['curl_fail'], $GLOBALS['xmlrpcstr']['curl_fail']. ': '. curl_error($curl));
  1456. if(!$keepalive)
  1457. {
  1458. curl_close($curl);
  1459. }
  1460. }
  1461. else
  1462. {
  1463. if(!$keepalive)
  1464. {
  1465. curl_close($curl);
  1466. }
  1467. $resp =& $msg->parseResponse($result, true, $this->return_type);
  1468. }
  1469. return $resp;
  1470. }
  1471. /**
  1472. * Send an array of request messages and return an array of responses.
  1473. * Unless $this->no_multicall has been set to true, it will try first
  1474. * to use one single xmlrpc call to server method system.multicall, and
  1475. * revert to sending many successive calls in case of failure.
  1476. * This failure is also stored in $this->no_multicall for subsequent calls.
  1477. * Unfortunately, there is no server error code universally used to denote
  1478. * the fact that multicall is unsupported, so there is no way to reliably
  1479. * distinguish between that and a temporary failure.
  1480. * If you are sure that server supports multicall and do not want to
  1481. * fallback to using many single calls, set the fourth parameter to FALSE.
  1482. *
  1483. * NB: trying to shoehorn extra functionality into existing syntax has resulted
  1484. * in pretty much convoluted code...
  1485. *
  1486. * @access public
  1487. * @param array $msgs an array of xmlrpcmsg objects
  1488. * @param integer $timeout connection timeout (in seconds)
  1489. * @param string $method the http protocol variant to be used
  1490. * @param boolen fallback When true, upon receiveing an error during multicall, multiple single calls will be attempted
  1491. */
  1492. function multicall($msgs, $timeout=0, $method='http', $fallback=true)
  1493. {
  1494. if(!$this->no_multicall)
  1495. {
  1496. $results = $this->_try_multicall($msgs, $timeout, $method);
  1497. if(is_array($results))

Large files files are truncated, but you can click here to view the full file