]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/XMLRPC/xmlrpcs.inc
Reformat code
[SourceForge/phpwiki.git] / lib / XMLRPC / xmlrpcs.inc
1 <?php
2 // by Edd Dumbill (C) 1999-2002
3 // <edd@usefulinc.com>
4
5 // Copyright (c) 1999,2000,2002 Edd Dumbill.
6 // All rights reserved.
7 //
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions
10 // are met:
11 //
12 //    * Redistributions of source code must retain the above copyright
13 //      notice, this list of conditions and the following disclaimer.
14 //
15 //    * Redistributions in binary form must reproduce the above
16 //      copyright notice, this list of conditions and the following
17 //      disclaimer in the documentation and/or other materials provided
18 //      with the distribution.
19 //
20 //    * Neither the name of the "XML-RPC for PHP" nor the names of its
21 //      contributors may be used to endorse or promote products derived
22 //      from this software without specific prior written permission.
23 //
24 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35 // OF THE POSSIBILITY OF SUCH DAMAGE.
36
37 // XML RPC Server class
38 // requires: xmlrpc.inc
39
40 // listMethods: either a string, or nothing
41 $_xmlrpcs_listMethods_sig = array(array($xmlrpcArray, $xmlrpcString), array($xmlrpcArray));
42 $_xmlrpcs_listMethods_doc = 'This method lists all the methods that the XML-RPC server knows how to dispatch';
43 function _xmlrpcs_listMethods($server, $m)
44 {
45     global $xmlrpcerr, $xmlrpcstr, $_xmlrpcs_dmap;
46     $v = new xmlrpcval();
47     $dmap = $server->dmap;
48     $outAr = array();
49     for (reset($dmap); list($key, $val) = each($dmap);) {
50         $outAr[] = new xmlrpcval($key, 'string');
51     }
52     $dmap = $_xmlrpcs_dmap;
53     for (reset($dmap); list($key, $val) = each($dmap);) {
54         $outAr[] = new xmlrpcval($key, 'string');
55     }
56     $v->addArray($outAr);
57     return new xmlrpcresp($v);
58 }
59
60 $_xmlrpcs_methodSignature_sig = array(array($xmlrpcArray, $xmlrpcString));
61 $_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)';
62 function _xmlrpcs_methodSignature($server, $m)
63 {
64     global $xmlrpcerr, $xmlrpcstr, $_xmlrpcs_dmap;
65
66     $methName = $m->getParam(0);
67     $methName = $methName->scalarval();
68     if (ereg("^system\.", $methName)) {
69         $dmap = $_xmlrpcs_dmap;
70         $sysCall = 1;
71     } else {
72         $dmap = $server->dmap;
73         $sysCall = 0;
74     }
75     //  print "<!-- ${methName} -->\n";
76     if (isset($dmap[$methName])) {
77         if ($dmap[$methName]['signature']) {
78             $sigs = array();
79             $thesigs = $dmap[$methName]['signature'];
80             for ($i = 0; $i < sizeof($thesigs); $i++) {
81                 $cursig = array();
82                 $inSig = $thesigs[$i];
83                 for ($j = 0; $j < sizeof($inSig); $j++) {
84                     $cursig[] = new xmlrpcval($inSig[$j], 'string');
85                 }
86                 $sigs[] = new xmlrpcval($cursig, 'array');
87             }
88             $r = new xmlrpcresp(new xmlrpcval($sigs, 'array'));
89         } else {
90             $r = new xmlrpcresp(new xmlrpcval('undef', 'string'));
91         }
92     } else {
93         $r = new xmlrpcresp(0, $xmlrpcerr['introspect_unknown'], $xmlrpcstr['introspect_unknown']);
94     }
95     return $r;
96 }
97
98 $_xmlrpcs_methodHelp_sig = array(array($xmlrpcString, $xmlrpcString));
99 $_xmlrpcs_methodHelp_doc = 'Returns help text if defined for the method passed, otherwise returns an empty string';
100 function _xmlrpcs_methodHelp($server, $m)
101 {
102     global $xmlrpcerr, $xmlrpcstr, $_xmlrpcs_dmap;
103
104     $methName = $m->getParam(0);
105     $methName = $methName->scalarval();
106     if (ereg("^system\.", $methName)) {
107         $dmap = $_xmlrpcs_dmap;
108         $sysCall = 1;
109     } else {
110         $dmap = $server->dmap;
111         $sysCall = 0;
112     }
113     // print "<!-- ${methName} -->\n";
114     if (isset($dmap[$methName])) {
115         if ($dmap[$methName]['docstring']) {
116             $r = new xmlrpcresp(new xmlrpcval($dmap[$methName]['docstring']), 'string');
117         } else {
118             $r = new xmlrpcresp(new xmlrpcval('', 'string'));
119         }
120     } else {
121         $r = new xmlrpcresp(0, $xmlrpcerr['introspect_unknown'], $xmlrpcstr['introspect_unknown']);
122     }
123     return $r;
124 }
125
126 $_xmlrpcs_multicall_sig = array(array($xmlrpcArray, $xmlrpcArray));
127 $_xmlrpcs_multicall_doc = 'Boxcar multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details';
128
129 function _xmlrpcs_multicall_error($err)
130 {
131     if (is_string($err)) {
132         global $xmlrpcerr, $xmlrpcstr;
133         $str = $xmlrpcstr["multicall_${err}"];
134         $code = $xmlrpcerr["multicall_${err}"];
135     } else {
136         $code = $err->faultCode();
137         $str = $err->faultString();
138     }
139     $struct['faultCode'] = new xmlrpcval($code, 'int');
140     $struct['faultString'] = new xmlrpcval($str, 'string');
141     return new xmlrpcval($struct, 'struct');
142 }
143
144 function _xmlrpcs_multicall_do_call($server, $call)
145 {
146     if ($call->kindOf() != 'struct') {
147         return _xmlrpcs_multicall_error('notstruct');
148     }
149     $methName = $call->structmem('methodName');
150     if (!$methName) {
151         return _xmlrpcs_multicall_error('nomethod');
152     }
153     if ($methName->kindOf() != 'scalar' || $methName->scalartyp() != 'string') {
154         return _xmlrpcs_multicall_error('notstring');
155     }
156     if ($methName->scalarval() == 'system.multicall') {
157         return _xmlrpcs_multicall_error('recursion');
158     }
159
160     $params = $call->structmem('params');
161     if (!$params) {
162         return _xmlrpcs_multicall_error('noparams');
163     }
164     if ($params->kindOf() != 'array') {
165         return _xmlrpcs_multicall_error('notarray');
166     }
167     $numParams = $params->arraysize();
168
169     $msg = new xmlrpcmsg($methName->scalarval());
170     for ($i = 0; $i < $numParams; $i++) {
171         $msg->addParam($params->arraymem($i));
172     }
173
174     $result = $server->execute($msg);
175
176     if ($result->faultCode() != 0) {
177         return _xmlrpcs_multicall_error($result); // Method returned fault.
178     }
179
180     return new xmlrpcval(array($result->value()), 'array');
181 }
182
183 function _xmlrpcs_multicall($server, $m)
184 {
185     $calls = $m->getParam(0);
186     $numCalls = $calls->arraysize();
187     $result = array();
188
189     for ($i = 0; $i < $numCalls; $i++) {
190         $call = $calls->arraymem($i);
191         $result[$i] = _xmlrpcs_multicall_do_call($server, $call);
192     }
193
194     return new xmlrpcresp(new xmlrpcval($result, 'array'));
195 }
196
197 $_xmlrpcs_dmap = array(
198     'system.listMethods' => array(
199         'function' => '_xmlrpcs_listMethods',
200         'signature' => $_xmlrpcs_listMethods_sig,
201         'docstring' => $_xmlrpcs_listMethods_doc),
202     'system.methodHelp' => array(
203         'function' => '_xmlrpcs_methodHelp',
204         'signature' => $_xmlrpcs_methodHelp_sig,
205         'docstring' => $_xmlrpcs_methodHelp_doc),
206     'system.methodSignature' => array(
207         'function' => '_xmlrpcs_methodSignature',
208         'signature' => $_xmlrpcs_methodSignature_sig,
209         'docstring' => $_xmlrpcs_methodSignature_doc),
210     'system.multicall' => array(
211         'function' => '_xmlrpcs_multicall',
212         'signature' => $_xmlrpcs_multicall_sig,
213         'docstring' => $_xmlrpcs_multicall_doc
214     )
215 );
216
217 $_xmlrpc_debuginfo = '';
218 function xmlrpc_debugmsg($m)
219 {
220     global $_xmlrpc_debuginfo;
221     $_xmlrpc_debuginfo = $_xmlrpc_debuginfo . $m . "\n";
222 }
223
224 class xmlrpc_server
225 {
226     var $dmap = array();
227
228     function xmlrpc_server($dispMap = '', $serviceNow = 1)
229     {
230         global $HTTP_RAW_POST_DATA;
231         // dispMap is a dispatch array of methods
232         // mapped to function names and signatures
233         // if a method
234         // doesn't appear in the map then an unknown
235         // method error is generated
236         /* milosch - changed to make passing dispMap optional.
237          * instead, you can use the class add_to_map() function
238          * to add functions manually (borrowed from SOAPX4)
239          */
240         if ($dispMap) {
241             $this->dmap = $dispMap;
242             if ($serviceNow) {
243                 $this->service();
244             }
245         }
246     }
247
248     function serializeDebug()
249     {
250         global $_xmlrpc_debuginfo;
251         if ($_xmlrpc_debuginfo != '') {
252             return "<!-- DEBUG INFO:\n\n" . xmlrpc_encode_entitites($_xmlrpc_debuginfo) . "\n-->\n";
253         } else {
254             return '';
255         }
256     }
257
258     function service()
259     {
260         //global $xmlrpc_defencoding;
261
262         $r = $this->parseRequest();
263         //$payload='<?xml version="1.0" encoding="' . $xmlrpc_defencoding . '"?' . '>' . "\n"
264         $payload = '<?xml version="1.0" ?' . '>' . "\n"
265             . $this->serializeDebug()
266             . $r->serialize();
267         header('Content-Type: text/xml');
268         header('Content-Length: ' . (int)strlen($payload));
269         print $payload;
270     }
271
272     /*
273     add a method to the dispatch map
274     */
275     function add_to_map($methodname, $function, $sig, $doc)
276     {
277         $this->dmap[$methodname] = array(
278             'function' => $function,
279             'signature' => $sig,
280             'docstring' => $doc
281         );
282     }
283
284     function verifySignature($in, $sig)
285     {
286         for ($i = 0; $i < sizeof($sig); $i++) {
287             // check each possible signature in turn
288             $cursig = $sig[$i];
289             if (sizeof($cursig) == $in->getNumParams() + 1) {
290                 $itsOK = 1;
291                 for ($n = 0; $n < $in->getNumParams(); $n++) {
292                     $p = $in->getParam($n);
293                     // print "<!-- $p -->\n";
294                     if ($p->kindOf() == 'scalar') {
295                         $pt = $p->scalartyp();
296                     } else {
297                         $pt = $p->kindOf();
298                     }
299                     // $n+1 as first type of sig is return type
300                     if ($pt != $cursig[$n + 1]) {
301                         $itsOK = 0;
302                         $pno = $n + 1;
303                         $wanted = $cursig[$n + 1];
304                         $got = $pt;
305                         break;
306                     }
307                 }
308                 if ($itsOK) {
309                     return array(1, '');
310                 }
311             }
312         }
313         if (isset($wanted))
314             return array(0, "Wanted ${wanted}, got ${got} at param ${pno})");
315         else
316             return array(0, "No method signature matches number of parameters");
317     }
318
319     function parseRequest($data = '')
320     {
321         global $_xh, $HTTP_RAW_POST_DATA;
322         global $xmlrpcerr, $xmlrpcstr, $xmlrpcerrxml, $xmlrpc_defencoding,
323                $_xmlrpcs_dmap, $xmlrpc_internalencoding;
324
325         if ($data == '') {
326             $data = $HTTP_RAW_POST_DATA;
327         }
328         // G. Giunta 2005/02/13: we do NOT expect to receive html entities
329         // so we do not try to convert them into xml character entities
330         //$data = xmlrpc_html_entity_xlate($data);
331         $parser = xml_parser_create($xmlrpc_defencoding);
332
333         $_xh[$parser] = array();
334         //$_xh[$parser]['st']='';
335         //$_xh[$parser]['cm']=0;
336         $_xh[$parser]['isf'] = 0;
337         $_xh[$parser]['isf_reason'] = '';
338         $_xh[$parser]['params'] = array();
339         $_xh[$parser]['stack'] = array();
340         $_xh[$parser]['valuestack'] = array();
341         $_xh[$parser]['method'] = '';
342
343         // decompose incoming XML into request structure
344
345         xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
346         // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
347         // the xml parser to give us back data in the expected charset
348         xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $xmlrpc_internalencoding);
349
350         xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
351         xml_set_character_data_handler($parser, 'xmlrpc_cd');
352         xml_set_default_handler($parser, 'xmlrpc_dh');
353         if (!xml_parse($parser, $data, 1)) {
354             // return XML error as a faultCode
355             $r = new xmlrpcresp(0,
356                 $xmlrpcerrxml + xml_get_error_code($parser),
357                 sprintf('XML error: %s at line %d',
358                     xml_error_string(xml_get_error_code($parser)),
359                     xml_get_current_line_number($parser)));
360             xml_parser_free($parser);
361         } else
362             if ($_xh[$parser]['isf']) {
363                 xml_parser_free($parser);
364                 $r = new xmlrpcresp(0,
365                     $xmlrpcerr['invalid_request'],
366                     $xmlrpcstr['invalid_request'] . ' ' . $_xh[$parser]['isf_reason']);
367             } else {
368                 xml_parser_free($parser);
369
370                 $m = new xmlrpcmsg($_xh[$parser]['method']);
371                 // now add parameters in
372                 $plist = '';
373                 //$allOK = 1;
374                 for ($i = 0; $i < sizeof($_xh[$parser]['params']); $i++) {
375                     //print "<!-- " . $_xh[$parser]['params'][$i]. "-->\n";
376                     $plist .= "$i - " . $_xh[$parser]['params'][$i] . ";\n";
377                     //$allOK = 0;
378                     //@eval('$m->addParam(' . $_xh[$parser]['params'][$i]. '); $allOK=1;');
379                     @$m->addParam($_xh[$parser]['params'][$i]);
380                     //if (!$allOK)
381                     //{
382                     //  break;
383                     //}
384                 }
385                 // uncomment this to really see what the server's getting!
386                 // xmlrpc_debugmsg($plist);
387                 //if (!$allOK)
388                 //{
389                 //      $r = new xmlrpcresp(0,
390                 //              $xmlrpcerr['incorrect_params'],
391                 //              $xmlrpcstr['incorrect_params'] . ": xml error in param " . $i);
392                 //}
393                 //else
394                 //{
395                 $r = $this->execute($m);
396                 //}
397             }
398         return $r;
399     }
400
401     function execute($m)
402     {
403         global $xmlrpcerr, $xmlrpcstr, $_xmlrpcs_dmap;
404         // now to deal with the method
405         $methName = $m->method();
406         $sysCall = ereg("^system\.", $methName);
407         $dmap = $sysCall ? $_xmlrpcs_dmap : $this->dmap;
408
409         if (!isset($dmap[$methName]['function'])) {
410             // No such method
411             return new xmlrpcresp(0,
412                 $xmlrpcerr['unknown_method'],
413                 $xmlrpcstr['unknown_method']);
414         }
415
416         // Check signature.
417         if (isset($dmap[$methName]['signature'])) {
418             $sig = $dmap[$methName]['signature'];
419             list($ok, $errstr) = $this->verifySignature($m, $sig);
420             if (!$ok) {
421                 // Didn't match.
422                 return new xmlrpcresp(
423                     0,
424                     $xmlrpcerr['incorrect_params'],
425                     $xmlrpcstr['incorrect_params'] . ": ${errstr}"
426                 );
427             }
428         }
429
430         $func = $dmap[$methName]['function'];
431
432         if ($sysCall) {
433             return call_user_func($func, $this, $m);
434         } else {
435             return call_user_func($func, $m);
436         }
437     }
438
439     function echoInput()
440     {
441         global $HTTP_RAW_POST_DATA;
442
443         // a debugging routine: just echos back the input
444         // packet as a string value
445
446         $r = new xmlrpcresp;
447         $r->xv = new xmlrpcval("'Aha said I: '" . $HTTP_RAW_POST_DATA, 'string');
448         print $r->serialize();
449     }
450 }