~daniel-sonck/smdb/1.0

« back to all changes in this revision

Viewing changes to PHP/lib/xmlrpcs.inc

  • Committer: Daniel Sonck
  • Date: 2012-05-30 00:22:57 UTC
  • Revision ID: daniel.sonck@ziggo.nl-20120530002257-exlbee8j0tov1e0z
PHP server side added

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
// by Edd Dumbill (C) 1999-2002
 
3
// <edd@usefulinc.com>
 
4
// $Id: xmlrpcs.inc,v 1.71 2008/10/29 23:41:28 ggiunta Exp $
 
5
 
 
6
// Copyright (c) 1999,2000,2002 Edd Dumbill.
 
7
// All rights reserved.
 
8
//
 
9
// Redistribution and use in source and binary forms, with or without
 
10
// modification, are permitted provided that the following conditions
 
11
// are met:
 
12
//
 
13
//    * Redistributions of source code must retain the above copyright
 
14
//      notice, this list of conditions and the following disclaimer.
 
15
//
 
16
//    * Redistributions in binary form must reproduce the above
 
17
//      copyright notice, this list of conditions and the following
 
18
//      disclaimer in the documentation and/or other materials provided
 
19
//      with the distribution.
 
20
//
 
21
//    * Neither the name of the "XML-RPC for PHP" nor the names of its
 
22
//      contributors may be used to endorse or promote products derived
 
23
//      from this software without specific prior written permission.
 
24
//
 
25
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
26
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
27
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 
28
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 
29
// REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 
30
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 
31
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 
32
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 
33
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 
34
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
35
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 
36
// OF THE POSSIBILITY OF SUCH DAMAGE.
 
37
 
 
38
        // XML RPC Server class
 
39
        // requires: xmlrpc.inc
 
40
 
 
41
        $GLOBALS['xmlrpcs_capabilities'] = array(
 
42
                // xmlrpc spec: always supported
 
43
                'xmlrpc' => new xmlrpcval(array(
 
44
                        'specUrl' => new xmlrpcval('http://www.xmlrpc.com/spec', 'string'),
 
45
                        'specVersion' => new xmlrpcval(1, 'int')
 
46
                ), 'struct'),
 
47
                // if we support system.xxx functions, we always support multicall, too...
 
48
                // Note that, as of 2006/09/17, the following URL does not respond anymore
 
49
                'system.multicall' => new xmlrpcval(array(
 
50
                        'specUrl' => new xmlrpcval('http://www.xmlrpc.com/discuss/msgReader$1208', 'string'),
 
51
                        'specVersion' => new xmlrpcval(1, 'int')
 
52
                ), 'struct'),
 
53
                // introspection: version 2! we support 'mixed', too
 
54
                'introspection' => new xmlrpcval(array(
 
55
                        'specUrl' => new xmlrpcval('http://phpxmlrpc.sourceforge.net/doc-2/ch10.html', 'string'),
 
56
                        'specVersion' => new xmlrpcval(2, 'int')
 
57
                ), 'struct')
 
58
        );
 
59
 
 
60
        /* Functions that implement system.XXX methods of xmlrpc servers */
 
61
        $_xmlrpcs_getCapabilities_sig=array(array($GLOBALS['xmlrpcStruct']));
 
62
        $_xmlrpcs_getCapabilities_doc='This method lists all the capabilites that the XML-RPC server has: the (more or less standard) extensions to the xmlrpc spec that it adheres to';
 
63
        $_xmlrpcs_getCapabilities_sdoc=array(array('list of capabilities, described as structs with a version number and url for the spec'));
 
64
        function _xmlrpcs_getCapabilities($server, $m=null)
 
65
        {
 
66
                $outAr = $GLOBALS['xmlrpcs_capabilities'];
 
67
                // NIL extension
 
68
                if ($GLOBALS['xmlrpc_null_extension']) {
 
69
                        $outAr['nil'] = new xmlrpcval(array(
 
70
                                'specUrl' => new xmlrpcval('http://www.ontosys.com/xml-rpc/extensions.php', 'string'),
 
71
                                'specVersion' => new xmlrpcval(1, 'int')
 
72
                        ), 'struct');
 
73
                }
 
74
                return new xmlrpcresp(new xmlrpcval($outAr, 'struct'));
 
75
        }
 
76
 
 
77
        // listMethods: signature was either a string, or nothing.
 
78
        // The useless string variant has been removed
 
79
        $_xmlrpcs_listMethods_sig=array(array($GLOBALS['xmlrpcArray']));
 
80
        $_xmlrpcs_listMethods_doc='This method lists all the methods that the XML-RPC server knows how to dispatch';
 
81
        $_xmlrpcs_listMethods_sdoc=array(array('list of method names'));
 
82
        function _xmlrpcs_listMethods($server, $m=null) // if called in plain php values mode, second param is missing
 
83
        {
 
84
 
 
85
                $outAr=array();
 
86
                foreach($server->dmap as $key => $val)
 
87
                {
 
88
                        $outAr[]=new xmlrpcval($key, 'string');
 
89
                }
 
90
                if($server->allow_system_funcs)
 
91
                {
 
92
                        foreach($GLOBALS['_xmlrpcs_dmap'] as $key => $val)
 
93
                        {
 
94
                                $outAr[]=new xmlrpcval($key, 'string');
 
95
                        }
 
96
                }
 
97
                return new xmlrpcresp(new xmlrpcval($outAr, 'array'));
 
98
        }
 
99
 
 
100
        $_xmlrpcs_methodSignature_sig=array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcString']));
 
101
        $_xmlrpcs_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)';
 
102
        $_xmlrpcs_methodSignature_sdoc=array(array('list of known signatures, each sig being an array of xmlrpc type names', 'name of method to be described'));
 
103
        function _xmlrpcs_methodSignature($server, $m)
 
