- remove a test for php versions lower than 5.0
[plcapi.git] / lib / xmlrpcs.inc
index 9fc4d0c..97035e4 100644 (file)
                $outAr=array();\r
                foreach($server->dmap as $key => $val)\r
                {\r
-                       $outAr[]=&new xmlrpcval($key, 'string');\r
+                       $outAr[]=new xmlrpcval($key, 'string');\r
                }\r
                if($server->allow_system_funcs)\r
                {\r
                        foreach($GLOBALS['_xmlrpcs_dmap'] as $key => $val)\r
                        {\r
-                               $outAr[]=&new xmlrpcval($key, 'string');\r
+                               $outAr[]=new xmlrpcval($key, 'string');\r
                        }\r
                }\r
                return new xmlrpcresp(new xmlrpcval($outAr, 'array'));\r
                                        $cursig=array();\r
                                        foreach($inSig as $sig)\r
                                        {\r
-                                               $cursig[]=&new xmlrpcval($sig, 'string');\r
+                                               $cursig[]=new xmlrpcval($sig, 'string');\r
                                        }\r
-                                       $sigs[]=&new xmlrpcval($cursig, 'array');\r
+                                       $sigs[]=new xmlrpcval($cursig, 'array');\r
                                }\r
-                               $r=&new xmlrpcresp(new xmlrpcval($sigs, 'array'));\r
+                               $r=new xmlrpcresp(new xmlrpcval($sigs, 'array'));\r
                        }\r
                        else\r
                        {\r
                                // NB: according to the official docs, we should be returning a\r
                                // "none-array" here, which means not-an-array\r
-                               $r=&new xmlrpcresp(new xmlrpcval('undef', 'string'));\r
+                               $r=new xmlrpcresp(new xmlrpcval('undef', 'string'));\r
                        }\r
                }\r
                else\r
                {\r
-                       $r=&new xmlrpcresp(0,$GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);\r
+                       $r=new xmlrpcresp(0,$GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);\r
                }\r
                return $r;\r
        }\r
                {\r
                        if(isset($dmap[$methName]['docstring']))\r
                        {\r
-                               $r=&new xmlrpcresp(new xmlrpcval($dmap[$methName]['docstring']), 'string');\r
+                               $r=new xmlrpcresp(new xmlrpcval($dmap[$methName]['docstring']), 'string');\r
                        }\r
                        else\r
                        {\r
-                               $r=&new xmlrpcresp(new xmlrpcval('', 'string'));\r
+                               $r=new xmlrpcresp(new xmlrpcval('', 'string'));\r
                        }\r
                }\r
                else\r
                {\r
-                       $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);\r
+                       $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);\r
                }\r
                return $r;\r
        }\r
                        $str = $err->faultString();\r
                }\r
                $struct = array();\r
-               $struct['faultCode'] =& new xmlrpcval($code, 'int');\r
-               $struct['faultString'] =& new xmlrpcval($str, 'string');\r
+               $struct['faultCode'] = new xmlrpcval($code, 'int');\r
+               $struct['faultString'] = new xmlrpcval($str, 'string');\r
                return new xmlrpcval($struct, 'struct');\r
        }\r
 \r
                }\r
                $numParams = $params->arraysize();\r
 \r
-               $msg =& new xmlrpcmsg($methName->scalarval());\r
+               $msg = new xmlrpcmsg($methName->scalarval());\r
                for($i = 0; $i < $numParams; $i++)\r
                {\r
                        if(!$msg->addParam($params->arraymem($i)))\r
 \r
        $GLOBALS['_xmlrpcs_occurred_errors'] = '';\r
        $GLOBALS['_xmlrpcs_prev_ehandler'] = '';\r
+\r
        /**\r
        * Error handler used to track errors that occur during server-side execution of PHP code.\r
        * This allows to report back to the client whether an internal error has occurred or not\r
                        return;\r
 \r
                //if($errcode != E_NOTICE && $errcode != E_WARNING && $errcode != E_USER_NOTICE && $errcode != E_USER_WARNING)\r
-               if($errcode != 2048) // do not use E_STRICT by name, since on PHP 4 it will not be defined\r
+               if($errcode != E_STRICT)\r
                {\r
                        $GLOBALS['_xmlrpcs_occurred_errors'] = $GLOBALS['_xmlrpcs_occurred_errors'] . $errstring . "\n";\r
                }\r
 \r
        class xmlrpc_server\r
        {\r
-               /// array defining php functions exposed as xmlrpc methods by this server\r
+               /**\r
+               * Array defining php functions exposed as xmlrpc methods by this server\r
+               * @access private\r
+               */\r
                var $dmap=array();\r
                /**\r
-               * Defines how functions in dmap will be invokde: either using an xmlrpc msg object\r
+               * Defines how functions in dmap will be invoked: either using an xmlrpc msg object\r
                * or plain php values.\r
                * valid strings are 'xmlrpcvals', 'phpvals' or 'epivals'\r
                */\r
                var $functions_parameters_type='xmlrpcvals';\r
+               /**\r
+               * Option used for fine-tuning the encoding the php values returned from\r
+               * functions registered in the dispatch map when the functions_parameters_types\r
+               * member is set to 'phpvals'\r
+               * @see php_xmlrpc_encode for a list of values\r
+               */\r
+               var $phpvals_encoding_options = array( 'auto_dates' );\r
                /// controls wether the server is going to echo debugging messages back to the client as comments in response body. valid values: 0,1,2,3\r
                var $debug = 1;\r
                /**\r
+               * Controls behaviour of server when invoked user function throws an exception:\r
+               * 0 = catch it and return an 'internal error' xmlrpc response (default)\r
+               * 1 = catch it and return an xmlrpc response with the error corresponding to the exception\r
+               * 2 = allow the exception to float to the upper layers\r
+               */\r
+               var $exception_handling = 0;\r
+               /**\r
                * When set to true, it will enable HTTP compression of the response, in case\r
                * the client has declared its support for compression in the request.\r
                */\r
                * NB: pretty dangerous if you accept every charset and do not have mbstring enabled)\r
                */\r
                var $response_charset_encoding = '';\r
