topical media & game development
mashup-delicious-07-delicious-summary2-lib-xmlrpcs.inc / inc
<?php
// by Edd Dumbill (C) 1999-2002
// <edd@usefulinc.com>
//
// Copyright (c) 1999,2000,2002 Edd Dumbill.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// * Neither the name of the "XML-RPC for PHP" nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS 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
// REGENTS OR CONTRIBUTORS 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.
// XML RPC Server class
// requires: xmlrpc.inc
/* Functions that implement system.XXX methods of xmlrpc servers */
// listMethods: signature was either a string, or nothing.
// The useless string variant has been removed
GLOBALS['xmlrpcArray']));
_xmlrpcs_listMethods_sdoc=array(array('list of method names'));
function _xmlrpcs_listMethods(m=null) // if called in plain php values mode, second param is missing
{
server->dmap as val)
{
key, 'string');
}
if(GLOBALS['_xmlrpcs_dmap'] as val)
{
key, 'string');
}
}
outAr, 'array');
return new xmlrpcresp(_xmlrpcs_methodSignature_sig=array(array(GLOBALS['xmlrpcString']));
_xmlrpcs_methodSignature_sdoc=array(array('list of known signatures, each sig being an array of xmlrpc type names', 'name of method to be described'));
function _xmlrpcs_methodSignature(m)
{
// let accept as parameter both an xmlrpcval or string
if (is_object(methName=methName=methName=methName, "system.") === 0)
{
GLOBALS['_xmlrpcs_dmap']; dmap=sysCall=0;
}
if(isset(methName]))
{
if(isset(methName]['signature']))
{
dmap[inSig)
{
inSig as cursig[]=&new xmlrpcval(sigs[]=&new xmlrpcval(r=&new xmlrpcresp(new xmlrpcval(r=&new xmlrpcresp(new xmlrpcval('undef', 'string'));
}
}
else
{
GLOBALS['xmlrpcerr']['introspect_unknown'], r;
}
GLOBALS['xmlrpcString'], _xmlrpcs_methodHelp_doc='Returns help text if defined for the method passed, otherwise returns an empty string';
server, m))
{
m->getParam(0);
methName->scalarval();
}
else
{
m;
}
if(strpos(dmap=sysCall=1;
}
else
{
server->dmap; dmap[dmap[r=&new xmlrpcresp(new xmlrpcval(methName]['docstring']), 'string');
}
else
{
r=&new xmlrpcresp(0, GLOBALS['xmlrpcstr']['introspect_unknown']);
}
return _xmlrpcs_multicall_sig = array(array(GLOBALS['xmlrpcArray']));
_xmlrpcs_multicall_sdoc = array(array('list of response structs, where each struct has the usual members', 'list of calls, with each call being represented as a struct, with members "methodname" and "params"'));
function _xmlrpcs_multicall_error(err))
{
GLOBALS['xmlrpcstr']["multicall_code = {err}"];
}
else
{
err->faultCode();
err->faultString();
}
struct['faultCode'] =& new xmlrpcval(struct['faultString'] =& new xmlrpcval(struct, 'struct');
}
function _xmlrpcs_multicall_do_call(call)
{
if(methName = @methName)
{
return _xmlrpcs_multicall_error('nomethod');
}
if(methName->scalartyp() != 'string')
{
return _xmlrpcs_multicall_error('notstring');
}
if(params = @params)
{
return _xmlrpcs_multicall_error('noparams');
}
if(numParams = msg =& new xmlrpcmsg(i = 0; numParams; msg->addParam(i)))
{
GLOBALS['xmlrpcerr']['incorrect_params'],
i));
}
}
server->execute(result->faultCode() != 0)
{
return _xmlrpcs_multicall_error(result->value()), 'array');
}
function _xmlrpcs_multicall_do_call_phpvals(call)
{
if(!is_array(call))
{
return _xmlrpcs_multicall_error('nomethod');
}
if (!is_string(call['methodName'] == 'system.multicall')
{
return _xmlrpcs_multicall_error('recursion');
}
if(!array_key_exists('params', call['params']))
{
return _xmlrpcs_multicall_error('notarray');
}
// this is a real dirty and simplistic hack, since we might have received a
// base64 or datetime values, but they will be listed as strings here...
call['params']);
call['params'] as pt[] = php_2_xmlrpc_type(gettype(result = call['methodName'], pt);
if(result); // Method returned fault.
}
return new xmlrpcval(array(server, result = array();
// let accept a plain list of php parameters, beside a single xmlrpc msg object
if (is_object(calls = numCalls = i = 0; numCalls; call = i);
i] = _xmlrpcs_multicall_do_call(call);
}
}
else
{
m);
for(i < i++)
{
i] = _xmlrpcs_multicall_do_call_phpvals(m[result, 'array'));
}
_xmlrpcs_listMethods_sig,
'docstring' => _xmlrpcs_listMethods_sdoc),
'system.methodHelp' => array(
'function' => '_xmlrpcs_methodHelp',
'signature' => _xmlrpcs_methodHelp_doc,
'signature_docs' => _xmlrpcs_methodSignature_sig,
'docstring' => _xmlrpcs_methodSignature_sdoc),
'system.multicall' => array(
'function' => '_xmlrpcs_multicall',
'signature' => _xmlrpcs_multicall_doc,
'signature_docs' => GLOBALS['_xmlrpcs_occurred_errors'] = '';
Error handler used to track errors that occur during server-side execution of PHP code.
This allows to report back to the client whether an internal error has occurred or not
using an xmlrpc response object, instead of letting the client deal with the html junk
that a PHP execution error on the server generally entails.
NB: in fact a user defined error handler can only handle WARNING, NOTICE and USER_* errors.
function _xmlrpcs_errorHandler(errcode, filename=null, context=null)
{
// obey the @ protocol
if (error_reporting() == 0)
return;
//if(errcode != E_WARNING && errcode != E_USER_WARNING)
if(GLOBALS['_xmlrpcs_occurred_errors'] = errstring . "\n";
}
// Try to avoid as much as possible disruption to the previous error handling
// mechanism in place
if(errcode))
{
error_log(GLOBALS['_xmlrpcs_prev_ehandler'] != '_xmlrpcs_errorHandler')
{
// NB: this code will NOT work on php < 4.0.2: only 2 params were used for error handlers
if(is_array(GLOBALS['_xmlrpcs_prev_ehandler'][0]->errcode, filename, context);
}
else
{
errcode, filename, context);
}
}
}
}
Add a string to the debug info that can be later seralized by the server
as part of the response message.
Note that for best compatbility, the debug string should be encoded using
the GLOBALS['xmlrpc_internalencoding'] character set.
parameter: string
function xmlrpc_debugmsg(m)
{
m . "\n";
}
class xmlrpc_server
{
array defining php functions exposed as xmlrpc methods by this server
var
functions_parameters_type='xmlrpcvals';
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
var
compress_response = false;
List of http compression methods accepted by the server for requests.
NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
var shall we serve calls to system.* methods?
var allow_system_funcs = true;
list of charset encodings natively accepted for requests
var
response_charset_encoding = '';
storage for internal debug info
var
user_data = null;
parameter: array servicenow set to false to prevent the server from runnung upon construction
function xmlrpc_server(serviceNow=true)
{
// if ZLIB is enabled, let the server by default accept compressed requests,
// and compress responses sent to clients that support them
if(function_exists('gzinflate'))
{
this->compress_response = true;
}
// by default the xml parser can support these 3 charset encodings
dispMap)
{
dispMap;
if(this->service();
}
}
}
Set debug level of server.
parameter: integer
function setDebug(in)
{
in;
}
Return a string with the serialized representation of all debug info
parameter: string
function serializeDebug(charset_encoding='')
{
// Tough encoding problem: which internal charset should we assume for debug info?
// It might contain a copy of raw data received from client, ie with unknown encoding,
// intermixed with php generated data and user generated data...
// so we split it: system debug is base 64 encoded,
// user debug info should be encoded by the end user using the INTERNAL_ENCODING
this->debug_info != '')
{
this->debug_info)."\n-->\n";
}
if(out .= "<!-- DEBUG INFO:\n" . xmlrpc_encode_entitites(str_replace('--', '_-', GLOBALS['xmlrpc_internalencoding'], out .= "<![CDATA[ DEBUG INFO:\n\n" . str_replace(']]>', ']_]_>', out;
}
Execute the xmlrpc request, printing the response
parameter: string
function service(data=null, data === null)
{
GLOBALS['HTTP_RAW_POST_DATA']) ? raw_data = this->debug_info = '';
// Echo back what we received, before parsing it
if(this->debugmsg("+++GOT+++\n" . r = data, resp_charset, r)
{
this->parseRequest(req_charset);
}
// save full body of request into response, for more debugging usages
raw_data;
if(GLOBALS['_xmlrpcs_occurred_errors'])
{
GLOBALS['_xmlrpcs_occurred_errors'] . "+++END+++");
}
this->xml_header(this->debug > 0)
{
payload . resp_charset);
}
// G. Giunta 2006-01-27: do not create response serialization if it has
// already happened. Helps building json magic
if (empty(r->serialize(payload = r->payload;
if (payload;
}
// if we get a warning/error that has output some text before here, then we cannot
// add a new header. We cannot say we are sending xml, either...
if(!headers_sent())
{
header('Content-Type: '.php_no_self_compress = ini_get('zlib.output_compression') == '' && (ini_get('output_handler') != 'ob_gzhandler');
if(resp_encoding != ''
&& resp_encoding, 'gzip') !== false)
{
payload);
header("Content-Encoding: gzip");
header("Vary: Accept-Encoding");
}
elseif (strpos(payload = gzcompress(php_no_self_compress)
{
header('Content-Length: ' . (int)strlen(payload;
// return request, in case subclasses want it
return
Add a method to the dispatch map
parameter: string methodname the name with which the method will be made available
parameter: string sig the array of valid method signatures
parameter: string
function add_to_map(methodname,sig=null,this->dmap[function,
'docstring' => sig)
{
methodname]['signature'] =
Verify type and number of parameters received against a list of known signatures
parameter: array in array of either xmlrpcval objects or xmlrpc type definitions
parameter: array
function verifySignature(in, in))
{
in->getNumParams();
}
else
{
in);
}
foreach(cursig)
{
if(count(numParams+1)
{
n=0; numParams; in))
{
in->getParam(p->kindOf() == 'scalar')
{
p->scalartyp();
}
else
{
p->kindOf();
}
}
else
{
in[in[n+1, as first member of sig is return type
if(cursig[cursig[GLOBALS['xmlrpcValue'])
{
pno=wanted=n+1];
pt;
break;
}
}
if(wanted))
{
return array(0, "Wanted {got} at param
Parse http headers received along with xmlrpc request. If needed, inflate request
returns: null on success or an xmlrpcresp
@access private
function parseRequestHeaders(&data, &resp_encoding, &_SERVER))
{
GLOBALS['HTTP_SERVER_VARS'];
}
if(this->debugmsg(''); // empty line
foreach(getallheaders() as val)
{
name: _SERVER['HTTP_CONTENT_ENCODING']))
{
_SERVER['HTTP_CONTENT_ENCODING']);
}
else
{
content_encoding != '' && strlen(content_encoding == 'deflate' || content_encoding, content_encoding == 'deflate' && data))
{
degzdata;
if(this->debugmsg("\n+++INFLATED REQUEST+++[".strlen(data . "\n+++END+++");
}
}
elseif(degzdata = @gzinflate(substr(data = this->debug > 1)
data)." chars]+++\n" . r =& new xmlrpcresp(0, GLOBALS['xmlrpcstr']['server_decompress_fail']);
return r =& new xmlrpcresp(0, GLOBALS['xmlrpcstr']['server_cannot_decompress']);
return this->response_charset_encoding == 'auto')
{
_SERVER['HTTP_ACCEPT_CHARSET']))
{
// here we should check if we can match the client-requested encoding
// with the encodings we know we can generate.
@todo we should parse q=0.x preferences instead of getting first charset specified...
_SERVER['HTTP_ACCEPT_CHARSET']));
// Give preference to internal encoding
this->internal_encoding, 'UTF-8', 'ISO-8859-1', 'US-ASCII');
foreach (
charset)
{
foreach (
accepted)
if (strpos(
charset) === 0)
{
charset;
break;
}
if (
resp_encoding =
_SERVER['HTTP_ACCEPT_ENCODING']))
{
_SERVER['HTTP_ACCEPT_ENCODING'];
}
else
{
req_encoding = guess_encoding(isset(
_SERVER['CONTENT_TYPE'] : '',
data the xml request
parameter: string
data,
data=='')
//{
//
GLOBALS['HTTP_RAW_POST_DATA'];
//}
// G. Giunta 2005/02/13: we do NOT expect to receive html entities
// so we do not try to convert them into xml character entities
//
data);
GLOBALS['_xh']['ac']='';
GLOBALS['_xh']['valuestack'] = array();
GLOBALS['_xh']['pt']=array();
GLOBALS['_xh']['isf_reason']='';
GLOBALS['_xh']['rt']='';
// decompose incoming XML into request structure
if (
req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
// the following code might be better for mb_string enabled installs, but
// makes the lib about 200% slower...
//if (!is_valid_charset(
req_encoding);
GLOBALS['xmlrpc_defencoding'];
}
@BUG this will fail on PHP 5 if charset is not specified in the xml prologue,
// the encoding is not UTF8 and there are non-ascii chars in the text...
req_encoding);
}
else
{
parser, XML_OPTION_CASE_FOLDING, true);
// G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
// the xml parser to give us back data in the expected charset
xml_parser_set_option(
GLOBALS['xmlrpc_internalencoding']);
if (
parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
else
xml_set_element_handler(
parser, 'xmlrpc_cd');
xml_set_default_handler(
parser,
r=&new xmlrpcresp(0,
parser),
sprintf('XML error:
\%s at line
\%d, column
\%d',
xml_error_string(xml_get_error_code(
parser), xml_get_current_column_number(
parser);
}
elseif (
parser);
GLOBALS['xmlrpcerr']['invalid_request'],
GLOBALS['_xh']['isf_reason']);
}
else
{
xml_parser_free(
this->functions_parameters_type != 'xmlrpcvals')
{
if(
this->debugmsg("\n+++PARSED+++\n".var_export(
r =
GLOBALS['_xh']['method'],
GLOBALS['_xh']['pt']);
}
else
{
// build an xmlrpcmsg object with data parsed from xml
GLOBALS['_xh']['method']);
// now add parameters in
for(
i
<count(
i++)
{
GLOBALS['_xh']['params'][
this->debug > 1)
{
m, true)."\n+++END+++");
}
this->execute(
r;
}
Execute a method invoked by the client, checking parameters used
parameter: mixed params array with method parameters as php types (if m is method name only)
parameter: array
function execute(m, paramtypes=null)
{
if (is_object(methName = methName = sysCall = methName, "system.") === 0);
sysCall ? this->dmap;
if(!isset(methName]['function']))
{
// No such method
return new xmlrpcresp(0,
GLOBALS['xmlrpcstr']['unknown_method']);
}
// Check signature
if(isset(methName]['signature']))
{
dmap[m))
{
list(errstr) = m, ok, this->verifySignature(sig);
}
if(!GLOBALS['xmlrpcerr']['incorrect_params'],
{errstr}"
);
}
}
dmap[func) && strpos(func = explode('::', func))
{
error_log("XML-RPC: xmlrpc_server::execute: function GLOBALS['xmlrpcerr']['server_error'],
this->debug > 2)
{
m))
{
if(r = call_user_func(this, r = call_user_func(m);
}
if (!is_a(func registered as method handler does not return an xmlrpcresp object");
if (is_a(r =& new xmlrpcresp(r =& new xmlrpcresp(
0,
GLOBALS['xmlrpcstr']['server_error'] . ": function does not return xmlrpcresp object"
);
}
}
}
else
{
// call a 'plain php' function
if(params, r = call_user_func_array(params);
}
else
{
// 3rd API convention for method-handling functions: EPI-style
if (r = call_user_func_array(methName, this->user_data));
// mimic EPI behaviour: if we get an array that looks like an error, make it
// an eror response
if (is_array(r) && array_key_exists('faultString', r =& new xmlrpcresp(0, (integer)r['faultString']);
}
else
{
// functions using EPI api should NOT return resp objects,
// so make sure we encode the return type correctly
r, array('extension_api')));
}
}
else
{
func, r, 'xmlrpcresp'))
{
// what should we assume here about automatic encoding of datetimes
// and php classes instances???
r, array('auto_dates')));
}
}
if(GLOBALS['_xmlrpcs_prev_ehandler'])
{
set_error_handler(r;
}
add a string to the 'internal debug message' (separate from 'user debug message')
parameter: string
function debugmsg(string)
{
string."\n";
}
@access private
function xml_header(charset_encoding != '')
{
return "<?xml version=\"1.0\" encoding=\"
A debugging routine: just echoes back the input packet as a string value
DEPRECATED!
function echoInput()
{
r=&new xmlrpcresp(new xmlrpcval( "'Aha said I: '" . r->serialize();
}
}
?>
(C) Æliens
20/2/2008
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.
<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "UA-2780434-1";
urchinTracker();
</script>