xmlrpc.inc.php

Go to the documentation of this file.
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'=>'&#x20AC;', '\x81'=>'?', '\x82'=>'&#x201A;', '\x83'=>'&#x0192;',
00158    '\x84'=>'&#x201E;', '\x85'=>'&#x2026;', '\x86'=>'&#x2020;', \x87'=>'&#x2021;',
00159    '\x88'=>'&#x02C6;', '\x89'=>'&#x2030;', '\x8A'=>'&#x0160;', '\x8B'=>'&#x2039;',
00160    '\x8C'=>'&#x0152;', '\x8D'=>'?', '\x8E'=>'&#x017D;', '\x8F'=>'?',
00161    '\x90'=>'?', '\x91'=>'&#x2018;', '\x92'=>'&#x2019;', '\x93'=>'&#x201C;',
00162    '\x94'=>'&#x201D;', '\x95'=>'&#x2022;', '\x96'=>'&#x2013;', '\x97'=>'&#x2014;',
00163    '\x98'=>'&#x02DC;', '\x99'=>'&#x2122;', '\x9A'=>'&#x0161;', '\x9B'=>'&#x203A;',
00164    '\x9C'=>'&#x0153;', '\x9D'=>'?', '\x9E'=>'&#x017E;', '\x9F'=>'&#x0178;'
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('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $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('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $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('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $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 .= '&quot;';
00316                                         break;
00317                                 case 38:
00318                                         $escaped_data .= '&amp;';
00319                                         break;
00320                                 case 39:
00321                                         $escaped_data .= '&apos;';
00322                                         break;
00323                                 case 60:
00324                                         $escaped_data .= '&lt;';
00325                                         break;
00326                                 case 62:
00327                                         $escaped_data .= '&gt;';
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 ?>



Generated on Wed Jun 25 17:25:59 2008 by  doxygen 1.5.5