-               /// storage for internal debug info\r
+               /**\r
+               * Storage for internal debug info\r
+               * @access private\r
+               */\r
                var $debug_info = '';\r
-               /// extra data passed at runtime to method handling functions. Used only by EPI layer\r
+               /**\r
+               * Extra data passed at runtime to method handling functions. Used only by EPI layer\r
+               */\r
                var $user_data = null;\r
 \r
                /**\r
                        if ($data === null)\r
                        {\r
                                // workaround for a known bug in php ver. 5.2.2 that broke $HTTP_RAW_POST_DATA\r
-                               $ver = phpversion();\r
-                               if ($ver[0] >= 5)\r
-                               {\r
-                                       $data = file_get_contents('php://input');\r
-                               }\r
-                               else\r
-                               {\r
-                                       $data = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';\r
-                               }\r
+                $data = file_get_contents('php://input');\r
                        }\r
                        $raw_data = $data;\r
 \r
                        }\r
                        else\r
                        {\r
-                               error_log('XML-RPC: xmlrpc_server::service: http headers already sent before response is fully generated. Check for php warning or error messages');\r
+                               error_log('XML-RPC: '.__METHOD__.': http headers already sent before response is fully generated. Check for php warning or error messages');\r
                        }\r
 \r
                        print $payload;\r
                        }\r
                        if ($sigdoc)\r
                        {\r
-                           $this->dmap[$methodname]['signature_docs'] = $sigdoc;\r
+                               $this->dmap[$methodname]['signature_docs'] = $sigdoc;\r
                        }\r
                }\r
 \r
                                                }\r
                                                else\r
                                                {\r
-                                                       $pt= $in[$n] == 'i4' ? 'int' : $in[$n]; // dispatch maps never use i4...\r
+                                                       $pt= $in[$n] == 'i4' ? 'int' : strtolower($in[$n]); // dispatch maps never use i4...\r
                                                }\r
 \r
                                                // param index is $n+1, as first member of sig is return type\r
                */\r
                function parseRequestHeaders(&$data, &$req_encoding, &$resp_encoding, &$resp_compression)\r
                {\r
-                       // Play nice to PHP 4.0.x: superglobals were not yet invented...\r
-                       if(!isset($_SERVER))\r
+                       // check if $_SERVER is populated: it might have been disabled via ini file\r
+                       // (this is true even when in CLI mode)\r
+                       if (count($_SERVER) == 0)\r
                        {\r
-                               $_SERVER = $GLOBALS['HTTP_SERVER_VARS'];\r
+                               error_log('XML-RPC: '.__METHOD__.': cannot parse request headers as $_SERVER is not populated');\r
                        }\r
 \r
                        if($this->debug > 1)\r
                                                }\r
                                                else\r
                                                {\r
-                                                       $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_decompress_fail'], $GLOBALS['xmlrpcstr']['server_decompress_fail']);\r
+                                                       $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_decompress_fail'], $GLOBALS['xmlrpcstr']['server_decompress_fail']);\r
                                                        return $r;\r
                                                }\r
                                        }\r
                                        else\r
                                        {\r
                                                //error_log('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');\r
-                                               $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_cannot_decompress'], $GLOBALS['xmlrpcstr']['server_cannot_decompress']);\r
+                                               $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_cannot_decompress'], $GLOBALS['xmlrpcstr']['server_cannot_decompress']);\r
                                                return $r;\r
                                        }\r
                                }\r
                                // makes the lib about 200% slower...\r
                                //if (!is_valid_charset($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))\r
                                {\r
-                                       error_log('XML-RPC: xmlrpc_server::parseRequest: invalid charset encoding of received request: '.$req_encoding);\r
+                                       error_log('XML-RPC: '.__METHOD__.': invalid charset encoding of received request: '.$req_encoding);\r
                                        $req_encoding = $GLOBALS['xmlrpc_defencoding'];\r
                                }\r
                                /// @BUG this will fail on PHP 5 if charset is not specified in the xml prologue,\r
                        if(!xml_parse($parser, $data, 1))\r
                        {\r
                                // return XML error as a faultCode\r
-                               $r=&new xmlrpcresp(0,\r
+                               $r=new xmlrpcresp(0,\r
                                $GLOBALS['xmlrpcerrxml']+xml_get_error_code($parser),\r
                                sprintf('XML error: %s at line %d, column %d',\r
                                        xml_error_string(xml_get_error_code($parser)),\r
                        elseif ($GLOBALS['_xh']['isf'])\r
                        {\r
                                xml_parser_free($parser);\r
-                               $r=&new xmlrpcresp(0,\r
+                               $r=new xmlrpcresp(0,\r
                                        $GLOBALS['xmlrpcerr']['invalid_request'],\r
                                        $GLOBALS['xmlrpcstr']['invalid_request'] . ' ' . $GLOBALS['_xh']['isf_reason']);\r
                        }\r
                        else\r
                        {\r
                                xml_parser_free($parser);\r
-                               if ($this->functions_parameters_type != 'xmlrpcvals')\r
+                               // small layering violation in favor of speed and memory usage:\r
+                               // we should allow the 'execute' method handle this, but in the\r
+                               // most common scenario (xmlrpcvals type server with some methods\r
+                               // registered as phpvals) that would mean a useless encode+decode pass\r
+                               if ($this->functions_parameters_type != 'xmlrpcvals' || (isset($this->dmap[$GLOBALS['_xh']['method']]['parameters_type']) && ($this->dmap[$GLOBALS['_xh']['method']]['parameters_type'] == 'phpvals')))\r
                                {\r
                                        if($this->debug > 1)\r
                                        {\r
                                else\r
                                {\r
                                        // build an xmlrpcmsg object with data parsed from xml\r
-                                       $m=&new xmlrpcmsg($GLOBALS['_xh']['method']);\r
+                                       $m=new xmlrpcmsg($GLOBALS['_xh']['method']);\r
                                        // now add parameters in\r
                                        for($i=0; $i<count($GLOBALS['_xh']['params']); $i++)\r
                                        {\r
                        // verify that function to be invoked is in fact callable\r
                        if(!is_callable($func))\r
                        {\r
-                               error_log("XML-RPC: xmlrpc_server::execute: function $func registered as method handler is not callable");\r
+                               error_log("XML-RPC: ".__METHOD__.": function $func registered as method handler is not callable");\r
                                return new xmlrpcresp(\r
                                        0,\r
                                        $GLOBALS['xmlrpcerr']['server_error'],\r
                        {\r
                                $GLOBALS['_xmlrpcs_prev_ehandler'] = set_error_handler('_xmlrpcs_errorHandler');\r
                        }\r
-                       if (is_object($m))\r
+                       try\r
                        {\r
-                               if($sysCall)\r
-                               {\r
-                                       $r = call_user_func($func, $this, $m);\r
-                               }\r
-                               else\r
-                               {\r
-                                       $r = call_user_func($func, $m);\r
-                               }\r
-                               if (!is_a($r, 'xmlrpcresp'))\r
+                               // Allow mixed-convention servers\r
+                               if (is_object($m))\r
                                {\r
-                                       error_log("XML-RPC: xmlrpc_server::execute: function $func registered as method handler does not return an xmlrpcresp object");\r
-                                       if (is_a($r, 'xmlrpcval'))\r
+                                       if($sysCall)\r
                                        {\r
-                                               $r =& new xmlrpcresp($r);\r
+                                               $r = call_user_func($func, $this, $m);\r
                                        }\r
                                        else\r
                                        {\r
-                                               $r =& new xmlrpcresp(\r
-                                                       0,\r
-                                                       $GLOBALS['xmlrpcerr']['server_error'],\r
-                                                       $GLOBALS['xmlrpcstr']['server_error'] . ": function does not return xmlrpcresp object"\r
-                                               );\r
+                                               $r = call_user_func($func, $m);\r
+                                       }\r
+                                       if (!is_a($r, 'xmlrpcresp'))\r
+                                       {\r
+                                               error_log("XML-RPC: ".__METHOD__.": function $func registered as method handler does not return an xmlrpcresp object");\r
+                                               if (is_a($r, 'xmlrpcval'))\r
+                                               {\r
+                                                       $r = new xmlrpcresp($r);\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       $r = new xmlrpcresp(\r
+                                                               0,\r
+                                                               $GLOBALS['xmlrpcerr']['server_error'],\r
+                                                               $GLOBALS['xmlrpcstr']['server_error'] . ": function does not return xmlrpcresp object"\r
+                                                       );\r
+                                               }\r
                                        }\r
-                               }\r
-                       }\r
-                       else\r
-                       {\r
-                               // call a 'plain php' function\r
-                               if($sysCall)\r
-                               {\r
-                                       array_unshift($params, $this);\r
-                                       $r = call_user_func_array($func, $params);\r
                                }\r
                                else\r
                                {\r
-                                       // 3rd API convention for method-handling functions: EPI-style\r
-                                       if ($this->functions_parameters_type == 'epivals')\r
+                                       // call a 'plain php' function\r
+                                       if($sysCall)\r
                                        {\r
-                                               $r = call_user_func_array($func, array($methName, $params, $this->user_data));\r
-                                               // mimic EPI behaviour: if we get an array that looks like an error, make it\r
-                                               // an eror response\r
-                                               if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r))\r
+                                               array_unshift($params, $this);\r
+                                               $r = call_user_func_array($func, $params);\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               // 3rd API convention for method-handling functions: EPI-style\r
+                                               if ($this->functions_parameters_type == 'epivals')\r
                                                {\r
-                                                       $r =& new xmlrpcresp(0, (integer)$r['faultCode'], (string)$r['faultString']);\r
+                                                       $r = call_user_func_array($func, array($methName, $params, $this->user_data));\r
+                                                       // mimic EPI behaviour: if we get an array that looks like an error, make it\r
+                                                       // an eror response\r
+                                                       if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r))\r
+                                                       {\r
+                                                               $r = new xmlrpcresp(0, (integer)$r['faultCode'], (string)$r['faultString']);\r
+                                                       }\r
+                                                       else\r
+                                                       {\r
+                                                               // functions using EPI api should NOT return resp objects,\r
+                                                               // so make sure we encode the return type correctly\r
+                                                               $r = new xmlrpcresp(php_xmlrpc_encode($r, array('extension_api')));\r
+                                                       }\r
                                                }\r
                                                else\r
                                                {\r
-                                                       // functions using EPI api should NOT return resp objects,\r
-                                                       // so make sure we encode the return type correctly\r
-                                                       $r =& new xmlrpcresp(php_xmlrpc_encode($r, array('extension_api')));\r
+                                                       $r = call_user_func_array($func, $params);\r
                                                }\r
                                        }\r
-                                       else\r
+                                       // the return type can be either an xmlrpcresp object or a plain php value...\r
+                                       if (!is_a($r, 'xmlrpcresp'))\r
                                        {\r
-                                               $r = call_user_func_array($func, $params);\r
+                                               // what should we assume here about automatic encoding of datetimes\r
+                                               // and php classes instances???\r
+                                               $r = new xmlrpcresp(php_xmlrpc_encode($r, $this->phpvals_encoding_options));\r
                                        }\r
                                }\r
-                               // the return type can be either an xmlrpcresp object or a plain php value...\r
-                               if (!is_a($r, 'xmlrpcresp'))\r
+                       }\r
+                       catch(Exception $e)\r
+                       {\r
+                               // (barring errors in the lib) an uncatched exception happened\r
+                               // in the called function, we wrap it in a proper error-response\r
+                               switch($this->exception_handling)\r
                                {\r
-                                       // what should we assume here about automatic encoding of datetimes\r
-                                       // and php classes instances???\r
-                                       $r =& new xmlrpcresp(php_xmlrpc_encode($r, array('auto_dates')));\r
+                                       case 2:\r
+                                               throw $e;\r
+                                               break;\r
+                                       case 1:\r
+                                               $r = new xmlrpcresp(0, $e->getCode(), $e->getMessage());\r
+                                               break;\r
+                                       default:\r
+                                               $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_error'], $GLOBALS['xmlrpcstr']['server_error']);\r
                                }\r
                        }\r
                        if($this->debug > 2)\r
                */\r
                function echoInput()\r
                {\r
-                       $r=&new xmlrpcresp(new xmlrpcval( "'Aha said I: '" . $GLOBALS['HTTP_RAW_POST_DATA'], 'string'));\r
+                       $r=new xmlrpcresp(new xmlrpcval( "'Aha said I: '" . $GLOBALS['HTTP_RAW_POST_DATA'], 'string'));\r
                        print $r->serialize();\r
                }\r
        }\r