]> git.mjollnir.org Git - s9y.git/commitdiff
fixing remote code execution vulnerability
authornohn <nohn>
Wed, 29 Jun 2005 13:05:28 +0000 (13:05 +0000)
committernohn <nohn>
Wed, 29 Jun 2005 13:05:28 +0000 (13:05 +0000)
bundled-libs/XML/RPC.php
bundled-libs/XML/RPC/Dump.php [new file with mode: 0644]
bundled-libs/XML/RPC/Server.php
docs/NEWS

index ad3bfc1c2e3733095bc790469b2ea620f9eb7d3d..493db1ef804c897a4a772676cb64ace0f220e630 100644 (file)
 <?php
-// /* vim: set expandtab tabstop=4 shiftwidth=4: */
-// by Edd Dumbill (C) 1999-2001
-// <edd@usefulinc.com>
-// $Id: RPC.php,v 1.2 2004/07/26 15:07:54 nohn Exp $
-
-// License is granted to use or modify this software ("XML-RPC for PHP")
-// for commercial or non-commercial use provided the copyright of the author
-// is preserved in any distributed or derivative work.
-
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR
-// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// Adapted to PEAR standards by Stig S�her Bakken <stig@php.net> and
-// Martin Jansen <mj@php.net>
-// /* $id$ */
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * PHP implementation of the XML-RPC protocol
+ *
+ * This is a PEAR-ified version of Useful inc's XML-RPC for PHP.
+ * It has support for HTTP transport, proxies and authentication.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: License is granted to use or modify this software
+ * ("XML-RPC for PHP") for commercial or non-commercial use provided the
+ * copyright of the author is preserved in any distributed or derivative work.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESSED OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill <edd@usefulinc.com>
+ * @author     Stig Bakken <stig@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    CVS: $Id: RPC.php,v 1.76 2005/06/29 12:22:29 toby Exp $
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+
 
 if (!function_exists('xml_parser_create')) {
-// Win 32 fix. From: "Leo West" <lwest@imaginet.fr>
-    if ($WINDIR) {
-        dl("php_xml.dll");
-    } else {
-        dl("xml.so");
-    }
+    PEAR::loadExtension('xml');
 }
 
-define('XML_RPC_ERROR_INVALID_TYPE',        101);
-define('XML_RPC_ERROR_NON_NUMERIC_FOUND',   102);
-define('XML_RPC_ERROR_CONNECTION_FAILED',   103);
+/**#@+
+ * Error constants
+ */
+/**
+ * Parameter values don't match parameter types
+ */
+define('XML_RPC_ERROR_INVALID_TYPE', 101);
+/**
+ * Parameter declared to be numeric but the values are not
+ */
+define('XML_RPC_ERROR_NON_NUMERIC_FOUND', 102);
+/**
+ * Communication error
+ */
+define('XML_RPC_ERROR_CONNECTION_FAILED', 103);
+/**
+ * The array or struct has already been started
+ */
 define('XML_RPC_ERROR_ALREADY_INITIALIZED', 104);
+/**
+ * Incorrect parameters submitted
+ */
+define('XML_RPC_ERROR_INCORRECT_PARAMS', 105);
+/**
+ * Programming error by developer
+ */
+define('XML_RPC_ERROR_PROGRAMMING', 106);
+/**#@-*/
 
-$GLOBALS['XML_RPC_I4'] = "i4";
-$GLOBALS['XML_RPC_Int'] = "int";
-$GLOBALS['XML_RPC_Boolean'] = "boolean";
-$GLOBALS['XML_RPC_Double'] = "double";
-$GLOBALS['XML_RPC_String'] = "string";
-$GLOBALS['XML_RPC_DateTime'] = "dateTime.iso8601";
-$GLOBALS['XML_RPC_Base64'] = "base64";
-$GLOBALS['XML_RPC_Array'] = "array";
-$GLOBALS['XML_RPC_Struct'] = "struct";
-
-$GLOBALS['XML_RPC_Types'] = array($GLOBALS['XML_RPC_I4'] => 1,
-                                $GLOBALS['XML_RPC_Int'] => 1,
-                                $GLOBALS['XML_RPC_Boolean'] => 1,
-                                $GLOBALS['XML_RPC_String'] => 1,
-                                $GLOBALS['XML_RPC_Double'] => 1,
-                                $GLOBALS['XML_RPC_DateTime'] => 1,
-                                $GLOBALS['XML_RPC_Base64'] => 1,
-                                $GLOBALS['XML_RPC_Array'] => 2,
-                                $GLOBALS['XML_RPC_Struct'] => 3);
-
-$GLOBALS['XML_RPC_entities'] = array("quot" => '"',
-                                     "amp" => "&",
-                                     "lt" => "<",
-                                     "gt" => ">",
-                                     "apos" => "'");
-
-$GLOBALS['XML_RPC_err']["unknown_method"] = 1;
-$GLOBALS['XML_RPC_str']["unknown_method"] = "Unknown method";
-$GLOBALS['XML_RPC_err']["invalid_return"] = 2;
-$GLOBALS['XML_RPC_str']["invalid_return"] = "Invalid return payload: enabling debugging to examine incoming payload";
-$GLOBALS['XML_RPC_err']["incorrect_params"] = 3;
-$GLOBALS['XML_RPC_str']["incorrect_params"] = "Incorrect parameters passed to method";
-$GLOBALS['XML_RPC_err']["introspect_unknown"] = 4;
-$GLOBALS['XML_RPC_str']["introspect_unknown"] = "Can't introspect: method unknown";
-$GLOBALS['XML_RPC_err']["http_error"] = 5;
-$GLOBALS['XML_RPC_str']["http_error"] = "Didn't receive 200 OK from remote server.";
-
-$GLOBALS['XML_RPC_defencoding'] = "UTF-8";
-
-// let user errors start at 800
-$GLOBALS['XML_RPC_erruser'] = 800;
 
-// let XML parse errors start at 100
-$GLOBALS['XML_RPC_errxml'] = 100;
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_I4']
+ */
+$GLOBALS['XML_RPC_I4'] = 'i4';
 
-// formulate backslashes for escaping regexp
-$GLOBALS['XML_RPC_backslash'] = chr(92) . chr(92);
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Int']
+ */
+$GLOBALS['XML_RPC_Int'] = 'int';
 
-$GLOBALS['XML_RPC_twoslash'] = $GLOBALS['XML_RPC_backslash'] . $GLOBALS['XML_RPC_backslash'];
-$GLOBALS['XML_RPC_twoslash'] = "2SLS";
-
-// used to store state during parsing
-// quick explanation of components:
-//   st - used to build up a string for evaluation
-//   ac - used to accumulate values
-//   qt - used to decide if quotes are needed for evaluation
-//   cm - used to denote struct or array (comma needed)
-//   isf - used to indicate a fault
-//   lv - used to indicate "looking for a value": implements
-//        the logic to allow values with no types to be strings
-//   params - used to store parameters in method calls
-//   method - used to store method name
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Boolean']
+ */
+$GLOBALS['XML_RPC_Boolean'] = 'boolean';
 
-$GLOBALS['XML_RPC_xh'] = array();
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Double']
+ */
+$GLOBALS['XML_RPC_Double'] = 'double';
 
-function XML_RPC_entity_decode($string)
-{
-    $top = split("&", $string);
-    $op = "";
-    $i = 0;
-    while($i < sizeof($top)) {
-        if (ereg("^([#a-zA-Z0-9]+);", $top[$i], $regs)) {
-            $op .= ereg_replace("^[#a-zA-Z0-9]+;",
-                                XML_RPC_lookup_entity($regs[1]),
-                                $top[$i]);
-        } else {
-            if ($i == 0) {
-                $op = $top[$i];
-            } else {
-                $op .= "&" . $top[$i];
-            }
-        }
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_String']
+ */
+$GLOBALS['XML_RPC_String'] = 'string';
 
-        $i++;
-    }
-    return $op;
-}
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_DateTime']
+ */
+$GLOBALS['XML_RPC_DateTime'] = 'dateTime.iso8601';
 
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Base64']
+ */
+$GLOBALS['XML_RPC_Base64'] = 'base64';
 
-function XML_RPC_lookup_entity($ent)
-{
-    global $XML_RPC_entities;
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Array']
+ */
+$GLOBALS['XML_RPC_Array'] = 'array';
 
-    if ($XML_RPC_entities[strtolower($ent)]) {
-        return $XML_RPC_entities[strtolower($ent)];
-    }
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Struct']
+ */
+$GLOBALS['XML_RPC_Struct'] = 'struct';
 
-    if (ereg("^#([0-9]+)$", $ent, $regs)) {
-        return chr($regs[1]);
-    }
 
-    return "?";
-}
+/**
+ * Data type meta-types
+ * @global array $GLOBALS['XML_RPC_Types']
+ */
+$GLOBALS['XML_RPC_Types'] = array(
+    $GLOBALS['XML_RPC_I4']       => 1,
+    $GLOBALS['XML_RPC_Int']      => 1,
+    $GLOBALS['XML_RPC_Boolean']  => 1,
+    $GLOBALS['XML_RPC_String']   => 1,
+    $GLOBALS['XML_RPC_Double']   => 1,
+    $GLOBALS['XML_RPC_DateTime'] => 1,
+    $GLOBALS['XML_RPC_Base64']   => 1,
+    $GLOBALS['XML_RPC_Array']    => 2,
+    $GLOBALS['XML_RPC_Struct']   => 3,
+);
+
+
+/**
+ * Error message numbers
+ * @global array $GLOBALS['XML_RPC_err']
+ */
+$GLOBALS['XML_RPC_err'] = array(
+    'unknown_method'      => 1,
+    'invalid_return'      => 2,
+    'incorrect_params'    => 3,
+    'introspect_unknown'  => 4,
+    'http_error'          => 5,
+    'not_response_object' => 6,
+);
+
+/**
+ * Error message strings
+ * @global array $GLOBALS['XML_RPC_str']
+ */
+$GLOBALS['XML_RPC_str'] = array(
+    'unknown_method'      => 'Unknown method',
+    'invalid_return'      => 'Invalid return payload: enable debugging to examine incoming payload',
+    'incorrect_params'    => 'Incorrect parameters passed to method',
+    'introspect_unknown'  => 'Can\'t introspect: method unknown',
+    'http_error'          => 'Didn\'t receive 200 OK from remote server.',
+    'not_response_object' => 'The requested method didn\'t return an XML_RPC_Response object.',
+);
+
 
+/**
+ * Default XML encoding (ISO-8859-1, UTF-8 or US-ASCII)
+ * @global string $GLOBALS['XML_RPC_defencoding']
+ */
+$GLOBALS['XML_RPC_defencoding'] = 'UTF-8';
+
+/**
+ * User error codes start at 800
+ * @global int $GLOBALS['XML_RPC_erruser']
+ */
+$GLOBALS['XML_RPC_erruser'] = 800;
 