104
        {
 
105
                // let accept as parameter both an xmlrpcval or string
 
106
                if (is_object($m))
 
107
                {
 
108
                        $methName=$m->getParam(0);
 
109
                        $methName=$methName->scalarval();
 
110
                }
 
111
                else
 
112
                {
 
113
                        $methName=$m;
 
114
                }
 
115
                if(strpos($methName, "system.") === 0)
 
116
                {
 
117
                        $dmap=$GLOBALS['_xmlrpcs_dmap']; $sysCall=1;
 
118
                }
 
119
                else
 
120
                {
 
121
                        $dmap=$server->dmap; $sysCall=0;
 
122
                }
 
123
                if(isset($dmap[$methName]))
 
124
                {
 
125
                        if(isset($dmap[$methName]['signature']))
 
126
                        {
 
127
                                $sigs=array();
 
128
                                foreach($dmap[$methName]['signature'] as $inSig)
 
129
                                {
 
130
                                        $cursig=array();
 
131
                                        foreach($inSig as $sig)
 
132
                                        {
 
133
                                                $cursig[]=new xmlrpcval($sig, 'string');
 
134
                                        }
 
135
                                        $sigs[]=new xmlrpcval($cursig, 'array');
 
136
                                }
 
137
                                $r=new xmlrpcresp(new xmlrpcval($sigs, 'array'));
 
138
                        }
 
139
                        else
 
140
                        {
 
141
                                // NB: according to the official docs, we should be returning a
 
142
                                // "none-array" here, which means not-an-array
 
143
                                $r=new xmlrpcresp(new xmlrpcval('undef', 'string'));
 
144
                        }
 
145
                }
 
146
                else
 
147
                {
 
148
                        $r=new xmlrpcresp(0,$GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
 
149
                }
 
150
                return $r;
 
151
        }
 
152
 
 
153
        $_xmlrpcs_methodHelp_sig=array(array($GLOBALS['xmlrpcString'], $GLOBALS['xmlrpcString']));
 
154
        $_xmlrpcs_methodHelp_doc='Returns help text if defined for the method passed, otherwise returns an empty string';
 
155
        $_xmlrpcs_methodHelp_sdoc=array(array('method description', 'name of the method to be described'));
 
156
        function _xmlrpcs_methodHelp($server, $m)
 
157
        {
 
158
                // let accept as parameter both an xmlrpcval or string
 
159
                if (is_object($m))
 
160
                {
 
161
                        $methName=$m->getParam(0);
 
162
                        $methName=$methName->scalarval();
 
163
                }
 
164
                else
 
165
                {
 
166
                        $methName=$m;
 
167
                }
 
168
                if(strpos($methName, "system.") === 0)
 
169
                {
 
170
                        $dmap=$GLOBALS['_xmlrpcs_dmap']; $sysCall=1;
 
171
                }
 
172
                else
 
173
                {
 
174
                        $dmap=$server->dmap; $sysCall=0;
 
175
                }
 
176
                if(isset($dmap[$methName]))
 
177
                {
 
178
                        if(isset($dmap[$methName]['docstring']))
 
179
                        {
 
180
                                $r=new xmlrpcresp(new xmlrpcval($dmap[$methName]['docstring']), 'string');
 
181
                        }
 
182
                        else
 
183
                        {
 
184
                                $r=new xmlrpcresp(new xmlrpcval('', 'string'));
 
185
                        }
 
186
                }
 
187
                else
 
188
                {
 
189
                        $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
 
190
                }
 
191
                return $r;
 
192
        }
 
193
 
 
194
        $_xmlrpcs_multicall_sig = array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcArray']));
 
195
        $_xmlrpcs_multicall_doc = 'Boxcar multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details';
 
196
        $_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"'));
 
197
        function _xmlrpcs_multicall_error($err)
 
198
        {
 
199
                if(is_string($err))
 
200
                {
 
201
                        $str = $GLOBALS['xmlrpcstr']["multicall_${err}"];
 
202
                        $code = $GLOBALS['xmlrpcerr']["multicall_${err}"];
 
203
                }
 
204
                else
 
205
                {
 
206
                        $code = $err->faultCode();
 
207
                        $str = $err->faultString();
 
208
                }
 
209
                $struct = array();
 
210
                $struct['faultCode'] = new xmlrpcval($code, 'int');
 
211
                $struct['faultString'] = new xmlrpcval($str, 'string');
 
212
                return new xmlrpcval($struct, 'struct');
 
213
        }
 
214
 
 
215
        function _xmlrpcs_multicall_do_call($server, $call)
 
216
        {
 
217
                if($call->kindOf() != 'struct')
 
218
                {
 
219
                        return _xmlrpcs_multicall_error('notstruct');
 
220
                }
 
221
                $methName = @$call->structmem('methodName');
 
222
                if(!$methName)
 
223
                {
 
224
                        return _xmlrpcs_multicall_error('nomethod');
 
225
                }
 
226
                if($methName->kindOf() != 'scalar' || $methName->scalartyp() != 'string')
 
227
                {
 
228
                        return _xmlrpcs_multicall_error('notstring');
 
229
                }
 
230
                if($methName->scalarval() == 'system.multicall')
 
231
                {
 
232
                        return _xmlrpcs_multicall_error('recursion');
 
233
                }
 
234
 
 
235
                $params = @$call->structmem('params');
 
236
                if(!$params)
 
237
                {
 
238
                        return _xmlrpcs_multicall_error('noparams');
 
239
                }
 
240
                if($params->kindOf() != 'array')
 
241
                {
 
242
                        return _xmlrpcs_multicall_error('notarray');
 
243
                }
 
244
                $numParams = $params->arraysize();
 
245
 
 
246
                $msg = new xmlrpcmsg($methName->scalarval());
 
247
                for($i = 0; $i < $numParams; $i++)
 
248
                {
 
249
                        if(!$msg->addParam($params->arraymem($i)))
 
250
                        {
 
251
                                $i++;
 
252
                                return _xmlrpcs_multicall_error(new xmlrpcresp(0,
 
253
                                        $GLOBALS['xmlrpcerr']['incorrect_params'],
 
254
                                        $GLOBALS['xmlrpcstr']['incorrect_params'] . ": probable xml error in param " . $i));
 
255
                        }
 
256
                }
 
257
 
 
258
                $result = $server->execute($msg);
 
259
 
 
260
                if($result->faultCode() != 0)
 
261
                {
 
262
                        return _xmlrpcs_multicall_error($result);               // Method returned fault.
 
263
                }
 
264
 
 
265
                return new xmlrpcval(array($result->value()), 'array');
 
266
        }
 
267
 
 
268
        function _xmlrpcs_multicall_do_call_phpvals($server, $call)
 
269
        {
 
270
                if(!is_array($call))
 
271
                {
 
272
                        return _xmlrpcs_multicall_error('notstruct');
 
273
                }
 
274
                if(!array_key_exists('methodName', $call))
 
275
                {
 
276
                        return _xmlrpcs_multicall_error('nomethod');
 
277
                }
 
278
                if (!is_string($call['methodName']))
 
279
                {
 
280
                        return _xmlrpcs_multicall_error('notstring');
 
281
                }
 
282
                if($call['methodName'] == 'system.multicall')
 
283
                {
 
284
                        return _xmlrpcs_multicall_error('recursion');
 
285
                }
 
286
                if(!array_key_exists('params', $call))
 
287
                {
 
288
                        return _xmlrpcs_multicall_error('noparams');
 
289
                }
 
290
                if(!is_array($call['params']))
 
291
                {
 
292
                        return _xmlrpcs_multicall_error('notarray');
 
293
                }
 
294
 
 
295
                // this is a real dirty and simplistic hack, since we might have received a
 
296
                // base64 or datetime values, but they will be listed as strings here...
 
297
                $numParams = count($call['params']);
 
298
                $pt = array();
 
299
                foreach($call['params'] as $val)
 
300
                        $pt[] = php_2_xmlrpc_type(gettype($val));
 
301
 
 
302
                $result = $server->execute($call['methodName'], $call['params'], $pt);
 
303
 
 
304
                if($result->faultCode() != 0)
 
305
                {
 
306
                        return _xmlrpcs_multicall_error($result);               // Method returned fault.
 
307
                }
 
308
 
 
309
                return new xmlrpcval(array($result->value()), 'array');
 
310
        }
 
311
 
 
312
        function _xmlrpcs_multicall($server, $m)
 
313
        {
 
314
                $result = array();
 
315
                // let accept a plain list of php parameters, beside a single xmlrpc msg object
 
316
                if (is_object($m))
 
317
                {
 
318
                        $calls = $m->getParam(0);
 
319
                        $numCalls = $calls->arraysize();
 
320
                        for($i = 0; $i < $numCalls; $i++)
 
321
                        {
 
322
                                $call = $calls->arraymem($i);
 
323
                                $result[$i] = _xmlrpcs_multicall_do_call($server, $call);
 
324
                        }
 
325
                }
 
326
                else
 
327
                {
 
328
                        $numCalls=count($m);
 
329
                        for($i = 0; $i < $numCalls; $i++)
 
330
                        {
 
331
                                $result[$i] = _xmlrpcs_multicall_do_call_phpvals($server, $m[$i]);
 
332
                        }
 
333
                }
 
334
 
 
335
                return new xmlrpcresp(new xmlrpcval($result, 'array'));
 
336
        }
 
337
 
 
338
        $GLOBALS['_xmlrpcs_dmap']=array(
 
339
                'system.listMethods' => array(
 
340
                        'function' => '_xmlrpcs_listMethods',
 
341
                        'signature' => $_xmlrpcs_listMethods_sig,
 
342
                        'docstring' => $_xmlrpcs_listMethods_doc,
 
343
                        'signature_docs' => $_xmlrpcs_listMethods_sdoc),
 
344
                'system.methodHelp' => array(
 
345
                        'function' => '_xmlrpcs_methodHelp',
 
346
                        'signature' => $_xmlrpcs_methodHelp_sig,
 
347
                        'docstring' => $_xmlrpcs_methodHelp_doc,
 
348
                        'signature_docs' => $_xmlrpcs_methodHelp_sdoc),
 
349
                'system.methodSignature' => array(
 
350
                        'function' => '_xmlrpcs_methodSignature',
 
351
                        'signature' => $_xmlrpcs_methodSignature_sig,
 
352
                        'docstring' => $_xmlrpcs_methodSignature_doc,
 
353
                        'signature_docs' => $_xmlrpcs_methodSignature_sdoc),
 
354
                'system.multicall' => array(
 
355
                        'function' => '_xmlrpcs_multicall',
 
356
                        'signature' => $_xmlrpcs_multicall_sig,
 
357
                        'docstring' => $_xmlrpcs_multicall_doc,
 
358
                        'signature_docs' => $_xmlrpcs_multicall_sdoc),
 
359
                'system.getCapabilities' => array(
 
360
                        'function' => '_xmlrpcs_getCapabilities',
 
361
                        'signature' => $_xmlrpcs_getCapabilities_sig,
 
362
                        'docstring' => $_xmlrpcs_getCapabilities_doc,
 
363
                        'signature_docs' => $_xmlrpcs_getCapabilities_sdoc)
 
364
        );
 
365
 
 
366
        $GLOBALS['_xmlrpcs_occurred_errors'] = '';
 
367
        $GLOBALS['_xmlrpcs_prev_ehandler'] = '';
 
368
 
 
369
        /**
 
370
        * Error handler used to track errors that occur during server-side execution of PHP code.
 
371
        * This allows to report back to the client whether an internal error has occurred or not
 
372
        * using an xmlrpc response object, instead of letting the client deal with the html junk
 
373
        * that a PHP execution error on the server generally entails.
 
374
        *
 
375
        * NB: in fact a user defined error handler can only handle WARNING, NOTICE and USER_* errors.
 
376
        *
 
377
        */
 
378
        function _xmlrpcs_errorHandler($errcode, $errstring, $filename=null, $lineno=null, $context=null)
 
379
        {
 
380
                // obey the @ protocol
 
381
                if (error_reporting() == 0)
 
382
                        return;
 
383
 
 
384
                //if($errcode != E_NOTICE && $errcode != E_WARNING && $errcode != E_USER_NOTICE && $errcode != E_USER_WARNING)
 
385
                if($errcode != E_STRICT)
 
386
                {
 
387
                        $GLOBALS['_xmlrpcs_occurred_errors'] = $GLOBALS['_xmlrpcs_occurred_errors'] . $errstring . "\n";
 
388
                }
 
389
                // Try to avoid as much as possible disruption to the previous error handling
 
390
                // mechanism in place
 
391
                if($GLOBALS['_xmlrpcs_prev_ehandler'] == '')
 
392
                {
 
393
                        // The previous error handler was the default: all we should do is log error
 
394
                        // to the default error log (if level high enough)
 
395
                        if(ini_get('log_errors') && (intval(ini_get('error_reporting')) & $errcode))
 
396
                        {
 
397
                                error_log($errstring);
 
398
                        }
 
399
                }
 
400
                else
 
401
                {
 
402
                        // Pass control on to previous error handler, trying to avoid loops...
 
403
                        if($GLOBALS['_xmlrpcs_prev_ehandler'] != '_xmlrpcs_errorHandler')
 
404
                        {
 
405
                                // NB: this code will NOT work on php < 4.0.2: only 2 params were used for error handlers
 
406
                                if(is_array($GLOBALS['_xmlrpcs_prev_ehandler']))
 
407
                                {
 
408
                                        // the following works both with static class methods and plain object methods as error handler
 
409
                                        call_user_func_array($GLOBALS['_xmlrpcs_prev_ehandler'], array($errcode, $errstring, $filename, $lineno, $context));
 
410
                                }
 
411
                                else
 
412
                                {
 
413
                                        $GLOBALS['_xmlrpcs_prev_ehandler']($errcode, $errstring, $filename, $lineno, $context);
 
414
                                }
 
415
                        }
 
416
                }
 
417
        }
 
418
 
 
419
        $GLOBALS['_xmlrpc_debuginfo']='';
 
420
 
 
421
        /**
 
422
        * Add a string to the debug info that can be later seralized by the server
 
423
        * as part of the response message.
 
424
        * Note that for best compatbility, the debug string should be encoded using
 
425
        * the $GLOBALS['xmlrpc_internalencoding'] character set.
 
426
        * @param string $m
 
427
        * @access public
 
428
        */
 
429
        function xmlrpc_debugmsg($m)
 
430
        {
 
431
                $GLOBALS['_xmlrpc_debuginfo'] .= $m . "\n";
 
432
        }
 
433
 
 
434
        class xmlrpc_server
 
435
        {
 
436
                /**
 
437
                * Array defining php functions exposed as xmlrpc methods by this server
 
438
                * @access private
 
439
                */
 
440
                var $dmap=array();
 
441
                /**
 
442
                * Defines how functions in dmap will be invoked: either using an xmlrpc msg object
 
443
                * or plain php values.
 
444
                * valid strings are 'xmlrpcvals', 'phpvals' or 'epivals'
 
445
                */
 
446
                var $functions_parameters_type='xmlrpcvals';
 
447
                /**
 
448
                * Option used for fine-tuning the encoding the php values returned from
 
449
                * functions registered in the dispatch map when the functions_parameters_types
 
450
                * member is set to 'phpvals'
 
451
                * @see php_xmlrpc_encode for a list of values
 
452
                */
 
453
                var $phpvals_encoding_options = array( 'auto_dates' );
 
454
                /// 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
 
455
                var $debug = 1;
 
456
                /**
 
457
                * Controls behaviour of server when invoked user function throws an exception:
 
458
                * 0 = catch it and return an 'internal error' xmlrpc response (default)
 
459
                * 1 = catch it and return an xmlrpc response with the error corresponding to the exception
 
460
                * 2 = allow the exception to float to the upper layers
 
461
                */
 
462
                var $exception_handling = 0;
 
463
                /**
 
464
                * When set to true, it will enable HTTP compression of the response, in case
 
465
                * the client has declared its support for compression in the request.
 
466
                */
 
467
                var $compress_response = false;
 
468
                /**
 
469
                * List of http compression methods accepted by the server for requests.
 
470
                * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
 
471
                */
 
472
                var $accepted_compression = array();
 
473
                /// shall we serve calls to system.* methods?
 
474
                var $allow_system_funcs = true;
 
475
                /// list of charset encodings natively accepted for requests
 
476
                var $accepted_charset_encodings = array();
 
477
                /**
 
478
                * charset encoding to be used for response.
 
479
                * NB: if we can, we will convert the generated response from internal_encoding to the intended one.
 
480
                * can be: a supported xml encoding (only UTF-8 and ISO-8859-1 at present, unless mbstring is enabled),
 
481
                * null (leave unspecified in response, convert output stream to US_ASCII),
 
482
                * 'default' (use xmlrpc library default as specified in xmlrpc.inc, convert output stream if needed),
 
483
                * or 'auto' (use client-specified charset encoding or same as request if request headers do not specify it (unless request is US-ASCII: then use library default anyway).
 
484
                * NB: pretty dangerous if you accept every charset and do not have mbstring enabled)
 
485
                */
 
486
                var $response_charset_encoding = '';
 
487
                /**
 
488
                * Storage for internal debug info
 
489
                * @access private
 
490
                */
 
491
                var $debug_info = '';
 
492
                /**
 
493
                * Extra data passed at runtime to method handling functions. Used only by EPI layer
 
494
                */
 
495
                var $user_data = null;
 
496
 
 
497
                /**
 
498
                * @param array $dispmap the dispatch map withd efinition of exposed services
 
499
                * @param boolean $servicenow set to false to prevent the server from runnung upon construction
 
500
                */
 
501
                function xmlrpc_server($dispMap=null, $serviceNow=true)
 
502
                {
 
503
                        // if ZLIB is enabled, let the server by default accept compressed requests,
 
504
                        // and compress responses sent to clients that support them
 
505
                        if(function_exists('gzinflate'))
 
506
                        {
 
507
                                $this->accepted_compression = array('gzip', 'deflate');
 
508
                                $this->compress_response = true;
 
509
                        }
 
510
 
 
511
                        // by default the xml parser can support these 3 charset encodings
 
512
                        $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
 
513
 
 
514
                        // dispMap is a dispatch array of methods
 
515
                        // mapped to function names and signatures
 
516
                        // if a method
 
517
                        // doesn't appear in the map then an unknown
 
518
                        // method error is generated
 
519
                        /* milosch - changed to make passing dispMap optional.
 
520
                         * instead, you can use the class add_to_map() function
 
521
                         * to add functions manually (borrowed from SOAPX4)
 
522
                         */
 
523
                        if($dispMap)
 
524
                        {
 
525
                                $this->dmap = $dispMap;
 
526
                                if($serviceNow)
 
527
                                {
 
528
                                        $this->service();
 
529
                                }
 
530
                        }
 
531
                }
 
532
 
 
533
                /**
 
534
                * Set debug level of server.
 
535
                * @param integer $in debug lvl: determines info added to xmlrpc responses (as xml comments)
 
536
                * 0 = no debug info,
 
537
                * 1 = msgs set from user with debugmsg(),
 
538
                * 2 = add complete xmlrpc request (headers and body),
 
539
                * 3 = add also all processing warnings happened during method processing
 
540
                * (NB: this involves setting a custom error handler, and might interfere
 
541
                * with the standard processing of the php function exposed as method. In
 
542
                * particular, triggering an USER_ERROR level error will not halt script
 
543
                * execution anymore, but just end up logged in the xmlrpc response)
 
544
                * Note that info added at elevel 2 and 3 will be base64 encoded
 
545
                * @access public
 
546
                */
 
547
                function setDebug($in)
 
548
                {
 
549
                        $this->debug=$in;
 
550
                }
 
551
 
 
552
                /**
 
553
                * Return a string with the serialized representation of all debug info
 
554
                * @param string $charset_encoding the target charset encoding for the serialization
 
555
                * @return string an XML comment (or two)
 
556
                */
 
557
                function serializeDebug($charset_encoding='')
 
558
                {
 
559
                        // Tough encoding problem: which internal charset should we assume for debug info?
 
560
                        // It might contain a copy of raw data received from client, ie with unknown encoding,
 
561
                        // intermixed with php generated data and user generated data...
 
562
                        // so we split it: system debug is base 64 encoded,
 
563
                        // user debug info should be encoded by the end user using the INTERNAL_ENCODING
 
564
                        $out = '';
 
565
                        if ($this->debug_info != '')
 
566
                        {
 
567
                                $out .= "<!-- SERVER DEBUG INFO (BASE64 ENCODED):\n".base64_encode($this->debug_info)."\n-->\n";
 
568
                        }
 
569
                        if($GLOBALS['_xmlrpc_debuginfo']!='')
 
570
                        {
 
571
 
 
572
                                $out .= "<!-- DEBUG INFO:\n" . xmlrpc_encode_entitites(str_replace('--', '_-', $GLOBALS['_xmlrpc_debuginfo']), $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "\n-->\n";
 
573
                                // NB: a better solution MIGHT be to use CDATA, but we need to insert it
 
574
                                // into return payload AFTER the beginning tag
 
575
                                //$out .= "<![CDATA[ DEBUG INFO:\n\n" . str_replace(']]>', ']_]_>', $GLOBALS['_xmlrpc_debuginfo']) . "\n]]>\n";
 
576
                        }
 
577
                        return $out;
 
578
                }
 
579
 
 
580
                /**
 
581
                * Execute the xmlrpc request, printing the response
 
582
                * @param string $data the request body. If null, the http POST request will be examined
 
583
                * @return xmlrpcresp the response object (usually not used by caller...)
 
584
                * @access public
 
585
                */
 
586
                function service($data=null, $return_payload=false)
 
587
                {
 
588
                        if ($data === null)
 
589
                        {
 
590
                                // workaround for a known bug in php ver. 5.2.2 that broke $HTTP_RAW_POST_DATA
 
591
                                $ver = phpversion();
 
592
                                if ($ver[0] >= 5)
 
593
                                {
 
594
                                        $data = file_get_contents('php://input');
 
595
                                }
 
596
                                else
 
597
                                {
 
598
                                        $data = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
 
599
                                }
 
600
                        }
 
601
                        $raw_data = $data;
 
602
 
 
603
                        // reset internal debug info
 
604
                        $this->debug_info = '';
 
605
 
 
606
                        // Echo back what we received, before parsing it
 
607
                        if($this->debug > 1)
 
608
                        {
 
609
                                $this->debugmsg("+++GOT+++\n" . $data . "\n+++END+++");
 
610
                        }
 
611
 
 
612
                        $r = $this->parseRequestHeaders($data, $req_charset, $resp_charset, $resp_encoding);
 
613
                        if (!$r)
 
614
                        {
 
615
                                $r=$this->parseRequest($data, $req_charset);
 
616
                        }
 
617
 
 
618
                        // save full body of request into response, for more debugging usages
 
619
                        $r->raw_data = $raw_data;
 
620
 
 
621
                        if($this->debug > 2 && $GLOBALS['_xmlrpcs_occurred_errors'])
 
622
                        {
 
623
                                $this->debugmsg("+++PROCESSING ERRORS AND WARNINGS+++\n" .
 
624
                                        $GLOBALS['_xmlrpcs_occurred_errors'] . "+++END+++");
 
625
                        }
 
626
 
 
627
                        $payload=$this->xml_header($resp_charset);
 
628
                        if($this->debug > 0)
 
629
                        {
 
630
                                $payload = $payload . $this->serializeDebug($resp_charset);
 
631
                        }
 
632
 
 
633
                        // G. Giunta 2006-01-27: do not create response serialization if it has
 
634
                        // already happened. Helps building json magic
 
635
                        if (empty($r->payload))
 
636
                        {
 
637
                                $r->serialize($resp_charset);
 
638
                        }
 
639
                        $payload = $payload . $r->payload;
 
640
 
 
641
                        if ($return_payload)
 
642
                        {
 
643
                                return $payload;
 
644
                        }
 
645
 
 
646
                        // if we get a warning/error that has output some text before here, then we cannot
 
647
                        // add a new header. We cannot say we are sending xml, either...
 
648
                        if(!headers_sent())
 
649
                        {
 
650
                                header('Content-Type: '.$r->content_type);
 
651
                                // we do not know if client actually told us an accepted charset, but if he did
 
652
                                // we have to tell him what we did
 
653
                                header("Vary: Accept-Charset");
 
654
 
 
655
                                // http compression of output: only
 
656
                                // if we can do it, and we want to do it, and client asked us to,
 
657
                                // and php ini settings do not force it already
 
658
                                $php_no_self_compress = !ini_get('zlib.output_compression') && (ini_get('output_handler') != 'ob_gzhandler');
 
659
                                if($this->compress_response && function_exists('gzencode') && $resp_encoding != ''
 
660
                                        && $php_no_self_compress)
 
661
                                {
 
662
                                        if(strpos($resp_encoding, 'gzip') !== false)
 
663
                                        {
 
664
                                                $payload = gzencode($payload);
 
665
                                                header("Content-Encoding: gzip");
 
666
                                                header("Vary: Accept-Encoding");
 
667
                                        }
 
668
                                        elseif (strpos($resp_encoding, 'deflate') !== false)
 
669
                                        {
 
670
                                                $payload = gzcompress($payload);
 
671
                                                header("Content-Encoding: deflate");
 
672
                                                header("Vary: Accept-Encoding");
 
673
                                        }
 
674
                                }
 
675
 
 
676
                                // do not ouput content-length header if php is compressing output for us:
 
677
                                // it will mess up measurements
 
678
                                if($php_no_self_compress)
 
679
                                {
 
680
                                        header('Content-Length: ' . (int)strlen($payload));
 
681
                                }
 
682
                        }
 
683
                        else
 
684
                        {
 
685
                                error_log('XML-RPC: '.__METHOD__.': http headers already sent before response is fully generated. Check for php warning or error messages');
 
686
                        }
 
687
 
 
688
                        print $payload;
 
689
 
 
690
                        // return request, in case subclasses want it
 
691
                        return $r;
 
692
                }
 
693
 
 
694
                /**
 
695
                * Add a method to the dispatch map
 
696
                * @param string $methodname the name with which the method will be made available
 
697
                * @param string $function the php function that will get invoked
 
698
                * @param array $sig the array of valid method signatures
 
699
                * @param string $doc method documentation
 
700
                * @param array $sigdoc the array of valid method signatures docs (one string per param, one for return type)
 
701
                * @access public
 
702
                */
 
703
                function add_to_map($methodname,$function,$sig=null,$doc=false,$sigdoc=false)
 
704
                {
 
705
                        $this->dmap[$methodname] = array(
 
706
                                'function'      => $function,
 
707
                                'docstring' => $doc
 
708
                        );
 
709
                        if ($sig)
 
710
                        {
 
711
                                $this->dmap[$methodname]['signature'] = $sig;
 
712
                        }
 
713
                        if ($sigdoc)
 
714
                        {
 
715
                                $this->dmap[$methodname]['signature_docs'] = $sigdoc;
 
716
                        }
 
717
                }
 
718
 
 
719
                /**
 
720
                * Verify type and number of parameters received against a list of known signatures
 
721
                * @param array $in array of either xmlrpcval objects or xmlrpc type definitions
 
722
                * @param array $sig array of known signatures to match against
 
723
                * @access private
 
724
                */
 
725
                function verifySignature($in, $sig)
 
726
                {
 
727
                        // check each possible signature in turn
 
728
                        if (is_object($in))
 
729
                        {
 
730
                                $numParams = $in->getNumParams();
 
731
                        }
 
732
                        else
 
733
                        {
 
734
                                $numParams = count($in);
 
735
                        }
 
736
                        foreach($sig as $cursig)
 
737
                        {
 
738
                                if(count($cursig)==$numParams+1)
 
739
                                {
 
740
                                        $itsOK=1;
 
741
                                        for($n=0; $n<$numParams; $n++)
 
742
                                        {
 
743
                                                if (is_object($in))
 
744
                                                {
 
745
                                                        $p=$in->getParam($n);
 
746
                                                        if($p->kindOf() == 'scalar')
 
747
                                                        {
 
748
                                                                $pt=$p->scalartyp();
 
749
                                                        }
 
750
                                                        else
 
751
                                                        {
 
752
                                                                $pt=$p->kindOf();
 
753
                                                        }
 
754
                                                }
 
755
                                                else
 
756
                                                {
 
757
                                                        $pt= $in[$n] == 'i4' ? 'int' : strtolower($in[$n]); // dispatch maps never use i4...
 
758
                                                }
 
759
 
 
760
                                                // param index is $n+1, as first member of sig is return type
 
761
                                                if($pt != $cursig[$n+1] && $cursig[$n+1] != $GLOBALS['xmlrpcValue'])
 
762
                                                {
 
763
                                                        $itsOK=0;
 
764
                                                        $pno=$n+1;
 
765
                                                        $wanted=$cursig[$n+1];
 
766
                                                        $got=$pt;
 
767
                                                        break;
 
768
                                                }
 
769
                                        }
 
770
                                        if($itsOK)
 
771
                                        {
 
772
                                                return array(1,'');
 
773
                                        }
 
774
                                }
 
775
                        }
 
776
                        if(isset($wanted))
 
777
                        {
 
778
                                return array(0, "Wanted ${wanted}, got ${got} at param ${pno}");
 
779
                        }
 
780
                        else
 
781
                        {
 
782
                                return array(0, "No method signature matches number of parameters");
 
783
                        }
 
784
                }
 
785
 
 
786
                /**
 
787
                * Parse http headers received along with xmlrpc request. If needed, inflate request
 
788
                * @return null on success or an xmlrpcresp
 
789
                * @access private
 
790
                */
 
791
                function parseRequestHeaders(&$data, &$req_encoding, &$resp_encoding, &$resp_compression)
 
792
                {
 
793
                        // check if $_SERVER is populated: it might have been disabled via ini file
 
794
                        // (this is true even when in CLI mode)
 
795
                        if (count($_SERVER) == 0)
 
796
                        {
 
797
                                error_log('XML-RPC: '.__METHOD__.': cannot parse request headers as $_SERVER is not populated');
 
798
                        }
 
799
 
 
800
                        if($this->debug > 1)
 
801
                        {
 
802
                                if(function_exists('getallheaders'))
 
803
                                {
 
804
                                        $this->debugmsg(''); // empty line
 
805
                                        foreach(getallheaders() as $name => $val)
 
806
                                        {
 
807
                                                $this->debugmsg("HEADER: $name: $val");
 
808
                                        }
 
809
                                }
 
810
 
 
811
                        }
 
812
 
 
813
                        if(isset($_SERVER['HTTP_CONTENT_ENCODING']))
 
814
                        {
 
815
                                $content_encoding = str_replace('x-', '', $_SERVER['HTTP_CONTENT_ENCODING']);
 
816
                        }
 
817
                        else
 
818
                        {
 
819
                                $content_encoding = '';
 
820
                        }
 
821
 
 
822
                        // check if request body has been compressed and decompress it
 
823
                        if($content_encoding != '' && strlen($data))
 
824
                        {
 
825
                                if($content_encoding == 'deflate' || $content_encoding == 'gzip')
 
826
                                {
 
827
                                        // if decoding works, use it. else assume data wasn't gzencoded
 
828
                                        if(function_exists('gzinflate') && in_array($content_encoding, $this->accepted_compression))
 
829
                                        {
 
830
                                                if($content_encoding == 'deflate' && $degzdata = @gzuncompress($data))
 
831
                                                {
 
832
                                                        $data = $degzdata;
 
833
                                                        if($this->debug > 1)
 
834
                                                        {
 
835
                                                                $this->debugmsg("\n+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
 
836
                                                        }
 
837
                                                }
 
838
                                                elseif($content_encoding == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
 
839
                                                {
 
840
                                                        $data = $degzdata;
 
841
                                                        if($this->debug > 1)
 
842
                                                                $this->debugmsg("+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
 
843
                                                }
 
844
                                                else
 
845
                                                {
 
846
                                                        $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_decompress_fail'], $GLOBALS['xmlrpcstr']['server_decompress_fail']);
 
847
                                                        return $r;
 
848
                                                }
 
849
                                        }
 
850
                                        else
 
851
                                        {
 
852
                                                //error_log('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
 
853
                                                $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_cannot_decompress'], $GLOBALS['xmlrpcstr']['server_cannot_decompress']);
 
854
                                                return $r;
 
855
                                        }
 
856
                                }
 
857
                        }
 
858
 
 
859
                        // check if client specified accepted charsets, and if we know how to fulfill
 
860
                        // the request
 
861
                        if ($this->response_charset_encoding == 'auto')
 
862
                        {
 
863
                                $resp_encoding = '';
 
864
                                if (isset($_SERVER['HTTP_ACCEPT_CHARSET']))
 
865
                                {
 
866
                                        // here we should check if we can match the client-requested encoding
 
867
                                        // with the encodings we know we can generate.
 
868
                                        /// @todo we should parse q=0.x preferences instead of getting first charset specified...
 
869
                                        $client_accepted_charsets = explode(',', strtoupper($_SERVER['HTTP_ACCEPT_CHARSET']));
 
870
                                        // Give preference to internal encoding
 
871
                                        $known_charsets = array($GLOBALS['xmlrpc_internalencoding'], 'UTF-8', 'ISO-8859-1', 'US-ASCII');
 
872
                                        foreach ($known_charsets as $charset)
 
873
                                        {
 
874
                                                foreach ($client_accepted_charsets as $accepted)
 
875
                                                        if (strpos($accepted, $charset) === 0)
 
876
                                                        {
 
877
                                                                $resp_encoding = $charset;
 
878
                                                                break;
 
879
                                                        }
 
880
                                                if ($resp_encoding)
 
881
                                                        break;
 
882
                                        }
 
883
                                }
 
884
                        }
 
885
                        else
 
886
                        {
 
887
                                $resp_encoding = $this->response_charset_encoding;
 
888
                        }
 
889
 
 
890
                        if (isset($_SERVER['HTTP_ACCEPT_ENCODING']))
 
891
                        {
 
892
                                $resp_compression = $_SERVER['HTTP_ACCEPT_ENCODING'];
 
893
                        }
 
894
                        else
 
895
                        {
 
896
                                $resp_compression = '';
 
897
                        }
 
898
 
 
899
                        // 'guestimate' request encoding
 
900
                        /// @todo check if mbstring is enabled and automagic input conversion is on: it might mingle with this check???
 
901
                        $req_encoding = guess_encoding(isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '',
 
902
                                $data);
 
903
 
 
904
                        return null;
 
905
                }
 
906
 
 
907
                /**
 
908
                * Parse an xml chunk containing an xmlrpc request and execute the corresponding
 
909
                * php function registered with the server
 
910
                * @param string $data the xml request
 
911
                * @param string $req_encoding (optional) the charset encoding of the xml request
 
912
                * @return xmlrpcresp
 
913
                * @access private
 
914
                */
 
915
                function parseRequest($data, $req_encoding='')
 
916
                {
 
917
                        // 2005/05/07 commented and moved into caller function code
 
918
                        //if($data=='')
 
919
                        //{
 
920
                        //      $data=$GLOBALS['HTTP_RAW_POST_DATA'];
 
921
                        //}
 
922
 
 
923
                        // G. Giunta 2005/02/13: we do NOT expect to receive html entities
 
924
                        // so we do not try to convert them into xml character entities
 
925
                        //$data = xmlrpc_html_entity_xlate($data);
 
926
 
 
927
                        $GLOBALS['_xh']=array();
 
928
                        $GLOBALS['_xh']['ac']='';
 
929
                        $GLOBALS['_xh']['stack']=array();
 
930
                        $GLOBALS['_xh']['valuestack'] = array();
 
931
                        $GLOBALS['_xh']['params']=array();
 
932
                        $GLOBALS['_xh']['pt']=array();
 
933
                        $GLOBALS['_xh']['isf']=0;
 
934
                        $GLOBALS['_xh']['isf_reason']='';
 
935
                        $GLOBALS['_xh']['method']=false; // so we can check later if we got a methodname or not
 
936
                        $GLOBALS['_xh']['rt']='';
 
937
 
 
938
                        // decompose incoming XML into request structure
 
939
                        if ($req_encoding != '')
 
940
                        {
 
941
                                if (!in_array($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
 
942
                                // the following code might be better for mb_string enabled installs, but
 
943
                                // makes the lib about 200% slower...
 
944
                                //if (!is_valid_charset($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
 
945
                                {
 
946
                                        error_log('XML-RPC: '.__METHOD__.': invalid charset encoding of received request: '.$req_encoding);
 
947
                                        $req_encoding = $GLOBALS['xmlrpc_defencoding'];
 
948
                                }
 
949
                                /// @BUG this will fail on PHP 5 if charset is not specified in the xml prologue,
 
950
                                // the encoding is not UTF8 and there are non-ascii chars in the text...
 
951
                                /// @todo use an ampty string for php 5 ???
 
952
                                $parser = xml_parser_create($req_encoding);
 
953
                        }
 
954
                        else
 
955
                        {
 
956
                                $parser = xml_parser_create();
 
957
                        }
 
958
 
 
959
                        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
 
960
                        // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
 
961
                        // the xml parser to give us back data in the expected charset
 
962
                        // What if internal encoding is not in one of the 3 allowed?
 
963
                        // we use the broadest one, ie. utf8
 
964
                        // This allows to send data which is native in various charset,
 
965
                        // by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
 
966
                        if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
 
967
                        {
 
968
                                xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
 
969
                        }
 
970
                        else
 
971
                        {
 
972
                                xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
 
973
                        }
 
974
 
 
975
                        if ($this->functions_parameters_type != 'xmlrpcvals')
 
976
                                xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
 
977
                        else
 
978
                                xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
 
979
                        xml_set_character_data_handler($parser, 'xmlrpc_cd');
 
980
                        xml_set_default_handler($parser, 'xmlrpc_dh');
 
981
                        if(!xml_parse($parser, $data, 1))
 
982
                        {
 
983
                                // return XML error as a faultCode
 
984
                                $r=new xmlrpcresp(0,
 
985
                                $GLOBALS['xmlrpcerrxml']+xml_get_error_code($parser),
 
986
                                sprintf('XML error: %s at line %d, column %d',
 
987
                                        xml_error_string(xml_get_error_code($parser)),
 
988
                                        xml_get_current_line_number($parser), xml_get_current_column_number($parser)));
 
989
                                xml_parser_free($parser);
 
990
                        }
 
991
                        elseif ($GLOBALS['_xh']['isf'])
 
992
                        {
 
993
                                xml_parser_free($parser);
 
994
                                $r=new xmlrpcresp(0,
 
995
                                        $GLOBALS['xmlrpcerr']['invalid_request'],
 
996
                                        $GLOBALS['xmlrpcstr']['invalid_request'] . ' ' . $GLOBALS['_xh']['isf_reason']);
 
997
                        }
 
998
                        else
 
999
                        {
 
1000
                                xml_parser_free($parser);
 
1001
                                // small layering violation in favor of speed and memory usage:
 
1002
                                // we should allow the 'execute' method handle this, but in the
 
1003
                                // most common scenario (xmlrpcvals type server with some methods
 
1004
                                // registered as phpvals) that would mean a useless encode+decode pass
 
1005
                                if ($this->functions_parameters_type != 'xmlrpcvals' || (isset($this->dmap[$GLOBALS['_xh']['method']]['parameters_type']) && ($this->dmap[$GLOBALS['_xh']['method']]['parameters_type'] == 'phpvals')))
 
1006
                                {
 
1007
                                        if($this->debug > 1)
 
1008
                                        {
 
1009
                                                $this->debugmsg("\n+++PARSED+++\n".var_export($GLOBALS['_xh']['params'], true)."\n+++END+++");
 
1010
                                        }
 
1011
                                        $r = $this->execute($GLOBALS['_xh']['method'], $GLOBALS['_xh']['params'], $GLOBALS['_xh']['pt']);
 
1012
                                }
 
1013
                                else
 
1014
                                {
 
1015
                                        // build an xmlrpcmsg object with data parsed from xml
 
1016
                                        $m=new xmlrpcmsg($GLOBALS['_xh']['method']);
 
1017
                                        // now add parameters in
 
1018
                                        for($i=0; $i<count($GLOBALS['_xh']['params']); $i++)
 
1019
                                        {
 
1020
                                                $m->addParam($GLOBALS['_xh']['params'][$i]);
 
1021
                                        }
 
1022
 
 
1023
                                        if($this->debug > 1)
 
1024
                                        {
 
1025
                                                $this->debugmsg("\n+++PARSED+++\n".var_export($m, true)."\n+++END+++");
 
1026
                                        }
 
1027
                                        $r = $this->execute($m);
 
1028
                                }
 
1029
                        }
 
1030
                        return $r;
 
1031
                }
 
1032
 
 
1033
                /**
 
1034
                * Execute a method invoked by the client, checking parameters used
 
1035
                * @param mixed $m either an xmlrpcmsg obj or a method name
 
1036
                * @param array $params array with method parameters as php types (if m is method name only)
 
1037
                * @param array $paramtypes array with xmlrpc types of method parameters (if m is method name only)
 
1038
                * @return xmlrpcresp
 
1039
                * @access private
 
1040
                */
 
1041
                function execute($m, $params=null, $paramtypes=null)
 
1042
                {
 
1043
                        if (is_object($m))
 
1044
                        {
 
1045
                                $methName = $m->method();
 
1046
                        }
 
1047
                        else
 
1048
                        {
 
1049
                                $methName = $m;
 
1050
                        }
 
1051
                        $sysCall = $this->allow_system_funcs && (strpos($methName, "system.") === 0);
 
1052
                        $dmap = $sysCall ? $GLOBALS['_xmlrpcs_dmap'] : $this->dmap;
 
1053
 
 
1054
                        if(!isset($dmap[$methName]['function']))
 
1055
                        {
 
1056
                                // No such method
 
1057
                                return new xmlrpcresp(0,
 
1058
                                        $GLOBALS['xmlrpcerr']['unknown_method'],
 
1059
                                        $GLOBALS['xmlrpcstr']['unknown_method']);
 
1060
                        }
 
1061
 
 
1062
                        // Check signature
 
1063
                        if(isset($dmap[$methName]['signature']))
 
1064
                        {
 
1065
                                $sig = $dmap[$methName]['signature'];
 
1066
                                if (is_object($m))
 
1067
                                {
 
1068
                                        list($ok, $errstr) = $this->verifySignature($m, $sig);
 
1069
                                }
 
1070
                                else
 
1071
                                {
 
1072
                                        list($ok, $errstr) = $this->verifySignature($paramtypes, $sig);
 
1073
                                }
 
1074
                                if(!$ok)
 
1075
                                {
 
1076
                                        // Didn't match.
 
1077
                                        return new xmlrpcresp(
 
1078
                                                0,
 
1079
                                                $GLOBALS['xmlrpcerr']['incorrect_params'],
 
1080
                                                $GLOBALS['xmlrpcstr']['incorrect_params'] . ": ${errstr}"
 
1081
                                        );
 
1082
                                }
 
1083
                        }
 
1084
 
 
1085
                        $func = $dmap[$methName]['function'];
 
1086
                        // let the 'class::function' syntax be accepted in dispatch maps
 
1087
                        if(is_string($func) && strpos($func, '::'))
 
1088
                        {
 
1089
                                $func = explode('::', $func);
 
1090
                        }
 
1091
                        // verify that function to be invoked is in fact callable
 
1092
                        if(!is_callable($func))
 
1093
                        {
 
1094
                                error_log("XML-RPC: ".__METHOD__.": function $func registered as method handler is not callable");
 
1095
                                return new xmlrpcresp(
 
1096
                                        0,
 
1097
                                        $GLOBALS['xmlrpcerr']['server_error'],
 
1098
                                        $GLOBALS['xmlrpcstr']['server_error'] . ": no function matches method"
 
1099
                                );
 
1100
                        }
 
1101
 
 
1102
                        // If debug level is 3, we should catch all errors generated during
 
1103
                        // processing of user function, and log them as part of response
 
1104
                        if($this->debug > 2)
 
1105
                        {
 
1106
                                $GLOBALS['_xmlrpcs_prev_ehandler'] = set_error_handler('_xmlrpcs_errorHandler');
 
1107
                        }
 
1108
                        try
 
1109
                        {
 
1110
                                // Allow mixed-convention servers
 
1111
                                if (is_object($m))
 
1112
                                {
 
1113
                                        if($sysCall)
 
1114
                                        {
 
1115
                                                $r = call_user_func($func, $this, $m);
 
1116
                                        }
 
1117
                                        else
 
1118
                                        {
 
1119
                                                $r = call_user_func($func, $m);
 
1120
                                        }
 
1121
                                        if (!is_a($r, 'xmlrpcresp'))
 
1122
                                        {
 
1123
                                                error_log("XML-RPC: ".__METHOD__.": function $func registered as method handler does not return an xmlrpcresp object");
 
1124
                                                if (is_a($r, 'xmlrpcval'))
 
1125
                                                {
 
1126
                                                        $r = new xmlrpcresp($r);
 
1127
                                                }
 
1128
                                                else
 
1129
                                                {
 
1130
                                                        $r = new xmlrpcresp(
 
1131
                                                                0,
 
1132
                                                                $GLOBALS['xmlrpcerr']['server_error'],
 
1133
                                                                $GLOBALS['xmlrpcstr']['server_error'] . ": function does not return xmlrpcresp object"
 
1134
                                                        );
 
1135
                                                }
 
1136
                                        }
 
1137
                                }
 
1138
                                else
 
1139
                                {
 
1140
                                        // call a 'plain php' function
 
1141
                                        if($sysCall)
 
1142
                                        {
 
1143
                                                array_unshift($params, $this);
 
1144
                                                $r = call_user_func_array($func, $params);
 
1145
                                        }
 
1146
                                        else
 
1147
                                        {
 
1148
                                                // 3rd API convention for method-handling functions: EPI-style
 
1149
                                                if ($this->functions_parameters_type == 'epivals')
 
1150
                                                {
 
1151
                                                        $r = call_user_func_array($func, array($methName, $params, $this->user_data));
 
1152
                                                        // mimic EPI behaviour: if we get an array that looks like an error, make it
 
1153
                                                        // an eror response
 
1154
                                                        if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r))
 
1155
                                                        {
 
1156
                                                                $r = new xmlrpcresp(0, (integer)$r['faultCode'], (string)$r['faultString']);
 
1157
                                                        }
 
1158
                                                        else
 
1159
                                                        {
 
1160
                                                                // functions using EPI api should NOT return resp objects,
 
1161
                                                                // so make sure we encode the return type correctly
 
1162
                                                                $r = new xmlrpcresp(php_xmlrpc_encode($r, array('extension_api')));
 
1163
                                                        }
 
1164
                                                }
 
1165
                                                else
 
1166
                                                {
 
1167
                                                        $r = call_user_func_array($func, $params);
 
1168
                                                }
 
1169
                                        }
 
1170
                                        // the return type can be either an xmlrpcresp object or a plain php value...
 
1171
                                        if (!is_a($r, 'xmlrpcresp'))
 
1172
                                        {
 
1173
                                                // what should we assume here about automatic encoding of datetimes
 
1174
                                                // and php classes instances???
 
1175
                                                $r = new xmlrpcresp(php_xmlrpc_encode($r, $this->phpvals_encoding_options));
 
1176
                                        }
 
1177
                                }
 
1178
                        }
 
1179
                        catch(Exception $e)
 
1180
                        {
 
1181
                                // (barring errors in the lib) an uncatched exception happened
 
1182
                                // in the called function, we wrap it in a proper error-response
 
1183
                                switch($this->exception_handling)
 
1184
                                {
 
1185
                                        case 2:
 
1186
                                                throw $e;
 
1187
                                                break;
 
1188
                                        case 1:
 
1189
                                                $r = new xmlrpcresp(0, $e->getCode(), $e->getMessage());
 
1190
                                                break;
 
1191
                                        default:
 
1192
                                                $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_error'], $GLOBALS['xmlrpcstr']['server_error']);
 
1193
                                }
 
1194
                        }
 
1195
                        if($this->debug > 2)
 
1196
                        {
 
1197
                                // note: restore the error handler we found before calling the
 
1198
                                // user func, even if it has been changed inside the func itself
 
1199
                                if($GLOBALS['_xmlrpcs_prev_ehandler'])
 
1200
                                {
 
1201
                                        set_error_handler($GLOBALS['_xmlrpcs_prev_ehandler']);
 
1202
                                }
 
1203
                                else
 
1204
                                {
 
1205
                                        restore_error_handler();
 
1206
                                }
 
1207
                        }
 
1208
                        return $r;
 
1209
                }
 
1210
 
 
1211
                /**
 
1212
                * add a string to the 'internal debug message' (separate from 'user debug message')
 
1213
                * @param string $strings
 
1214
                * @access private
 
1215
                */
 
1216
                function debugmsg($string)
 
1217
                {
 
1218
                        $this->debug_info .= $string."\n";
 
1219
                }
 
1220
 
 
1221
                /**
 
1222
                * @access private
 
1223
                */
 
1224
                function xml_header($charset_encoding='')
 
1225
                {
 
1226
                        if ($charset_encoding != '')
 
1227
                        {
 
1228
                                return "<?xml version=\"1.0\" encoding=\"$charset_encoding\"?" . ">\n";
 
1229
                        }
 
1230
                        else
 
1231
                        {
 
1232
                                return "<?xml version=\"1.0\"?" . ">\n";
 
1233
                        }
 
1234
                }
 
1235
 
 
1236
                /**
 
1237
                * A debugging routine: just echoes back the input packet as a string value
 
1238
                * DEPRECATED!
 
1239
                */
 
1240
                function echoInput()
 
1241
                {
 
1242
                        $r=new xmlrpcresp(new xmlrpcval( "'Aha said I: '" . $GLOBALS['HTTP_RAW_POST_DATA'], 'string'));
 
1243
                        print $r->serialize();
 
1244
                }
 
1245
        }
 
1246
?>
 
 
b'\\ No newline at end of file'