]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/XMLRPC/xmlrpcs.inc
Remove svn:keywords
[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                 {
51                         $outAr[]=new xmlrpcval($key, 'string');
52                 }
53                 $dmap=$_xmlrpcs_dmap;
54                 for(reset($dmap); list($key, $val)=each($dmap); )
55                 {
56                         $outAr[]=new xmlrpcval($key, 'string');
57                 }
58                 $v->addArray($outAr);
59                 return new xmlrpcresp($v);
60         }
61
62         $_xmlrpcs_methodSignature_sig=array(array($xmlrpcArray, $xmlrpcString));
63         $_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)';
64         function _xmlrpcs_methodSignature($server, $m)
65         {
66                 global $xmlrpcerr, $xmlrpcstr, $_xmlrpcs_dmap;
67
68                 $methName=$m->getParam(0);
69                 $methName=$methName->scalarval();
70                 if (ereg("^system\.", $methName))
71                 {
72                         $dmap=$_xmlrpcs_dmap; $sysCall=1;
73                 }
74                 else
75                 {
76                         $dmap=$server->dmap; $sysCall=0;
77                 }
78                 //      print "<!-- ${methName} -->\n";
79                 if (isset($dmap[$methName]))
80                 {
81                         if ($dmap[$methName]['signature'])
82                         {
83                                 $sigs=array();
84                                 $thesigs=$dmap[$methName]['signature'];
85                                 for($i=0; $i<sizeof($thesigs); $i++)
86                                 {
87                                         $cursig=array();
88                                         $inSig=$thesigs[$i];
89                                         for($j=0; $j<sizeof($inSig); $j++)
90                                         {
91                                                 $cursig[]=new xmlrpcval($inSig[$j], 'string');
92                                         }
93                                         $sigs[]=new xmlrpcval($cursig, 'array');
94                                 }
95                                 $r=new xmlrpcresp(new xmlrpcval($sigs, 'array'));
96                         }
97                         else
98                         {
99                                 $r=new xmlrpcresp(new xmlrpcval('undef', 'string'));
100                         }
101                 }
102                 else
103                 {
104                         $r=new xmlrpcresp(0,$xmlrpcerr['introspect_unknown'], $xmlrpcstr['introspect_unknown']);
105                 }
106                 return $r;
107         }
108
109         $_xmlrpcs_methodHelp_sig=array(array($xmlrpcString, $xmlrpcString));
110         $_xmlrpcs_methodHelp_doc='Returns help text if defined for the method passed, otherwise returns an empty string';
111         function _xmlrpcs_methodHelp($server, $m)
112         {
113                 global $xmlrpcerr, $xmlrpcstr, $_xmlrpcs_dmap;
114
115                 $methName=$m->getParam(0);
116                 $methName=$methName->scalarval();
117                 if (ereg("^system\.", $methName))
118                 {
119                         $dmap=$_xmlrpcs_dmap; $sysCall=1;
120                 }
121                 else
122                 {
123                         $dmap=$server->dmap; $sysCall=0;
124                 }
125                 // print "<!-- ${methName} -->\n";
126                 if (isset($dmap[$methName]))
127                 {
128                         if ($dmap[$methName]['docstring'])
129                         {
130                                 $r=new xmlrpcresp(new xmlrpcval($dmap[$methName]['docstring']), 'string');
131                         }
132                         else
133                         {
134                                 $r=new xmlrpcresp(new xmlrpcval('', 'string'));
135                         }
136                 }
137                 else
138                 {
139                         $r=new xmlrpcresp(0, $xmlrpcerr['introspect_unknown'], $xmlrpcstr['introspect_unknown']);
140                 }
141                 return $r;
142         }
143
144         $_xmlrpcs_multicall_sig = array(array($xmlrpcArray, $xmlrpcArray));
145         $_xmlrpcs_multicall_doc = 'Boxcar multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details';
146
147         function _xmlrpcs_multicall_error($err)
148         {
149                 if (is_string($err))
150                 {
151                         global $xmlrpcerr, $xmlrpcstr;
152                         $str  = $xmlrpcstr["multicall_${err}"];
153                         $code = $xmlrpcerr["multicall_${err}"];
154                 }
155                 else
156                 {
157                         $code = $err->faultCode();
158                         $str = $err->faultString();
159                 }
160                 $struct['faultCode'] = new xmlrpcval($code, 'int');
161                 $struct['faultString'] = new xmlrpcval($str, 'string');
162                 return new xmlrpcval($struct, 'struct');
163         }
164
165         function _xmlrpcs_multicall_do_call($server, $call)
166         {
167                 if ($call->kindOf() != 'struct')
168                 {
169                         return _xmlrpcs_multicall_error('notstruct');
170                 }
171                 $methName = $call->structmem('methodName');
172                 if (!$methName)
173                 {
174                         return _xmlrpcs_multicall_error('nomethod');
175                 }
176                 if ($methName->kindOf() != 'scalar' || $methName->scalartyp() != 'string')
177                 {
178                         return _xmlrpcs_multicall_error('notstring');
179                 }
180                 if ($methName->scalarval() == 'system.multicall')
181                 {
182                         return _xmlrpcs_multicall_error('recursion');
183                 }
184
185                 $params = $call->structmem('params');
186                 if (!$params)
187                 {
188                         return _xmlrpcs_multicall_error('noparams');
189                 }
190                 if ($params->kindOf() != 'array')
191                 {
192                         return _xmlrpcs_multicall_error('notarray');
193                 }
194                 $numParams = $params->arraysize();
195
196                 $msg = new xmlrpcmsg($methName->scalarval());
197                 for ($i = 0; $i < $numParams; $i++)
198                 {
199                         $msg->addParam($params->arraymem($i));
200                 }
201
202                 $result = $server->execute($msg);
203
204                 if ($result->faultCode() != 0)
205                 {
206                         return _xmlrpcs_multicall_error($result);    // Method returned fault.
207                 }
208
209                 return new xmlrpcval(array($result->value()), 'array');
210         }
211
212         function _xmlrpcs_multicall($server, $m)
213         {
214                 $calls = $m->getParam(0);
215                 $numCalls = $calls->arraysize();
216                 $result = array();
217
218                 for ($i = 0; $i < $numCalls; $i++)
219                 {
220                         $call = $calls->arraymem($i);
221                         $result[$i] = _xmlrpcs_multicall_do_call($server, $call);
222                 }
223
224                 return new xmlrpcresp(new xmlrpcval($result, 'array'));
225         }
226
227         $_xmlrpcs_dmap=array(
228                 'system.listMethods' => array(
229                         'function' => '_xmlrpcs_listMethods',
230                         'signature' => $_xmlrpcs_listMethods_sig,
231                         'docstring' => $_xmlrpcs_listMethods_doc),
232                 'system.methodHelp' => array(
233                         'function' => '_xmlrpcs_methodHelp',
234                         'signature' => $_xmlrpcs_methodHelp_sig,
235                         'docstring' => $_xmlrpcs_methodHelp_doc),
236                 'system.methodSignature' => array(
237                         'function' => '_xmlrpcs_methodSignature',
238                         'signature' => $_xmlrpcs_methodSignature_sig,
239                         'docstring' => $_xmlrpcs_methodSignature_doc),
240                 'system.multicall' => array(
241                         'function' => '_xmlrpcs_multicall',
242                         'signature' => $_xmlrpcs_multicall_sig,
243                         'docstring' => $_xmlrpcs_multicall_doc
244                 )
245         );
246
247         $_xmlrpc_debuginfo='';
248         function xmlrpc_debugmsg($m)
249         {
250                 global $_xmlrpc_debuginfo;
251                 $_xmlrpc_debuginfo=$_xmlrpc_debuginfo . $m . "\n";
252         }
253
254         class xmlrpc_server
255         {
256                 var $dmap=array();
257
258                 function xmlrpc_server($dispMap='', $serviceNow=1)
259                 {
260                         global $HTTP_RAW_POST_DATA;
261                         // dispMap is a dispatch array of methods
262                         // mapped to function names and signatures
263                         // if a method
264                         // doesn't appear in the map then an unknown
265                         // method error is generated
266                         /* milosch - changed to make passing dispMap optional.
267                          * instead, you can use the class add_to_map() function
268                          * to add functions manually (borrowed from SOAPX4)
269                          */
270                         if($dispMap)
271                         {
272                                 $this->dmap = $dispMap;
273                                 if($serviceNow)
274                                 {
275                                         $this->service();
276                                 }
277                         }
278                 }
279
280                 function serializeDebug()
281                 {
282                         global $_xmlrpc_debuginfo;
283                         if ($_xmlrpc_debuginfo!='')
284                         {
285                                 return "<!-- DEBUG INFO:\n\n" . xmlrpc_encode_entitites($_xmlrpc_debuginfo) . "\n-->\n";
286                         }
287                         else
288                         {
289                                 return '';
290                         }
291                 }
292
293                 function service()
294                 {
295                         //global $xmlrpc_defencoding;
296
297                         $r=$this->parseRequest();
298                         //$payload='<?xml version="1.0" encoding="' . $xmlrpc_defencoding . '"?' . '>' . "\n"
299                         $payload='<?xml version="1.0" ?' . '>' . "\n"
300                                 . $this->serializeDebug()
301                                 . $r->serialize();
302                         header('Content-Type: text/xml');
303                         header('Content-Length: ' . (int)strlen($payload));
304                         print $payload;
305                 }
306
307                 /*
308                 add a method to the dispatch map
309                 */
310                 function add_to_map($methodname,$function,$sig,$doc)
311                 {
312                         $this->dmap[$methodname] = array(
313                                 'function'  => $function,
314                                 'signature' => $sig,
315                                 'docstring' => $doc
316                         );
317                 }
318
319                 function verifySignature($in, $sig)
320                 {
321                         for($i=0; $i<sizeof($sig); $i++)
322                         {
323                                 // check each possible signature in turn
324                                 $cursig=$sig[$i];
325                                 if (sizeof($cursig)==$in->getNumParams()+1)
326                                 {
327                                         $itsOK=1;
328                                         for($n=0; $n<$in->getNumParams(); $n++)
329                                         {
330                                                 $p=$in->getParam($n);
331                                                 // print "<!-- $p -->\n";
332                                                 if ($p->kindOf() == 'scalar')
333                                                 {
334                                                         $pt=$p->scalartyp();
335                                                 }
336                                                 else
337                                                 {
338                                                         $pt=$p->kindOf();
339                                                 }
340                                                 // $n+1 as first type of sig is return type
341                                                 if ($pt != $cursig[$n+1])
342                                                 {
343                                                         $itsOK=0;
344                                                         $pno=$n+1; $wanted=$cursig[$n+1]; $got=$pt;
345                                                         break;
346                                                 }
347                                         }
348                                         if ($itsOK)
349                                         {
350                                                 return array(1,'');
351                                         }
352                                 }
353                         }
354                         if (isset($wanted))
355                                 return array(0, "Wanted ${wanted}, got ${got} at param ${pno})");
356                         else
357                                 return array(0, "No method signature matches number of parameters");
358                 }
359
360                 function parseRequest($data='')
361                 {
362                         global $_xh,$HTTP_RAW_POST_DATA;
363                         global $xmlrpcerr, $xmlrpcstr, $xmlrpcerrxml, $xmlrpc_defencoding,
364                         $_xmlrpcs_dmap, $xmlrpc_internalencoding;
365
366                         if ($data=='')
367                         {
368                                 $data=$HTTP_RAW_POST_DATA;
369                         }
370             // G. Giunta 2005/02/13: we do NOT expect to receive html entities
371             // so we do not try to convert them into xml character entities
372                         //$data = xmlrpc_html_entity_xlate($data);
373                         $parser = xml_parser_create($xmlrpc_defencoding);
374
375                         $_xh[$parser]=array();
376                         //$_xh[$parser]['st']='';
377                         //$_xh[$parser]['cm']=0;
378                         $_xh[$parser]['isf']=0;
379                         $_xh[$parser]['isf_reason']='';
380                         $_xh[$parser]['params']=array();
381                         $_xh[$parser]['stack']=array();
382                         $_xh[$parser]['valuestack'] = array();
383                         $_xh[$parser]['method']='';
384
385                         // decompose incoming XML into request structure
386
387                         xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
388             // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
389             // the xml parser to give us back data in the expected charset
390             xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $xmlrpc_internalencoding);
391
392                         xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
393                         xml_set_character_data_handler($parser, 'xmlrpc_cd');
394                         xml_set_default_handler($parser, 'xmlrpc_dh');
395                         if (!xml_parse($parser, $data, 1))
396                         {
397                                 // return XML error as a faultCode
398                                 $r=new xmlrpcresp(0,
399                                 $xmlrpcerrxml+xml_get_error_code($parser),
400                                 sprintf('XML error: %s at line %d',
401                                         xml_error_string(xml_get_error_code($parser)),
402                                         xml_get_current_line_number($parser)));
403                                 xml_parser_free($parser);
404                         }
405                         else
406                         if ($_xh[$parser]['isf'])
407                         {
408                                 xml_parser_free($parser);
409                                 $r=new xmlrpcresp(0,
410                                         $xmlrpcerr['invalid_request'],
411                                         $xmlrpcstr['invalid_request'] . ' ' . $_xh[$parser]['isf_reason']);
412                         }
413                         else
414                         {
415                                 xml_parser_free($parser);
416
417                                 $m=new xmlrpcmsg($_xh[$parser]['method']);
418                                 // now add parameters in
419                                 $plist='';
420                                 //$allOK = 1;
421                                 for($i=0; $i<sizeof($_xh[$parser]['params']); $i++)
422                                 {
423                                         //print "<!-- " . $_xh[$parser]['params'][$i]. "-->\n";
424                                         $plist.="$i - " .  $_xh[$parser]['params'][$i]. ";\n";
425                                         //$allOK = 0;
426                                         //@eval('$m->addParam(' . $_xh[$parser]['params'][$i]. '); $allOK=1;');
427                                         @$m->addParam($_xh[$parser]['params'][$i]);
428                                         //if (!$allOK)
429                                         //{
430                                         //      break;
431                                         //}
432                                 }
433                                 // uncomment this to really see what the server's getting!
434                                 // xmlrpc_debugmsg($plist);
435                                 //if (!$allOK)
436                                 //{
437                                 //      $r = new xmlrpcresp(0,
438                                 //              $xmlrpcerr['incorrect_params'],
439                                 //              $xmlrpcstr['incorrect_params'] . ": xml error in param " . $i);
440                                 //}
441                                 //else
442                                 //{
443                                         $r = $this->execute($m);
444                                 //}
445                         }
446                         return $r;
447                 }
448
449                 function execute ($m)
450                 {
451                         global $xmlrpcerr, $xmlrpcstr, $_xmlrpcs_dmap;
452                         // now to deal with the method
453                         $methName = $m->method();
454                         $sysCall = ereg("^system\.", $methName);
455                         $dmap = $sysCall ? $_xmlrpcs_dmap : $this->dmap;
456
457                         if (!isset($dmap[$methName]['function']))
458                         {
459                                 // No such method
460                                 return new xmlrpcresp(0,
461                                         $xmlrpcerr['unknown_method'],
462                                         $xmlrpcstr['unknown_method']);
463                         }
464
465                         // Check signature.
466                         if (isset($dmap[$methName]['signature']))
467                         {
468                                 $sig = $dmap[$methName]['signature'];
469                                 list($ok, $errstr) = $this->verifySignature($m, $sig);
470                                 if(!$ok)
471                                 {
472                                         // Didn't match.
473                                         return new xmlrpcresp(
474                                                 0,
475                                                 $xmlrpcerr['incorrect_params'],
476                                                 $xmlrpcstr['incorrect_params'] . ": ${errstr}"
477                                         );
478                                 }
479                         }
480
481                         $func = $dmap[$methName]['function'];
482
483                         if ($sysCall)
484                         {
485                                 return call_user_func($func, $this, $m);
486                         }
487                         else
488                         {
489                                 return call_user_func($func, $m);
490                         }
491                 }
492
493                 function echoInput()
494                 {
495                         global $HTTP_RAW_POST_DATA;
496
497                         // a debugging routine: just echos back the input
498                         // packet as a string value
499
500                         $r=new xmlrpcresp;
501                         $r->xv=new xmlrpcval( "'Aha said I: '" . $HTTP_RAW_POST_DATA, 'string');
502                         print $r->serialize();
503                 }
504         }
505 ?>