-function XML_RPC_se($parser, $name, $attrs)
+/**
+ * XML parse error codes start at 100
+ * @global int $GLOBALS['XML_RPC_errxml']
+ */
+$GLOBALS['XML_RPC_errxml'] = 100;
+
+
+/**
+ * Compose backslashes for escaping regexp
+ * @global string $GLOBALS['XML_RPC_backslash']
+ */
+$GLOBALS['XML_RPC_backslash'] = chr(92) . chr(92);
+
+
+/**
+ * Stores state during parsing
+ *
+ * quick explanation of components:
+ *   + st     = builds up a string for evaluation
+ *   + ac     = accumulates values
+ *   + qt     = decides if quotes are needed for evaluation
+ *   + cm     = denotes struct or array (comma needed)
+ *   + isf    = indicates a fault
+ *   + lv     = indicates "looking for a value": implements the logic
+ *               to allow values with no types to be strings
+ *   + params = stores parameters in method calls
+ *   + method = stores method name
+ *
+ * @global array $GLOBALS['XML_RPC_xh']
+ */
+$GLOBALS['XML_RPC_xh'] = array();
+
+
+/**
+ * Start element handler for the XML parser
+ *
+ * @return void
+ */
+function XML_RPC_se($parser_resource, $name, $attrs)
 {
     global $XML_RPC_xh, $XML_RPC_DateTime, $XML_RPC_String;
+    $parser = (int) $parser_resource;
 
     switch ($name) {
-    case "STRUCT":
-    case "ARRAY":
-        $XML_RPC_xh[$parser]['st'] .= "array(";
+    case 'STRUCT':
+    case 'ARRAY':
+        $XML_RPC_xh[$parser]['st'] .= 'array(';
         $XML_RPC_xh[$parser]['cm']++;
         // this last line turns quoting off
         // this means if we get an empty array we'll
@@ -157,47 +236,47 @@ function XML_RPC_se($parser, $name, $attrs)
         $XML_RPC_xh[$parser]['qt'] = 0;
         break;
 
-    case "NAME":
-        $XML_RPC_xh[$parser]['st'] .= "'";
-        $XML_RPC_xh[$parser]['ac'] = "";
+    case 'NAME':
+        $XML_RPC_xh[$parser]['st'] .= '"';
+        $XML_RPC_xh[$parser]['ac'] = '';
         break;
 
-    case "FAULT":
+    case 'FAULT':
         $XML_RPC_xh[$parser]['isf'] = 1;
         break;
 
-    case "PARAM":
-        $XML_RPC_xh[$parser]['st'] = "";
+    case 'PARAM':
+        $XML_RPC_xh[$parser]['st'] = '';
         break;
 
-    case "VALUE":
-        $XML_RPC_xh[$parser]['st'] .= "new XML_RPC_Value(";
+    case 'VALUE':
+        $XML_RPC_xh[$parser]['st'] .= 'new XML_RPC_Value(';
         $XML_RPC_xh[$parser]['lv'] = 1;
         $XML_RPC_xh[$parser]['vt'] = $XML_RPC_String;
-        $XML_RPC_xh[$parser]['ac'] = "";
+        $XML_RPC_xh[$parser]['ac'] = '';
         $XML_RPC_xh[$parser]['qt'] = 0;
         // look for a value: if this is still 1 by the
         // time we reach the first data segment then the type is string
         // by implication and we need to add in a quote
         break;
 
-    case "I4":
-    case "INT":
-    case "STRING":
-    case "BOOLEAN":
-    case "DOUBLE":
-    case "DATETIME.ISO8601":
-    case "BASE64":
-        $XML_RPC_xh[$parser]['ac'] = ""; // reset the accumulator
+    case 'I4':
+    case 'INT':
+    case 'STRING':
+    case 'BOOLEAN':
+    case 'DOUBLE':
+    case 'DATETIME.ISO8601':
+    case 'BASE64':
+        $XML_RPC_xh[$parser]['ac'] = ''; // reset the accumulator
 
-        if ($name == "DATETIME.ISO8601" || $name == "STRING") {
+        if ($name == 'DATETIME.ISO8601' || $name == 'STRING') {
             $XML_RPC_xh[$parser]['qt'] = 1;
 
-            if ($name == "DATETIME.ISO8601") {
+            if ($name == 'DATETIME.ISO8601') {
                 $XML_RPC_xh[$parser]['vt'] = $XML_RPC_DateTime;
             }
 
-        } elseif ($name == "BASE64") {
+        } elseif ($name == 'BASE64') {
             $XML_RPC_xh[$parser]['qt'] = 2;
         } else {
             // No quoting is required here -- but
@@ -207,88 +286,93 @@ function XML_RPC_se($parser, $name, $attrs)
         }
         break;
 
-    case "MEMBER":
-        $XML_RPC_xh[$parser]['ac'] = "";
-        break;
-
-    default:
-        break;
+    case 'MEMBER':
+        $XML_RPC_xh[$parser]['ac'] = '';
     }
 
-    if ($name!="VALUE") {
+    if ($name != 'VALUE') {
         $XML_RPC_xh[$parser]['lv'] = 0;
     }
 }
 
-
-function XML_RPC_ee($parser, $name)
+/**
+ * End element handler for the XML parser
+ *
+ * @return void
+ */
+function XML_RPC_ee($parser_resource, $name)
 {
-    global $XML_RPC_xh,$XML_RPC_Types,$XML_RPC_String;
+    global $XML_RPC_xh, $XML_RPC_Types, $XML_RPC_String;
+    $parser = (int) $parser_resource;
 
     switch ($name) {
-    case "STRUCT":
-    case "ARRAY":
-        if ($XML_RPC_xh[$parser]['cm'] && substr($XML_RPC_xh[$parser]['st'], -1) == ',') {
-            $XML_RPC_xh[$parser]['st'] = substr($XML_RPC_xh[$parser]['st'],0,-1);
+    case 'STRUCT':
+    case 'ARRAY':
+        if ($XML_RPC_xh[$parser]['cm']
+            && substr($XML_RPC_xh[$parser]['st'], -1) == ',')
+        {
+            $XML_RPC_xh[$parser]['st'] = substr($XML_RPC_xh[$parser]['st'], 0, -1);
         }
 
-        $XML_RPC_xh[$parser]['st'] .= ")";
+        $XML_RPC_xh[$parser]['st'] .= ')';
         $XML_RPC_xh[$parser]['vt'] = strtolower($name);
         $XML_RPC_xh[$parser]['cm']--;
         break;
 
-    case "NAME":
-        $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'] . "' => ";
+    case 'NAME':
+        $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'] . '" => ';
         break;
 
-    case "BOOLEAN":
+    case 'BOOLEAN':
         // special case here: we translate boolean 1 or 0 into PHP
         // constants true or false
         if ($XML_RPC_xh[$parser]['ac'] == '1') {
-            $XML_RPC_xh[$parser]['ac'] = "true";
+            $XML_RPC_xh[$parser]['ac'] = 'true';
         } else {
-            $XML_RPC_xh[$parser]['ac'] = "false";
+            $XML_RPC_xh[$parser]['ac'] = 'false';
         }
 
         $XML_RPC_xh[$parser]['vt'] = strtolower($name);
         // Drop through intentionally.
 
-    case "I4":
-    case "INT":
-    case "STRING":
-    case "DOUBLE":
-    case "DATETIME.ISO8601":
-    case "BASE64":
+    case 'I4':
+    case 'INT':
+    case 'STRING':
+    case 'DOUBLE':
+    case 'DATETIME.ISO8601':
+    case 'BASE64':
         if ($XML_RPC_xh[$parser]['qt'] == 1) {
             // we use double quotes rather than single so backslashification works OK
-            $XML_RPC_xh[$parser]['st'] .= "\"" . $XML_RPC_xh[$parser]['ac'] . "\"";
+            $XML_RPC_xh[$parser]['st'] .= '"' . $XML_RPC_xh[$parser]['ac'] . '"';
         } elseif ($XML_RPC_xh[$parser]['qt'] == 2) {
-            $XML_RPC_xh[$parser]['st'] .= "base64_decode('" . $XML_RPC_xh[$parser]['ac'] . "')";
-        } elseif ($name=="BOOLEAN") {
+            $XML_RPC_xh[$parser]['st'] .= 'base64_decode("'
+                                        . $XML_RPC_xh[$parser]['ac'] . '")';
+        } elseif ($name == 'BOOLEAN') {
             $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'];
         } else {
             // we have an I4, INT or a DOUBLE
             // we must check that only 0123456789-.<space> are characters here
-            if (!ereg("^\-?[0123456789 \t\.]+$", $XML_RPC_xh[$parser]['ac'])) {
-                PEAR::raiseError("Non-numeric value received in INT or DOUBLE", XML_RPC_ERROR_NON_NUMERIC_FOUND);
-                $XML_RPC_xh[$parser]['st'] .= "ERROR_NON_NUMERIC_FOUND";
+            if (!ereg("^[+-]?[0123456789 \t\.]+$", $XML_RPC_xh[$parser]['ac'])) {
+                XML_RPC_Base::raiseError('Non-numeric value received in INT or DOUBLE',
+                                         XML_RPC_ERROR_NON_NUMERIC_FOUND);
+                $XML_RPC_xh[$parser]['st'] .= 'XML_RPC_ERROR_NON_NUMERIC_FOUND';
             } else {
                 // it's ok, add it on
                 $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'];
             }
         }
 
-        $XML_RPC_xh[$parser]['ac'] = "";
+        $XML_RPC_xh[$parser]['ac'] = '';
         $XML_RPC_xh[$parser]['qt'] = 0;
         $XML_RPC_xh[$parser]['lv'] = 3; // indicate we've found a value
         break;
 
-    case "VALUE":
+    case 'VALUE':
         // deal with a string value
         if (strlen($XML_RPC_xh[$parser]['ac']) > 0 &&
             $XML_RPC_xh[$parser]['vt'] == $XML_RPC_String) {
 
-            $XML_RPC_xh[$parser]['st'] .= "\"" . $XML_RPC_xh[$parser]['ac'] . "\"";
+            $XML_RPC_xh[$parser]['st'] .= '"' . $XML_RPC_xh[$parser]['ac'] . '"';
         }
 
         // This if () detects if no scalar was inside <VALUE></VALUE>
@@ -298,41 +382,28 @@ function XML_RPC_ee($parser, $name)
         }
         $XML_RPC_xh[$parser]['st'] .= ", '" . $XML_RPC_xh[$parser]['vt'] . "')";
         if ($XML_RPC_xh[$parser]['cm']) {
-            $XML_RPC_xh[$parser]['st'] .= ",";
+            $XML_RPC_xh[$parser]['st'] .= ',';
         }
         break;
 
-    case "MEMBER":
-        $XML_RPC_xh[$parser]['ac'] = "";
+    case 'MEMBER':
+        $XML_RPC_xh[$parser]['ac'] = '';
         $XML_RPC_xh[$parser]['qt'] = 0;
         break;
 
-    case "DATA":
-        $XML_RPC_xh[$parser]['ac'] = "";
+    case 'DATA':
+        $XML_RPC_xh[$parser]['ac'] = '';
         $XML_RPC_xh[$parser]['qt'] = 0;
         break;
 
-    case "PARAM":
+    case 'PARAM':
         $XML_RPC_xh[$parser]['params'][] = $XML_RPC_xh[$parser]['st'];
         break;
 
-    case "METHODNAME":
-        $XML_RPC_xh[$parser]['method'] = ereg_replace("^[\n\r\t ]+", "", $XML_RPC_xh[$parser]['ac']);
-        break;
-
-    case "BOOLEAN":
-        // special case here: we translate boolean 1 or 0 into PHP
-        // constants true or false
-        if ($XML_RPC_xh[$parser]['ac'] == '1') {
-            $XML_RPC_xh[$parser]['ac'] = "true";
-        } else {
-            $XML_RPC_xh[$parser]['ac'] = "false";
-        }
-
-        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
-        break;
-
-    default:
+    case 'METHODNAME':
+    case 'RPCMETHODNAME':
+        $XML_RPC_xh[$parser]['method'] = ereg_replace("^[\n\r\t ]+", '',
+                                                      $XML_RPC_xh[$parser]['ac']);
         break;
     }
 
@@ -342,10 +413,15 @@ function XML_RPC_ee($parser, $name)
     }
 }
 
-
-function XML_RPC_cd($parser, $data)
+/**
+ * Character data handler for the XML parser
+ *
+ * @return void
+ */
+function XML_RPC_cd($parser_resource, $data)
 {
     global $XML_RPC_xh, $XML_RPC_backslash;
+    $parser = (int) $parser_resource;
 
     if ($XML_RPC_xh[$parser]['lv'] != 3) {
         // "lookforvalue==3" means that we've found an entire value
@@ -370,58 +446,263 @@ function XML_RPC_cd($parser, $data)
     }
 }
 
-
-function XML_RPC_dh($parser, $data)
-{
-    global $XML_RPC_xh;
-
-    if (substr($data, 0, 1) == "&" && substr($data, -1, 1) == ";") {
-        if ($XML_RPC_xh[$parser]['lv'] == 1) {
-            $XML_RPC_xh[$parser]['qt'] = 1;
-            $XML_RPC_xh[$parser]['lv'] = 2;
-        }
-        $XML_RPC_xh[$parser]['ac'] .= str_replace('$', '\$',
-            str_replace('"', '\"', str_replace(chr(92),
-                $XML_RPC_backslash, $data)));
-    }
-}
-
 /**
- * Base class
+ * The common methods and properties for all of the XML_RPC classes
  *
- * This class provides common functions for all of the XML_RPC classes.
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill <edd@usefulinc.com>
+ * @author     Stig Bakken <stig@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/XML_RPC
  */
 class XML_RPC_Base {
+
+    /**
+     * PEAR Error handling
+     *
+     * @return object  PEAR_Error object
+     */
     function raiseError($msg, $code)
     {
         include_once 'PEAR.php';
-        PEAR::raiseError(get_class($this) . ": " . $msg, $code);
+        if (is_object(@$this)) {
+            return PEAR::raiseError(get_class($this) . ': ' . $msg, $code);
+        } else {
+            return PEAR::raiseError('XML_RPC: ' . $msg, $code);
+        }
+    }
+
+    /**
+     * Tell whether something is a PEAR_Error object
+     *
+     * @param mixed $value  the item to check
+     *
+     * @return bool  whether $value is a PEAR_Error object or not
+     *
+     * @access public
+     */
+    function isError($value)
+    {
+        return is_a($value, 'PEAR_Error');
     }
 }
 
+/**
+ * The methods and properties for submitting XML RPC requests
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill <edd@usefulinc.com>
+ * @author     Stig Bakken <stig@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/XML_RPC
+ */
 class XML_RPC_Client extends XML_RPC_Base {
-    var $path;
-    var $server;
-    var $port;
-    var $errno;
-    var $errstring;
+
+    /**
+     * The path and name of the RPC server script you want the request to go to
+     * @var string
+     */
+    var $path = '';
+
+    /**
+     * The name of the remote server to connect to
+     * @var string
+     */
+    var $server = '';
+
+    /**
+     * The protocol to use in contacting the remote server
+     * @var string
+     */
+    var $protocol = 'http://';
+
+    /**
+     * The port for connecting to the remote server
+     *
+     * The default is 80 for http:// connections
+     * and 443 for https:// and ssl:// connections.
+     *
+     * @var integer
+     */
+    var $port = 80;
+
+    /**
+     * A user name for accessing the RPC server
+     * @var string
+     * @see XML_RPC_Client::setCredentials()
+     */
+    var $username = '';
+
+    /**
+     * A password for accessing the RPC server
+     * @var string
+     * @see XML_RPC_Client::setCredentials()
+     */
+    var $password = '';
+
+    /**
+     * The name of the proxy server to use, if any
+     * @var string
+     */
+    var $proxy = '';
+
+    /**
+     * The protocol to use in contacting the proxy server, if any
+     * @var string
+     */
+    var $proxy_protocol = 'http://';
+
+    /**
+     * The port for connecting to the proxy server
+     *
+     * The default is 8080 for http:// connections
+     * and 443 for https:// and ssl:// connections.
+     *
+     * @var integer
+     */
+    var $proxy_port = 8080;
+
+    /**
+     * A user name for accessing the proxy server
+     * @var string
+     */
+    var $proxy_user = '';
+
+    /**
+     * A password for accessing the proxy server
+     * @var string
+     */
+    var $proxy_pass = '';
+
+    /**
+     * The error number, if any
+     * @var integer
+     */
+    var $errno = 0;
+
+    /**
+     * The error message, if any
+     * @var string
+     */
+    var $errstr = '';
+
+    /**
+     * The current debug mode (1 = on, 0 = off)
+     * @var integer
+     */
     var $debug = 0;
-    var $username = "";
-    var $password = "";
 
-    function XML_RPC_Client($path, $server, $port = 80,
-                            $proxy = '', $proxy_port = 8080,
+    /**
+     * The HTTP headers for the current request.
+     * @var string
+     */
+    var $headers = '';
+
+
+    /**
+     * Sets the object's properties
+     *
+     * @param string  $path        the path and name of the RPC server script
+     *                              you want the request to go to
+     * @param string  $server      the URL of the remote server to connect to.
+     *                              If this parameter doesn't specify a
+     *                              protocol and $port is 443, ssl:// is
+     *                              assumed.
+     * @param integer $port        a port for connecting to the remote server.
+     *                              Defaults to 80 for http:// connections and
+     *                              443 for https:// and ssl:// connections.
+     * @param string  $proxy       the URL of the proxy server to use, if any.
+     *                              If this parameter doesn't specify a
+     *                              protocol and $port is 443, ssl:// is
+     *                              assumed.
+     * @param integer $proxy_port  a port for connecting to the remote server.
+     *                              Defaults to 8080 for http:// connections and
+     *                              443 for https:// and ssl:// connections.
+     * @param string  $proxy_user  a user name for accessing the proxy server
+     * @param string  $proxy_pass  a password for accessing the proxy server
+     *
+     * @return void
+     */
+    function XML_RPC_Client($path, $server, $port = 0,
+                            $proxy = '', $proxy_port = 0,
                             $proxy_user = '', $proxy_pass = '')
     {
-        $this->port = $port;
-        $this->server = $server;
-        $this->path = $path;
-        $this->proxy = $proxy;
-        $this->proxy_port = $proxy_port;
+        $this->path       = $path;
         $this->proxy_user = $proxy_user;
         $this->proxy_pass = $proxy_pass;
+
+        preg_match('@^(http://|https://|ssl://)?(.*)$@', $server, $match);
+        if ($match[1] == '') {
+            if ($port == 443) {
+                $this->server   = $match[2];
+                $this->protocol = 'ssl://';
+                $this->port     = 443;
+            } else {
+                $this->server = $match[2];
+                if ($port) {
+                    $this->port = $port;
+                }
+            }
+        } elseif ($match[1] == 'http://') {
+            $this->server = $match[2];
+            if ($port) {
+                $this->port = $port;
+            }
+        } else {
+            $this->server   = $match[2];
+            $this->protocol = 'ssl://';
+            if ($port) {
+                $this->port = $port;
+            } else {
+                $this->port = 443;
+            }
+        }
+
+        if ($proxy) {
+            preg_match('@^(http://|https://|ssl://)?(.*)$@', $proxy, $match);
+            if ($match[1] == '') {
+                if ($proxy_port == 443) {
+                    $this->proxy          = $match[2];
+                    $this->proxy_protocol = 'ssl://';
+                    $this->proxy_port     = 443;
+                } else {
+                    $this->proxy = $match[2];
+                    if ($proxy_port) {
+                        $this->proxy_port = $proxy_port;
+                    }
+                }
+            } elseif ($match[1] == 'http://') {
+                $this->proxy = $match[2];
+                if ($proxy_port) {
+                    $this->proxy_port = $proxy_port;
+                }
+            } else {
+                $this->proxy          = $match[2];
+                $this->proxy_protocol = 'ssl://';
+                if ($proxy_port) {
+                    $this->proxy_port = $proxy_port;
+                } else {
+                    $this->proxy_port = 443;
+                }
+            }
+        }
     }
 
+    /**
+     * Change the current debug mode
+     *
+     * @param int $in  where 1 = on, 0 = off
+     *
+     * @return void
+     */
     function setDebug($in)
     {
         if ($in) {
@@ -431,96 +712,216 @@ class XML_RPC_Client extends XML_RPC_Base {
         }
     }
 
+    /**
+     * Set username and password properties for connecting to the RPC server
+     *
+     * @param string $u  the user name
+     * @param string $p  the password
+     *
+     * @return void
+     *
+     * @see XML_RPC_Client::$username, XML_RPC_Client::$password
+     */
     function setCredentials($u, $p)
     {
         $this->username = $u;
         $this->password = $p;
     }
 
+    /**
+     * Transmit the RPC request via HTTP 1.0 protocol
+     *
+     * @param object $msg       the XML_RPC_Message object
+     * @param int    $timeout   how many seconds to wait for the request
+     *
+     * @return object  an XML_RPC_Response object.  0 is returned if any
+     *                  problems happen.
+     *
+     * @see XML_RPC_Message, XML_RPC_Client::XML_RPC_Client(),
+     *      XML_RPC_Client::setCredentials()
+     */
     function send($msg, $timeout = 0)
     {
-        // where msg is an xmlrpcmsg
+        if (strtolower(get_class($msg)) != 'xml_rpc_message') {
+            $this->errstr = 'send()\'s $msg parameter must be an'
+                          . ' XML_RPC_Message object.';
+            $this->raiseError($this->errstr, XML_RPC_ERROR_PROGRAMMING);
+            return 0;
+        }
         $msg->debug = $this->debug;
         return $this->sendPayloadHTTP10($msg, $this->server, $this->port,
                                         $timeout, $this->username,
                                         $this->password);
     }
 
-    function sendPayloadHTTP10($msg, $server, $port, $timeout=0,
-                               $username = "", $password = "")
+    /**
+     * Transmit the RPC request via HTTP 1.0 protocol
+     *
+     * Requests should be sent using XML_RPC_Client send() rather than
+     * calling this method directly.
+     *
+     * @param object $msg       the XML_RPC_Message object
+     * @param string $server    the server to send the request to
+     * @param int    $port      the server port send the request to
+     * @param int    $timeout   how many seconds to wait for the request
+     *                           before giving up
+     * @param string $username  a user name for accessing the RPC server
+     * @param string $password  a password for accessing the RPC server
+     *
+     * @return object  an XML_RPC_Response object.  0 is returned if any
+     *                  problems happen.
+     *
+     * @access protected
+     * @see XML_RPC_Client::send()
+     */
+    function sendPayloadHTTP10($msg, $server, $port, $timeout = 0,
+                               $username = '', $password = '')
     {
-        // If we're using a proxy open a socket to the proxy server instead to the xml-rpc server
-        if ($this->proxy){
+        /*
+         * If we're using a proxy open a socket to the proxy server
+         * instead to the xml-rpc server
+         */
+        if ($this->proxy) {
+            if ($this->proxy_protocol == 'http://') {
+                $protocol = '';
+            } else {
+                $protocol = $this->proxy_protocol;
+            }
             if ($timeout > 0) {
-                $fp = fsockopen($this->proxy, $this->proxy_port, $this->errno, $this->errstr, $timeout);
+                $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port,
+                                 $this->errno, $this->errstr, $timeout);
             } else {
-                $fp = fsockopen($this->proxy, $this->proxy_port, $this->errno, $this->errstr);
+                $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port,
+                                 $this->errno, $this->errstr);
             }
         } else {
+            if ($this->protocol == 'http://') {
+                $protocol = '';
+            } else {
+                $protocol = $this->protocol;
+            }
             if ($timeout > 0) {
-                $fp = fsockopen($server, $port, $this->errno, $this->errstr, $timeout);
+                $fp = @fsockopen($protocol . $server, $port,
+                                 $this->errno, $this->errstr, $timeout);
             } else {
-                $fp = fsockopen($server, $port, $this->errno, $this->errstr);
+                $fp = @fsockopen($protocol . $server, $port,
+                                 $this->errno, $this->errstr);
             }
         }
 
+        /*
+         * Just raising the error without returning it is strange,
+         * but keep it here for backwards compatibility.
+         */
         if (!$fp && $this->proxy) {
-            $this->raiseError(
-                "Connection to proxy server " . $this->proxy . ":" . $this->proxy_port . " failed",
-                XML_RPC_ERROR_CONNECTION_FAILED);
+            $this->raiseError('Connection to proxy server '
+                              . $this->proxy . ':' . $this->proxy_port
+                              . ' failed. ' . $this->errstr,
+                              XML_RPC_ERROR_CONNECTION_FAILED);
+            return 0;
         } elseif (!$fp) {
-            $this->raiseError(
-                "Connection to RPC server " . $this->server . " failed",
-                XML_RPC_ERROR_CONNECTION_FAILED);
+            $this->raiseError('Connection to RPC server '
+                              . $server . ':' . $port
+                              . ' failed. ' . $this->errstr,
+                              XML_RPC_ERROR_CONNECTION_FAILED);
+            return 0;
+        }
+
+        if ($timeout) {
+            stream_set_timeout($fp, $timeout);
+        }
+
+        // Pre-emptive BC hacks for fools calling sendPayloadHTTP10() directly
+        if ($username != $this->username) {
+            $this->setCredentials($username, $password);
         }
 
         // Only create the payload if it was not created previously
         if (empty($msg->payload)) {
             $msg->createPayload();
         }
+        $this->createHeaders($msg);
 
-        // thanks to Grant Rauscher <grant7@firstworld.net> for this
-        $credentials = "";
-        if ($username != "") {
-            $credentials = "Authorization: Basic " .
-                base64_encode($username . ":" . $password) . "\r\n";
+        $op  = $this->headers . "\r\n\r\n";
+        $op .= $msg->payload;
+
+        if (!fputs($fp, $op, strlen($op))) {
+            $this->errstr = 'Write error';
+            return 0;
         }
+        $resp = $msg->parseResponseFile($fp);
 
+        $meta = stream_get_meta_data($fp);
+        if ($meta['timed_out']) {
+            fclose($fp);
+            $this->errstr = 'RPC server did not send response before timeout.';
+            $this->raiseError($this->errstr, XML_RPC_ERROR_CONNECTION_FAILED);
+            return 0;
+        }
 
-        if ($this->proxy) {
-            $op = "POST http://" . $this->server;
+        fclose($fp);
+        return $resp;
+    }
 
+    /**
+     * Determines the HTTP headers and puts it in the $headers property
+     *
+     * @param object $msg       the XML_RPC_Message object
+     *
+     * @return boolean  TRUE if okay, FALSE if the message payload isn't set.
+     *
+     * @access protected
+     */
+    function createHeaders($msg)
+    {
+        if (empty($msg->payload)) {
+            return false;
+        }
+        if ($this->proxy) {
+            $this->headers = 'POST ' . $this->protocol . $this->server;
             if ($this->proxy_port) {
-                $op .= ":" . $this->port;
+                $this->headers .= ':' . $this->port;
             }
         } else {
-           $op = "POST ";
+           $this->headers = 'POST ';
         }
-
-        $op .= $this->path. " HTTP/1.0\r\n" .
-               "User-Agent: PEAR XML_RPC\r\n" .
-               "Host: " . $this->server . "\r\n";
-        if ($this->proxy && $this->proxy_user != '') {
-            $op .= 'Proxy-Authorization: Basic ' .
-                base64_encode($this->proxy_user . ':' . $this->proxy_pass) .
-                "\r\n";
+        $this->headers .= $this->path. " HTTP/1.0\r\n";
+        
+        $this->headers .= "User-Agent: PEAR XML_RPC\r\n";
+        $this->headers .= 'Host: ' . $this->server . "\r\n";
+
+        if ($this->proxy && $this->proxy_user) {
+            $this->headers .= 'Proxy-Authorization: Basic '
+                     . base64_encode("$this->proxy_user:$this->proxy_pass")
+                     . "\r\n";
         }
-        $op .= $credentials .
-               "Content-Type: text/xml\r\n" .
-               "Content-Length: " . strlen($msg->payload) . "\r\n\r\n" .
-               $msg->payload;
 
-        if (!fputs($fp, $op, strlen($op))) {
-            $this->errstr = "Write error";
-            return 0;
+        // thanks to Grant Rauscher <grant7@firstworld.net> for this
+        if ($this->username) {
+            $this->headers .= 'Authorization: Basic '
+                     . base64_encode("$this->username:$this->password")
+                     . "\r\n";
         }
-        $resp = $msg->parseResponseFile($fp);
-        fclose($fp);
-        return $resp;
+
+        $this->headers .= "Content-Type: text/xml\r\n";
+        $this->headers .= 'Content-Length: ' . strlen($msg->payload);
+        return true;
     }
 }
 
-
+/**
+ * The methods and properties for interpreting responses to XML RPC requests
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill <edd@usefulinc.com>
+ * @author     Stig Bakken <stig@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/XML_RPC
+ */
 class XML_RPC_Response extends XML_RPC_Base
 {
     var $xv;
@@ -528,7 +929,10 @@ class XML_RPC_Response extends XML_RPC_Base
     var $fs;
     var $hdrs;
 
-    function XML_RPC_Response($val, $fcode = 0, $fstr = "")
+    /**
+     * @return void
+     */
+    function XML_RPC_Response($val, $fcode = 0, $fstr = '')
     {
         if ($fcode != 0) {
             $this->fn = $fcode;
@@ -538,6 +942,9 @@ class XML_RPC_Response extends XML_RPC_Base
         }
     }
 
+    /**
+     * @return int  the error code
+     */
     function faultCode()
     {
         if (isset($this->fn)) {
@@ -547,16 +954,25 @@ class XML_RPC_Response extends XML_RPC_Base
         }
     }
 
+    /**
+     * @return string  the error string
+     */
     function faultString()
     {
         return $this->fs;
     }
 
+    /**
+     * @return mixed  the value
+     */
     function value()
     {
         return $this->xv;
     }
 
+    /**
+     * @return string  the error message in XML format
+     */
     function serialize()
     {
         $rs = "<methodResponse>\n";
@@ -584,107 +1000,263 @@ class XML_RPC_Response extends XML_RPC_Base
     }
 }
 
-
+/**
+ * The methods and properties for composing XML RPC messages
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill <edd@usefulinc.com>
+ * @author     Stig Bakken <stig@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/XML_RPC
+ */
 class XML_RPC_Message extends XML_RPC_Base
 {
-    var $payload;
-    var $methodname;
-    var $params = array();
+    /**
+     * The current debug mode (1 = on, 0 = off)
+     * @var integer
+     */
     var $debug = 0;
 
+    /**
+     * The encoding to be used for outgoing messages
+     *
+     * Defaults to the value of <var>$GLOBALS['XML_RPC_defencoding']</var>
+     *
+     * @var string
+     * @see XML_RPC_Message::setSendEncoding(),
+     *      $GLOBALS['XML_RPC_defencoding'], XML_RPC_Message::xml_header()
+     */
+    var $send_encoding = '';
+
+    /**
+     * The method presently being evaluated
+     * @var string
+     */
+    var $methodname = '';
+
+    /**
+     * @var array
+     */
+    var $params = array();
+
+    /**
+     * The XML message being generated
+     * @var string
+     */
+    var $payload = '';
+
+    /**
+     * @return void
+     */
     function XML_RPC_Message($meth, $pars = 0)
     {
         $this->methodname = $meth;
-        if (is_array($pars) && sizeof($pars)>0) {
-            for($i = 0; $i < sizeof($pars); $i++) {
+        if (is_array($pars) && sizeof($pars) > 0) {
+            for ($i = 0; $i < sizeof($pars); $i++) {
                 $this->addParam($pars[$i]);
             }
         }
     }
 
+    /**
+     * Produces the XML declaration including the encoding attribute
+     *
+     * The encoding is determined by this class' <var>$send_encoding</var>
+     * property.  If the <var>$send_encoding</var> property is not set, use
+     * <var>$GLOBALS['XML_RPC_defencoding']</var>.
+     *
+     * @return string  the XML declaration and <methodCall> element
+     *
+     * @see XML_RPC_Message::setSendEncoding(),
+     *      XML_RPC_Message::$send_encoding, $GLOBALS['XML_RPC_defencoding']
+     */
     function xml_header()
     {
-        return "<?xml version=\"1.0\"?>\n<methodCall>\n";
+        global $XML_RPC_defencoding;
+        if (!$this->send_encoding) {
+            $this->send_encoding = $XML_RPC_defencoding;
+        }
+        return '<?xml version="1.0" encoding="' . $this->send_encoding . '"?>'
+               . "\n<methodCall>\n";
     }
 
+    /**
+     * @return string  the closing </methodCall> tag
+     */
     function xml_footer()
     {
         return "</methodCall>\n";
     }
 
+    /**
+     * @return void
+     *
+     * @uses XML_RPC_Message::xml_header(), XML_RPC_Message::xml_footer()
+     */
     function createPayload()
     {
         $this->payload = $this->xml_header();
-        $this->payload .= "<methodName>" . $this->methodname . "</methodName>\n";
+        $this->payload .= '<methodName>' . $this->methodname . "</methodName>\n";
         $this->payload .= "<params>\n";
-        for($i = 0; $i < sizeof($this->params); $i++) {
+        for ($i = 0; $i < sizeof($this->params); $i++) {
             $p = $this->params[$i];
             $this->payload .= "<param>\n" . $p->serialize() . "</param>\n";
         }
         $this->payload .= "</params>\n";
         $this->payload .= $this->xml_footer();
-        $this->payload = str_replace("\n", "\r\n", $this->payload);
+        $this->payload = ereg_replace("[\r\n]+", "\r\n", $this->payload);
     }
 
-    function method($meth = "")
+    /**
+     * @return string  the name of the method
+     */
+    function method($meth = '')
     {
-        if ($meth != "") {
+        if ($meth != '') {
             $this->methodname = $meth;
         }
         return $this->methodname;
     }
 
+    /**
+     * @return string  the payload
+     */
     function serialize()
     {
         $this->createPayload();
         return $this->payload;
     }
 
+    /**
+     * @return void
+     */
     function addParam($par)
     {
         $this->params[] = $par;
     }
 
+    /**
+     * Obtains an XML_RPC_Value object for the given parameter
+     *
+     * @param int $i  the index number of the parameter to obtain
+     *
+     * @return object  the XML_RPC_Value object.
+     *                  If the parameter doesn't exist, an XML_RPC_Response object.
+     *
+     * @since Returns XML_RPC_Response object on error since Release 1.3.0
+     */
     function getParam($i)
     {
-        return $this->params[$i];
+        global $XML_RPC_err, $XML_RPC_str;
+
+        if (isset($this->params[$i])) {
+            return $this->params[$i];
+        } else {
+            $this->raiseError('The submitted request did not contain this parameter',
+                              XML_RPC_ERROR_INCORRECT_PARAMS);
+            return new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'],
+                                        $XML_RPC_str['incorrect_params']);
+        }
     }
 
+    /**
+     * @return int  the number of parameters
+     */
     function getNumParams()
     {
         return sizeof($this->params);
     }
 
-    function parseResponseFile($fp)
+    /**
+     * Sets the XML declaration's encoding attribute
+     *
+     * @param string $type  the encoding type (ISO-8859-1, UTF-8 or US-ASCII)
+     *
+     * @return void
+     *
+     * @see XML_RPC_Message::$send_encoding, XML_RPC_Message::xml_header()
+     * @since Method available since Release 1.2.0
+     */
+    function setSendEncoding($type)
     {
-        $ipd = "";
+        $this->send_encoding = $type;
+    }
+
+    /**
+     * Determine the XML's encoding via the encoding attribute
+     * in the XML declaration
+     *
+     * If the encoding parameter is not set or is not ISO-8859-1, UTF-8
+     * or US-ASCII, $XML_RPC_defencoding will be returned.
+     *
+     * @param string $data  the XML that will be parsed
+     *
+     * @return string  the encoding to be used
+     *
+     * @link   http://php.net/xml_parser_create
+     * @since  Method available since Release 1.2.0
+     */
+    function getEncoding($data)
+    {
+        global $XML_RPC_defencoding;
+
+        if (preg_match('/<\?xml[^>]*\s*encoding\s*=\s*[\'"]([^"\']*)[\'"]/i',
+                       $data, $match))
+        {
+            $match[1] = trim(strtoupper($match[1]));
+            switch ($match[1]) {
+                case 'ISO-8859-1':
+                case 'UTF-8':
+                case 'US-ASCII':
+                    return $match[1];
+                    break;
+
+                default:
+                    return $XML_RPC_defencoding;
+            }
+        } else {
+            return $XML_RPC_defencoding;
+        }
+    }
 
-        while($data = fread($fp, 32768)) {
+    /**
+     * @return object  a new XML_RPC_Response object
+     */
+    function parseResponseFile($fp)
+    {
+        $ipd = '';
+        while ($data = @fread($fp, 8192)) {
             $ipd .= $data;
         }
         return $this->parseResponse($ipd);
     }
 
-    function parseResponse($data = "")
+    /**
+     * @return object  a new XML_RPC_Response object
+     */
+    function parseResponse($data = '')
     {
-        global $XML_RPC_xh,$XML_RPC_err,$XML_RPC_str;
-        global $XML_RPC_defencoding;
+        global $XML_RPC_xh, $XML_RPC_err, $XML_RPC_str, $XML_RPC_defencoding;
 
-        $parser = xml_parser_create($XML_RPC_defencoding);
+        $encoding = $this->getEncoding($data);
+        $parser_resource = xml_parser_create($encoding);
+        $parser = (int) $parser_resource;
 
         $XML_RPC_xh[$parser] = array();
 
-        $XML_RPC_xh[$parser]['st'] = "";
+        $XML_RPC_xh[$parser]['st'] = '';
         $XML_RPC_xh[$parser]['cm'] = 0;
         $XML_RPC_xh[$parser]['isf'] = 0;
-        $XML_RPC_xh[$parser]['ac'] = "";
-        $XML_RPC_xh[$parser]['qt'] = "";
+        $XML_RPC_xh[$parser]['ac'] = '';
+        $XML_RPC_xh[$parser]['qt'] = '';
 
-        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
-        xml_set_element_handler($parser, "XML_RPC_se", "XML_RPC_ee");
-        xml_set_character_data_handler($parser, "XML_RPC_cd");
-        xml_set_default_handler($parser, "XML_RPC_dh");
-        $xmlrpc_value = new XML_RPC_Value;
+        xml_parser_set_option($parser_resource, XML_OPTION_CASE_FOLDING, true);
+        xml_set_element_handler($parser_resource, 'XML_RPC_se', 'XML_RPC_ee');
+        xml_set_character_data_handler($parser_resource, 'XML_RPC_cd');
 
         $hdrfnd = 0;
         if ($this->debug) {
@@ -693,49 +1265,54 @@ class XML_RPC_Message extends XML_RPC_Base
             print "\n---END---\n</PRE>";
         }
 
-        // see if we got an HTTP 200 OK, else bomb
-        // but only do this if we're using the HTTP protocol.
-        if (ereg("^HTTP",$data) &&
-            !ereg("^HTTP/[0-9\.]+ 200 ", $data)) {
-                $errstr = substr($data, 0, strpos($data, "\n")-1);
-                error_log("HTTP error, got response: " . $errstr);
-                $r = new XML_RPC_Response(0, $XML_RPC_err["http_error"],
-                                          $XML_RPC_str["http_error"] . " (" .
-                                          $errstr . ")");
-                xml_parser_free($parser);
+        // See if response is a 200 or a 100 then a 200, else raise error.
+        // But only do this if we're using the HTTP protocol.
+        if (ereg('^HTTP', $data) &&
+            !ereg('^HTTP/[0-9\.]+ 200 ', $data) &&
+            !preg_match('@^HTTP/[0-9\.]+ 10[0-9]([A-Za-z ]+)?[\r\n]+HTTP/[0-9\.]+ 200@', $data))
+        {
+                $errstr = substr($data, 0, strpos($data, "\n") - 1);
+                error_log('HTTP error, got response: ' . $errstr);
+                $r = new XML_RPC_Response(0, $XML_RPC_err['http_error'],
+                                          $XML_RPC_str['http_error'] . ' (' .
+                                          $errstr . ')');
+                xml_parser_free($parser_resource);
                 return $r;
         }
         // gotta get rid of headers here
 
 
-        if ((!$hdrfnd) && ($brpos = strpos($data,"\r\n\r\n"))) {
+        if (!$hdrfnd && ($brpos = strpos($data,"\r\n\r\n"))) {
             $XML_RPC_xh[$parser]['ha'] = substr($data, 0, $brpos);
             $data = substr($data, $brpos + 4);
             $hdrfnd = 1;
         }
 
-        // be tolerant of junk after methodResponse (e.g. javascript automatically inserted by free hosts)
-        // thanks to Luca Mariano <luca.mariano@email.it>
+        /*
+         * be tolerant of junk after methodResponse
+         * (e.g. javascript automatically inserted by free hosts)
+         * thanks to Luca Mariano <luca.mariano@email.it>
+         */
         $data = substr($data, 0, strpos($data, "</methodResponse>") + 17);
 
-        if (!xml_parse($parser, $data, sizeof($data))) {
+        if (!xml_parse($parser_resource, $data, sizeof($data))) {
             // thanks to Peter Kocks <peter.kocks@baygate.com>
-            if ((xml_get_current_line_number($parser)) == 1) {
-                $errstr = "XML error at line 1, check URL";
+            if (xml_get_current_line_number($parser_resource) == 1) {
+                $errstr = 'XML error at line 1, check URL';
             } else {
-                $errstr = sprintf("XML error: %s at line %d",
-                                  xml_error_string(xml_get_error_code($parser)),
-                                  xml_get_current_line_number($parser));
+                $errstr = sprintf('XML error: %s at line %d',
+                                  xml_error_string(xml_get_error_code($parser_resource)),
+                                  xml_get_current_line_number($parser_resource));
             }
             error_log($errstr);
-            $r = new XML_RPC_Response(0, $XML_RPC_err["invalid_return"],
-                                      $XML_RPC_str["invalid_return"]);
-            xml_parser_free($parser);
+            $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'],
+                                      $XML_RPC_str['invalid_return']);
+            xml_parser_free($parser_resource);
             return $r;
         }
-        xml_parser_free($parser);
+        xml_parser_free($parser_resource);
         if ($this->debug) {
-            print "<PRE>---EVALING---[" .
+            print '<PRE>---EVALING---[' .
             strlen($XML_RPC_xh[$parser]['st']) . " chars]---\n" .
             htmlspecialchars($XML_RPC_xh[$parser]['st']) . ";\n---END---</PRE>";
         }
@@ -743,15 +1320,15 @@ class XML_RPC_Message extends XML_RPC_Base
             // then something odd has happened
             // and it's time to generate a client side error
             // indicating something odd went on
-            $r = new XML_RPC_Response(0, $XML_RPC_err["invalid_return"],
-                                         $XML_RPC_str["invalid_return"]);
+            $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'],
+                                      $XML_RPC_str['invalid_return']);
         } else {
             eval('$v=' . $XML_RPC_xh[$parser]['st'] . '; $allOK=1;');
             if ($XML_RPC_xh[$parser]['isf']) {
-                $f = $v->structmem("faultCode");
-                $fs = $v->structmem("faultString");
+                $f = $v->structmem('faultCode');
+                $fs = $v->structmem('faultString');
                 $r = new XML_RPC_Response($v, $f->scalarval(),
-                                              $fs->scalarval());
+                                          $fs->scalarval());
             } else {
                 $r = new XML_RPC_Response($v);
             }
@@ -759,26 +1336,43 @@ class XML_RPC_Message extends XML_RPC_Base
         $r->hdrs = split("\r?\n", $XML_RPC_xh[$parser]['ha'][1]);
         return $r;
     }
-
 }
 
-
+/**
+ * The methods and properties that represent data in XML RPC format
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill <edd@usefulinc.com>
+ * @author     Stig Bakken <stig@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/XML_RPC
+ */
 class XML_RPC_Value extends XML_RPC_Base
 {
     var $me = array();
     var $mytype = 0;
 
-    function XML_RPC_Value($val = -1, $type = "")
+    /**
+     * @return void
+     */
+    function XML_RPC_Value($val = -1, $type = '')
     {
         global $XML_RPC_Types;
         $this->me = array();
         $this->mytype = 0;
-        if ($val != -1 || $type != "") {
-            if ($type == "") {
-                $type="string";
+        if ($val != -1 || $type != '') {
+            if ($type == '') {
+                $type = 'string';
             }
-            if ($XML_RPC_Types[$type] == 1) {
-                $this->addScalar($val,$type);
+            if (!array_key_exists($type, $XML_RPC_Types)) {
+                // XXX
+                // need some way to report this error
+            } elseif ($XML_RPC_Types[$type] == 1) {
+                $this->addScalar($val, $type);
             } elseif ($XML_RPC_Types[$type] == 2) {
                 $this->addArray($val);
             } elseif ($XML_RPC_Types[$type] == 3) {
@@ -787,26 +1381,30 @@ class XML_RPC_Value extends XML_RPC_Base
         }
     }
 
-    function addScalar($val, $type = "string")
+    /**
+     * @return int  returns 1 if successful or 0 if there are problems
+     */
+    function addScalar($val, $type = 'string')
     {
         global $XML_RPC_Types, $XML_RPC_Boolean;
 
         if ($this->mytype == 1) {
-            $this->raiseError("Scalar can have only one value", XML_RPC_ERROR_INVALID_TYPE);
+            $this->raiseError('Scalar can have only one value',
+                              XML_RPC_ERROR_INVALID_TYPE);
             return 0;
         }
         $typeof = $XML_RPC_Types[$type];
         if ($typeof != 1) {
-            $this->raiseError("Not a scalar type (${typeof})", XML_RPC_ERROR_INVALID_TYPE);
+            $this->raiseError("Not a scalar type (${typeof})",
+                              XML_RPC_ERROR_INVALID_TYPE);
             return 0;
         }
 
         if ($type == $XML_RPC_Boolean) {
-            if (strcasecmp($val,"true") == 0 ||
-                $val == 1 ||
-                ($val == true &&
-                    strcasecmp($val,"false"))) {
-
+            if (strcasecmp($val, 'true') == 0
+                || $val == 1
+                || ($val == true && strcasecmp($val, 'false')))
+            {
                 $val = 1;
             } else {
                 $val = 0;
@@ -815,9 +1413,9 @@ class XML_RPC_Value extends XML_RPC_Base
 
         if ($this->mytype == 2) {
             // we're adding to an array here
-            $ar = $this->me["array"];
+            $ar = $this->me['array'];
             $ar[] = new XML_RPC_Value($val, $type);
-            $this->me["array"] = $ar;
+            $this->me['array'] = $ar;
         } else {
             // a scalar, so set the value and remember we're scalar
             $this->me[$type] = $val;
@@ -826,95 +1424,117 @@ class XML_RPC_Value extends XML_RPC_Base
         return 1;
     }
 
+    /**
+     * @return int  returns 1 if successful or 0 if there are problems
+     */
     function addArray($vals)
     {
         global $XML_RPC_Types;
         if ($this->mytype != 0) {
             $this->raiseError(
-                "Already initialized as a [" . $this->kindOf() . "]",
-                XML_RPC_ERROR_ALREADY_INITIALIZED);
+                    'Already initialized as a [' . $this->kindOf() . ']',
+                    XML_RPC_ERROR_ALREADY_INITIALIZED);
             return 0;
         }
-        $this->mytype = $XML_RPC_Types["array"];
-        $this->me["array"] = $vals;
+        $this->mytype = $XML_RPC_Types['array'];
+        $this->me['array'] = $vals;
         return 1;
     }
 
+    /**
+     * @return int  returns 1 if successful or 0 if there are problems
+     */
     function addStruct($vals)
     {
         global $XML_RPC_Types;
         if ($this->mytype != 0) {
             $this->raiseError(
-                "Already initialized as a [" . $this->kindOf() . "]",
-                XML_RPC_ERROR_ALREADY_INITIALIZED);
+                    'Already initialized as a [' . $this->kindOf() . ']',
+                    XML_RPC_ERROR_ALREADY_INITIALIZED);
             return 0;
         }
-        $this->mytype = $XML_RPC_Types["struct"];
-        $this->me["struct"] = $vals;
+        $this->mytype = $XML_RPC_Types['struct'];
+        $this->me['struct'] = $vals;
         return 1;
     }
 
+    /**
+     * @return void
+     */
     function dump($ar)
     {
         reset($ar);
-        while (list( $key, $val ) = each($ar)) {
-            echo "$key => $val<br>";
+        foreach ($ar as $key => $val) {
+            echo "$key => $val<br />";
             if ($key == 'array') {
-                while ( list( $key2, $val2 ) = each( $val ) ) {
-                    echo "-- $key2 => $val2<br>";
+                foreach ($val as $key2 => $val2) {
+                    echo "-- $key2 => $val2<br />";
                 }
             }
         }
     }
 
+    /**
+     * @return string  the data type of the current value
+     */
     function kindOf()
     {
         switch ($this->mytype) {
         case 3:
-            return "struct";
-            break;
+            return 'struct';
+
         case 2:
-            return "array";
-            break;
+            return 'array';
+
         case 1:
-            return "scalar";
-            break;
+            return 'scalar';
+
         default:
-            return "undef";
+            return 'undef';
         }
     }
 
+    /**
+     * @return string  the data in XML format
+     */
     function serializedata($typ, $val)
     {
-        $rs = "";
+        $rs = '';
         global $XML_RPC_Types, $XML_RPC_Base64, $XML_RPC_String, $XML_RPC_Boolean;
+        if (!array_key_exists($typ, $XML_RPC_Types)) {
+            // XXX
+            // need some way to report this error
+            return;
+        }
         switch ($XML_RPC_Types[$typ]) {
         case 3:
             // struct
             $rs .= "<struct>\n";
             reset($val);
-            while(list($key2, $val2) = each($val)) {
+            foreach ($val as $key2 => $val2) {
                 $rs .= "<member><name>${key2}</name>\n";
                 $rs .= $this->serializeval($val2);
                 $rs .= "</member>\n";
             }
-            $rs .= "</struct>";
+            $rs .= '</struct>';
             break;
+
         case 2:
             // array
             $rs .= "<array>\n<data>\n";
-            for($i = 0; $i < sizeof($val); $i++) {
+            for ($i = 0; $i < sizeof($val); $i++) {
                 $rs .= $this->serializeval($val[$i]);
             }
             $rs .= "</data>\n</array>";
             break;
+
         case 1:
             switch ($typ) {
             case $XML_RPC_Base64:
                 $rs .= "<${typ}>" . base64_encode($val) . "</${typ}>";
                 break;
             case $XML_RPC_Boolean:
-                $rs .= "<${typ}>" . ($val ? "1" : "0") . "</${typ}>";
+                $rs .= "<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
                 break;
             case $XML_RPC_String:
                 $rs .= "<${typ}>" . htmlspecialchars($val). "</${typ}>";
@@ -922,52 +1542,67 @@ class XML_RPC_Value extends XML_RPC_Base
             default:
                 $rs .= "<${typ}>${val}</${typ}>";
             }
-            break;
-        default:
-            break;
         }
         return $rs;
     }
 
+    /**
+     * @return string  the data in XML format
+     */
     function serialize()
     {
         return $this->serializeval($this);
     }
 
+    /**
+     * @return string  the data in XML format
+     */
     function serializeval($o)
     {
-        $rs = "";
+        $rs = '';
         $ar = $o->me;
         reset($ar);
         list($typ, $val) = each($ar);
-        $rs .= "<value>";
+        $rs .= '<value>';
         $rs .= $this->serializedata($typ, $val);
         $rs .= "</value>\n";
         return $rs;
     }
 
+    /**
+     * @return mixed  the contents of the element requested
+     */
     function structmem($m)
     {
-        $nv = $this->me["struct"][$m];
-        return $nv;
+        return $this->me['struct'][$m];
     }
 
+    /**
+     * @return void
+     */
     function structreset()
     {
-        reset($this->me["struct"]);
+        reset($this->me['struct']);
     }
 
+    /**
+     * @return  the key/value pair of the struct's current element
+     */
     function structeach()
     {
-        return each($this->me["struct"]);
+        return each($this->me['struct']);
     }
 
-    function getval() {
+    /**
+     * @return mixed  the current value
+     */
+    function getval()
+    {
         // UNSTABLE
         global $XML_RPC_BOOLEAN, $XML_RPC_Base64;
 
         reset($this->me);
-        list($a,$b) = each($this->me);
+        $b = current($this->me);
 
         // contributed by I Sofer, 2001-03-24
         // add support for nested arrays to scalarval
@@ -995,194 +1630,224 @@ class XML_RPC_Value extends XML_RPC_Base
         return $b;
     }
 
+    /**
+     * @return mixed
+     */
     function scalarval()
     {
         global $XML_RPC_Boolean, $XML_RPC_Base64;
         reset($this->me);
-        list($a,$b) = each($this->me);
-        return $b;
+        return current($this->me);
     }
 
+    /**
+     * @return string
+     */
     function scalartyp()
     {
         global $XML_RPC_I4, $XML_RPC_Int;
         reset($this->me);
-        list($a,$b) = each($this->me);
+        $a = key($this->me);
         if ($a == $XML_RPC_I4) {
             $a = $XML_RPC_Int;
         }
         return $a;
     }
 
+    /**
+     * @return mixed  the struct's current element
+     */
     function arraymem($m)
     {
-        $nv = $this->me["array"][$m];
-        return $nv;
+        return $this->me['array'][$m];
     }
 
+    /**
+     * @return int  the number of elements in the array
+     */
     function arraysize()
     {
         reset($this->me);
-        list($a,$b) = each($this->me);
+        list($a, $b) = each($this->me);
         return sizeof($b);
     }
-}
 
+    /**
+     * Determines if the item submitted is an XML_RPC_Value object
+     *
+     * @param mixed $val  the variable to be evaluated
+     *
+     * @return bool  TRUE if the item is an XML_RPC_Value object
+     *
+     * @static
+     * @since Method available since Release 1.3.0
+     */
+    function isValue($val)
+    {
+        return (strtolower(get_class($val)) == 'xml_rpc_value');
+    }
+}
 
 /**
- * date helpers
+ * Return an ISO8601 encoded string
+ *
+ * While timezones ought to be supported, the XML-RPC spec says:
+ *
+ * "Don't assume a timezone. It should be specified by the server in its
+ * documentation what assumptions it makes about timezones."
+ *
+ * This routine always assumes localtime unless $utc is set to 1, in which
+ * case UTC is assumed and an adjustment for locale is made when encoding.
+ *
+ * @return string  the formatted date
  */
-function XML_RPC_iso8601_encode($timet, $utc = 0) {
-    // return an ISO8601 encoded string
-    // really, timezones ought to be supported
-    // but the XML-RPC spec says:
-    //
-    // "Don't assume a timezone. It should be specified by the server in its
-    // documentation what assumptions it makes about timezones."
-    //
-    // these routines always assume localtime unless
-    // $utc is set to 1, in which case UTC is assumed
-    // and an adjustment for locale is made when encoding
+function XML_RPC_iso8601_encode($timet, $utc = 0)
+{
     if (!$utc) {
-        $t = strftime("%Y%m%dT%H:%M:%S", $timet);
+        $t = strftime('%Y%m%dT%H:%M:%S', $timet);
     } else {
-        if (function_exists("gmstrftime")) {
+        if (function_exists('gmstrftime')) {
             // gmstrftime doesn't exist in some versions
             // of PHP
-            $t = gmstrftime("%Y%m%dT%H:%M:%S", $timet);
+            $t = gmstrftime('%Y%m%dT%H:%M:%S', $timet);
         } else {
-            $t = strftime("%Y%m%dT%H:%M:%S", $timet - date("Z"));
+            $t = strftime('%Y%m%dT%H:%M:%S', $timet - date('Z'));
         }
     }
-
     return $t;
 }
 
-function XML_RPC_iso8601_decode($idate, $utc = 0) {
-    // return a timet in the localtime, or UTC
+/**
+ * Convert a datetime string into a Unix timestamp
+ *
+ * While timezones ought to be supported, the XML-RPC spec says:
+ *
+ * "Don't assume a timezone. It should be specified by the server in its
+ * documentation what assumptions it makes about timezones."
+ *
+ * This routine always assumes localtime unless $utc is set to 1, in which
+ * case UTC is assumed and an adjustment for locale is made when encoding.
+ *
+ * @return int  the unix timestamp of the date submitted
+ */
+function XML_RPC_iso8601_decode($idate, $utc = 0)
+{
     $t = 0;
-    if (ereg("([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})", $idate, $regs)) {
-
+    if (ereg('([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})', $idate, $regs)) {
         if ($utc) {
             $t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
         } else {
             $t = mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
         }
     }
-
     return $t;
 }
 
 /**
- * Takes a message in PHP XML_RPC object format and translates it into native PHP types.
+ * Converts an XML_RPC_Value object into native PHP types
  *
- * @author Dan Libby <dan@libby.com>
- **/
-function XML_RPC_decode($XML_RPC_val) {
+ * @param object $XML_RPC_val  the XML_RPC_Value object to decode
+ *
+ * @return mixed  the PHP values
+ */
+function XML_RPC_decode($XML_RPC_val)
+{
     $kind = $XML_RPC_val->kindOf();
 
-   if ($kind == "scalar") {
-      return $XML_RPC_val->scalarval();
-
-   } elseif ($kind == "array") {
-      $size = $XML_RPC_val->arraysize();
-      $arr = array();
+    if ($kind == 'scalar') {
+        return $XML_RPC_val->scalarval();
 
-      for($i = 0; $i < $size; $i++) {
-         $arr[] = XML_RPC_decode($XML_RPC_val->arraymem($i));
-      }
-      return $arr;
-
-   } elseif ($kind == "struct") {
-      $XML_RPC_val->structreset();
-      $arr = array();
+    } elseif ($kind == 'array') {
+        $size = $XML_RPC_val->arraysize();
+        $arr = array();
+        for ($i = 0; $i < $size; $i++) {
+            $arr[] = XML_RPC_decode($XML_RPC_val->arraymem($i));
+        }
+        return $arr;
 
-      while(list($key,$value) = $XML_RPC_val->structeach()) {
-         $arr[$key] = XML_RPC_decode($value);
-      }
-      return $arr;
-   }
+    } elseif ($kind == 'struct') {
+        $XML_RPC_val->structreset();
+        $arr = array();
+        while (list($key, $value) = $XML_RPC_val->structeach()) {
+            $arr[$key] = XML_RPC_decode($value);
+        }
+        return $arr;
+    }
 }
 
 /**
- * Takes native php types and encodes them into XML_RPC PHP object format.
+ * Converts native PHP types into an XML_RPC_Value object
  *
- * Feature creep -- could support more types via optional type argument.
+ * @param mixed $php_val  the PHP value or variable you want encoded
  *
- * @author Dan Libby <dan@libby.com>
- **/
-function XML_RPC_encode($php_val) {
-   global $XML_RPC_Boolean;
-   global $XML_RPC_Int;
-   global $XML_RPC_Double;
-   global $XML_RPC_String;
-   global $XML_RPC_Array;
-   global $XML_RPC_Struct;
-
-   $type = gettype($php_val);
-   $XML_RPC_val = new XML_RPC_Value;
-
-   switch ($type) {
-   case "array":
-       $keys = array_keys($php_val);
-       $count = count($php_val);
-       $firstkey = $keys[0];
-       $lastkey = $keys[$count - 1];
-       if ($firstkey === 0 && is_int($lastkey) && ($lastkey + 1) == $count) {
-           $is_continuous = true;
-           $expected = 0;
-           foreach ($keys as $actual) {
-               if ($actual != $expected) {
-                   $is_continuous = false;
-                   break;
-               }
-               $expected++;
-           }
+ * @return object  the XML_RPC_Value object
+ */
+function XML_RPC_encode($php_val)
+{
+    global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double, $XML_RPC_String,
+           $XML_RPC_Array, $XML_RPC_Struct;
 
-           if ($is_continuous) {
-               $arr = array();
-               foreach ($php_val as $k => $v) {
-                   $arr[$k] = XML_RPC_encode($v);
-               }
-               $XML_RPC_val->addArray($arr);
-               break;
+    $type = gettype($php_val);
+    $XML_RPC_val = new XML_RPC_Value;
+
+    switch ($type) {
+    case 'array':
+        if (empty($php_val)) {
+            $XML_RPC_val->addArray($php_val);
+            break;
+        }
+        $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
+        if (empty($tmp)) {
+           $arr = array();
+           foreach ($php_val as $k => $v) {
+               $arr[$k] = XML_RPC_encode($v);
            }
-       }
-       // fall though if not numerical and continuous
-   case "object":
-       $arr = array();
-       foreach ($php_val as $k => $v) {
-           $arr[$k] = XML_RPC_encode($v);
-       }
-       $XML_RPC_val->addStruct($arr);
-       break;
-
-   case "integer":
-       $XML_RPC_val->addScalar($php_val, $XML_RPC_Int);
-       break;
-
-   case "double":
-       $XML_RPC_val->addScalar($php_val, $XML_RPC_Double);
-       break;
-
-   case "string":
-   case "NULL":
-       $XML_RPC_val->addScalar($php_val, $XML_RPC_String);
-       break;
-
-   // <G_Giunta_2001-02-29>
-   // Add support for encoding/decoding of booleans, since they are supported in PHP
-   case "boolean":
-       $XML_RPC_val->addScalar($php_val, $XML_RPC_Boolean);
-       break;
-   // </G_Giunta_2001-02-29>
-
-   case "unknown type":
-   default:
-       $XML_RPC_val = false;
-       break;
-   }
-   return $XML_RPC_val;
+           $XML_RPC_val->addArray($arr);
+           break;
+        }
+        // fall though if it's not an enumerated array
+
+    case 'object':
+        $arr = array();
+        foreach ($php_val as $k => $v) {
+            $arr[$k] = XML_RPC_encode($v);
+        }
+        $XML_RPC_val->addStruct($arr);
+        break;
+
+    case 'integer':
+        $XML_RPC_val->addScalar($php_val, $XML_RPC_Int);
+        break;
+
+    case 'double':
+        $XML_RPC_val->addScalar($php_val, $XML_RPC_Double);
+        break;
+
+    case 'string':
+    case 'NULL':
+        $XML_RPC_val->addScalar($php_val, $XML_RPC_String);
+        break;
+
+    case 'boolean':
+        // Add support for encoding/decoding of booleans, since they
+        // are supported in PHP
+        // by <G_Giunta_2001-02-29>
+        $XML_RPC_val->addScalar($php_val, $XML_RPC_Boolean);
+        break;
+
+    case 'unknown type':
+    default:
+        $XML_RPC_val = false;
+    }
+    return $XML_RPC_val;
 }
 
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * c-hanging-comment-ender-p: nil
+ * End:
+ */
+
 ?>
diff --git a/bundled-libs/XML/RPC/Dump.php b/bundled-libs/XML/RPC/Dump.php
new file mode 100644 (file)
index 0000000..366307c
--- /dev/null
@@ -0,0 +1,187 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Function and class to dump XML_RPC_Value objects in a nice way
+ *
+ * Should be helpful as a normal var_dump(..) displays all internals which
+ * doesn't really give you an overview due to too much information.
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Christian Weiske <cweiske@php.net>
+ * @version    CVS: $Id: Dump.php,v 1.7 2005/01/24 03:47:55 danielc Exp $
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+
+
+/**
+ * Pull in the XML_RPC class
+ */
+require_once 'XML/RPC.php';
+
+
+/**
+ * Generates the dump of the XML_RPC_Value and echoes it
+ *
+ * @param object $value  the XML_RPC_Value object to dump
+ *
+ * @return void
+ */
+function XML_RPC_Dump($value)
+{
+    $dumper = new XML_RPC_Dump();
+    echo $dumper->generateDump($value);
+}
+
+
+/**
+ * Class which generates a dump of a XML_RPC_Value object
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Christian Weiske <cweiske@php.net>
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+class XML_RPC_Dump
+{
+    /**
+     * The indentation array cache
+     * @var array
+     */
+    var $arIndent      = array();
+
+    /**
+     * The spaces used for indenting the XML
+     * @var string
+     */
+    var $strBaseIndent = '    ';
+
+    /**
+     * Returns the dump in XML format without printing it out
+     *
+     * @param object $value   the XML_RPC_Value object to dump
+     * @param int    $nLevel  the level of indentation
+     *
+     * @return string  the dump
+     */
+    function generateDump($value, $nLevel = 0)
+    {
+        if (!is_object($value) && get_class($value) != 'xml_rpc_value') {
+            require_once 'PEAR.php';
+            PEAR::raiseError('Tried to dump non-XML_RPC_Value variable' . "\r\n",
+                             0, PEAR_ERROR_PRINT);
+            if (is_object($value)) {
+                $strType = get_class($value);
+            } else {
+                $strType = gettype($value);
+            }
+            return $this->getIndent($nLevel) . 'NOT A XML_RPC_Value: '
+                   . $strType . "\r\n";
+        }
+
+        switch ($value->kindOf()) {
+        case 'struct':
+            $ret = $this->genStruct($value, $nLevel);
+            break;
+        case 'array':
+            $ret = $this->genArray($value, $nLevel);
+            break;
+        case 'scalar':
+            $ret = $this->genScalar($value->scalarval(), $nLevel);
+            break;
+        default:
+            require_once 'PEAR.php';
+            PEAR::raiseError('Illegal type "' . $value->kindOf()
+                             . '" in XML_RPC_Value' . "\r\n", 0,
+                             PEAR_ERROR_PRINT);
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Returns the scalar value dump
+     *
+     * @param object $value   the scalar XML_RPC_Value object to dump
+     * @param int    $nLevel  the level of indentation
+     *
+     * @return string  Dumped version of the scalar value
+     */
+    function genScalar($value, $nLevel)
+    {
+        if (gettype($value) == 'object') {
+            $strClass = ' ' . get_class($value);
+        } else {
+            $strClass = '';
+        }
+        return $this->getIndent($nLevel) . gettype($value) . $strClass
+               . ' ' . $value . "\r\n";
+    }
+
+    /**
+     * Returns the dump of a struct
+     *
+     * @param object $value   the struct XML_RPC_Value object to dump
+     * @param int    $nLevel  the level of indentation
+     *
+     * @return string  Dumped version of the scalar value
+     */
+    function genStruct($value, $nLevel)
+    {
+        $value->structreset();
+        $strOutput = $this->getIndent($nLevel) . 'struct' . "\r\n";
+        while (list($key, $keyval) = $value->structeach()) {
+            $strOutput .= $this->getIndent($nLevel + 1) . $key . "\r\n";
+            $strOutput .= $this->generateDump($keyval, $nLevel + 2);
+        }
+        return $strOutput;
+    }
+
+    /**
+     * Returns the dump of an array
+     *
+     * @param object $value   the array XML_RPC_Value object to dump
+     * @param int    $nLevel  the level of indentation
+     *
+     * @return string  Dumped version of the scalar value
+     */
+    function genArray($value, $nLevel)
+    {
+        $nSize     = $value->arraysize();
+        $strOutput = $this->getIndent($nLevel) . 'array' . "\r\n";
+        for($nA = 0; $nA < $nSize; $nA++) {
+            $strOutput .= $this->getIndent($nLevel + 1) . $nA . "\r\n";
+            $strOutput .= $this->generateDump($value->arraymem($nA),
+                                              $nLevel + 2);
+        }
+        return $strOutput;
+    }
+
+    /**
+     * Returns the indent for a specific level and caches it for faster use
+     *
+     * @param int $nLevel  the level
+     *
+     * @return string  the indented string
+     */
+    function getIndent($nLevel)
+    {
+        if (!isset($this->arIndent[$nLevel])) {
+            $this->arIndent[$nLevel] = str_repeat($this->strBaseIndent, $nLevel);
+        }
+        return $this->arIndent[$nLevel];
+    }
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * c-hanging-comment-ender-p: nil
+ * End:
+ */
+
+?>
index 9f2f743de12efced2c412cb3f30b1a9749e4dfbf..579411dd97b15dbad0a037e12acf4bbce63dc635 100644 (file)
 <?php
-// /* vim: set expandtab tabstop=4 shiftwidth=4: */
-// by Edd Dumbill (C) 1999,2000
-// <edd@usefulinc.com>
-
-// License is granted to use or modify this software ("XML-RPC for PHP")
-// for commercial or non-commercial use provided the copyright of the author
-// is preserved in any distributed or derivative work.
-
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR
-// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// Adapted to PEAR standards by Stig S�her Bakken <stig@php.net>
-// /* $Id: Server.php,v 1.2 2004/07/26 15:08:11 nohn Exp $ */
-
-require_once "XML/RPC.php";
-
-// listMethods: either a string, or nothing
-$GLOBALS['XML_RPC_Server_listMethods_sig'] =
-    array(array($GLOBALS['XML_RPC_Array'], $GLOBALS['XML_RPC_String']),
-          array($GLOBALS['XML_RPC_Array']));
-$GLOBALS['XML_RPC_Server_listMethods_doc'] =
-    'This method lists all the methods that the XML-RPC server knows how to dispatch';
 
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Server commands for our PHP implementation of the XML-RPC protocol
+ *
+ * This is a PEAR-ified version of Useful inc's XML-RPC for PHP.
+ * It has support for HTTP transport, proxies and authentication.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: License is granted to use or modify this software
+ * ("XML-RPC for PHP") for commercial or non-commercial use provided the
+ * copyright of the author is preserved in any distributed or derivative work.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESSED OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill <edd@usefulinc.com>
+ * @author     Stig Bakken <stig@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    CVS: $Id: Server.php,v 1.26 2005/05/09 21:39:47 danielc Exp $
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+
+
+/**
+ * Pull in the XML_RPC class
+ */
+require_once dirname(__FILE__) . '/../RPC.php';
+
+
+/**
+ * signature for system.listMethods: return = array,
+ * parameters = a string or nothing
+ * @global array $GLOBALS['XML_RPC_Server_listMethods_sig']
+ */
+$GLOBALS['XML_RPC_Server_listMethods_sig'] = array(
+    array($GLOBALS['XML_RPC_Array'],
+          $GLOBALS['XML_RPC_String']
+    ),
+    array($GLOBALS['XML_RPC_Array'])
+);
+
+/**
+ * docstring for system.listMethods
+ * @global string $GLOBALS['XML_RPC_Server_listMethods_doc']
+ */
+$GLOBALS['XML_RPC_Server_listMethods_doc'] = 'This method lists all the'
+        . ' methods that the XML-RPC server knows how to dispatch';
+
+/**
+ * signature for system.methodSignature: return = array,
+ * parameters = string
+ * @global array $GLOBALS['XML_RPC_Server_methodSignature_sig']
+ */
+$GLOBALS['XML_RPC_Server_methodSignature_sig'] = array(
+    array($GLOBALS['XML_RPC_Array'],
+          $GLOBALS['XML_RPC_String']
+    )
+);
+
+/**
+ * docstring for system.methodSignature
+ * @global string $GLOBALS['XML_RPC_Server_methodSignature_doc']
+ */
+$GLOBALS['XML_RPC_Server_methodSignature_doc'] = 'Returns an array of known'
+        . ' signatures (an array of arrays) for the method name passed. If'
+        . ' no signatures are known, returns a none-array (test for type !='
+        . ' array to detect missing signature)';
+
+/**
+ * signature for system.methodHelp: return = string,
+ * parameters = string
+ * @global array $GLOBALS['XML_RPC_Server_methodHelp_sig']
+ */
+$GLOBALS['XML_RPC_Server_methodHelp_sig'] = array(
+    array($GLOBALS['XML_RPC_String'],
+          $GLOBALS['XML_RPC_String']
+    )
+);
+
+/**
+ * docstring for methodHelp
+ * @global string $GLOBALS['XML_RPC_Server_methodHelp_doc']
+ */
+$GLOBALS['XML_RPC_Server_methodHelp_doc'] = 'Returns help text if defined'
+        . ' for the method passed, otherwise returns an empty string';
+
+/**
+ * dispatch map for the automatically declared XML-RPC methods.
+ * @global array $GLOBALS['XML_RPC_Server_dmap']
+ */
+$GLOBALS['XML_RPC_Server_dmap'] = array(
+    'system.listMethods' => array(
+        'function'  => 'XML_RPC_Server_listMethods',
+        'signature' => $GLOBALS['XML_RPC_Server_listMethods_sig'],
+        'docstring' => $GLOBALS['XML_RPC_Server_listMethods_doc']
+    ),
+    'system.methodHelp' => array(
+        'function'  => 'XML_RPC_Server_methodHelp',
+        'signature' => $GLOBALS['XML_RPC_Server_methodHelp_sig'],
+        'docstring' => $GLOBALS['XML_RPC_Server_methodHelp_doc']
+    ),
+    'system.methodSignature' => array(
+        'function'  => 'XML_RPC_Server_methodSignature',
+        'signature' => $GLOBALS['XML_RPC_Server_methodSignature_sig'],
+        'docstring' => $GLOBALS['XML_RPC_Server_methodSignature_doc']
+    )
+);
+
+/**
+ * @global string $GLOBALS['XML_RPC_Server_debuginfo']
+ */
+$GLOBALS['XML_RPC_Server_debuginfo'] = '';
+
+
+/**
+ * Lists all the methods that the XML-RPC server knows how to dispatch
+ *
+ * @return object  a new XML_RPC_Response object
+ */
 function XML_RPC_Server_listMethods($server, $m)
 {
     global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
+
     $v = new XML_RPC_Value();
-    $dmap = $server->dmap;
     $outAr = array();
-    for (reset($dmap); list($key, $val) = each($dmap); ) {
-        $outAr[] = new XML_RPC_Value($key, "string");
+    foreach ($server->dmap as $key => $val) {
+        $outAr[] = new XML_RPC_Value($key, 'string');
     }
-    $dmap = $XML_RPC_Server_dmap;
-    for (reset($dmap); list($key, $val) = each($dmap); ) {
-        $outAr[] = new XML_RPC_Value($key, "string");
+    foreach ($XML_RPC_Server_dmap as $key => $val) {
+        $outAr[] = new XML_RPC_Value($key, 'string');
     }
     $v->addArray($outAr);
     return new XML_RPC_Response($v);
 }
 
-$GLOBALS['XML_RPC_Server_methodSignature_sig'] =
-    array(array($GLOBALS['XML_RPC_Array'], $GLOBALS['XML_RPC_String']));
-$GLOBALS['XML_RPC_Server_methodSignature_doc'] =
-    'Returns an array of known signatures (an array of arrays) for the method name passed. If no signatures are known, returns a none-array (test for type != array to detect missing signature)';
-
+/**
+ * Returns an array of known signatures (an array of arrays)
+ * for the given method
+ *
+ * If no signatures are known, returns a none-array
+ * (test for type != array to detect missing signature)
+ *
+ * @return object  a new XML_RPC_Response object
+ */
 function XML_RPC_Server_methodSignature($server, $m)
 {
     global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
 
     $methName = $m->getParam(0);
     $methName = $methName->scalarval();
-    if (strpos($methName, "system.") === 0) {
+    if (strpos($methName, 'system.') === 0) {
         $dmap = $XML_RPC_Server_dmap;
         $sysCall = 1;
     } else {
@@ -67,40 +173,41 @@ function XML_RPC_Server_methodSignature($server, $m)
     }
     //  print "<!-- ${methName} -->\n";
     if (isset($dmap[$methName])) {
-        if ($dmap[$methName]["signature"]) {
+        if ($dmap[$methName]['signature']) {
             $sigs = array();
-            $thesigs = $dmap[$methName]["signature"];
+            $thesigs = $dmap[$methName]['signature'];
             for ($i = 0; $i < sizeof($thesigs); $i++) {
                 $cursig = array();
                 $inSig = $thesigs[$i];
                 for ($j = 0; $j < sizeof($inSig); $j++) {
-                    $cursig[] = new XML_RPC_Value($inSig[$j], "string");
+                    $cursig[] = new XML_RPC_Value($inSig[$j], 'string');
                 }
-                $sigs[] = new XML_RPC_Value($cursig, "array");
+                $sigs[] = new XML_RPC_Value($cursig, 'array');
             }
-            $r = new XML_RPC_Response(new XML_RPC_Value($sigs, "array"));
+            $r = new XML_RPC_Response(new XML_RPC_Value($sigs, 'array'));
         } else {
-            $r = new XML_RPC_Response(new XML_RPC_Value("undef", "string"));
+            $r = new XML_RPC_Response(new XML_RPC_Value('undef', 'string'));
         }
     } else {
-        $r = new XML_RPC_Response(0, $XML_RPC_err["introspect_unknown"],
-                                     $XML_RPC_str["introspect_unknown"]);
+        $r = new XML_RPC_Response(0, $XML_RPC_err['introspect_unknown'],
+                                  $XML_RPC_str['introspect_unknown']);
     }
     return $r;
 }
 
-$GLOBALS['XML_RPC_Server_methodHelp_sig'] =
-    array(array($GLOBALS['XML_RPC_String'], $GLOBALS['XML_RPC_String']));
-$GLOBALS['XML_RPC_Server_methodHelp_doc'] =
-    'Returns help text if defined for the method passed, otherwise returns an empty string';
-
+/**
+ * Returns help text if defined for the method passed, otherwise returns
+ * an empty string
+ *
+ * @return object  a new XML_RPC_Response object
+ */
 function XML_RPC_Server_methodHelp($server, $m)
 {
     global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
 
     $methName = $m->getParam(0);
     $methName = $methName->scalarval();
-    if (strpos($methName, "system.") === 0) {
+    if (strpos($methName, 'system.') === 0) {
         $dmap = $XML_RPC_Server_dmap;
         $sysCall = 1;
     } else {
@@ -109,83 +216,222 @@ function XML_RPC_Server_methodHelp($server, $m)
     }
     //  print "<!-- ${methName} -->\n";
     if (isset($dmap[$methName])) {
-        if ($dmap[$methName]["docstring"]) {
-            $r = new XML_RPC_Response(new XML_RPC_Value($dmap[$methName]["docstring"]), "string");
+        if ($dmap[$methName]['docstring']) {
+            $r = new XML_RPC_Response(new XML_RPC_Value($dmap[$methName]['docstring']),
+                                                        'string');
         } else {
-            $r = new XML_RPC_Response(new XML_RPC_Value("", "string"));
+            $r = new XML_RPC_Response(new XML_RPC_Value('', 'string'));
         }
     } else {
-        $r = new XML_RPC_Response(0, $XML_RPC_err["introspect_unknown"],
-                                     $XML_RPC_str["introspect_unknown"]);
+        $r = new XML_RPC_Response(0, $XML_RPC_err['introspect_unknown'],
+                                     $XML_RPC_str['introspect_unknown']);
     }
     return $r;
 }
 
-$GLOBALS['XML_RPC_Server_dmap'] = array(
-    "system.listMethods" =>
-        array("function"  => "XML_RPC_Server_listMethods",
-              "signature" => $GLOBALS['XML_RPC_Server_listMethods_sig'],
-              "docstring" => $GLOBALS['XML_RPC_Server_listMethods_doc']),
-
-    "system.methodHelp" =>
-        array("function"  => "XML_RPC_Server_methodHelp",
-              "signature" => $GLOBALS['XML_RPC_Server_methodHelp_sig'],
-              "docstring" => $GLOBALS['XML_RPC_Server_methodHelp_doc']),
-
-    "system.methodSignature" =>
-        array("function"  => "XML_RPC_Server_methodSignature",
-              "signature" => $GLOBALS['XML_RPC_Server_methodSignature_sig'],
-              "docstring" => $GLOBALS['XML_RPC_Server_methodSignature_doc'])
-);
-
-$GLOBALS['XML_RPC_Server_debuginfo'] = "";
-
+/**
+ * @return void
+ */
 function XML_RPC_Server_debugmsg($m)
 {
     global $XML_RPC_Server_debuginfo;
     $XML_RPC_Server_debuginfo = $XML_RPC_Server_debuginfo . $m . "\n";
 }
 
+
+/**
+ * A server for receiving and replying to XML RPC requests
+ *
+ * <code>
+ * $server = new XML_RPC_Server(
+ *     array(
+ *         'isan8' =>
+ *             array(
+ *                 'function' => 'is_8',
+ *                 'signature' =>
+ *                      array(
+ *                          array('boolean', 'int'),
+ *                          array('boolean', 'int', 'boolean'),
+ *                          array('boolean', 'string'),
+ *                          array('boolean', 'string', 'boolean'),
+ *                      ),
+ *                 'docstring' => 'Is the value an 8?'
+ *             ),
+ *     ),
+ *     1,
+ *     0
+ * ); 
+ * </code>
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill <edd@usefulinc.com>
+ * @author     Stig Bakken <stig@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/XML_RPC
+ */
 class XML_RPC_Server
 {
+    /**
+     * The dispatch map, listing the methods this server provides.
+     * @var array
+     */
     var $dmap = array();
 
-    function XML_RPC_Server($dispMap, $serviceNow = 1)
+    /**
+     * The present response's encoding
+     * @var string
+     * @see XML_RPC_Message::getEncoding()
+     */
+    var $encoding = '';
+
+    /**
+     * Debug mode (0 = off, 1 = on)
+     * @var integer
+     */
+    var $debug = 0;
+
+    /**
+     * The response's HTTP headers
+     * @var string
+     */
+    var $server_headers = '';
+
+    /**
+     * The response's XML payload
+     * @var string
+     */
+    var $server_payload = '';
+
+
+    /**
+     * Constructor for the XML_RPC_Server class
+     *
+     * @param array $dispMap   the dispatch map. An associative array
+     *                          explaining each function. The keys of the main
+     *                          array are the procedure names used by the
+     *                          clients. The value is another associative array
+     *                          that contains up to three elements:
+     *                            + The 'function' element's value is the name
+     *                              of the function or method that gets called.
+     *                              To define a class' method: 'class::method'.
+     *                            + The 'signature' element (optional) is an
+     *                              array describing the return values and
+     *                              parameters
+     *                            + The 'docstring' element (optional) is a
+     *                              string describing what the method does
+     * @param int $serviceNow  should the HTTP response be sent now?
+     *                          (1 = yes, 0 = no)
+     * @param int $debug       should debug output be displayed?
+     *                          (1 = yes, 0 = no)
+     *
+     * @return void
+     */
+    function XML_RPC_Server($dispMap, $serviceNow = 1, $debug = 0)
     {
         global $HTTP_RAW_POST_DATA;
-        // dispMap is a despatch array of methods
-        // mapped to function names and signatures
-        // if a method
-        // doesn't appear in the map then an unknown
-        // method error is generated
+
+        if ($debug) {
+            $this->debug = 1;
+        } else {
+            $this->debug = 0;
+        }
+
         $this->dmap = $dispMap;
+
         if ($serviceNow) {
             $this->service();
+        } else {
+            $this->createServerPayload();
+            $this->createServerHeaders();
         }
     }
 
+    /**
+     * @return string  the debug information if debug debug mode is on
+     */
     function serializeDebug()
     {
-        global $XML_RPC_Server_debuginfo;
-        if ($XML_RPC_Server_debuginfo != "") {
-            return "<!-- DEBUG INFO:\n\n" . preg_replace('/-(?=-)/', '- ',
-                    $XML_RPC_Server_debuginfo) . "\n-->\n";
+        global $XML_RPC_Server_debuginfo, $HTTP_RAW_POST_DATA;
+
+        if ($this->debug) {
+            XML_RPC_Server_debugmsg('vvv POST DATA RECEIVED BY SERVER vvv' . "\n"
+                                    . $HTTP_RAW_POST_DATA
+                                    . "\n" . '^^^ END POST DATA ^^^');
+        }
+
+        if ($XML_RPC_Server_debuginfo != '') {
+            return "<!-- PEAR XML_RPC SERVER DEBUG INFO:\n\n"
+                   . preg_replace('/-(?=-)/', '- ', $XML_RPC_Server_debuginfo)
+                   . "-->\n";
         } else {
-            return "";
+            return '';
         }
     }
 
+    /**
+     * Sends the response
+     *
+     * The encoding and content-type are determined by
+     * XML_RPC_Message::getEncoding()
+     *
+     * @return void
+     *
+     * @uses XML_RPC_Server::createServerPayload(),
+     *       XML_RPC_Server::createServerHeaders()
+     */
     function service()
+    {
+        $this->createServerPayload();
+        $this->createServerHeaders();
+        header($this->server_headers);
+        print $this->server_payload;
+    }
+
+    /**
+     * Generates the payload and puts it in the $server_payload property
+     *
+     * @return void
+     *
+     * @uses XML_RPC_Server::parseRequest(), XML_RPC_Server::$encoding,
+     *       XML_RPC_Response::serialize(), XML_RPC_Server::serializeDebug()
+     */
+    function createServerPayload()
     {
         $r = $this->parseRequest();
-        $payload = "<?xml version=\"1.0\"?>\n" .
-            $this->serializeDebug() .
-            $r->serialize();
-        header('Content-Length: ' . strlen($payload));
-        header('Content-Type: text/xml');
-        print $payload;
+        $this->server_payload = '<?xml version="1.0" encoding="'
+                              . $this->encoding . '"?>' . "\n"
+                              . $this->serializeDebug()
+                              . $r->serialize();
     }
 
+    /**
+     * Determines the HTTP headers and puts them in the $server_headers
+     * property
+     *
+     * @return boolean  TRUE if okay, FALSE if $server_payload isn't set.
+     *
+     * @uses XML_RPC_Server::createServerPayload(),
+     *       XML_RPC_Server::$server_headers
+     */
+    function createServerHeaders()
+    {
+        if (!$this->server_payload) {
+            return false;
+        }
+        $this->server_headers = 'Content-Length: '
+                              . strlen($this->server_payload) . "\r\n"
+                              . 'Content-Type: text/xml;'
+                              . ' charset=' . $this->encoding;
+        return true;
+    }
+
+    /**
+     * @return array
+     */
     function verifySignature($in, $sig)
     {
         for ($i = 0; $i < sizeof($sig); $i++) {
@@ -196,7 +442,7 @@ class XML_RPC_Server
                 for ($n = 0; $n < $in->getNumParams(); $n++) {
                     $p = $in->getParam($n);
                     // print "<!-- $p -->\n";
-                    if ($p->kindOf() == "scalar") {
+                    if ($p->kindOf() == 'scalar') {
                         $pt = $p->scalartyp();
                     } else {
                         $pt = $p->kindOf();
@@ -210,103 +456,157 @@ class XML_RPC_Server
                         break;
                     }
                 }
-                if ($itsOK)
+                if ($itsOK) {
                     return array(1);
+                }
             }
         }
-        return array(0, "Wanted ${wanted}, got ${got} at param ${pno})");
+        if (isset($wanted)) {
+            return array(0, "Wanted ${wanted}, got ${got} at param ${pno}");
+        } else {
+            $allowed = array();
+            foreach ($sig as $val) {
+                end($val);
+                $allowed[] = key($val);
+            }
+            $allowed = array_unique($allowed);
+            $last = count($allowed) - 1;
+            if ($last > 0) {
+                $allowed[$last] = 'or ' . $allowed[$last];
+            }
+            return array(0,
+                         'Signature permits ' . implode(', ', $allowed)
+                                . ' parameters but the request had '
+                                . $in->getNumParams());
+        }
     }
 
-    function parseRequest($data = "")
+    /**
+     * @return object  a new XML_RPC_Response object
+     *
+     * @uses XML_RPC_Message::getEncoding(), XML_RPC_Server::$encoding
+     */
+    function parseRequest($data = '')
     {
-        global $XML_RPC_xh,$HTTP_RAW_POST_DATA;
-        global $XML_RPC_err, $XML_RPC_str, $XML_RPC_errxml,
-            $XML_RPC_defencoding, $XML_RPC_Server_dmap;
+        global $XML_RPC_xh, $HTTP_RAW_POST_DATA,
+                $XML_RPC_err, $XML_RPC_str, $XML_RPC_errxml,
+                $XML_RPC_defencoding, $XML_RPC_Server_dmap;
 
-        if ($data == "") {
+        if ($data == '') {
             $data = $HTTP_RAW_POST_DATA;
         }
-        $parser = xml_parser_create($XML_RPC_defencoding);
+
+        $this->encoding = XML_RPC_Message::getEncoding($data);
+        $parser_resource = xml_parser_create($this->encoding);
+        $parser = (int) $parser_resource;
 
         $XML_RPC_xh[$parser] = array();
-        $XML_RPC_xh[$parser]['st'] = "";
-        $XML_RPC_xh[$parser]['cm'] = 0;
-        $XML_RPC_xh[$parser]['isf'] = 0;
+        $XML_RPC_xh[$parser]['st']     = '';
+        $XML_RPC_xh[$parser]['cm']     = 0;
+        $XML_RPC_xh[$parser]['isf']    = 0;
         $XML_RPC_xh[$parser]['params'] = array();
-        $XML_RPC_xh[$parser]['method'] = "";
+        $XML_RPC_xh[$parser]['method'] = '';
 
         $plist = '';
 
         // decompose incoming XML into request structure
 
-        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
-        xml_set_element_handler($parser, "XML_RPC_se", "XML_RPC_ee");
-        xml_set_character_data_handler($parser, "XML_RPC_cd");
-        xml_set_default_handler($parser, "XML_RPC_dh");
-        if (!xml_parse($parser, $data, 1)) {
+        xml_parser_set_option($parser_resource, XML_OPTION_CASE_FOLDING, true);
+        xml_set_element_handler($parser_resource, 'XML_RPC_se', 'XML_RPC_ee');
+        xml_set_character_data_handler($parser_resource, 'XML_RPC_cd');
+        if (!xml_parse($parser_resource, $data, 1)) {
             // return XML error as a faultCode
             $r = new XML_RPC_Response(0,
-                                      $XML_RPC_errxml+xml_get_error_code($parser),
-                                      sprintf("XML error: %s at line %d",
-                                              xml_error_string(xml_get_error_code($parser)),
-                                              xml_get_current_line_number($parser)));
-            xml_parser_free($parser);
+                                      $XML_RPC_errxml+xml_get_error_code($parser_resource),
+                                      sprintf('XML error: %s at line %d',
+                                              xml_error_string(xml_get_error_code($parser_resource)),
+                                              xml_get_current_line_number($parser_resource)));
+            xml_parser_free($parser_resource);
         } else {
-            xml_parser_free($parser);
+            xml_parser_free($parser_resource);
             $m = new XML_RPC_Message($XML_RPC_xh[$parser]['method']);
             // now add parameters in
             for ($i = 0; $i < sizeof($XML_RPC_xh[$parser]['params']); $i++) {
-                // print "<!-- " . $XML_RPC_xh[$parser]['params'][$i]. "-->\n";
+                // print '<!-- ' . $XML_RPC_xh[$parser]['params'][$i]. "-->\n";
                 $plist .= "$i - " . $XML_RPC_xh[$parser]['params'][$i] . " \n";
-                eval('$m->addParam(' . $XML_RPC_xh[$parser]['params'][$i] . ");");
+                eval('$m->addParam(' . $XML_RPC_xh[$parser]['params'][$i] . ');');
             }
             XML_RPC_Server_debugmsg($plist);
+
             // now to deal with the method
             $methName = $XML_RPC_xh[$parser]['method'];
-            if (strpos($methName, "system.") === 0) {
+            if (strpos($methName, 'system.') === 0) {
                 $dmap = $XML_RPC_Server_dmap;
                 $sysCall = 1;
             } else {
                 $dmap = $this->dmap;
                 $sysCall = 0;
             }
-            if (isset($dmap[$methName]['function']) && is_callable($dmap[$methName]['function'])) {
+
+            if (isset($dmap[$methName]['function'])
+                && is_string($dmap[$methName]['function'])
+                && strpos($dmap[$methName]['function'], '::') !== false)
+            {
+                $dmap[$methName]['function'] =
+                        explode('::', $dmap[$methName]['function']);
+            }
+
+            if (isset($dmap[$methName]['function'])
+                && is_callable($dmap[$methName]['function']))
+            {
                 // dispatch if exists
                 if (isset($dmap[$methName]['signature'])) {
                     $sr = $this->verifySignature($m,
                                                  $dmap[$methName]['signature'] );
                 }
-                if ( (!isset($dmap[$methName]['signature'])) || $sr[0]) {
+                if (!isset($dmap[$methName]['signature']) || $sr[0]) {
                     // if no signature or correct signature
                     if ($sysCall) {
                         $r = call_user_func($dmap[$methName]['function'], $this, $m);
                     } else {
                         $r = call_user_func($dmap[$methName]['function'], $m);
                     }
+                    if (!is_a($r, 'XML_RPC_Response')) {
+                        $r = new XML_RPC_Response(0, $XML_RPC_err['not_response_object'],
+                                                  $XML_RPC_str['not_response_object']);
+                    }
                 } else {
-                    $r = new XML_RPC_Response(0, $XML_RPC_err["incorrect_params"],
-                                                 $XML_RPC_str["incorrect_params"] .
-                                                 ": " . $sr[1]);
+                    $r = new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'],
+                                              $XML_RPC_str['incorrect_params']
+                                              . ': ' . $sr[1]);
                 }
             } else {
                 // else prepare error response
-                $r = new XML_RPC_Response(0, $XML_RPC_err["unknown_method"],
-                                             $XML_RPC_str["unknown_method"]);
+                $r = new XML_RPC_Response(0, $XML_RPC_err['unknown_method'],
+                                          $XML_RPC_str['unknown_method']);
             }
         }
         return $r;
     }
 
-  function echoInput() {
+    /**
+     * Echos back the input packet as a string value
+     *
+     * @return void
+     *
+     * Useful for debugging.
+     */
+    function echoInput()
+    {
         global $HTTP_RAW_POST_DATA;
 
-        // a debugging routine: just echos back the input
-        // packet as a string value
-
-        $r = new XML_RPC_Response;
-        $r->xv = new XML_RPC_Value("'Aha said I: '" . $HTTP_RAW_POST_DATA, "string");
+        $r = new XML_RPC_Response(0);
+        $r->xv = new XML_RPC_Value("'Aha said I: '" . $HTTP_RAW_POST_DATA, 'string');
         print $r->serialize();
-  }
+    }
 }
 
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * c-hanging-comment-ender-p: nil
+ * End:
+ */
+
 ?>
index 5055e4220d7402332a6537bb119c675758b2b7c6..1ad27dea09fb0eec01c81dc39d451d67af2a89f0 100644 (file)
--- a/docs/NEWS
+++ b/docs/NEWS
@@ -124,9 +124,15 @@ Version 0.9 ()
     * "Comments" Sidebar plugin can now have a custom title
       (garvinhicking)
 
-Version 0.8.2 ()
+Version 0.8.2 (June 29th, 2005)
 ------------------------------------------------------------------------
 
+    * fixed remote code execution vulnerability. Thanks to Gulftech 
+      Research for pointing out that bug and Stefan Esser for helping 
+      fix it (nohn) 
+    * Updated Spartacus to most recent version (nohn) 
+
     * fixed serendipity_traversePath() -  PHP5 issue with array_merge()
       Thanks to jdhawk for the fix (flotsam)