[ Index ]

PHP Cross Reference of Nucleus CMS 3.32

title

Body

[close]

/nucleus/libs/ -> xmlrpc.inc.php (source)

   1  <?php
   2  // by Edd Dumbill (C) 1999-2002
   3  // <edd@usefulinc.com>
   4  // $Original: xmlrpc.inc,v 1.158 2007/03/01 21:21:02 ggiunta Exp $
   5  // $Id: xmlrpc.inc.php 1199 2007-09-07 07:03:41Z kimitake $
   6  
   7  
   8  // Copyright (c) 1999,2000,2002 Edd Dumbill.
   9  // All rights reserved.
  10  //
  11  // Redistribution and use in source and binary forms, with or without
  12  // modification, are permitted provided that the following conditions
  13  // are met:
  14  //
  15  //    * Redistributions of source code must retain the above copyright
  16  //      notice, this list of conditions and the following disclaimer.
  17  //
  18  //    * Redistributions in binary form must reproduce the above
  19  //      copyright notice, this list of conditions and the following
  20  //      disclaimer in the documentation and/or other materials provided
  21  //      with the distribution.
  22  //
  23  //    * Neither the name of the "XML-RPC for PHP" nor the names of its
  24  //      contributors may be used to endorse or promote products derived
  25  //      from this software without specific prior written permission.
  26  //
  27  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  28  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  29  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  30  // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  31  // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  32  // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  33  // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  34  // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  35  // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  36  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  37  // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  38  // OF THE POSSIBILITY OF SUCH DAMAGE.
  39  
  40      if(!function_exists('xml_parser_create'))
  41      {
  42          // For PHP 4 onward, XML functionality is always compiled-in on windows:
  43          // no more need to dl-open it. It might have been compiled out on *nix...
  44          if(strtoupper(substr(PHP_OS, 0, 3) != 'WIN'))
  45          {
  46              dl('xml.so');
  47          }
  48      }
  49  
  50      // Try to be backward compat with php < 4.2 (are we not being nice ?)
  51      $phpversion = phpversion();
  52      if($phpversion[0] == '4' && $phpversion[2] < 2)
  53      {
  54          // give an opportunity to user to specify where to include other files from
  55          if(!defined('PHP_XMLRPC_COMPAT_DIR'))
  56          {
  57              define('PHP_XMLRPC_COMPAT_DIR',dirname(__FILE__).'/compat/');
  58          }
  59          if($phpversion[2] == '0')
  60          {
  61              if($phpversion[4] < 6)
  62              {
  63                  include(PHP_XMLRPC_COMPAT_DIR.'is_callable.php');
  64              }
  65              include(PHP_XMLRPC_COMPAT_DIR.'is_scalar.php');
  66              include(PHP_XMLRPC_COMPAT_DIR.'array_key_exists.php');
  67              include(PHP_XMLRPC_COMPAT_DIR.'version_compare.php');
  68          }
  69          include(PHP_XMLRPC_COMPAT_DIR.'var_export.php');
  70          include(PHP_XMLRPC_COMPAT_DIR.'is_a.php');
  71      }
  72  
  73      // G. Giunta 2005/01/29: declare global these variables,
  74      // so that xmlrpc.inc will work even if included from within a function
  75      // Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used.
  76      $GLOBALS['xmlrpcI4']='i4';
  77      $GLOBALS['xmlrpcInt']='int';
  78      $GLOBALS['xmlrpcBoolean']='boolean';
  79      $GLOBALS['xmlrpcDouble']='double';
  80      $GLOBALS['xmlrpcString']='string';
  81      $GLOBALS['xmlrpcDateTime']='dateTime.iso8601';
  82      $GLOBALS['xmlrpcBase64']='base64';
  83      $GLOBALS['xmlrpcArray']='array';
  84      $GLOBALS['xmlrpcStruct']='struct';
  85      $GLOBALS['xmlrpcValue']='undefined';
  86  
  87      $GLOBALS['xmlrpcTypes']=array(
  88          $GLOBALS['xmlrpcI4']       => 1,
  89          $GLOBALS['xmlrpcInt']      => 1,
  90          $GLOBALS['xmlrpcBoolean']  => 1,
  91          $GLOBALS['xmlrpcString']   => 1,
  92          $GLOBALS['xmlrpcDouble']   => 1,
  93          $GLOBALS['xmlrpcDateTime'] => 1,
  94          $GLOBALS['xmlrpcBase64']   => 1,
  95          $GLOBALS['xmlrpcArray']    => 2,
  96          $GLOBALS['xmlrpcStruct']   => 3
  97      );
  98  
  99      $GLOBALS['xmlrpc_valid_parents'] = array(
 100          'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
 101          'BOOLEAN' => array('VALUE'),
 102          'I4' => array('VALUE'),
 103          'INT' => array('VALUE'),
 104          'STRING' => array('VALUE'),
 105          'DOUBLE' => array('VALUE'),
 106          'DATETIME.ISO8601' => array('VALUE'),
 107          'BASE64' => array('VALUE'),
 108          'MEMBER' => array('STRUCT'),
 109          'NAME' => array('MEMBER'),
 110          'DATA' => array('ARRAY'),
 111          'ARRAY' => array('VALUE'),
 112          'STRUCT' => array('VALUE'),
 113          'PARAM' => array('PARAMS'),
 114          'METHODNAME' => array('METHODCALL'),
 115          'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
 116          'FAULT' => array('METHODRESPONSE'),
 117          'NIL' => array('VALUE') // only used when extension activated
 118      );
 119  
 120      // define extra types for supporting NULL (useful for json or <NIL/>)
 121      $GLOBALS['xmlrpcNull']='null';
 122      $GLOBALS['xmlrpcTypes']['null']=1;
 123  
 124      // Not in use anymore since 2.0. Shall we remove it?
 125      /// @deprecated
 126      $GLOBALS['xmlEntities']=array(
 127          'amp'  => '&',
 128          'quot' => '"',
 129          'lt'   => '<',
 130          'gt'   => '>',
 131          'apos' => "'"
 132      );
 133  
 134      // tables used for transcoding different charsets into us-ascii xml
 135  
 136      $GLOBALS['xml_iso88591_Entities']=array();
 137      $GLOBALS['xml_iso88591_Entities']['in'] = array();
 138      $GLOBALS['xml_iso88591_Entities']['out'] = array();
 139      for ($i = 0; $i < 32; $i++)
 140      {
 141          $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
 142          $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
 143      }
 144      for ($i = 160; $i < 256; $i++)
 145      {
 146          $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
 147          $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
 148      }
 149  
 150      /// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159.
 151      /// These will NOT be present in true ISO-8859-1, but will save the unwary
 152      /// windows user from sending junk.
 153  /*
 154  $cp1252_to_xmlent =
 155    array(
 156     '\x80'=>'&#x20AC;', '\x81'=>'?', '\x82'=>'&#x201A;', '\x83'=>'&#x0192;',
 157     '\x84'=>'&#x201E;', '\x85'=>'&#x2026;', '\x86'=>'&#x2020;', \x87'=>'&#x2021;',
 158     '\x88'=>'&#x02C6;', '\x89'=>'&#x2030;', '\x8A'=>'&#x0160;', '\x8B'=>'&#x2039;',
 159     '\x8C'=>'&#x0152;', '\x8D'=>'?', '\x8E'=>'&#x017D;', '\x8F'=>'?',
 160     '\x90'=>'?', '\x91'=>'&#x2018;', '\x92'=>'&#x2019;', '\x93'=>'&#x201C;',
 161     '\x94'=>'&#x201D;', '\x95'=>'&#x2022;', '\x96'=>'&#x2013;', '\x97'=>'&#x2014;',
 162     '\x98'=>'&#x02DC;', '\x99'=>'&#x2122;', '\x9A'=>'&#x0161;', '\x9B'=>'&#x203A;',
 163     '\x9C'=>'&#x0153;', '\x9D'=>'?', '\x9E'=>'&#x017E;', '\x9F'=>'&#x0178;'
 164    );
 165  */
 166  
 167      $GLOBALS['xmlrpcerr']['unknown_method']=1;
 168      $GLOBALS['xmlrpcstr']['unknown_method']='Unknown method';
 169      $GLOBALS['xmlrpcerr']['invalid_return']=2;
 170      $GLOBALS['xmlrpcstr']['invalid_return']='Invalid return payload: enable debugging to examine incoming payload';
 171      $GLOBALS['xmlrpcerr']['incorrect_params']=3;
 172      $GLOBALS['xmlrpcstr']['incorrect_params']='Incorrect parameters passed to method';
 173      $GLOBALS['xmlrpcerr']['introspect_unknown']=4;
 174      $GLOBALS['xmlrpcstr']['introspect_unknown']="Can't introspect: method unknown";
 175      $GLOBALS['xmlrpcerr']['http_error']=5;
 176      $GLOBALS['xmlrpcstr']['http_error']="Didn't receive 200 OK from remote server.";
 177      $GLOBALS['xmlrpcerr']['no_data']=6;
 178      $GLOBALS['xmlrpcstr']['no_data']='No data received from server.';
 179      $GLOBALS['xmlrpcerr']['no_ssl']=7;
 180      $GLOBALS['xmlrpcstr']['no_ssl']='No SSL support compiled in.';
 181      $GLOBALS['xmlrpcerr']['curl_fail']=8;
 182      $GLOBALS['xmlrpcstr']['curl_fail']='CURL error';
 183      $GLOBALS['xmlrpcerr']['invalid_request']=15;
 184      $GLOBALS['xmlrpcstr']['invalid_request']='Invalid request payload';
 185      $GLOBALS['xmlrpcerr']['no_curl']=16;
 186      $GLOBALS['xmlrpcstr']['no_curl']='No CURL support compiled in.';
 187      $GLOBALS['xmlrpcerr']['server_error']=17;
 188      $GLOBALS['xmlrpcstr']['server_error']='Internal server error';
 189      $GLOBALS['xmlrpcerr']['multicall_error']=18;
 190      $GLOBALS['xmlrpcstr']['multicall_error']='Received from server invalid multicall response';
 191  
 192      $GLOBALS['xmlrpcerr']['multicall_notstruct'] = 9;
 193      $GLOBALS['xmlrpcstr']['multicall_notstruct'] = 'system.multicall expected struct';
 194      $GLOBALS['xmlrpcerr']['multicall_nomethod']  = 10;
 195      $GLOBALS['xmlrpcstr']['multicall_nomethod']  = 'missing methodName';
 196      $GLOBALS['xmlrpcerr']['multicall_notstring'] = 11;
 197      $GLOBALS['xmlrpcstr']['multicall_notstring'] = 'methodName is not a string';
 198      $GLOBALS['xmlrpcerr']['multicall_recursion'] = 12;
 199      $GLOBALS['xmlrpcstr']['multicall_recursion'] = 'recursive system.multicall forbidden';
 200      $GLOBALS['xmlrpcerr']['multicall_noparams']  = 13;
 201      $GLOBALS['xmlrpcstr']['multicall_noparams']  = 'missing params';
 202      $GLOBALS['xmlrpcerr']['multicall_notarray']  = 14;
 203      $GLOBALS['xmlrpcstr']['multicall_notarray']  = 'params is not an array';
 204  
 205      $GLOBALS['xmlrpcerr']['cannot_decompress']=103;
 206      $GLOBALS['xmlrpcstr']['cannot_decompress']='Received from server compressed HTTP and cannot decompress';
 207      $GLOBALS['xmlrpcerr']['decompress_fail']=104;
 208      $GLOBALS['xmlrpcstr']['decompress_fail']='Received from server invalid compressed HTTP';
 209      $GLOBALS['xmlrpcerr']['dechunk_fail']=105;
 210      $GLOBALS['xmlrpcstr']['dechunk_fail']='Received from server invalid chunked HTTP';
 211      $GLOBALS['xmlrpcerr']['server_cannot_decompress']=106;
 212      $GLOBALS['xmlrpcstr']['server_cannot_decompress']='Received from client compressed HTTP request and cannot decompress';
 213      $GLOBALS['xmlrpcerr']['server_decompress_fail']=107;
 214      $GLOBALS['xmlrpcstr']['server_decompress_fail']='Received from client invalid compressed HTTP request';
 215  
 216      // The charset encoding used by the server for received messages and
 217      // by the client for received responses when received charset cannot be determined
 218      // or is not supported
 219      $GLOBALS['xmlrpc_defencoding']='UTF-8';
 220  
 221      // The encoding used internally by PHP.
 222      // String values received as xml will be converted to this, and php strings will be converted to xml
 223      // as if having been coded with this
 224      $GLOBALS['xmlrpc_internalencoding']='ISO-8859-1';
 225  
 226      $GLOBALS['xmlrpcName']='XML-RPC for PHP';
 227      $GLOBALS['xmlrpcVersion']='2.2';
 228  
 229      // let user errors start at 800
 230      $GLOBALS['xmlrpcerruser']=800;
 231      // let XML parse errors start at 100
 232      $GLOBALS['xmlrpcerrxml']=100;
 233  
 234      // formulate backslashes for escaping regexp
 235      // Not in use anymore since 2.0. Shall we remove it?
 236      /// @deprecated
 237      $GLOBALS['xmlrpc_backslash']=chr(92).chr(92);
 238  
 239      // set to TRUE to enable correct decoding of <NIL/> values
 240      $GLOBALS['xmlrpc_null_extension']=false;
 241  
 242      // used to store state during parsing
 243      // quick explanation of components:
 244      //   ac - used to accumulate values
 245      //   isf - used to indicate a parsing fault (2) or xmlrpcresp fault (1)
 246      //   isf_reason - used for storing xmlrpcresp fault string
 247      //   lv - used to indicate "looking for a value": implements
 248      //        the logic to allow values with no types to be strings
 249      //   params - used to store parameters in method calls
 250      //   method - used to store method name
 251      //   stack - array with genealogy of xml elements names:
 252      //           used to validate nesting of xmlrpc elements
 253      $GLOBALS['_xh']=null;
 254  
 255      /**
 256      * Convert a string to the correct XML representation in a target charset
 257      * To help correct communication of non-ascii chars inside strings, regardless
 258      * of the charset used when sending requests, parsing them, sending responses
 259      * and parsing responses, an option is to convert all non-ascii chars present in the message
 260      * into their equivalent 'charset entity'. Charset entities enumerated this way
 261      * are independent of the charset encoding used to transmit them, and all XML
 262      * parsers are bound to understand them.
 263      * Note that in the std case we are not sending a charset encoding mime type
 264      * along with http headers, so we are bound by RFC 3023 to emit strict us-ascii.
 265      *
 266      * @todo do a bit of basic benchmarking (strtr vs. str_replace)
 267      * @todo    make usage of iconv() or recode_string() or mb_string() where available
 268      */
 269  	function xmlrpc_encode_entitites($data, $src_encoding='', $dest_encoding='')
 270      {
 271          if ($src_encoding == '')
 272          {
 273              // lame, but we know no better...
 274              $src_encoding = $GLOBALS['xmlrpc_internalencoding'];
 275          }
 276  
 277          switch(strtoupper($src_encoding.'_'.$dest_encoding))
 278          {
 279              case 'ISO-8859-1_':
 280              case 'ISO-8859-1_US-ASCII':
 281                  $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
 282                  $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
 283                  break;
 284              case 'ISO-8859-1_UTF-8':
 285                  $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
 286                  $escaped_data = utf8_encode($escaped_data);
 287                  break;
 288              case 'ISO-8859-1_ISO-8859-1':
 289              case 'US-ASCII_US-ASCII':
 290              case 'US-ASCII_UTF-8':
 291              case 'US-ASCII_':
 292              case 'US-ASCII_ISO-8859-1':
 293              case 'UTF-8_UTF-8':
 294                  $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
 295                  break;
 296              case 'UTF-8_':
 297              case 'UTF-8_US-ASCII':
 298              case 'UTF-8_ISO-8859-1':
 299      // NB: this will choke on invalid UTF-8, going most likely beyond EOF
 300      $escaped_data = '';
 301      // be kind to users creating string xmlrpcvals out of different php types
 302      $data = (string) $data;
 303      $ns = strlen ($data);
 304      for ($nn = 0; $nn < $ns; $nn++)
 305      {
 306          $ch = $data[$nn];
 307          $ii = ord($ch);
 308          //1 7 0bbbbbbb (127)
 309          if ($ii < 128)
 310          {
 311              /// @todo shall we replace this with a (supposedly) faster str_replace?
 312              switch($ii){
 313                  case 34:
 314                      $escaped_data .= '&quot;';
 315                      break;
 316                  case 38:
 317                      $escaped_data .= '&amp;';
 318                      break;
 319                  case 39:
 320                      $escaped_data .= '&apos;';
 321                      break;
 322                  case 60:
 323                      $escaped_data .= '&lt;';
 324                      break;
 325                  case 62:
 326                      $escaped_data .= '&gt;';
 327                      break;
 328                  default:
 329                      $escaped_data .= $ch;
 330              } // switch
 331          }
 332          //2 11 110bbbbb 10bbbbbb (2047)
 333          else if ($ii>>5 == 6)
 334          {
 335              $b1 = ($ii & 31);
 336              $ii = ord($data[$nn+1]);
 337              $b2 = ($ii & 63);
 338              $ii = ($b1 * 64) + $b2;
 339              $ent = sprintf ('&#%d;', $ii);
 340              $escaped_data .= $ent;
 341              $nn += 1;
 342          }
 343          //3 16 1110bbbb 10bbbbbb 10bbbbbb
 344          else if ($ii>>4 == 14)
 345          {
 346              $b1 = ($ii & 31);
 347              $ii = ord($data[$nn+1]);
 348              $b2 = ($ii & 63);
 349              $ii = ord($data[$nn+2]);
 350              $b3 = ($ii & 63);
 351              $ii = ((($b1 * 64) + $b2) * 64) + $b3;
 352              $ent = sprintf ('&#%d;', $ii);
 353              $escaped_data .= $ent;
 354              $nn += 2;
 355          }
 356          //4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
 357          else if ($ii>>3 == 30)
 358          {
 359              $b1 = ($ii & 31);
 360              $ii = ord($data[$nn+1]);
 361              $b2 = ($ii & 63);
 362              $ii = ord($data[$nn+2]);
 363              $b3 = ($ii & 63);
 364              $ii = ord($data[$nn+3]);
 365              $b4 = ($ii & 63);
 366              $ii = ((((($b1 * 64) + $b2) * 64) + $b3) * 64) + $b4;
 367              $ent = sprintf ('&#%d;', $ii);
 368              $escaped_data .= $ent;
 369              $nn += 3;
 370          }
 371      }
 372                  break;
 373              default:
 374                  $escaped_data = '';
 375                  error_log("Converting from $src_encoding to $dest_encoding: not supported...");
 376          }
 377          return $escaped_data;
 378      }
 379  
 380      /// xml parser handler function for opening element tags
 381  	function xmlrpc_se($parser, $name, $attrs, $accept_single_vals=false)
 382      {
 383          // if invalid xmlrpc already detected, skip all processing
 384          if ($GLOBALS['_xh']['isf'] < 2)
 385          {
 386              // check for correct element nesting
 387              // top level element can only be of 2 types
 388              /// @todo optimization creep: save this check into a bool variable, instead of using count() every time:
 389              ///       there is only a single top level element in xml anyway
 390              if (count($GLOBALS['_xh']['stack']) == 0)
 391              {
 392                  if ($name != 'METHODRESPONSE' && $name != 'METHODCALL' && (
 393                      $name != 'VALUE' && !$accept_single_vals))
 394                  {
 395                      $GLOBALS['_xh']['isf'] = 2;
 396                      $GLOBALS['_xh']['isf_reason'] = 'missing top level xmlrpc element';
 397                      return;
 398                  }
 399                  else
 400                  {
 401                      $GLOBALS['_xh']['rt'] = strtolower($name);
 402                  }
 403              }
 404              else
 405              {
 406                  // not top level element: see if parent is OK
 407                  $parent = end($GLOBALS['_xh']['stack']);
 408                  if (!array_key_exists($name, $GLOBALS['xmlrpc_valid_parents']) || !in_array($parent, $GLOBALS['xmlrpc_valid_parents'][$name]))
 409                  {
 410                      $GLOBALS['_xh']['isf'] = 2;
 411                      $GLOBALS['_xh']['isf_reason'] = "xmlrpc element $name cannot be child of $parent";
 412                      return;
 413                  }
 414              }
 415  
 416              switch($name)
 417              {
 418                  // optimize for speed switch cases: most common cases first
 419                  case 'VALUE':
 420                      /// @todo we could check for 2 VALUE elements inside a MEMBER or PARAM element
 421                      $GLOBALS['_xh']['vt']='value'; // indicator: no value found yet
 422                      $GLOBALS['_xh']['ac']='';
 423                      $GLOBALS['_xh']['lv']=1;
 424                      $GLOBALS['_xh']['php_class']=null;
 425                      break;
 426                  case 'I4':
 427                  case 'INT':
 428                  case 'STRING':
 429                  case 'BOOLEAN':
 430                  case 'DOUBLE':
 431                  case 'DATETIME.ISO8601':
 432                  case 'BASE64':
 433                      if ($GLOBALS['_xh']['vt']!='value')
 434                      {
 435                          //two data elements inside a value: an error occurred!
 436                          $GLOBALS['_xh']['isf'] = 2;
 437                          $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
 438                          return;
 439                      }
 440                      $GLOBALS['_xh']['ac']=''; // reset the accumulator
 441                      break;
 442                  case 'STRUCT':
 443                  case 'ARRAY':
 444                      if ($GLOBALS['_xh']['vt']!='value')
 445                      {
 446                          //two data elements inside a value: an error occurred!
 447                          $GLOBALS['_xh']['isf'] = 2;
 448                          $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
 449                          return;
 450                      }
 451                      // create an empty array to hold child values, and push it onto appropriate stack
 452                      $cur_val = array();
 453                      $cur_val['values'] = array();
 454                      $cur_val['type'] = $name;
 455                      // check for out-of-band information to rebuild php objs
 456                      // and in case it is found, save it
 457                      if (@isset($attrs['PHP_CLASS']))
 458                      {
 459                          $cur_val['php_class'] = $attrs['PHP_CLASS'];
 460                      }
 461                      $GLOBALS['_xh']['valuestack'][] = $cur_val;
 462                      $GLOBALS['_xh']['vt']='data'; // be prepared for a data element next
 463                      break;
 464                  case 'DATA':
 465                      if ($GLOBALS['_xh']['vt']!='data')
 466                      {
 467                          //two data elements inside a value: an error occurred!
 468                          $GLOBALS['_xh']['isf'] = 2;
 469                          $GLOBALS['_xh']['isf_reason'] = "found two data elements inside an array element";
 470                          return;
 471                      }
 472                  case 'METHODCALL':
 473                  case 'METHODRESPONSE':
 474                  case 'PARAMS':
 475                      // valid elements that add little to processing
 476                      break;
 477                  case 'METHODNAME':
 478                  case 'NAME':
 479                      /// @todo we could check for 2 NAME elements inside a MEMBER element
 480                      $GLOBALS['_xh']['ac']='';
 481                      break;
 482                  case 'FAULT':
 483                      $GLOBALS['_xh']['isf']=1;
 484                      break;
 485                  case 'MEMBER':
 486                      $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
 487                      //$GLOBALS['_xh']['ac']='';
 488                      // Drop trough intentionally
 489                  case 'PARAM':
 490                      // clear value type, so we can check later if no value has been passed for this param/member
 491                      $GLOBALS['_xh']['vt']=null;
 492                      break;
 493                  case 'NIL':
 494                      if ($GLOBALS['xmlrpc_null_extension'])
 495                      {
 496                          if ($GLOBALS['_xh']['vt']!='value')
 497                          {
 498                              //two data elements inside a value: an error occurred!
 499                              $GLOBALS['_xh']['isf'] = 2;
 500                              $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
 501                              return;
 502                          }
 503                          $GLOBALS['_xh']['ac']=''; // reset the accumulator
 504                          break;
 505                      }
 506                      // we do not support the <NIL/> extension, so
 507                      // drop through intentionally
 508                  default:
 509                      /// INVALID ELEMENT: RAISE ISF so that it is later recognized!!!
 510                      $GLOBALS['_xh']['isf'] = 2;
 511                      $GLOBALS['_xh']['isf_reason'] = "found not-xmlrpc xml element $name";
 512                      break;
 513              }
 514  
 515              // Save current element name to stack, to validate nesting
 516              $GLOBALS['_xh']['stack'][] = $name;
 517  
 518              /// @todo optimization creep: move this inside the big switch() above
 519              if($name!='VALUE')
 520              {
 521                  $GLOBALS['_xh']['lv']=0;
 522              }
 523          }
 524      }
 525  
 526      /// Used in decoding xml chunks that might represent single xmlrpc values
 527  	function xmlrpc_se_any($parser, $name, $attrs)
 528      {
 529          xmlrpc_se($parser, $name, $attrs, true);
 530      }
 531  
 532      /// xml parser handler function for close element tags
 533  	function xmlrpc_ee($parser, $name, $rebuild_xmlrpcvals = true)
 534      {
 535          if ($GLOBALS['_xh']['isf'] < 2)
 536          {
 537              // push this element name from stack
 538              // NB: if XML validates, correct opening/closing is guaranteed and
 539              // we do not have to check for $name == $curr_elem.
 540              // we also checked for proper nesting at start of elements...
 541              $curr_elem = array_pop($GLOBALS['_xh']['stack']);
 542  
 543              switch($name)
 544              {
 545                  case 'VALUE':
 546                      // This if() detects if no scalar was inside <VALUE></VALUE>
 547                      if ($GLOBALS['_xh']['vt']=='value')
 548                      {
 549                          $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
 550                          $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcString'];
 551                      }
 552  
 553                      if ($rebuild_xmlrpcvals)
 554                      {
 555                          // build the xmlrpc val out of the data received, and substitute it
 556                          $temp =& new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']);
 557                          // in case we got info about underlying php class, save it
 558                          // in the object we're rebuilding
 559                          if (isset($GLOBALS['_xh']['php_class']))
 560                              $temp->_php_class = $GLOBALS['_xh']['php_class'];
 561                          // check if we are inside an array or struct:
 562                          // if value just built is inside an array, let's move it into array on the stack
 563                          $vscount = count($GLOBALS['_xh']['valuestack']);
 564                          if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
 565                          {
 566                              $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $temp;
 567                          }
 568                          else
 569                          {
 570                              $GLOBALS['_xh']['value'] = $temp;
 571                          }
 572                      }
 573                      else
 574                      {
 575                          /// @todo this needs to treat correctly php-serialized objects,
 576                          /// since std deserializing is done by php_xmlrpc_decode,
 577                          /// which we will not be calling...
 578                          if (isset($GLOBALS['_xh']['php_class']))
 579                          {
 580                          }
 581  
 582                          // check if we are inside an array or struct:
 583                          // if value just built is inside an array, let's move it into array on the stack
 584                          $vscount = count($GLOBALS['_xh']['valuestack']);
 585                          if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
 586                          {
 587                              $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $GLOBALS['_xh']['value'];
 588                          }
 589                      }
 590                      break;
 591                  case 'BOOLEAN':
 592                  case 'I4':
 593                  case 'INT':
 594                  case 'STRING':
 595                  case 'DOUBLE':
 596                  case 'DATETIME.ISO8601':
 597                  case 'BASE64':
 598                      $GLOBALS['_xh']['vt']=strtolower($name);
 599                      /// @todo: optimization creep - remove the if/elseif cycle below
 600                      /// since the case() in which we are already did that
 601                      if ($name=='STRING')
 602                      {
 603                          $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
 604                      }
 605                      elseif ($name=='DATETIME.ISO8601')
 606                      {
 607                          if (!preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $GLOBALS['_xh']['ac']))
 608                          {
 609                              error_log('XML-RPC: invalid value received in DATETIME: '.$GLOBALS['_xh']['ac']);
 610                          }
 611                          $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcDateTime'];
 612                          $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
 613                      }
 614                      elseif ($name=='BASE64')
 615                      {
 616                          /// @todo check for failure of base64 decoding / catch warnings
 617                          $GLOBALS['_xh']['value']=base64_decode($GLOBALS['_xh']['ac']);
 618                      }
 619                      elseif ($name=='BOOLEAN')
 620                      {
 621                          // special case here: we translate boolean 1 or 0 into PHP
 622                          // constants true or false.
 623                          // Strings 'true' and 'false' are accepted, even though the
 624                          // spec never mentions them (see eg. Blogger api docs)
 625                          // NB: this simple checks helps a lot sanitizing input, ie no
 626                          // security problems around here
 627                          if ($GLOBALS['_xh']['ac']=='1' || strcasecmp($GLOBALS['_xh']['ac'], 'true') == 0)
 628                          {
 629                              $GLOBALS['_xh']['value']=true;
 630                          }
 631                          else
 632                          {
 633                              // log if receiveing something strange, even though we set the value to false anyway