00001 <?php 00002 // by Edd Dumbill (C) 1999-2002 00003 // <edd@usefulinc.com> 00004 // $Original: xmlrpc.inc,v 1.158 2007/03/01 21:21:02 ggiunta Exp $ 00005 // $Id: xmlrpc.inc.php 1199 2007-09-07 07:03:41Z kimitake $ 00006 // $NucleusJP: xmlrpc.inc.php,v 1.6.2.2 2007/09/07 07:04:24 kimitake Exp $ 00007 00008 00009 // Copyright (c) 1999,2000,2002 Edd Dumbill. 00010 // All rights reserved. 00011 // 00012 // Redistribution and use in source and binary forms, with or without 00013 // modification, are permitted provided that the following conditions 00014 // are met: 00015 // 00016 // * Redistributions of source code must retain the above copyright 00017 // notice, this list of conditions and the following disclaimer. 00018 // 00019 // * Redistributions in binary form must reproduce the above 00020 // copyright notice, this list of conditions and the following 00021 // disclaimer in the documentation and/or other materials provided 00022 // with the distribution. 00023 // 00024 // * Neither the name of the "XML-RPC for PHP" nor the names of its 00025 // contributors may be used to endorse or promote products derived 00026 // from this software without specific prior written permission. 00027 // 00028 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00029 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00030 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00031 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00032 // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00033 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 00034 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 00035 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00036 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 00037 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00038 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 00039 // OF THE POSSIBILITY OF SUCH DAMAGE. 00040 00041 if(!function_exists('xml_parser_create')) 00042 { 00043 // For PHP 4 onward, XML functionality is always compiled-in on windows: 00044 // no more need to dl-open it. It might have been compiled out on *nix... 00045 if(strtoupper(substr(PHP_OS, 0, 3) != 'WIN')) 00046 { 00047 dl('xml.so'); 00048 } 00049 } 00050 00051 // Try to be backward compat with php < 4.2 (are we not being nice ?) 00052 $phpversion = phpversion(); 00053 if($phpversion[0] == '4' && $phpversion[2] < 2) 00054 { 00055 // give an opportunity to user to specify where to include other files from 00056 if(!defined('PHP_XMLRPC_COMPAT_DIR')) 00057 { 00058 define('PHP_XMLRPC_COMPAT_DIR',dirname(__FILE__).'/compat/'); 00059 } 00060 if($phpversion[2] == '0') 00061 { 00062 if($phpversion[4] < 6) 00063 { 00064 include(PHP_XMLRPC_COMPAT_DIR.'is_callable.php'); 00065 } 00066 include(PHP_XMLRPC_COMPAT_DIR.'is_scalar.php'); 00067 include(PHP_XMLRPC_COMPAT_DIR.'array_key_exists.php'); 00068 include(PHP_XMLRPC_COMPAT_DIR.'version_compare.php'); 00069 } 00070 include(PHP_XMLRPC_COMPAT_DIR.'var_export.php'); 00071 include(PHP_XMLRPC_COMPAT_DIR.'is_a.php'); 00072 } 00073 00074 // G. Giunta 2005/01/29: declare global these variables, 00075 // so that xmlrpc.inc will work even if included from within a function 00076 // Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used. 00077 $GLOBALS['xmlrpcI4']='i4'; 00078 $GLOBALS['xmlrpcInt']='int'; 00079 $GLOBALS['xmlrpcBoolean']='boolean'; 00080 $GLOBALS['xmlrpcDouble']='double'; 00081 $GLOBALS['xmlrpcString']='string'; 00082 $GLOBALS['xmlrpcDateTime']='dateTime.iso8601'; 00083 $GLOBALS['xmlrpcBase64']='base64'; 00084 $GLOBALS['xmlrpcArray']='array'; 00085 $GLOBALS['xmlrpcStruct']='struct'; 00086 $GLOBALS['xmlrpcValue']='undefined'; 00087 00088 $GLOBALS['xmlrpcTypes']=array( 00089 $GLOBALS['xmlrpcI4'] => 1, 00090 $GLOBALS['xmlrpcInt'] => 1, 00091 $GLOBALS['xmlrpcBoolean'] => 1, 00092 $GLOBALS['xmlrpcString'] => 1, 00093 $GLOBALS['xmlrpcDouble'] => 1, 00094 $GLOBALS['xmlrpcDateTime'] => 1, 00095 $GLOBALS['xmlrpcBase64'] => 1, 00096 $GLOBALS['xmlrpcArray'] => 2, 00097 $GLOBALS['xmlrpcStruct'] => 3 00098 ); 00099 00100 $GLOBALS['xmlrpc_valid_parents'] = array( 00101 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'), 00102 'BOOLEAN' => array('VALUE'), 00103 'I4' => array('VALUE'), 00104 'INT' => array('VALUE'), 00105 'STRING' => array('VALUE'), 00106 'DOUBLE' => array('VALUE'), 00107 'DATETIME.ISO8601' => array('VALUE'), 00108 'BASE64' => array('VALUE'), 00109 'MEMBER' => array('STRUCT'), 00110 'NAME' => array('MEMBER'), 00111 'DATA' => array('ARRAY'), 00112 'ARRAY' => array('VALUE'), 00113 'STRUCT' => array('VALUE'), 00114 'PARAM' => array('PARAMS'), 00115 'METHODNAME' => array('METHODCALL'), 00116 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'), 00117 'FAULT' => array('METHODRESPONSE'), 00118 'NIL' => array('VALUE') // only used when extension activated 00119 ); 00120 00121 // define extra types for supporting NULL (useful for json or <NIL/>) 00122 $GLOBALS['xmlrpcNull']='null'; 00123 $GLOBALS['xmlrpcTypes']['null']=1; 00124 00125 // Not in use anymore since 2.0. Shall we remove it? 00127 $GLOBALS['xmlEntities']=array( 00128 'amp' => '&', 00129 'quot' => '"', 00130 'lt' => '<', 00131 'gt' => '>', 00132 'apos' => "'" 00133 ); 00134 00135 // tables used for transcoding different charsets into us-ascii xml 00136 00137 $GLOBALS['xml_iso88591_Entities']=array(); 00138 $GLOBALS['xml_iso88591_Entities']['in'] = array(); 00139 $GLOBALS['xml_iso88591_Entities']['out'] = array(); 00140 for ($i = 0; $i < 32; $i++) 00141 { 00142 $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i); 00143 $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';'; 00144 } 00145 for ($i = 160; $i < 256; $i++) 00146 { 00147 $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i); 00148 $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';'; 00149 } 00150 00154 /* 00155 $cp1252_to_xmlent = 00156 array( 00157 '\x80'=>'€', '\x81'=>'?', '\x82'=>'‚', '\x83'=>'ƒ', 00158 '\x84'=>'„', '\x85'=>'…', '\x86'=>'†', \x87'=>'‡', 00159 '\x88'=>'ˆ', '\x89'=>'‰', '\x8A'=>'Š', '\x8B'=>'‹', 00160 '\x8C'=>'Œ', '\x8D'=>'?', '\x8E'=>'Ž', '\x8F'=>'?', 00161 '\x90'=>'?', '\x91'=>'‘', '\x92'=>'’', '\x93'=>'“', 00162 '\x94'=>'”', '\x95'=>'•', '\x96'=>'–', '\x97'=>'—', 00163 '\x98'=>'˜', '\x99'=>'™', '\x9A'=>'š', '\x9B'=>'›', 00164 '\x9C'=>'œ', '\x9D'=>'?', '\x9E'=>'ž', '\x9F'=>'Ÿ' 00165 ); 00166 */ 00167 00168 $GLOBALS['xmlrpcerr']['unknown_method']=1; 00169 $GLOBALS['xmlrpcstr']['unknown_method']='Unknown method'; 00170 $GLOBALS['xmlrpcerr']['invalid_return']=2; 00171 $GLOBALS['xmlrpcstr']['invalid_return']='Invalid return payload: enable debugging to examine incoming payload'; 00172 $GLOBALS['xmlrpcerr']['incorrect_params']=3; 00173 $GLOBALS['xmlrpcstr']['incorrect_params']='Incorrect parameters passed to method'; 00174 $GLOBALS['xmlrpcerr']['introspect_unknown']=4; 00175 $GLOBALS['xmlrpcstr']['introspect_unknown']="Can't introspect: method unknown"; 00176 $GLOBALS['xmlrpcerr']['http_error']=5; 00177 $GLOBALS['xmlrpcstr']['http_error']="Didn't receive 200 OK from remote server."; 00178 $GLOBALS['xmlrpcerr']['no_data']=6; 00179 $GLOBALS['xmlrpcstr']['no_data']='No data received from server.'; 00180 $GLOBALS['xmlrpcerr']['no_ssl']=7; 00181 $GLOBALS['xmlrpcstr']['no_ssl']='No SSL support compiled in.'; 00182 $GLOBALS['xmlrpcerr']['curl_fail']=8; 00183 $GLOBALS['xmlrpcstr']['curl_fail']='CURL error'; 00184 $GLOBALS['xmlrpcerr']['invalid_request']=15; 00185 $GLOBALS['xmlrpcstr']['invalid_request']='Invalid request payload'; 00186 $GLOBALS['xmlrpcerr']['no_curl']=16; 00187 $GLOBALS['xmlrpcstr']['no_curl']='No CURL support compiled in.'; 00188 $GLOBALS['xmlrpcerr']['server_error']=17; 00189 $GLOBALS['xmlrpcstr']['server_error']='Internal server error'; 00190 $GLOBALS['xmlrpcerr']['multicall_error']=18; 00191 $GLOBALS['xmlrpcstr']['multicall_error']='Received from server invalid multicall response'; 00192 00193 $GLOBALS['xmlrpcerr']['multicall_notstruct'] = 9; 00194 $GLOBALS['xmlrpcstr']['multicall_notstruct'] = 'system.multicall expected struct'; 00195 $GLOBALS['xmlrpcerr']['multicall_nomethod'] = 10; 00196 $GLOBALS['xmlrpcstr']['multicall_nomethod'] = 'missing methodName'; 00197 $GLOBALS['xmlrpcerr']['multicall_notstring'] = 11; 00198 $GLOBALS['xmlrpcstr']['multicall_notstring'] = 'methodName is not a string'; 00199 $GLOBALS['xmlrpcerr']['multicall_recursion'] = 12; 00200 $GLOBALS['xmlrpcstr']['multicall_recursion'] = 'recursive system.multicall forbidden'; 00201 $GLOBALS['xmlrpcerr']['multicall_noparams'] = 13; 00202 $GLOBALS['xmlrpcstr']['multicall_noparams'] = 'missing params'; 00203 $GLOBALS['xmlrpcerr']['multicall_notarray'] = 14; 00204 $GLOBALS['xmlrpcstr']['multicall_notarray'] = 'params is not an array'; 00205 00206 $GLOBALS['xmlrpcerr']['cannot_decompress']=103; 00207 $GLOBALS['xmlrpcstr']['cannot_decompress']='Received from server compressed HTTP and cannot decompress'; 00208 $GLOBALS['xmlrpcerr']['decompress_fail']=104; 00209 $GLOBALS['xmlrpcstr']['decompress_fail']='Received from server invalid compressed HTTP'; 00210 $GLOBALS['xmlrpcerr']['dechunk_fail']=105; 00211 $GLOBALS['xmlrpcstr']['dechunk_fail']='Received from server invalid chunked HTTP'; 00212 $GLOBALS['xmlrpcerr']['server_cannot_decompress']=106; 00213 $GLOBALS['xmlrpcstr']['server_cannot_decompress']='Received from client compressed HTTP request and cannot decompress'; 00214 $GLOBALS['xmlrpcerr']['server_decompress_fail']=107; 00215 $GLOBALS['xmlrpcstr']['server_decompress_fail']='Received from client invalid compressed HTTP request'; 00216 00217 // The charset encoding used by the server for received messages and 00218 // by the client for received responses when received charset cannot be determined 00219 // or is not supported 00220 $GLOBALS['xmlrpc_defencoding']='UTF-8'; 00221 00222 // The encoding used internally by PHP. 00223 // String values received as xml will be converted to this, and php strings will be converted to xml 00224 // as if having been coded with this 00225 $GLOBALS['xmlrpc_internalencoding']='ISO-8859-1'; 00226 00227 $GLOBALS['xmlrpcName']='XML-RPC for PHP'; 00228 $GLOBALS['xmlrpcVersion']='2.2'; 00229 00230 // let user errors start at 800 00231 $GLOBALS['xmlrpcerruser']=800; 00232 // let XML parse errors start at 100 00233 $GLOBALS['xmlrpcerrxml']=100; 00234 00235 // formulate backslashes for escaping regexp 00236 // Not in use anymore since 2.0. Shall we remove it? 00238 $GLOBALS['xmlrpc_backslash']=chr(92).chr(92); 00239 00240 // set to TRUE to enable correct decoding of <NIL/> values 00241 $GLOBALS['xmlrpc_null_extension']=false; 00242 00243 // used to store state during parsing 00244 // quick explanation of components: 00245 // ac - used to accumulate values 00246 // isf - used to indicate a parsing fault (2) or xmlrpcresp fault (1) 00247 // isf_reason - used for storing xmlrpcresp fault string 00248 // lv - used to indicate "looking for a value": implements 00249 // the logic to allow values with no types to be strings 00250 // params - used to store parameters in method calls 00251 // method - used to store method name 00252 // stack - array with genealogy of xml elements names: 00253 // used to validate nesting of xmlrpc elements 00254 $GLOBALS['_xh']=null; 00255 00270 function xmlrpc_encode_entitites($data, $src_encoding='', $dest_encoding='') 00271 { 00272 if ($src_encoding == '') 00273 { 00274 // lame, but we know no better... 00275 $src_encoding = $GLOBALS['xmlrpc_internalencoding']; 00276 } 00277 00278 switch(strtoupper($src_encoding.'_'.$dest_encoding)) 00279 { 00280 case 'ISO-8859-1_': 00281 case 'ISO-8859-1_US-ASCII': 00282 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&', '"', ''', '<', '>'), $data); 00283 $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data); 00284 break; 00285 case 'ISO-8859-1_UTF-8': 00286 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&', '"', ''', '<', '>'), $data); 00287 $escaped_data = utf8_encode($escaped_data); 00288 break; 00289 case 'ISO-8859-1_ISO-8859-1': 00290 case 'US-ASCII_US-ASCII': 00291 case 'US-ASCII_UTF-8': 00292 case 'US-ASCII_': 00293 case 'US-ASCII_ISO-8859-1': 00294 case 'UTF-8_UTF-8': 00295 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&', '"', ''', '<', '>'), $data); 00296 break; 00297 case 'UTF-8_': 00298 case 'UTF-8_US-ASCII': 00299 case 'UTF-8_ISO-8859-1': 00300 // NB: this will choke on invalid UTF-8, going most likely beyond EOF 00301 $escaped_data = ''; 00302 // be kind to users creating string xmlrpcvals out of different php types 00303 $data = (string) $data; 00304 $ns = strlen ($data); 00305 for ($nn = 0; $nn < $ns; $nn++) 00306 { 00307 $ch = $data[$nn]; 00308 $ii = ord($ch); 00309 //1 7 0bbbbbbb (127) 00310 if ($ii < 128) 00311 { 00313 switch($ii){ 00314 case 34: 00315 $escaped_data .= '"'; 00316 break; 00317 case 38: 00318 $escaped_data .= '&'; 00319 break; 00320 case 39: 00321 $escaped_data .= '''; 00322 break; 00323 case 60: 00324 $escaped_data .= '<'; 00325 break; 00326 case 62: 00327 $escaped_data .= '>'; 00328 break; 00329 default: 00330 $escaped_data .= $ch; 00331 } // switch 00332 } 00333 //2 11 110bbbbb 10bbbbbb (2047) 00334 else if ($ii>>5 == 6) 00335 { 00336 $b1 = ($ii & 31); 00337 $ii = ord($data[$nn+1]); 00338 $b2 = ($ii & 63); 00339 $ii = ($b1 * 64) + $b2; 00340 $ent = sprintf ('&#%d;', $ii); 00341 $escaped_data .= $ent; 00342 $nn += 1; 00343 } 00344 //3 16 1110bbbb 10bbbbbb 10bbbbbb 00345 else if ($ii>>4 == 14) 00346 { 00347 $b1 = ($ii & 31); 00348 $ii = ord($data[$nn+1]); 00349 $b2 = ($ii & 63); 00350 $ii = ord($data[$nn+2]); 00351 $b3 = ($ii & 63); 00352 $ii = ((($b1 * 64) + $b2) * 64) + $b3; 00353 $ent = sprintf ('&#%d;', $ii); 00354 $escaped_data .= $ent; 00355 $nn += 2; 00356 } 00357 //4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb 00358 else if ($ii>>3 == 30) 00359 { 00360 $b1 = ($ii & 31); 00361 $ii = ord($data[$nn+1]); 00362 $b2 = ($ii & 63); 00363 $ii = ord($data[$nn+2]); 00364 $b3 = ($ii & 63); 00365 $ii = ord($data[$nn+3]); 00366 $b4 = ($ii & 63); 00367 $ii = ((((($b1 * 64) + $b2) * 64) + $b3) * 64) + $b4; 00368 $ent = sprintf ('&#%d;', $ii); 00369 $escaped_data .= $ent; 00370 $nn += 3; 00371 } 00372 } 00373 break; 00374 default: 00375 $escaped_data = ''; 00376 error_log("Converting from $src_encoding to $dest_encoding: not supported..."); 00377 } 00378 return $escaped_data; 00379 } 00380 00382 function xmlrpc_se($parser, $name, $attrs, $accept_single_vals=false) 00383 { 00384 // if invalid xmlrpc already detected, skip all processing 00385 if ($GLOBALS['_xh']['isf'] < 2) 00386 { 00387 // check for correct element nesting 00388 // top level element can only be of 2 types 00391 if (count($GLOBALS['_xh']['stack']) == 0) 00392 { 00393 if ($name != 'METHODRESPONSE' && $name != 'METHODCALL' && ( 00394 $name != 'VALUE' && !$accept_single_vals)) 00395 { 00396 $GLOBALS['_xh']['isf'] = 2; 00397 $GLOBALS['_xh']['isf_reason'] = 'missing top level xmlrpc element'; 00398 return; 00399 } 00400 else 00401 { 00402 $GLOBALS['_xh']['rt'] = strtolower($name); 00403 } 00404 } 00405 else 00406 { 00407 // not top level element: see if parent is OK 00408 $parent = end($GLOBALS['_xh']['stack']); 00409 if (!array_key_exists($name, $GLOBALS['xmlrpc_valid_parents']) || !in_array($parent, $GLOBALS['xmlrpc_valid_parents'][$name])) 00410 { 00411 $GLOBALS['_xh']['isf'] = 2; 00412 $GLOBALS['_xh']['isf_reason'] = "xmlrpc element $name cannot be child of $parent"; 00413 return; 00414 } 00415 } 00416 00417 switch($name) 00418 { 00419 // optimize for speed switch cases: most common cases first 00420 case 'VALUE': 00422 $GLOBALS['_xh']['vt']='value'; // indicator: no value found yet 00423 $GLOBALS['_xh']['ac']=''; 00424 $GLOBALS['_xh']['lv']=1; 00425 $GLOBALS['_xh']['php_class']=null; 00426 break; 00427 case 'I4': 00428 case 'INT': 00429 case 'STRING': 00430 case 'BOOLEAN': 00431 case 'DOUBLE': 00432 case 'DATETIME.ISO8601': 00433 case 'BASE64': 00434 if ($GLOBALS['_xh']['vt']!='value') 00435 { 00436 //two data elements inside a value: an error occurred! 00437 $GLOBALS['_xh']['isf'] = 2; 00438 $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value"; 00439 return; 00440 } 00441 $GLOBALS['_xh']['ac']=''; // reset the accumulator 00442 break; 00443 case 'STRUCT': 00444 case 'ARRAY': 00445 if ($GLOBALS['_xh']['vt']!='value') 00446 { 00447 //two data elements inside a value: an error occurred! 00448 $GLOBALS['_xh']['isf'] = 2; 00449 $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value"; 00450 return; 00451 } 00452 // create an empty array to hold child values, and push it onto appropriate stack 00453 $cur_val = array(); 00454 $cur_val['values'] = array(); 00455 $cur_val['type'] = $name; 00456 // check for out-of-band information to rebuild php objs 00457 // and in case it is found, save it 00458 if (@isset($attrs['PHP_CLASS'])) 00459 { 00460 $cur_val['php_class'] = $attrs['PHP_CLASS']; 00461 } 00462 $GLOBALS['_xh']['valuestack'][] = $cur_val; 00463 $GLOBALS['_xh']['vt']='data'; // be prepared for a data element next 00464 break; 00465 case 'DATA': 00466 if ($GLOBALS['_xh']['vt']!='data') 00467 { 00468 //two data elements inside a value: an error occurred! 00469 $GLOBALS['_xh']['isf'] = 2; 00470 $GLOBALS['_xh']['isf_reason'] = "found two data elements inside an array element"; 00471 return; 00472 } 00473 case 'METHODCALL': 00474 case 'METHODRESPONSE': 00475 case 'PARAMS': 00476 // valid elements that add little to processing 00477 break; 00478 case 'METHODNAME': 00479 case 'NAME': 00481 $GLOBALS['_xh']['ac']=''; 00482 break; 00483 case 'FAULT': 00484 $GLOBALS['_xh']['isf']=1; 00485 break; 00486 case 'MEMBER': 00487 $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 00488 //$GLOBALS['_xh']['ac']=''; 00489 // Drop trough intentionally 00490 case 'PARAM': 00491 // clear value type, so we can check later if no value has been passed for this param/member 00492 $GLOBALS['_xh']['vt']=null; 00493 break; 00494 case 'NIL': 00495 if ($GLOBALS['xmlrpc_null_extension']) 00496 { 00497 if ($GLOBALS['_xh']['vt']!='value') 00498 { 00499 //two data elements inside a value: an error occurred! 00500 $GLOBALS['_xh']['isf'] = 2; 00501 $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value"; 00502 return; 00503 } 00504 $GLOBALS['_xh']['ac']=''; // reset the accumulator 00505 break; 00506 } 00507 // we do not support the <NIL/> extension, so 00508 // drop through intentionally 00509 default: 00511 $GLOBALS['_xh']['isf'] = 2; 00512 $GLOBALS['_xh']['isf_reason'] = "found not-xmlrpc xml element $name"; 00513 break; 00514 } 00515 00516 // Save current element name to stack, to validate nesting 00517 $GLOBALS['_xh']['stack'][] = $name; 00518 00520 if($name!='VALUE') 00521 { 00522 $GLOBALS['_xh']['lv']=0; 00523 } 00524 } 00525 } 00526 00528 function xmlrpc_se_any($parser, $name, $attrs) 00529 { 00530 xmlrpc_se($parser, $name, $attrs, true); 00531 } 00532 00534 function xmlrpc_ee($parser, $name, $rebuild_xmlrpcvals = true) 00535 { 00536 if ($GLOBALS['_xh']['isf'] < 2) 00537 { 00538 // push this element name from stack 00539 // NB: if XML validates, correct opening/closing is guaranteed and 00540 // we do not have to check for $name == $curr_elem. 00541 // we also checked for proper nesting at start of elements... 00542 $curr_elem = array_pop($GLOBALS['_xh']['stack']); 00543 00544 switch($name) 00545 { 00546 case 'VALUE': 00547 // This if() detects if no scalar was inside <VALUE></VALUE> 00548 if ($GLOBALS['_xh']['vt']=='value') 00549 { 00550 $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac']; 00551 $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcString']; 00552 } 00553 00554 if ($rebuild_xmlrpcvals) 00555 { 00556 // build the xmlrpc val out of the data received, and substitute it 00557 $temp =& new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']); 00558 // in case we got info about underlying php class, save it 00559 // in the object we're rebuilding 00560 if (isset($GLOBALS['_xh']['php_class'])) 00561 $temp->_php_class = $GLOBALS['_xh']['php_class']; 00562 // check if we are inside an array or struct: 00563 // if value just built is inside an array, let's move it into array on the stack 00564 $vscount = count($GLOBALS['_xh']['valuestack']); 00565 if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY') 00566 { 00567 $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $temp; 00568 } 00569 else 00570 { 00571 $GLOBALS['_xh']['value'] = $temp; 00572 } 00573 } 00574 else 00575 { 00579 if (isset($GLOBALS['_xh']['php_class'])) 00580 { 00581 } 00582 00583 // check if we are inside an array or struct: 00584 // if value just built is inside an array, let's move it into array on the stack 00585 $vscount = count($GLOBALS['_xh']['valuestack']); 00586 if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY') 00587 { 00588 $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $GLOBALS['_xh']['value']; 00589 } 00590 } 00591 break; 00592 case 'BOOLEAN': 00593 case 'I4': 00594 case 'INT': 00595 case 'STRING': 00596 case 'DOUBLE': 00597 case 'DATETIME.ISO8601': 00598 case 'BASE64': 00599 $GLOBALS['_xh']['vt']=strtolower($name); 00602 if ($name=='STRING') 00603 { 00604 $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac']; 00605 } 00606 elseif ($name=='DATETIME.ISO8601') 00607 { 00608 if (!preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $GLOBALS['_xh']['ac'])) 00609 { 00610 error_log('XML-RPC: invalid value received in DATETIME: '.$GLOBALS['_xh']['ac']); 00611 } 00612 $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcDateTime']; 00613 $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac']; 00614 } 00615 elseif ($name=='BASE64') 00616 { 00618 $GLOBALS['_xh']['value']=base64_decode($GLOBALS['_xh']['ac']); 00619 } 00620 elseif ($name=='BOOLEAN') 00621 { 00622 // special case here: we translate boolean 1 or 0 into PHP 00623 // constants true or false. 00624 // Strings 'true' and 'false' are accepted, even though the 00625 // spec never mentions them (see eg. Blogger api docs) 00626 // NB: this simple checks helps a lot sanitizing input, ie no 00627 // security problems around here 00628 if ($GLOBALS['_xh']['ac']=='1' || strcasecmp($GLOBALS['_xh']['ac'], 'true') == 0) 00629 { 00630 $GLOBALS['_xh']['value']=true; 00631 } 00632 else 00633 { 00634 // log if receiveing something strange, even though we set the value to false anyway 00635 if ($GLOBALS['_xh']['ac']!='0' && strcasecmp($_xh[$parser]['ac'], 'false') != 0) 00636 error_log('XML-RPC: invalid value received in BOOLEAN: '.$GLOBALS['_xh']['ac']); 00637 $GLOBALS['_xh']['value']=false; 00638 } 00639 } 00640 elseif ($name=='DOUBLE') 00641 { 00642 // we have a DOUBLE 00643 // we must check that only 0123456789-.<space> are characters here 00644 if (!preg_match('/^[+-]?[eE0123456789 \t.]+$/', $GLOBALS['_xh']['ac'])) 00645 { 00647 // than this! 00648 error_log('XML-RPC: non numeric value received in DOUBLE: '.$GLOBALS['_xh']['ac']); 00649 $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND'; 00650 } 00651 else 00652 { 00653 // it's ok, add it on 00654 $GLOBALS['_xh']['value']=(double)$GLOBALS['_xh']['ac']; 00655 } 00656 } 00657 else 00658 { 00659 // we have an I4/INT 00660 // we must check that only 0123456789-<space> are characters here 00661 if (!preg_match('/^[+-]?[0123456789 \t]+$/', $GLOBALS['_xh']['ac'])) 00662 { 00664 // than this! 00665 error_log('XML-RPC: non numeric value received in INT: '.$GLOBALS['_xh']['ac']); 00666 $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND'; 00667 } 00668 else 00669 { 00670 // it's ok, add it on 00671 $GLOBALS['_xh']['value']=(int)$GLOBALS['_xh']['ac']; 00672 } 00673 } 00674 //$GLOBALS['_xh']['ac']=''; // is this necessary? 00675 $GLOBALS['_xh']['lv']=3; // indicate we've found a value 00676 break; 00677 case 'NAME': 00678 $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name'] = $GLOBALS['_xh']['ac']; 00679 break; 00680 case 'MEMBER': 00681 //$GLOBALS['_xh']['ac']=''; // is this necessary? 00682 // add to array in the stack the last element built, 00683 // unless no VALUE was found 00684 if ($GLOBALS['_xh']['vt']) 00685 { 00686 $vscount = count($GLOBALS['_xh']['valuestack']); 00687 $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][$GLOBALS['_xh']['valuestack'][$vscount-1]['name']] = $GLOBALS['_xh']['value']; 00688 } else 00689 error_log('XML-RPC: missing VALUE inside STRUCT in received xml'); 00690 break; 00691 case 'DATA': 00692 //$GLOBALS['_xh']['ac']=''; // is this necessary? 00693 $GLOBALS['_xh']['vt']=null; // reset this to check for 2 data elements in a row - even if they're empty 00694 break; 00695 case 'STRUCT': 00696 case 'ARRAY': 00697 // fetch out of stack array of values, and promote it to current value 00698 $curr_val = array_pop($GLOBALS['_xh']['valuestack']); 00699 $GLOBALS['_xh']['value'] = $curr_val['values']; 00700 $GLOBALS['_xh']['vt']=strtolower($name); 00701 if (isset($curr_val['php_class'])) 00702 { 00703 $GLOBALS['_xh']['php_class'] = $curr_val['php_class']; 00704 } 00705 break; 00706 case 'PARAM': 00707 // add to array of params the current value, 00708 // unless no VALUE was found 00709 if ($GLOBALS['_xh']['vt']) 00710 { 00711 $GLOBALS['_xh']['params'][]=$GLOBALS['_xh']['value']; 00712 $GLOBALS['_xh']['pt'][]=$GLOBALS['_xh']['vt']; 00713 } 00714 else 00715 error_log('XML-RPC: missing VALUE inside PARAM in received xml'); 00716 break; 00717 case 'METHODNAME': 00718 $GLOBALS['_xh']['method']=preg_replace('/^[\n\r\t ]+/', '', $GLOBALS['_xh']['ac']); 00719 break; 00720 case 'NIL': 00721 if ($GLOBALS['xmlrpc_null_extension']) 00722 { 00723 $GLOBALS['_xh']['vt']='null'; 00724 $GLOBALS['_xh']['value']=null; 00725 $GLOBALS['_xh']['lv']=3; 00726 break; 00727 } 00728 // drop through intentionally if nil extension not enabled 00729 case 'PARAMS': 00730 case 'FAULT': 00731 case 'METHODCALL': 00732 case 'METHORESPONSE': 00733 break; 00734 default: 00735 // End of INVALID ELEMENT! 00736 // shall we add an assert here for unreachable code??? 00737 break; 00738 } 00739 } 00740 } 00741 00743 function xmlrpc_ee_fast($parser, $name) 00744 { 00745 xmlrpc_ee($parser, $name, false); 00746 } 00747 00749 function xmlrpc_cd($parser, $data) 00750 { 00751 // skip processing if xml fault already detected 00752 if ($GLOBALS['_xh']['isf'] < 2) 00753 { 00754 // "lookforvalue==3" means that we've found an entire value 00755 // and should discard any further character data 00756 if($GLOBALS['_xh']['lv']!=3) 00757 { 00758 // G. Giunta 2006-08-23: useless change of 'lv' from 1 to 2 00759 //if($GLOBALS['_xh']['lv']==1) 00760 //{ 00761 // if we've found text and we're just in a <value> then 00762 // say we've found a value 00763 //$GLOBALS['_xh']['lv']=2; 00764 //} 00765 // we always initialize the accumulator before starting parsing, anyway... 00766 //if(!@isset($GLOBALS['_xh']['ac'])) 00767 //{ 00768 // $GLOBALS['_xh']['ac'] = ''; 00769 //} 00770 $GLOBALS['_xh']['ac'].=$data; 00771 } 00772 } 00773 } 00774 00777 function xmlrpc_dh($parser, $data) 00778 { 00779 // skip processing if xml fault already detected 00780 if ($GLOBALS['_xh']['isf'] < 2) 00781 { 00782 if(substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';') 00783 { 00784 // G. Giunta 2006-08-25: useless change of 'lv' from 1 to 2 00785 //if($GLOBALS['_xh']['lv']==1) 00786 //{ 00787 // $GLOBALS['_xh']['lv']=2; 00788 //} 00789 $GLOBALS['_xh']['ac'].=$data; 00790 } 00791 } 00792 return true; 00793 } 00794 00795 class xmlrpc_client 00796 { 00797 var $path; 00798 var $server; 00799 var $port=0; 00800 var $method='http'; 00801 var $errno; 00802 var $errstr; 00803 var $debug=0; 00804 var $username=''; 00805 var $password=''; 00806 var $authtype=1; 00807 var $cert=''; 00808 var $certpass=''; 00809 var $cacert=''; 00810 var $cacertdir=''; 00811 var $key=''; 00812 var $keypass=''; 00813 var $verifypeer=true; 00814 var $verifyhost=1; 00815 var $no_multicall=false; 00816 var $proxy=''; 00817 var $proxyport=0; 00818 var $proxy_user=''; 00819 var $proxy_pass=''; 00820 var $proxy_authtype=1; 00821 var $cookies=array(); 00831 var $accepted_compression = array(); 00836 var $request_compression = ''; 00841 var $xmlrpc_curl_handle = null; 00843 var $keepalive = false; 00845 var $accepted_charset_encodings = array(); 00847 var $request_charset_encoding = ''; 00852 var $return_type = 'xmlrpcvals'; 00853 00860 function xmlrpc_client($path, $server='', $port='', $method='') 00861 { 00862 // allow user to specify all params in $path 00863 if($server == '' and $port == '' and $method == '') 00864 { 00865 $parts = parse_url($path); 00866 $server = $parts['host']; 00867 $path = $parts['path']; 00868 if(isset($parts['query'])) 00869 { 00870 $path .= '?'.$parts['query']; 00871 } 00872 if(isset($parts['fragment'])) 00873 { 00874 $path .= '#'.$parts['fragment']; 00875 } 00876 if(isset($parts['port'])) 00877 { 00878 $port = $parts['port']; 00879 } 00880 if(isset($parts['scheme'])) 00881 { 00882 $method = $parts['scheme']; 00883 } 00884 if(isset($parts['user'])) 00885 { 00886 $this->username = $parts['user']; 00887 } 00888 if(isset($parts['pass'])) 00889 { 00890 $this->password = $parts['pass']; 00891 } 00892 } 00893 if($path == '' || $path[0] != '/') 00894 { 00895 $this->path='/'.$path; 00896 } 00897 else 00898 { 00899 $this->path=$path; 00900 } 00901 $this->server=$server; 00902 if($port != '') 00903 { 00904 $this->port=$port; 00905 } 00906 if($method != '') 00907 { 00908 $this->method=$method; 00909 } 00910 00911 // if ZLIB is enabled, let the client by default accept compressed responses 00912 if(function_exists('gzinflate') || ( 00913 function_exists('curl_init') && (($info = curl_version()) && 00914 ((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version']))) 00915 )) 00916 { 00917 $this->accepted_compression = array('gzip', 'deflate'); 00918 } 00919 00920 // keepalives: enabled by default ONLY for PHP >= 4.3.8 00921 // (see http://curl.haxx.se/docs/faq.html#7.3) 00922 if(version_compare(phpversion(), '4.3.8') >= 0) 00923 { 00924 $this->keepalive = true; 00925 } 00926 00927 // by default the xml parser can support these 3 charset encodings 00928 $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII'); 00929 } 00930 00936 function setDebug($in) 00937 { 00938 $this->debug=$in; 00939 } 00940 00948 function setCredentials($u, $p, $t=1) 00949 { 00950 $this->username=$u; 00951 $this->password=$p; 00952 $this->authtype=$t; 00953 } 00954 00961 function setCertificate($cert, $certpass) 00962 { 00963 $this->cert = $cert; 00964 $this->certpass = $certpass; 00965 } 00966 00974 function setCaCertificate($cacert, $is_dir=false) 00975 { 00976 if ($is_dir) 00977 { 00978 $this->cacert = $cacert; 00979 } 00980 else 00981 { 00982 $this->cacertdir = $cacert; 00983 } 00984 } 00985 00994 function setKey($key, $keypass) 00995 { 00996 $this->key = $key; 00997 $this->keypass = $keypass; 00998 } 00999 01005 function setSSLVerifyPeer($i) 01006 { 01007 $this->verifypeer = $i; 01008 } 01009 01015 function setSSLVerifyHost($i) 01016 { 01017 $this->verifyhost = $i; 01018 } 01019 01029 function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1) 01030 { 01031 $this->proxy = $proxyhost; 01032 $this->proxyport = $proxyport; 01033 $this->proxy_user = $proxyusername; 01034 $this->proxy_pass = $proxypassword; 01035 $this->proxy_authtype = $proxyauthtype; 01036 } 01037 01046 function setAcceptedCompression($compmethod) 01047 { 01048 if ($compmethod == 'any') 01049 $this->accepted_compression = array('gzip', 'deflate'); 01050 else 01051 $this->accepted_compression = array($compmethod); 01052 } 01053 01061 function setRequestCompression($compmethod) 01062 { 01063 $this->request_compression = $compmethod; 01064 } 01065 01079 function setCookie($name, $value='', $path='', $domain='', $port=null) 01080 { 01081 $this->cookies[$name]['value'] = urlencode($value); 01082 if ($path || $domain || $port) 01083 { 01084 $this->cookies[$name]['path'] = $path; 01085 $this->cookies[$name]['domain'] = $domain; 01086 $this->cookies[$name]['port'] = $port; 01087 $this->cookies[$name]['version'] = 1; 01088 } 01089 else 01090 { 01091 $this->cookies[$name]['version'] = 0; 01092 } 01093 } 01094 01103 function& send($msg, $timeout=0, $method='') 01104 { 01105 // if user deos not specify http protocol, use native method of this client 01106 // (i.e. method set during call to constructor) 01107 if($method == '') 01108 { 01109 $method = $this->method; 01110 } 01111 01112 if(is_array($msg)) 01113 { 01114 // $msg is an array of xmlrpcmsg's 01115 $r = $this->multicall($msg, $timeout, $method); 01116 return $r; 01117 } 01118 elseif(is_string($msg)) 01119 { 01120 $n =& new xmlrpcmsg(''); 01121 $n->payload = $msg; 01122 $msg = $n; 01123 } 01124 01125 // where msg is an xmlrpcmsg 01126 $msg->debug=$this->debug; 01127 01128 if($method == 'https') 01129 { 01130 $r =& $this->sendPayloadHTTPS( 01131 $msg, 01132 $this->server, 01133 $this->port, 01134 $timeout, 01135 $this->username, 01136 $this->password, 01137 $this->authtype, 01138 $this->cert, 01139 $this->certpass, 01140 $this->cacert, 01141 $this->cacertdir, 01142 $this->proxy, 01143 $this->proxyport, 01144 $this->proxy_user, 01145 $this->proxy_pass, 01146 $this->proxy_authtype, 01147 $this->keepalive, 01148 $this->key, 01149 $this->keypass 01150 ); 01151 } 01152 elseif($method == 'http11') 01153 { 01154 $r =& $this->sendPayloadCURL( 01155 $msg, 01156 $this->server, 01157 $this->port, 01158 $timeout, 01159 $this->username, 01160 $this->password, 01161 $this->authtype, 01162 null, 01163 null, 01164 null, 01165 null, 01166 $this->proxy, 01167 $this->proxyport, 01168 $this->proxy_user, 01169 $this->proxy_pass, 01170 $this->proxy_authtype, 01171 'http', 01172 $this->keepalive 01173 ); 01174 } 01175 else 01176 { 01177 $r =& $this->sendPayloadHTTP10( 01178 $msg, 01179 $this->server, 01180 $this->port, 01181 $timeout, 01182 $this->username, 01183 $this->password, 01184 $this->authtype, 01185 $this->proxy, 01186 $this->proxyport, 01187 $this->proxy_user, 01188 $this->proxy_pass, 01189 $this->proxy_authtype 01190 ); 01191 } 01192 01193 return $r; 01194 } 01195 01199 function &sendPayloadHTTP10($msg, $server, $port, $timeout=0, 01200 $username='', $password='', $authtype=1, $proxyhost='', 01201 $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1) 01202 { 01203 if($port==0) 01204 { 01205 $port=80; 01206 } 01207 01208 // Only create the payload if it was not created previously 01209 if(empty($msg->payload)) 01210 { 01211 $msg->createPayload($this->request_charset_encoding); 01212 } 01213 01214 $payload = $msg->payload; 01215 // Deflate request body and set appropriate request headers 01216 if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate')) 01217 { 01218 if($this->request_compression == 'gzip') 01219 { 01220 $a = @gzencode($payload); 01221 if($a) 01222 { 01223 $payload = $a; 01224 $encoding_hdr = "Content-Encoding: gzip\r\n"; 01225 } 01226 } 01227 else 01228 { 01229 $a = @gzcompress($payload); 01230 if($a) 01231 { 01232 $payload = $a; 01233 $encoding_hdr = "Content-Encoding: deflate\r\n"; 01234 } 01235 } 01236 } 01237 else 01238 { 01239 $encoding_hdr = ''; 01240 } 01241 01242 // thanks to Grant Rauscher <grant7@firstworld.net> for this 01243 $credentials=''; 01244 if($username!='') 01245 { 01246 $credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n"; 01247 if ($authtype != 1) 01248 { 01249 error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth is supported with HTTP 1.0'); 01250 } 01251 } 01252 01253 $accepted_encoding = ''; 01254 if(is_array($this->accepted_compression) && count($this->accepted_compression)) 01255 { 01256 $accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n"; 01257 } 01258 01259 $proxy_credentials = ''; 01260 if($proxyhost) 01261 { 01262 if($proxyport == 0) 01263 { 01264 $proxyport = 8080; 01265 } 01266 $connectserver = $proxyhost; 01267 $connectport = $proxyport; 01268 $uri = 'http://'.$server.':'.$port.$this->path; 01269 if($proxyusername != '') 01270 { 01271 if ($proxyauthtype != 1) 01272 { 01273 error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth to proxy is supported with HTTP 1.0'); 01274 } 01275 $proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n"; 01276 } 01277 } 01278 else 01279 { 01280 $connectserver = $server; 01281 $connectport = $port; 01282 $uri = $this->path; 01283 } 01284 01285 // Cookie generation, as per rfc2965 (version 1 cookies) or 01286 // netscape's rules (version 0 cookies) 01287 $cookieheader=''; 01288 foreach ($this->cookies as $name => $cookie) 01289 { 01290 if ($cookie['version']) 01291 { 01292 $cookieheader .= 'Cookie: $Version="' . $cookie['version'] . '"; '; 01293 $cookieheader .= $name . '="' . $cookie['value'] . '";'; 01294 if ($cookie['path']) 01295 $cookieheader .= ' $Path="' . $cookie['path'] . '";'; 01296 if ($cookie['domain']) 01297 $cookieheader .= ' $Domain="' . $cookie['domain'] . '";'; 01298 if ($cookie['port']) 01299 $cookieheader .= ' $Port="' . $cookie['domain'] . '";'; 01300 $cookieheader = substr($cookieheader, 0, -1) . "\r\n"; 01301 } 01302 else 01303 { 01304 $cookieheader .= 'Cookie: ' . $name . '=' . $cookie['value'] . "\r\n"; 01305 } 01306 } 01307 01308 $op= 'POST ' . $uri. " HTTP/1.0\r\n" . 01309 'User-Agent: ' . $GLOBALS['xmlrpcName'] . ' ' . $GLOBALS['xmlrpcVersion'] . "\r\n" . 01310 'Host: '. $server . ':' . $port . "\r\n" . 01311 $credentials . 01312 $proxy_credentials . 01313 $accepted_encoding . 01314 $encoding_hdr . 01315 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings) . "\r\n" . 01316 $cookieheader . 01317 'Content-Type: ' . $msg->content_type . "\r\nContent-Length: " . 01318 strlen($payload) . "\r\n\r\n" . 01319 $payload; 01320 01321 if($this->debug > 1) 01322 { 01323 print "<PRE>\n---SENDING---\n" . htmlentities($op) . "\n---END---\n</PRE>"; 01324 // let the client see this now in case http times out... 01325 flush(); 01326 } 01327 01328 if($timeout>0) 01329 { 01330 $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout); 01331 } 01332 else 01333 { 01334 $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr); 01335 } 01336 if($fp) 01337 { 01338 if($timeout>0 && function_exists('stream_set_timeout')) 01339 { 01340 stream_set_timeout($fp, $timeout); 01341 } 01342 } 01343 else 01344 { 01345 $this->errstr='Connect error: '.$this->errstr; 01346 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')'); 01347 return $r; 01348 } 01349 01350 if(!fputs($fp, $op, strlen($op))) 01351 { 01352 $this->errstr='Write error'; 01353 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr); 01354 return $r; 01355 } 01356 else 01357 { 01358 // reset errno and errstr on succesful socket connection 01359 $this->errstr = ''; 01360 } 01361 // G. Giunta 2005/10/24: close socket before parsing. 01362 // should yeld slightly better execution times, and make easier recursive calls (e.g. to follow http redirects) 01363 $ipd=''; 01364 while($data=fread($fp, 32768)) 01365 { 01366 // shall we check for $data === FALSE? 01367 // as per the manual, it signals an error 01368 $ipd.=$data; 01369 } 01370 fclose($fp); 01371 $r =& $msg->parseResponse($ipd, false, $this->return_type); 01372 return $r; 01373 01374 } 01375 01379 function &sendPayloadHTTPS($msg, $server, $port, $timeout=0, $username='', 01380 $password='', $authtype=1, $cert='',$certpass='', $cacert='', $cacertdir='', 01381 $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1, 01382 $keepalive=false, $key='', $keypass='') 01383 { 01384 $r =& $this->sendPayloadCURL($msg, $server, $port, $timeout, $username, 01385 $password, $authtype, $cert, $certpass, $cacert, $cacertdir, $proxyhost, $proxyport, 01386 $proxyusername, $proxypassword, $proxyauthtype, 'https', $keepalive, $key, $keypass); 01387 return $r; 01388 } 01389 01396 function &sendPayloadCURL($msg, $server, $port, $timeout=0, $username='', 01397 $password='', $authtype=1, $cert='', $certpass='', $cacert='', $cacertdir='', 01398 $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1, $method='https', 01399 $keepalive=false, $key='', $keypass='') 01400 { 01401 if(!function_exists('curl_init')) 01402 { 01403 $this->errstr='CURL unavailable on this install'; 01404 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']); 01405 return $r; 01406 } 01407 if($method == 'https') 01408 { 01409 if(($info = curl_version()) && 01410 ((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version'])))) 01411 { 01412 $this->errstr='SSL unavailable on this install'; 01413 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']); 01414 return $r; 01415 } 01416 } 01417 01418 if($port == 0) 01419 { 01420 if($method == 'http') 01421 { 01422 $port = 80; 01423 } 01424 else 01425 { 01426 $port = 443; 01427 } 01428 } 01429 01430 // Only create the payload if it was not created previously 01431 if(empty($msg->payload)) 01432 { 01433 $msg->createPayload($this->request_charset_encoding); 01434 } 01435 01436 // Deflate request body and set appropriate request headers 01437 $payload = $msg->payload; 01438 if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate')) 01439 { 01440 if($this->request_compression == 'gzip') 01441 { 01442 $a = @gzencode($payload); 01443 if($a) 01444 { 01445 $payload = $a; 01446 $encoding_hdr = 'Content-Encoding: gzip'; 01447 } 01448 } 01449 else 01450 { 01451 $a = @gzcompress($payload); 01452 if($a) 01453 { 01454 $payload = $a; 01455 $encoding_hdr = 'Content-Encoding: deflate'; 01456 } 01457 } 01458 } 01459 else 01460 { 01461 $encoding_hdr = ''; 01462 } 01463 01464 if($this->debug > 1) 01465 { 01466 print "<PRE>\n---SENDING---\n" . htmlentities($payload) . "\n---END---\n</PRE>"; 01467 // let the client see this now in case http times out... 01468 flush(); 01469 } 01470 01471 if(!$keepalive || !$this->xmlrpc_curl_handle) 01472 { 01473 $curl = curl_init($method . '://' . $server . ':' . $port . $this->path); 01474 if($keepalive) 01475 { 01476 $this->xmlrpc_curl_handle = $curl; 01477 } 01478 } 01479 else 01480 { 01481 $curl = $this->xmlrpc_curl_handle; 01482 } 01483 01484 // results into variable 01485 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 01486 01487 if($this->debug) 01488 { 01489 curl_setopt($curl, CURLOPT_VERBOSE, 1); 01490 } 01491 curl_setopt($curl, CURLOPT_USERAGENT, $GLOBALS['xmlrpcName'].' '.$GLOBALS['xmlrpcVersion']); 01492 // required for XMLRPC: post the data 01493 curl_setopt($curl, CURLOPT_POST, 1); 01494 // the data 01495 curl_setopt($curl, CURLOPT_POSTFIELDS, $payload); 01496 01497 // return the header too 01498 curl_setopt($curl, CURLOPT_HEADER, 1); 01499 01500 // will only work with PHP >= 5.0 01501 // NB: if we set an empty string, CURL will add http header indicating 01502 // ALL methods it is supporting. This is possibly a better option than 01503 // letting the user tell what curl can / cannot do... 01504 if(is_array($this->accepted_compression) && count($this->accepted_compression)) 01505 { 01506 //curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression)); 01507 // empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?) 01508 if (count($this->accepted_compression) == 1) 01509 { 01510 curl_setopt($curl, CURLOPT_ENCODING, $this->accepted_compression[0]); 01511 } 01512 else 01513 curl_setopt($curl, CURLOPT_ENCODING, ''); 01514 } 01515 // extra headers 01516 $headers = array('Content-Type: ' . $msg->content_type , 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings)); 01517 // if no keepalive is wanted, let the server know it in advance 01518 if(!$keepalive) 01519 { 01520 $headers[] = 'Connection: close'; 01521 } 01522 // request compression header 01523 if($encoding_hdr) 01524 { 01525 $headers[] = $encoding_hdr; 01526 } 01527 01528 curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); 01529 // timeout is borked 01530 if($timeout) 01531 { 01532 curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1); 01533 } 01534 01535 if($username && $password) 01536 { 01537 curl_setopt($curl, CURLOPT_USERPWD, $username.':'.$password); 01538 if (defined('CURLOPT_HTTPAUTH')) 01539 { 01540 curl_setopt($curl, CURLOPT_HTTPAUTH, $authtype); 01541 } 01542 else if ($authtype != 1) 01543 { 01544 error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth is supported by the current PHP/curl install'); 01545 } 01546 } 01547 01548 if($method == 'https') 01549 { 01550 // set cert file 01551 if($cert) 01552 { 01553 curl_setopt($curl, CURLOPT_SSLCERT, $cert); 01554 } 01555 // set cert password 01556 if($certpass) 01557 { 01558 curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass); 01559 } 01560 // whether to verify remote host's cert 01561 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer); 01562 // set ca certificates file/dir 01563 if($cacert) 01564 { 01565 curl_setopt($curl, CURLOPT_CAINFO, $cacert); 01566 } 01567 if($cacertdir) 01568 { 01569 curl_setopt($curl, CURLOPT_CAPATH, $cacertdir); 01570 } 01571 // set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?) 01572 if($key) 01573 { 01574 curl_setopt($curl, CURLOPT_SSLKEY, $key); 01575 } 01576 // set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?) 01577 if($keypass) 01578 { 01579 curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $keypass); 01580 } 01581 // 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 01582 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost); 01583 } 01584 01585 // proxy info 01586 if($proxyhost) 01587 { 01588 if($proxyport == 0) 01589 { 01590 $proxyport = 8080; // NB: even for HTTPS, local connection is on port 8080 01591 } 01592 curl_setopt($curl, CURLOPT_PROXY,$proxyhost.':'.$proxyport); 01593 //curl_setopt($curl, CURLOPT_PROXYPORT,$proxyport); 01594 if($proxyusername) 01595 { 01596 curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyusername.':'.$proxypassword); 01597 if (defined('CURLOPT_PROXYAUTH')) 01598 { 01599 curl_setopt($curl, CURLOPT_PROXYAUTH, $proxyauthtype); 01600 } 01601 else if ($proxyauthtype != 1) 01602 { 01603 error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth to proxy is supported by the current PHP/curl install'); 01604 } 01605 } 01606 } 01607 01608 // NB: should we build cookie http headers by hand rather than let CURL do it? 01609 // the following code does not honour 'expires', 'path' and 'domain' cookie attributes 01610 // set to clint obj the the user... 01611 if (count($this->cookies)) 01612 { 01613 $cookieheader = ''; 01614 foreach ($this->cookies as $name => $cookie) 01615 { 01616 $cookieheader .= $name . '=' . $cookie['value'] . ', '; 01617 } 01618 curl_setopt($curl, CURLOPT_COOKIE, substr($cookieheader, 0, -2)); 01619 } 01620 01621 $result = curl_exec($curl); 01622 01623 if(!$result) 01624 { 01625 $this->errstr='no response'; 01626 $resp=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['curl_fail'], $GLOBALS['xmlrpcstr']['curl_fail']. ': '. curl_error($curl)); 01627 if(!$keepalive) 01628 { 01629 curl_close($curl); 01630 } 01631 } 01632 else 01633 { 01634 if(!$keepalive) 01635 { 01636 curl_close($curl); 01637 } 01638 $resp =& $msg->parseResponse($result, true, $this->return_type); 01639 } 01640 return $resp; 01641 } 01642 01665 function multicall($msgs, $timeout=0, $method='', $fallback=true) 01666 { 01667 if ($method == '') 01668 { 01669 $method = $this->method; 01670 } 01671 if(!$this->no_multicall) 01672 { 01673 $results = $this->_try_multicall($msgs, $timeout, $method); 01674 if(is_array($results)) 01675 { 01676 // System.multicall succeeded 01677 return $results; 01678 } 01679 else 01680 { 01681 // either system.multicall is unsupported by server, 01682 // or call failed for some other reason. 01683 if ($fallback) 01684 { 01685 // Don't try it next time... 01686 $this->no_multicall = true; 01687 } 01688 else 01689 { 01690 if (is_a($results, 'xmlrpcresp')) 01691 { 01692 $result = $results; 01693 } 01694 else 01695 { 01696 $result =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['multicall_error'], $GLOBALS['xmlrpcstr']['multicall_error']); 01697 } 01698 } 01699 } 01700 } 01701 else 01702 { 01703 // override fallback, in case careless user tries to do two 01704 // opposite things at the same time 01705 $fallback = true; 01706 } 01707 01708 $results = array(); 01709 if ($fallback) 01710 { 01711 // system.multicall is (probably) unsupported by server: 01712 // emulate multicall via multiple requests 01713 foreach($msgs as $msg) 01714 { 01715 $results[] =& $this->send($msg, $timeout, $method); 01716 } 01717 } 01718 else 01719 { 01720 // user does NOT want to fallback on many single calls: 01721 // since we should always return an array of responses, 01722 // return an array with the same error repeated n times 01723 foreach($msgs as $msg) 01724 { 01725 $results[] = $result; 01726 } 01727 } 01728 return $results; 01729 } 01730 01737 function _try_multicall($msgs, $timeout, $method) 01738 { 01739 // Construct multicall message 01740 $calls = array(); 01741 foreach($msgs as $msg) 01742 { 01743 $call['methodName'] =& new xmlrpcval($msg->method(),'string'); 01744 $numParams = $msg->getNumParams(); 01745 $params = array(); 01746 for($i = 0; $i < $numParams; $i++) 01747 { 01748 $params[$i] = $msg->getParam($i); 01749 } 01750 $call['params'] =& new xmlrpcval($params, 'array'); 01751 $calls[] =& new xmlrpcval($call, 'struct'); 01752 } 01753 $multicall =& new xmlrpcmsg('system.multicall'); 01754 $multicall->addParam(new xmlrpcval($calls, 'array')); 01755 01756 // Attempt RPC call 01757 $result =& $this->send($multicall, $timeout, $method); 01758 01759 if($result->faultCode() != 0) 01760 { 01761 // call to system.multicall failed 01762 return $result; 01763 } 01764 01765 // Unpack responses. 01766 $rets = $result->value(); 01767 01768 if ($this->return_type == 'xml') 01769 { 01770 return $rets; 01771 } 01772 else if ($this->return_type == 'phpvals') 01773 { 01775 $rets = $result->value(); 01776 if(!is_array($rets)) 01777 { 01778 return false; // bad return type from system.multicall 01779 } 01780 $numRets = count($rets); 01781 if($numRets != count($msgs)) 01782 { 01783 return false; // wrong number of return values. 01784 } 01785 01786 $response = array(); 01787 for($i = 0; $i < $numRets; $i++) 01788 { 01789 $val = $rets[$i]; 01790 if (!is_array($val)) { 01791 return false; 01792 } 01793 switch(count($val)) 01794 { 01795 case 1: 01796 if(!isset($val[0])) 01797 { 01798 return false; // Bad value 01799 } 01800 // Normal return value 01801 $response[$i] =& new xmlrpcresp($val[0], 0, '', 'phpvals'); 01802 break; 01803 case 2: 01805 $code = @$val['faultCode']; 01806 if(!is_int($code)) 01807 { 01808 return false; 01809 } 01810 $str = @$val['faultString']; 01811 if(!is_string($str)) 01812 { 01813 return false; 01814 } 01815 $response[$i] =& new xmlrpcresp(0, $code, $str); 01816 break; 01817 default: 01818 return false; 01819 } 01820 } 01821 return $response; 01822 } 01823 else // return type == 'xmlrpcvals' 01824 { 01825 $rets = $result->value(); 01826 if($rets->kindOf() != 'array') 01827 { 01828 return false; // bad return type from system.multicall 01829 } 01830 $numRets = $rets->arraysize(); 01831 if($numRets != count($msgs)) 01832 { 01833 return false; // wrong number of return values. 01834 } 01835 01836 $response = array(); 01837 for($i = 0; $i < $numRets; $i++) 01838 { 01839 $val = $rets->arraymem($i); 01840 switch($val->kindOf()) 01841 { 01842 case 'array': 01843 if($val->arraysize() != 1) 01844 { 01845 return false; // Bad value 01846 } 01847 // Normal return value 01848 $response[$i] =& new xmlrpcresp($val->arraymem(0)); 01849 break; 01850 case 'struct': 01851 $code = $val->structmem('faultCode'); 01852 if($code->kindOf() != 'scalar' || $code->scalartyp() != 'int') 01853 { 01854 return false; 01855 } 01856 $str = $val->structmem('faultString'); 01857 if($str->kindOf() != 'scalar' || $str->scalartyp() != 'string') 01858 { 01859 return false; 01860 } 01861 $response[$i] =& new xmlrpcresp(0, $code->scalarval(), $str->scalarval()); 01862 break; 01863 default: 01864 return false; 01865 } 01866 } 01867 return $response; 01868 } 01869 } 01870 } // end class xmlrpc_client 01871 01872 class xmlrpcresp 01873 { 01874 var $val = 0; 01875 var $valtyp; 01876 var $errno = 0; 01877 var $errstr = ''; 01878 var $payload; 01879 var $hdrs = array(); 01880 var $_cookies = array(); 01881 var $content_type = 'text/xml'; 01882 var $raw_data = ''; 01883 01894 function xmlrpcresp($val, $fcode = 0, $fstr = '', $valtyp='') 01895 { 01896 if($fcode != 0) 01897 { 01898 // error response 01899 $this->errno = $fcode; 01900 $this->errstr = $fstr; 01901 //$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later. 01902 } 01903 else 01904 { 01905 // successful response 01906 $this->val = $val; 01907 if ($valtyp == '') 01908 { 01909 // user did not declare type of response value: try to guess it 01910 if (is_object($this->val) && is_a($this->val, 'xmlrpcval')) 01911 { 01912 $this->valtyp = 'xmlrpcvals'; 01913 } 01914 else if (is_string($this->val)) 01915 { 01916 $this->valtyp = 'xml'; 01917 01918 } 01919 else 01920 { 01921 $this->valtyp = 'phpvals'; 01922 } 01923 } 01924 else 01925 { 01926 // user declares type of resp value: believe him 01927 $this->valtyp = $valtyp; 01928 } 01929 } 01930 } 01931 01937 function faultCode() 01938 { 01939 return $this->errno; 01940 } 01941 01947 function faultString() 01948 { 01949 return $this->errstr; 01950 } 01951 01957 function value() 01958 { 01959 return $this->val; 01960 } 01961 01973 function cookies() 01974 { 01975 return $this->_cookies; 01976 } 01977 01984 function serialize($charset_encoding='') 01985 { 01986 if ($charset_encoding != '') 01987 $this->content_type = 'text/xml; charset=' . $charset_encoding; 01988 else 01989 $this->content_type = 'text/xml'; 01990 $result = "<methodResponse>\n"; 01991 if($this->errno) 01992 { 01993 // G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients 01994 // by xml-encoding non ascii chars 01995 $result .= "<fault>\n" . 01996 "<value>\n<struct><member><name>faultCode</name>\n<value><int>" . $this->errno . 01997 "</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>" . 01998 xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "</string></value>\n</member>\n" . 01999 "</struct>\n</value>\n</fault>"; 02000 } 02001 else 02002 { 02003 if(!is_object($this->val) || !is_a($this->val, 'xmlrpcval')) 02004 { 02005 if (is_string($this->val) && $this->valtyp == 'xml') 02006 { 02007 $result .= "<params>\n<param>\n" . 02008 $this->val . 02009 "</param>\n</params>"; 02010 } 02011 else 02012 { 02014 die('cannot serialize xmlrpcresp objects whose content is native php values'); 02015 } 02016 } 02017 else 02018 { 02019 $result .= "<params>\n<param>\n" . 02020 $this->val->serialize($charset_encoding) . 02021 "</param>\n</params>"; 02022 } 02023 } 02024 $result .= "\n</methodResponse>"; 02025 $this->payload = $result; 02026 return $result; 02027 } 02028 } 02029 02030 class xmlrpcmsg 02031 { 02032 var $payload; 02033 var $methodname; 02034 var $params=array(); 02035 var $debug=0; 02036 var $content_type = 'text/xml'; 02037 02042 function xmlrpcmsg($meth, $pars=0) 02043 { 02044 $this->methodname=$meth; 02045 if(is_array($pars) && count($pars)>0) 02046 { 02047 for($i=0; $i<count($pars); $i++) 02048 { 02049 $this->addParam($pars[$i]); 02050 } 02051 } 02052 } 02053 02057 function xml_header($charset_encoding='') 02058 { 02059 if ($charset_encoding != '') 02060 { 02061 return "<?xml version=\"1.0\" encoding=\"$charset_encoding\" ?" . ">\n<methodCall>\n"; 02062 } 02063 else 02064 { 02065 return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n"; 02066 } 02067 } 02068 02072 function xml_footer() 02073 { 02074 return '</methodCall>'; 02075 } 02076 02080 function kindOf() 02081 { 02082 return 'msg'; 02083 } 02084 02088 function createPayload($charset_encoding='') 02089 { 02090 if ($charset_encoding != '') 02091 $this->content_type = 'text/xml; charset=' . $charset_encoding; 02092 else 02093 $this->content_type = 'text/xml'; 02094 $this->payload=$this->xml_header($charset_encoding); 02095 $this->payload.='<methodName>' . $this->methodname . "</methodName>\n"; 02096 $this->payload.="<params>\n"; 02097 for($i=0; $i<count($this->params); $i++) 02098 { 02099 $p=$this->params[$i]; 02100 $this->payload.="<param>\n" . $p->serialize($charset_encoding) . 02101 "</param>\n"; 02102 } 02103 $this->payload.="</params>\n"; 02104 $this->payload.=$this->xml_footer(); 02105 } 02106 02113 function method($meth='') 02114 { 02115 if($meth!='') 02116 { 02117 $this->methodname=$meth; 02118 } 02119 return $this->methodname; 02120 } 02121 02127 function serialize($charset_encoding='') 02128 { 02129 $this->createPayload($charset_encoding); 02130 return $this->payload; 02131 } 02132 02139 function addParam($par) 02140 { 02141 // add check: do not add to self params which are not xmlrpcvals 02142 if(is_object($par) && is_a($par, 'xmlrpcval')) 02143 { 02144 $this->params[]=$par; 02145 return true; 02146 } 02147 else 02148 { 02149 return false; 02150 } 02151 } 02152 02159 function getParam($i) { return $this->params[$i]; } 02160 02166 function getNumParams() { return count($this->params); } 02167 02175 function &parseResponseFile($fp) 02176 { 02177 $ipd=''; 02178 while($data=fread($fp, 32768)) 02179 { 02180 $ipd.=$data; 02181 } 02182 //fclose($fp); 02183 $r =& $this->parseResponse($ipd); 02184 return $r; 02185 } 02186 02191 function &parseResponseHeaders(&$data, $headers_processed=false) 02192 { 02193 // Support "web-proxy-tunelling" connections for https through proxies 02194 if(preg_match('/^HTTP\/1\.[0-1] 200 Connection established/', $data)) 02195 { 02196 // Look for CR/LF or simple LF as line separator, 02197 // (even though it is not valid http) 02198 $pos = strpos($data,"\r\n\r\n"); 02199 if($pos || is_int($pos)) 02200 { 02201 $bd = $pos+4; 02202 } 02203 else 02204 { 02205 $pos = strpos($data,"\n\n"); 02206 if($pos || is_int($pos)) 02207 { 02208 $bd = $pos+2; 02209 } 02210 else 02211 { 02212 // No separation between response headers and body: fault? 02213 $bd = 0; 02214 } 02215 } 02216 if ($bd) 02217 { 02218 // this filters out all http headers from proxy. 02219 // maybe we could take them into account, too? 02220 $data = substr($data, $bd); 02221 } 02222 else 02223 { 02224 error_log('XML-RPC: xmlrpcmsg::parseResponse: HTTPS via proxy error, tunnel connection possibly failed'); 02225 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (HTTPS via proxy error, tunnel connection possibly failed)'); 02226 return $r; 02227 } 02228 } 02229 02230 // Strip HTTP 1.1 100 Continue header if present 02231 while(preg_match('/^HTTP\/1\.1 1[0-9]{2} /', $data)) 02232 { 02233 $pos = strpos($data, 'HTTP', 12); 02234 // server sent a Continue header without any (valid) content following... 02235 // give the client a chance to know it 02236 if(!$pos && !is_int($pos)) // works fine in php 3, 4 and 5 02237 { 02238 break; 02239 } 02240 $data = substr($data, $pos); 02241 } 02242 if(!preg_match('/^HTTP\/[0-9.]+ 200 /', $data)) 02243 { 02244 $errstr= substr($data, 0, strpos($data, "\n")-1); 02245 error_log('XML-RPC: xmlrpcmsg::parseResponse: HTTP error, got response: ' .$errstr); 02246 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (' . $errstr . ')'); 02247 return $r; 02248 } 02249 02250 $GLOBALS['_xh']['headers'] = array(); 02251 $GLOBALS['_xh']['cookies'] = array(); 02252 02253 // be tolerant to usage of \n instead of \r\n to separate headers and data 02254 // (even though it is not valid http) 02255 $pos = strpos($data,"\r\n\r\n"); 02256 if($pos || is_int($pos)) 02257 { 02258 $bd = $pos+4; 02259 } 02260 else 02261 { 02262 $pos = strpos($data,"\n\n"); 02263 if($pos || is_int($pos)) 02264 { 02265 $bd = $pos+2; 02266 } 02267 else 02268 { 02269 // No separation between response headers and body: fault? 02270 // we could take some action here instead of going on... 02271 $bd = 0; 02272 } 02273 } 02274 // be tolerant to line endings, and extra empty lines 02275 $ar = split("\r?\n", trim(substr($data, 0, $pos))); 02276 while(list(,$line) = @each($ar)) 02277 { 02278 // take care of multi-line headers and cookies 02279 $arr = explode(':',$line,2); 02280 if(count($arr) > 1) 02281 { 02282 $header_name = strtolower(trim($arr[0])); 02287 if ($header_name == 'set-cookie' || $header_name == 'set-cookie2') 02288 { 02289 if ($header_name == 'set-cookie2') 02290 { 02291 // version 2 cookies: 02292 // there could be many cookies on one line, comma separated 02293 $cookies = explode(',', $arr[1]); 02294 } 02295 else 02296 { 02297 $cookies = array($arr[1]); 02298 } 02299 foreach ($cookies as $cookie) 02300 { 02301 // glue together all received cookies, using a comma to separate them 02302 // (same as php does with getallheaders()) 02303 if (isset($GLOBALS['_xh']['headers'][$header_name])) 02304 $GLOBALS['_xh']['headers'][$header_name] .= ', ' . trim($cookie); 02305 else 02306 $GLOBALS['_xh']['headers'][$header_name] = trim($cookie); 02307 // parse cookie attributes, in case user wants to correctly honour them 02308 // feature creep: only allow rfc-compliant cookie attributes? 02309 $cookie = explode(';', $cookie); 02310 foreach ($cookie as $pos => $val) 02311 { 02312 $val = explode('=', $val, 2); 02313 $tag = trim($val[0]); 02314 $val = trim(@$val[1]); 02316 if ($pos == 0) 02317 { 02318 $cookiename = $tag; 02319 $GLOBALS['_xh']['cookies'][$tag] = array(); 02320 $GLOBALS['_xh']['cookies'][$cookiename]['value'] = urldecode($val); 02321 } 02322 else 02323 { 02324 $GLOBALS['_xh']['cookies'][$cookiename][$tag] = $val; 02325 } 02326 } 02327 } 02328 } 02329 else 02330 { 02331 $GLOBALS['_xh']['headers'][$header_name] = trim($arr[1]); 02332 } 02333 } 02334 elseif(isset($header_name)) 02335 { 02337 $GLOBALS['_xh']['headers'][$header_name] .= ' ' . trim($line); 02338 } 02339 } 02340 02341 $data = substr($data, $bd); 02342 02343 if($this->debug && count($GLOBALS['_xh']['headers'])) 02344 { 02345 print '<PRE>'; 02346 foreach($GLOBALS['_xh']['headers'] as $header => $value) 02347 { 02348 print htmlentities("HEADER: $header: $value\n"); 02349 } 02350 foreach($GLOBALS['_xh']['cookies'] as $header => $value) 02351 { 02352 print htmlentities("COOKIE: $header={$value['value']}\n"); 02353 } 02354 print "</PRE>\n"; 02355 } 02356 02357 // if CURL was used for the call, http headers have been processed, 02358 // and dechunking + reinflating have been carried out 02359 if(!$headers_processed) 02360 { 02361 // Decode chunked encoding sent by http 1.1 servers 02362 if(isset($GLOBALS['_xh']['headers']['transfer-encoding']) && $GLOBALS['_xh']['headers']['transfer-encoding'] == 'chunked') 02363 { 02364 if(!$data = decode_chunked($data)) 02365 { 02366 error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to rebuild the chunked data received from server'); 02367 $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['dechunk_fail'], $GLOBALS['xmlrpcstr']['dechunk_fail']); 02368 return $r; 02369 } 02370 } 02371 02372 // Decode gzip-compressed stuff 02373 // code shamelessly inspired from nusoap library by Dietrich Ayala 02374 if(isset($GLOBALS['_xh']['headers']['content-encoding'])) 02375 { 02376 $GLOBALS['_xh']['headers']['content-encoding'] = str_replace('x-', '', $GLOBALS['_xh']['headers']['content-encoding']); 02377 if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' || $GLOBALS['_xh']['headers']['content-encoding'] == 'gzip') 02378 { 02379 // if decoding works, use it. else assume data wasn't gzencoded 02380 if(function_exists('gzinflate')) 02381 { 02382 if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) 02383 { 02384 $data = $degzdata; 02385 if($this->debug) 02386 print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>"; 02387 } 02388 elseif($GLOBALS['_xh']['headers']['content-encoding'] == 'gzip' && $degzdata = @gzinflate(substr($data, 10))) 02389 { 02390 $data = $degzdata; 02391 if($this->debug) 02392 print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>"; 02393 } 02394 else 02395 { 02396 error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to decode the deflated data received from server'); 02397 $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']); 02398 return $r; 02399 } 02400 } 02401 else 02402 { 02403 error_log('XML-RPC: xmlrpcmsg::parseResponse: the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.'); 02404 $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']); 02405 return $r; 02406 } 02407 } 02408 } 02409 } // end of 'if needed, de-chunk, re-inflate response' 02410 02411 // real stupid hack to avoid PHP 4 complaining about returning NULL by ref 02412 $r = null; 02413 $r =& $r; 02414 return $r; 02415 } 02416 02425 function &parseResponse($data='', $headers_processed=false, $return_type='xmlrpcvals') 02426 { 02427 if($this->debug) 02428 { 02429 //by maHo, replaced htmlspecialchars with htmlentities 02430 print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>"; 02431 } 02432 02433 if($data == '') 02434 { 02435 error_log('XML-RPC: xmlrpcmsg::parseResponse: no response received from server.'); 02436 $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']); 02437 return $r; 02438 } 02439 02440 $GLOBALS['_xh']=array(); 02441 02442 $raw_data = $data; 02443 // parse the HTTP headers of the response, if present, and separate them from data 02444 if(substr($data, 0, 4) == 'HTTP') 02445 { 02446 $r =& $this->parseResponseHeaders($data, $headers_processed); 02447 if ($r) 02448 { 02449 // failed processing of HTTP response headers 02450 // save into response obj the full payload received, for debugging 02451 $r->raw_data = $data; 02452 return $r; 02453 } 02454 } 02455 else 02456 { 02457 $GLOBALS['_xh']['headers'] = array(); 02458 $GLOBALS['_xh']['cookies'] = array(); 02459 } 02460 02461 if($this->debug) 02462 { 02463 $start = strpos($data, '<!-- SERVER DEBUG INFO (BASE64 ENCODED):'); 02464 if ($start) 02465 { 02466 $start += strlen('<!-- SERVER DEBUG INFO (BASE64 ENCODED):'); 02467 $end = strpos($data, '-->', $start); 02468 $comments = substr($data, $start, $end-$start); 02469 print "<PRE>---SERVER DEBUG INFO (DECODED) ---\n\t".htmlentities(str_replace("\n", "\n\t", base64_decode($comments)))."\n---END---\n</PRE>"; 02470 } 02471 } 02472 02473 // be tolerant of extra whitespace in response body 02474 $data = trim($data); 02475 02477 02478 // be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts) 02479 // idea from Luca Mariano <luca.mariano@email.it> originally in PEARified version of the lib 02480 $bd = false; 02481 // Poor man's version of strrpos for php 4... 02482 $pos = strpos($data, '</methodResponse>'); 02483 while($pos || is_int($pos)) 02484 { 02485 $bd = $pos+17; 02486 $pos = strpos($data, '</methodResponse>', $bd); 02487 } 02488 if($bd) 02489 { 02490 $data = substr($data, 0, $bd); 02491 } 02492 02493 // if user wants back raw xml, give it to him 02494 if ($return_type == 'xml') 02495 { 02496 $r =& new xmlrpcresp($data, 0, '', 'xml'); 02497 $r->hdrs = $GLOBALS['_xh']['headers']; 02498 $r->_cookies = $GLOBALS['_xh']['cookies']; 02499 $r->raw_data = $raw_data; 02500 return $r; 02501 } 02502 02503 // try to 'guestimate' the character encoding of the received response 02504 $resp_encoding = guess_encoding(@$GLOBALS['_xh']['headers']['content-type'], $data); 02505 02506 $GLOBALS['_xh']['ac']=''; 02507 //$GLOBALS['_xh']['qt']=''; //unused... 02508 $GLOBALS['_xh']['stack'] = array(); 02509 $GLOBALS['_xh']['valuestack'] = array(); 02510 $GLOBALS['_xh']['isf']=0; // 0 = OK, 1 for xmlrpc fault responses, 2 = invalid xmlrpc 02511 $GLOBALS['_xh']['isf_reason']=''; 02512 $GLOBALS['_xh']['rt']=''; // 'methodcall or 'methodresponse' 02513 02514 // if response charset encoding is not known / supported, try to use 02515 // the default encoding and parse the xml anyway, but log a warning... 02516 if (!in_array($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII'))) 02517 // the following code might be better for mb_string enabled installs, but 02518 // makes the lib about 200% slower... 02519 //if (!is_valid_charset($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII'))) 02520 { 02521 error_log('XML-RPC: xmlrpcmsg::parseResponse: invalid charset encoding of received response: '.$resp_encoding); 02522 $resp_encoding = $GLOBALS['xmlrpc_defencoding']; 02523 } 02524 $parser = xml_parser_create($resp_encoding); 02525 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true); 02526 // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell 02527 // the xml parser to give us back data in the expected charset 02528 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']); 02529 02530 if ($return_type == 'phpvals') 02531 { 02532 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast'); 02533 } 02534 else 02535 { 02536 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee'); 02537 } 02538 02539 xml_set_character_data_handler($parser, 'xmlrpc_cd'); 02540 xml_set_default_handler($parser, 'xmlrpc_dh'); 02541 02542 // first error check: xml not well formed 02543 if(!xml_parse($parser, $data, count($data))) 02544 { 02545 // thanks to Peter Kocks <peter.kocks@baygate.com> 02546 if((xml_get_current_line_number($parser)) == 1) 02547 { 02548 $errstr = 'XML error at line 1, check URL'; 02549 } 02550 else 02551 { 02552 $errstr = sprintf('XML error: %s at line %d, column %d', 02553 xml_error_string(xml_get_error_code($parser)), 02554 xml_get_current_line_number($parser), xml_get_current_column_number($parser)); 02555 } 02556 error_log($errstr); 02557 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return'].' ('.$errstr.')'); 02558 xml_parser_free($parser); 02559 if($this->debug) 02560 { 02561 print $errstr; 02562 } 02563 $r->hdrs = $GLOBALS['_xh']['headers']; 02564 $r->_cookies = $GLOBALS['_xh']['cookies']; 02565 $r->raw_data = $raw_data; 02566 return $r; 02567 } 02568 xml_parser_free($parser); 02569 // second error check: xml well formed but not xml-rpc compliant 02570 if ($GLOBALS['_xh']['isf'] > 1) 02571 { 02572 if ($this->debug) 02573 { 02575 } 02576 02577 $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], 02578 $GLOBALS['xmlrpcstr']['invalid_return'] . ' ' . $GLOBALS['_xh']['isf_reason']); 02579 } 02580 // third error check: parsing of the response has somehow gone boink. 02581 // NB: shall we omit this check, since we trust the parsing code? 02582 elseif ($return_type == 'xmlrpcvals' && !is_object($GLOBALS['_xh']['value'])) 02583 { 02584 // something odd has happened 02585 // and it's time to generate a client side error 02586 // indicating something odd went on 02587 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], 02588 $GLOBALS['xmlrpcstr']['invalid_return']); 02589 } 02590 else 02591 { 02592 if ($this->debug) 02593 { 02594 print "<PRE>---PARSED---\n"; 02595 // somehow htmlentities chokes on var_export, and some full html string... 02596 //print htmlentitites(var_export($GLOBALS['_xh']['value'], true)); 02597 print htmlspecialchars(var_export($GLOBALS['_xh']['value'], true)); 02598 print "\n---END---</PRE>"; 02599 } 02600 02601 // note that using =& will raise an error if $GLOBALS['_xh']['st'] does not generate an object. 02602 $v =& $GLOBALS['_xh']['value']; 02603 02604 if($GLOBALS['_xh']['isf']) 02605 { 02608 if ($return_type == 'xmlrpcvals') 02609 { 02610 $errno_v = $v->structmem('faultCode'); 02611 $errstr_v = $v->structmem('faultString'); 02612 $errno = $errno_v->scalarval(); 02613 $errstr = $errstr_v->scalarval(); 02614 } 02615 else 02616 { 02617 $errno = $v['faultCode']; 02618 $errstr = $v['faultString']; 02619 } 02620 02621 if($errno == 0) 02622 { 02623 // FAULT returned, errno needs to reflect that 02624 $errno = -1; 02625 } 02626 02627 $r =& new xmlrpcresp(0, $errno, $errstr); 02628 } 02629 else 02630 { 02631 $r=&new xmlrpcresp($v, 0, '', $return_type); 02632 } 02633 } 02634 02635 $r->hdrs = $GLOBALS['_xh']['headers']; 02636 $r->_cookies = $GLOBALS['_xh']['cookies']; 02637 $r->raw_data = $raw_data; 02638 return $r; 02639 } 02640 } 02641 02642 class xmlrpcval 02643 { 02644 var $me=array(); 02645 var $mytype=0; 02646 var $_php_class=null; 02647 02652 function xmlrpcval($val=-1, $type='') 02653 { 02656 if($val!==-1 || $type!='') 02657 { 02658 // optimization creep: inlined all work done by constructor 02659 switch($type) 02660 { 02661 case '': 02662 $this->mytype=1; 02663 $this->me['string']=$val; 02664 break; 02665 case 'i4': 02666 case 'int': 02667 case 'double': 02668 case 'string': 02669 case 'boolean': 02670 case 'dateTime.iso8601': 02671 case 'base64': 02672 case 'null': 02673 $this->mytype=1; 02674 $this->me[$type]=$val; 02675 break; 02676 case 'array': 02677 $this->mytype=2; 02678 $this->me['array']=$val; 02679 break; 02680 case 'struct': 02681 $this->mytype=3; 02682 $this->me['struct']=$val; 02683 break; 02684 default: 02685 error_log("XML-RPC: xmlrpcval::xmlrpcval: not a known type ($type)"); 02686 } 02687 /*if($type=='') 02688 { 02689 $type='string'; 02690 } 02691 if($GLOBALS['xmlrpcTypes'][$type]==1) 02692 { 02693 $this->addScalar($val,$type); 02694 } 02695 elseif($GLOBALS['xmlrpcTypes'][$type]==2) 02696 { 02697 $this->addArray($val); 02698 } 02699 elseif($GLOBALS['xmlrpcTypes'][$type]==3) 02700 { 02701 $this->addStruct($val); 02702 }*/ 02703 } 02704 } 02705 02712 function addScalar($val, $type='string') 02713 { 02714 $typeof=@$GLOBALS['xmlrpcTypes'][$type]; 02715 if($typeof!=1) 02716 { 02717 error_log("XML-RPC: xmlrpcval::addScalar: not a scalar type ($type)"); 02718 return 0; 02719 } 02720 02721 // coerce booleans into correct values 02722 // NB: we should iether do it for datetimes, integers and doubles, too, 02723 // or just plain remove this check, implemnted on booleans only... 02724 if($type==$GLOBALS['xmlrpcBoolean']) 02725 { 02726 if(strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false'))) 02727 { 02728 $val=true; 02729 } 02730 else 02731 { 02732 $val=false; 02733 } 02734 } 02735 02736 switch($this->mytype) 02737 { 02738 case 1: 02739 error_log('XML-RPC: xmlrpcval::addScalar: scalar xmlrpcval can have only one value'); 02740 return 0; 02741 case 3: 02742 error_log('XML-RPC: xmlrpcval::addScalar: cannot add anonymous scalar to struct xmlrpcval'); 02743 return 0; 02744 case 2: 02745 // we're adding a scalar value to an array here 02746 //$ar=$this->me['array']; 02747 //$ar[]=&new xmlrpcval($val, $type); 02748 //$this->me['array']=$ar; 02749 // Faster (?) avoid all the costly array-copy-by-val done here... 02750 $this->me['array'][]=&new xmlrpcval($val, $type); 02751 return 1; 02752 default: 02753 // a scalar, so set the value and remember we're scalar 02754 $this->me[$type]=$val; 02755 $this->mytype=$typeof; 02756 return 1; 02757 } 02758 } 02759 02768 function addArray($vals) 02769 { 02770 if($this->mytype==0) 02771 { 02772 $this->mytype=$GLOBALS['xmlrpcTypes']['array']; 02773 $this->me['array']=$vals; 02774 return 1; 02775 } 02776 elseif($this->mytype==2) 02777 { 02778 // we're adding to an array here 02779 $this->me['array'] = array_merge($this->me['array'], $vals); 02780 return 1; 02781 } 02782 else 02783 { 02784 error_log('XML-RPC: xmlrpcval::addArray: already initialized as a [' . $this->kindOf() . ']'); 02785 return 0; 02786 } 02787 } 02788 02797 function addStruct($vals) 02798 { 02799 if($this->mytype==0) 02800 { 02801 $this->mytype=$GLOBALS['xmlrpcTypes']['struct']; 02802 $this->me['struct']=$vals; 02803 return 1; 02804 } 02805 elseif($this->mytype==3) 02806 { 02807 // we're adding to a struct here 02808 $this->me['struct'] = array_merge($this->me['struct'], $vals); 02809 return 1; 02810 } 02811 else 02812 { 02813 error_log('XML-RPC: xmlrpcval::addStruct: already initialized as a [' . $this->kindOf() . ']'); 02814 return 0; 02815 } 02816 } 02817 02818 // poor man's version of print_r ??? 02819 // DEPRECATED! 02820 function dump($ar) 02821 { 02822 foreach($ar as $key => $val) 02823 { 02824 echo "$key => $val<br />"; 02825 if($key == 'array') 02826 { 02827 while(list($key2, $val2) = each($val)) 02828 { 02829 echo "-- $key2 => $val2<br />"; 02830 } 02831 } 02832 } 02833 } 02834 02840 function kindOf() 02841 { 02842 switch($this->mytype) 02843 { 02844 case 3: 02845 return 'struct'; 02846 break; 02847 case 2: 02848 return 'array'; 02849 break; 02850 case 1: 02851 return 'scalar'; 02852 break; 02853 default: 02854 return 'undef'; 02855 } 02856 } 02857 02861 function serializedata($typ, $val, $charset_encoding='') 02862 { 02863 $rs=''; 02864 switch(@$GLOBALS['xmlrpcTypes'][$typ]) 02865 { 02866 case 1: 02867 switch($typ) 02868 { 02869 case $GLOBALS['xmlrpcBase64']: 02870 $rs.="<${typ}>" . base64_encode($val) . "</${typ}>"; 02871 break; 02872 case $GLOBALS['xmlrpcBoolean']: 02873 $rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>"; 02874 break; 02875 case $GLOBALS['xmlrpcString']: 02876 // G. Giunta 2005/2/13: do NOT use htmlentities, since 02877 // it will produce named html entities, which are invalid xml 02878 $rs.="<${typ}>" . xmlrpc_encode_entitites($val, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding). "</${typ}>"; 02879 break; 02880 case $GLOBALS['xmlrpcInt']: 02881 case $GLOBALS['xmlrpcI4']: 02882 $rs.="<${typ}>".(int)$val."</${typ}>"; 02883 break; 02884 case $GLOBALS['xmlrpcDouble']: 02885 $rs.="<${typ}>".(double)$val."</${typ}>"; 02886 break; 02887 case $GLOBALS['xmlrpcNull']: 02888 $rs.="<nil/>"; 02889 break; 02890 default: 02891 // no standard type value should arrive here, but provide a possibility 02892 // for xmlrpcvals of unknown type... 02893 $rs.="<${typ}>${val}</${typ}>"; 02894 } 02895 break; 02896 case 3: 02897 // struct 02898 if ($this->_php_class) 02899 { 02900 $rs.='<struct php_class="' . $this->_php_class . "\">\n"; 02901 } 02902 else 02903 { 02904 $rs.="<struct>\n"; 02905 } 02906 foreach($val as $key2 => $val2) 02907 { 02908 $rs.='<member><name>'.xmlrpc_encode_entitites($key2, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding)."</name>\n"; 02909 //$rs.=$this->serializeval($val2); 02910 $rs.=$val2->serialize($charset_encoding); 02911 $rs.="</member>\n"; 02912 } 02913 $rs.='</struct>'; 02914 break; 02915 case 2: 02916 // array 02917 $rs.="<array>\n<data>\n"; 02918 for($i=0; $i<count($val); $i++) 02919 { 02920 //$rs.=$this->serializeval($val[$i]); 02921 $rs.=$val[$i]->serialize($charset_encoding); 02922 } 02923 $rs.="</data>\n</array>"; 02924 break; 02925 default: 02926 break; 02927 } 02928 return $rs; 02929 } 02930 02937 function serialize($charset_encoding='') 02938 { 02939 // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals... 02940 //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval'))) 02941 //{ 02942 reset($this->me); 02943 list($typ, $val) = each($this->me); 02944 return '<value>' . $this->serializedata($typ, $val, $charset_encoding) . "</value>\n"; 02945 //} 02946 } 02947 02948 // DEPRECATED 02949 function serializeval($o) 02950 { 02951 // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals... 02952 //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval'))) 02953 //{ 02954 $ar=$o->me; 02955 reset($ar); 02956 list($typ, $val) = each($ar); 02957 return '<value>' . $this->serializedata($typ, $val) . "</value>\n"; 02958 //} 02959 } 02960 02968 function structmemexists($m) 02969 { 02970 return array_key_exists($m, $this->me['struct']); 02971 } 02972 02980 function structmem($m) 02981 { 02982 return $this->me['struct'][$m]; 02983 } 02984 02989 function structreset() 02990 { 02991 reset($this->me['struct']); 02992 } 02993 02999 function structeach() 03000 { 03001 return each($this->me['struct']); 03002 } 03003 03004 // DEPRECATED! this code looks like it is very fragile and has not been fixed 03005 // for a long long time. Shall we remove it for 2.0? 03006 function getval() 03007 { 03008 // UNSTABLE 03009 reset($this->me); 03010 list($a,$b)=each($this->me); 03011 // contributed by I Sofer, 2001-03-24 03012 // add support for nested arrays to scalarval 03013 // i've created a new method here, so as to 03014 // preserve back compatibility 03015 03016 if(is_array($b)) 03017 { 03018 @reset($b); 03019 while(list($id,$cont) = @each($b)) 03020 { 03021 $b[$id] = $cont->scalarval(); 03022 } 03023 } 03024 03025 // add support for structures directly encoding php objects 03026 if(is_object($b)) 03027 { 03028 $t = get_object_vars($b); 03029 @reset($t); 03030 while(list($id,$cont) = @each($t)) 03031 { 03032 $t[$id] = $cont->scalarval(); 03033 } 03034 @reset($t); 03035 while(list($id,$cont) = @each($t)) 03036 { 03037 @$b->$id = $cont; 03038 } 03039 } 03040 // end contrib 03041 return $b; 03042 } 03043 03049 function scalarval() 03050 { 03051 reset($this->me); 03052 list(,$b)=each($this->me); 03053 return $b; 03054 } 03055 03062 function scalartyp() 03063 { 03064 reset($this->me); 03065 list($a,)=each($this->me); 03066 if($a==$GLOBALS['xmlrpcI4']) 03067 { 03068 $a=$GLOBALS['xmlrpcInt']; 03069 } 03070 return $a; 03071 } 03072 03079 function arraymem($m) 03080 { 03081 return $this->me['array'][$m]; 03082 } 03083 03089 function arraysize() 03090 { 03091 return count($this->me['array']); 03092 } 03093 03099 function structsize() 03100 { 03101 return count($this->me['struct']); 03102 } 03103 } 03104 03105 03106 // date helpers 03107 03125 function iso8601_encode($timet, $utc=0) 03126 { 03127 if(!$utc) 03128 { 03129 $t=strftime("%Y%m%dT%H:%M:%S", $timet); 03130 } 03131 else 03132 { 03133 if(function_exists('gmstrftime')) 03134 { 03135 // gmstrftime doesn't exist in some versions 03136 // of PHP 03137 $t=gmstrftime("%Y%m%dT%H:%M:%S", $timet); 03138 } 03139 else 03140 { 03141 $t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z')); 03142 } 03143 } 03144 return $t; 03145 } 03146 03153 function iso8601_decode($idate, $utc=0) 03154 { 03155 $t=0; 03156 if(preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $idate, $regs)) 03157 { 03158 if($utc) 03159 { 03160 $t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); 03161 } 03162 else 03163 { 03164 $t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); 03165 } 03166 } 03167 return $t; 03168 } 03169 03191 function php_xmlrpc_decode($xmlrpc_val, $options=array()) 03192 { 03193 switch($xmlrpc_val->kindOf()) 03194 { 03195 case 'scalar': 03196 if (in_array('extension_api', $options)) 03197 { 03198 reset($xmlrpc_val->me); 03199 list($typ,$val) = each($xmlrpc_val->me); 03200 switch ($typ) 03201 { 03202 case 'dateTime.iso8601': 03203 $xmlrpc_val->scalar = $val; 03204 $xmlrpc_val->xmlrpc_type = 'datetime'; 03205 $xmlrpc_val->timestamp = iso8601_decode($val); 03206 return $xmlrpc_val; 03207 case 'base64': 03208 $xmlrpc_val->scalar = $val; 03209 $xmlrpc_val->type = $typ; 03210 return $xmlrpc_val; 03211 default: 03212 return $xmlrpc_val->scalarval(); 03213 } 03214 } 03215 return $xmlrpc_val->scalarval(); 03216 case 'array': 03217 $size = $xmlrpc_val->arraysize(); 03218 $arr = array(); 03219 for($i = 0; $i < $size; $i++) 03220 { 03221 $arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i), $options); 03222 } 03223 return $arr; 03224 case 'struct': 03225 $xmlrpc_val->structreset(); 03226 // If user said so, try to rebuild php objects for specific struct vals. 03228 // shall we check for proper subclass of xmlrpcval instead of 03229 // presence of _php_class to detect what we can do? 03230 if (in_array('decode_php_objs', $options) && $xmlrpc_val->_php_class != '' 03231 && class_exists($xmlrpc_val->_php_class)) 03232 { 03233 $obj = @new $xmlrpc_val->_php_class; 03234 while(list($key,$value)=$xmlrpc_val->structeach()) 03235 { 03236 $obj->$key = php_xmlrpc_decode($value, $options); 03237 } 03238 return $obj; 03239 } 03240 else 03241 { 03242 $arr = array(); 03243 while(list($key,$value)=$xmlrpc_val->structeach()) 03244 { 03245 $arr[$key] = php_xmlrpc_decode($value, $options); 03246 } 03247 return $arr; 03248 } 03249 case 'msg': 03250 $paramcount = $xmlrpc_val->getNumParams(); 03251 $arr = array(); 03252 for($i = 0; $i < $paramcount; $i++) 03253 { 03254 $arr[] = php_xmlrpc_decode($xmlrpc_val->getParam($i)); 03255 } 03256 return $arr; 03257 } 03258 } 03259 03260 // This constant left here only for historical reasons... 03261 // it was used to decide if we have to define xmlrpc_encode on our own, but 03262 // we do not do it anymore 03263 if(function_exists('xmlrpc_decode')) 03264 { 03265 define('XMLRPC_EPI_ENABLED','1'); 03266 } 03267 else 03268 { 03269 define('XMLRPC_EPI_ENABLED','0'); 03270 } 03271 03289 function &php_xmlrpc_encode($php_val, $options=array()) 03290 { 03291 $type = gettype($php_val); 03292 switch($type) 03293 { 03294 case 'string': 03295 if (in_array('auto_dates', $options) && preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $php_val)) 03296 $xmlrpc_val =& new xmlrpcval($php_val, $GLOBALS['xmlrpcDateTime']); 03297 else 03298 $xmlrpc_val =& new xmlrpcval($php_val, $GLOBALS['xmlrpcString']); 03299 break; 03300 case 'integer': 03301 $xmlrpc_val =& new xmlrpcval($php_val, $GLOBALS['xmlrpcInt']); 03302 break; 03303 case 'double': 03304 $xmlrpc_val =& new xmlrpcval($php_val, $GLOBALS['xmlrpcDouble']); 03305 break; 03306 // <G_Giunta_2001-02-29> 03307 // Add support for encoding/decoding of booleans, since they are supported in PHP 03308 case 'boolean': 03309 $xmlrpc_val =& new xmlrpcval($php_val, $GLOBALS['xmlrpcBoolean']); 03310 break; 03311 // </G_Giunta_2001-02-29> 03312 case 'array': 03313 // PHP arrays can be encoded to either xmlrpc structs or arrays, 03314 // depending on wheter they are hashes or plain 0..n integer indexed 03315 // A shorter one-liner would be 03316 // $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1)); 03317 // but execution time skyrockets! 03318 $j = 0; 03319 $arr = array(); 03320 $ko = false; 03321 foreach($php_val as $key => $val) 03322 { 03323 $arr[$key] =& php_xmlrpc_encode($val, $options); 03324 if(!$ko && $key !== $j) 03325 { 03326 $ko = true; 03327 } 03328 $j++; 03329 } 03330 if($ko) 03331 { 03332 $xmlrpc_val =& new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']); 03333 } 03334 else 03335 { 03336 $xmlrpc_val =& new xmlrpcval($arr, $GLOBALS['xmlrpcArray']); 03337 } 03338 break; 03339 case 'object': 03340 if(is_a($php_val, 'xmlrpcval')) 03341 { 03342 $xmlrpc_val = $php_val; 03343 } 03344 else 03345 { 03346 $arr = array(); 03347 while(list($k,$v) = each($php_val)) 03348 { 03349 $arr[$k] = php_xmlrpc_encode($v, $options); 03350 } 03351 $xmlrpc_val =& new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']); 03352 if (in_array('encode_php_objs', $options)) 03353 { 03354 // let's save original class name into xmlrpcval: 03355 // might be useful later on... 03356 $xmlrpc_val->_php_class = get_class($php_val); 03357 } 03358 } 03359 break; 03360 case 'NULL': 03361 if (in_array('extension_api', $options)) 03362 { 03363 $xmlrpc_val =& new xmlrpcval('', $GLOBALS['xmlrpcString']); 03364 } 03365 if (in_array('null_extension', $options)) 03366 { 03367 $xmlrpc_val =& new xmlrpcval('', $GLOBALS['xmlrpcNull']); 03368 } 03369 else 03370 { 03371 $xmlrpc_val =& new xmlrpcval(); 03372 } 03373 break; 03374 case 'resource': 03375 if (in_array('extension_api', $options)) 03376 { 03377 $xmlrpc_val =& new xmlrpcval((int)$php_val, $GLOBALS['xmlrpcInt']); 03378 } 03379 else 03380 { 03381 $xmlrpc_val =& new xmlrpcval(); 03382 } 03383 // catch "user function", "unknown type" 03384 default: 03385 // giancarlo pinerolo <ping@alt.it> 03386 // it has to return 03387 // an empty object in case, not a boolean. 03388 $xmlrpc_val =& new xmlrpcval(); 03389 break; 03390 } 03391 return $xmlrpc_val; 03392 } 03393 03401 function php_xmlrpc_decode_xml($xml_val, $options=array()) 03402 { 03403 $GLOBALS['_xh'] = array(); 03404 $GLOBALS['_xh']['ac'] = ''; 03405 $GLOBALS['_xh']['stack'] = array(); 03406 $GLOBALS['_xh']['valuestack'] = array(); 03407 $GLOBALS['_xh']['params'] = array(); 03408 $GLOBALS['_xh']['pt'] = array(); 03409 $GLOBALS['_xh']['isf'] = 0; 03410 $GLOBALS['_xh']['isf_reason'] = ''; 03411 $GLOBALS['_xh']['method'] = false; 03412 $GLOBALS['_xh']['rt'] = ''; 03414 $parser = xml_parser_create(); 03415 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true); 03416 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']); 03417 xml_set_element_handler($parser, 'xmlrpc_se_any', 'xmlrpc_ee'); 03418 xml_set_character_data_handler($parser, 'xmlrpc_cd'); 03419 xml_set_default_handler($parser, 'xmlrpc_dh'); 03420 if(!xml_parse($parser, $xml_val, 1)) 03421 { 03422 $errstr = sprintf('XML error: %s at line %d, column %d', 03423 xml_error_string(xml_get_error_code($parser)), 03424 xml_get_current_line_number($parser), xml_get_current_column_number($parser)); 03425 error_log($errstr); 03426 xml_parser_free($parser); 03427 return false; 03428 } 03429 xml_parser_free($parser); 03430 if ($GLOBALS['_xh']['isf'] > 1) // test that $GLOBALS['_xh']['value'] is an obj, too??? 03431 { 03432 error_log($GLOBALS['_xh']['isf_reason']); 03433 return false; 03434 } 03435 switch ($GLOBALS['_xh']['rt']) 03436 { 03437 case 'methodresponse': 03438 $v =& $GLOBALS['_xh']['value']; 03439 if ($GLOBALS['_xh']['isf'] == 1) 03440 { 03441 $vc = $v->structmem('faultCode'); 03442 $vs = $v->structmem('faultString'); 03443 $r =& new xmlrpcresp(0, $vc->scalarval(), $vs->scalarval()); 03444 } 03445 else 03446 { 03447 $r =& new xmlrpcresp($v); 03448 } 03449 return $r; 03450 case 'methodcall': 03451 $m =& new xmlrpcmsg($GLOBALS['_xh']['method']); 03452 for($i=0; $i < count($GLOBALS['_xh']['params']); $i++) 03453 { 03454 $m->addParam($GLOBALS['_xh']['params'][$i]); 03455 } 03456 return $m; 03457 case 'value': 03458 return $GLOBALS['_xh']['value']; 03459 default: 03460 return false; 03461 } 03462 } 03463 03472 function decode_chunked($buffer) 03473 { 03474 // length := 0 03475 $length = 0; 03476 $new = ''; 03477 03478 // read chunk-size, chunk-extension (if any) and crlf 03479 // get the position of the linebreak 03480 $chunkend = strpos($buffer,"\r\n") + 2; 03481 $temp = substr($buffer,0,$chunkend); 03482 $chunk_size = hexdec( trim($temp) ); 03483 $chunkstart = $chunkend; 03484 while($chunk_size > 0) 03485 { 03486 $chunkend = strpos($buffer, "\r\n", $chunkstart + $chunk_size); 03487 03488 // just in case we got a broken connection 03489 if($chunkend == false) 03490 { 03491 $chunk = substr($buffer,$chunkstart); 03492 // append chunk-data to entity-body 03493 $new .= $chunk; 03494 $length += strlen($chunk); 03495 break; 03496 } 03497 03498 // read chunk-data and crlf 03499 $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart); 03500 // append chunk-data to entity-body 03501 $new .= $chunk; 03502 // length := length + chunk-size 03503 $length += strlen($chunk); 03504 // read chunk-size and crlf 03505 $chunkstart = $chunkend + 2; 03506 03507 $chunkend = strpos($buffer,"\r\n",$chunkstart)+2; 03508 if($chunkend == false) 03509 { 03510 break; //just in case we got a broken connection 03511 } 03512 $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart); 03513 $chunk_size = hexdec( trim($temp) ); 03514 $chunkstart = $chunkend; 03515 } 03516 return $new; 03517 } 03518 03533 function guess_encoding($httpheader='', $xmlchunk='', $encoding_prefs=null) 03534 { 03535 // discussion: see http://www.yale.edu/pclt/encoding/ 03536 // 1 - test if encoding is specified in HTTP HEADERS 03537 03538 //Details: 03539 // LWS: (\13\10)?( |\t)+ 03540 // token: (any char but excluded stuff)+ 03541 // header: Content-type = ...; charset=value(; ...)* 03542 // where value is of type token, no LWS allowed between 'charset' and value 03543 // Note: we do not check for invalid chars in VALUE: 03544 // this had better be done using pure ereg as below 03545 03547 $matches = array(); 03548 if(preg_match('/;\s*charset=([^;]+)/i', $httpheader, $matches)) 03549 { 03550 return strtoupper(trim($matches[1])); 03551 } 03552 03553 // 2 - scan the first bytes of the data for a UTF-16 (or other) BOM pattern 03554 // (source: http://www.w3.org/TR/2000/REC-xml-20001006) 03555 // NOTE: actually, according to the spec, even if we find the BOM and determine 03556 // an encoding, we should check if there is an encoding specified 03557 // in the xml declaration, and verify if they match. 03560 if(preg_match('/^(\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\x00\x00\xFF\xFE|\xFE\xFF\x00\x00)/', $xmlchunk)) 03561 { 03562 return 'UCS-4'; 03563 } 03564 elseif(preg_match('/^(\xFE\xFF|\xFF\xFE)/', $xmlchunk)) 03565 { 03566 return 'UTF-16'; 03567 } 03568 elseif(preg_match('/^(\xEF\xBB\xBF)/', $xmlchunk)) 03569 { 03570 return 'UTF-8'; 03571 } 03572 03573 // 3 - test if encoding is specified in the xml declaration 03574 // Details: 03575 // SPACE: (#x20 | #x9 | #xD | #xA)+ === [ \x9\xD\xA]+ 03576 // EQ: SPACE?=SPACE? === [ \x9\xD\xA]*=[ \x9\xD\xA]* 03577 if (preg_match('/^<\?xml\s+version\s*=\s*'. "((?:\"[a-zA-Z0-9_.:-]+\")|(?:'[a-zA-Z0-9_.:-]+'))". 03578 '\s+encoding\s*=\s*' . "((?:\"[A-Za-z][A-Za-z0-9._-]*\")|(?:'[A-Za-z][A-Za-z0-9._-]*'))/", 03579 $xmlchunk, $matches)) 03580 { 03581 return strtoupper(substr($matches[2], 1, -1)); 03582 } 03583 03584 // 4 - if mbstring is available, let it do the guesswork 03585 // NB: we favour finding an encoding that is compatible with what we can process 03586 if(extension_loaded('mbstring')) 03587 { 03588 if($encoding_prefs) 03589 { 03590 $enc = mb_detect_encoding($xmlchunk, $encoding_prefs); 03591 } 03592 else 03593 { 03594 $enc = mb_detect_encoding($xmlchunk); 03595 } 03596 // NB: mb_detect likes to call it ascii, xml parser likes to call it US_ASCII... 03597 // IANA also likes better US-ASCII, so go with it 03598 if($enc == 'ASCII') 03599 { 03600 $enc = 'US-'.$enc; 03601 } 03602 return $enc; 03603 } 03604 else 03605 { 03606 // no encoding specified: as per HTTP1.1 assume it is iso-8859-1? 03607 // Both RFC 2616 (HTTP 1.1) and 1945(http 1.0) clearly state that for text/xxx content types 03608 // this should be the standard. And we should be getting text/xml as request and response. 03609 // BUT we have to be backward compatible with the lib, which always used UTF-8 as default... 03610 return $GLOBALS['xmlrpc_defencoding']; 03611 } 03612 } 03613 03620 function is_valid_charset($encoding, $validlist) 03621 { 03622 $charset_supersets = array( 03623 'US-ASCII' => array ('ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4', 03624 'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8', 03625 'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-11', 'ISO-8859-12', 03626 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'UTF-8', 03627 'EUC-JP', 'EUC-', 'EUC-KR', 'EUC-CN') 03628 ); 03629 if (is_string($validlist)) 03630 $validlist = explode(',', $validlist); 03631 if (@in_array(strtoupper($encoding), $validlist)) 03632 return true; 03633 else 03634 { 03635 if (array_key_exists($encoding, $charset_supersets)) 03636 foreach ($validlist as $allowed) 03637 if (in_array($allowed, $charset_supersets[$encoding])) 03638 return true; 03639 return false; 03640 } 03641 } 03642 03643 ?>