OSDN Git Service

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