Take two:
[www-register-wizard.git] / libraries / Xmlrpcs.php
1 <?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');\r
2 /**\r
3  * CodeIgniter\r
4  *\r
5  * An open source application development framework for PHP 4.3.2 or newer\r
6  *\r
7  * @package             CodeIgniter\r
8  * @author              ExpressionEngine Dev Team\r
9  * @copyright   Copyright (c) 2008, EllisLab, Inc.\r
10  * @license             http://codeigniter.com/user_guide/license.html\r
11  * @link                http://codeigniter.com\r
12  * @since               Version 1.0\r
13  * @filesource\r
14  */\r
15 \r
16 if ( ! function_exists('xml_parser_create'))\r
17 {       \r
18         show_error('Your PHP installation does not support XML');\r
19 }\r
20 \r
21 if ( ! class_exists('CI_Xmlrpc'))\r
22 {\r
23         show_error('You must load the Xmlrpc class before loading the Xmlrpcs class in order to create a server.');\r
24 }\r
25 \r
26 // ------------------------------------------------------------------------\r
27 \r
28 /**\r
29  * XML-RPC server class\r
30  *\r
31  * @package             CodeIgniter\r
32  * @subpackage  Libraries\r
33  * @category    XML-RPC\r
34  * @author              ExpressionEngine Dev Team\r
35  * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html\r
36  */\r
37 class CI_Xmlrpcs extends CI_Xmlrpc\r
38 {\r
39         var $methods            = array();      //array of methods mapped to function names and signatures\r
40         var $debug_msg          = '';           // Debug Message\r
41         var $system_methods = array(); // XML RPC Server methods\r
42         var $controller_obj;\r
43 \r
44         var $object                     = FALSE;\r
45         \r
46         \r
47         //-------------------------------------\r
48         //  Constructor, more or less\r
49         //-------------------------------------\r
50 \r
51         function CI_Xmlrpcs($config=array())\r
52         {       \r
53                 parent::CI_Xmlrpc();\r
54                 $this->set_system_methods();\r
55         \r
56                 if (isset($config['functions']) && is_array($config['functions']))\r
57                 {\r
58                         $this->methods = array_merge($this->methods, $config['functions']);\r
59                 }\r
60                 \r
61                 log_message('debug', "XML-RPC Server Class Initialized");\r
62         }\r
63         \r
64         //-------------------------------------\r
65         //  Initialize Prefs and Serve\r
66         //-------------------------------------\r
67         \r
68         function initialize($config=array())\r
69         {       \r
70                 if (isset($config['functions']) && is_array($config['functions']))\r
71                 {\r
72                         $this->methods = array_merge($this->methods, $config['functions']);\r
73                 }\r
74                 \r
75                 if (isset($config['debug']))\r
76                 {\r
77                         $this->debug = $config['debug'];\r
78                 }\r
79                 \r
80                 if (isset($config['object']) && is_object($config['object']))\r
81                 {\r
82                         $this->object = $config['object'];\r
83                 }\r
84         }\r
85         \r
86         //-------------------------------------\r
87         //  Setting of System Methods\r
88         //-------------------------------------\r
89         \r
90         function set_system_methods ()\r
91         {\r
92                 $this->methods = array(\r
93                                         'system.listMethods'     => array(\r
94                                                                                                         'function' => 'this.listMethods',\r
95                                                                                                         'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString), array($this->xmlrpcArray)),\r
96                                                                                                         'docstring' => 'Returns an array of available methods on this server'),\r
97                                         'system.methodHelp'              => array(\r
98                                                                                                         'function' => 'this.methodHelp',\r
99                                                                                                         'signature' => array(array($this->xmlrpcString, $this->xmlrpcString)),\r
100                                                                                                         'docstring' => 'Returns a documentation string for the specified method'),\r
101                                         'system.methodSignature' => array(\r
102                                                                                                         'function' => 'this.methodSignature',\r
103                                                                                                         'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString)),\r
104                                                                                                         'docstring' => 'Returns an array describing the return type and required parameters of a method'),\r
105                                         'system.multicall'               => array(\r
106                                                                                                 'function' => 'this.multicall',\r
107                                                                                                 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcArray)),\r
108                                                                                                 'docstring' => 'Combine multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details')\r
109                                         );\r
110         }\r
111 \r
112 \r
113         //-------------------------------------\r
114         //  Main Server Function\r
115         //-------------------------------------\r
116         \r
117         function serve()\r
118         {\r
119                 $r = $this->parseRequest();\r
120                 $payload  = '<?xml version="1.0" encoding="'.$this->xmlrpc_defencoding.'"?'.'>'."\n";\r
121                 $payload .= $this->debug_msg;\r
122                 $payload .= $r->prepare_response();\r
123                 \r
124                 header("Content-Type: text/xml");\r
125                 header("Content-Length: ".strlen($payload));\r
126                 echo $payload;\r
127         }\r
128 \r
129         //-------------------------------------\r
130         //  Add Method to Class\r
131         //-------------------------------------\r
132         \r
133         function add_to_map($methodname,$function,$sig,$doc)\r
134         {\r
135                 $this->methods[$methodname] = array(\r
136                         'function'  => $function,\r
137                         'signature' => $sig,\r
138                         'docstring' => $doc\r
139                 );\r
140         }\r
141 \r
142 \r
143         //-------------------------------------\r
144         //  Parse Server Request\r
145         //-------------------------------------\r
146         \r
147         function parseRequest($data='')\r
148         {\r
149                 global $HTTP_RAW_POST_DATA;\r
150                 \r
151                 //-------------------------------------\r
152                 //  Get Data\r
153                 //-------------------------------------\r
154 \r
155                 if ($data == '')\r
156                 {\r
157                         $data = $HTTP_RAW_POST_DATA;\r
158                 }\r
159 \r
160                 //-------------------------------------\r
161                 //  Set up XML Parser\r
162                 //-------------------------------------\r
163                 \r
164                 $parser = xml_parser_create($this->xmlrpc_defencoding);\r
165                 $parser_object = new XML_RPC_Message("filler");\r
166                 \r
167                 $parser_object->xh[$parser]                                     = array();\r
168                 $parser_object->xh[$parser]['isf']                      = 0;\r
169                 $parser_object->xh[$parser]['isf_reason']       = '';\r
170                 $parser_object->xh[$parser]['params']           = array();\r
171                 $parser_object->xh[$parser]['stack']            = array();\r
172                 $parser_object->xh[$parser]['valuestack']       = array();\r
173                 $parser_object->xh[$parser]['method']           = '';\r
174 \r
175                 xml_set_object($parser, $parser_object);\r
176                 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);\r
177                 xml_set_element_handler($parser, 'open_tag', 'closing_tag');\r
178                 xml_set_character_data_handler($parser, 'character_data');\r
179                 //xml_set_default_handler($parser, 'default_handler');\r
180                 \r
181                 \r
182                 //-------------------------------------\r
183                 //  PARSE + PROCESS XML DATA\r
184                 //-------------------------------------         \r
185                 \r
186                 if ( ! xml_parse($parser, $data, 1))\r
187                 {\r
188                         // return XML error as a faultCode\r
189                         $r = new XML_RPC_Response(0,\r
190                         $this->xmlrpcerrxml + xml_get_error_code($parser),\r
191                         sprintf('XML error: %s at line %d',\r
192                                 xml_error_string(xml_get_error_code($parser)),\r
193                                 xml_get_current_line_number($parser)));\r
194                         xml_parser_free($parser);\r
195                 }\r
196                 elseif($parser_object->xh[$parser]['isf'])\r
197                 {\r
198                         return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']);\r
199                 }\r
200                 else\r
201                 {\r
202                         xml_parser_free($parser);\r
203                         \r
204                         $m = new XML_RPC_Message($parser_object->xh[$parser]['method']);\r
205                         $plist='';\r
206                         \r
207                         for($i=0; $i < sizeof($parser_object->xh[$parser]['params']); $i++)\r
208                         {\r
209                                 if ($this->debug === TRUE)\r
210                                 {\r
211                                         $plist .= "$i - " .  print_r(get_object_vars($parser_object->xh[$parser]['params'][$i]), TRUE). ";\n";\r
212                                 }\r
213                                 \r
214                                 $m->addParam($parser_object->xh[$parser]['params'][$i]);\r
215                         }\r
216                         \r
217                         if ($this->debug === TRUE)\r
218                         {\r
219                                 echo "<pre>";\r
220                                 echo "---PLIST---\n" . $plist . "\n---PLIST END---\n\n";\r
221                                 echo "</pre>";\r
222                         }\r
223                         \r
224                         $r = $this->_execute($m);\r
225                 }\r
226                 \r
227                 //-------------------------------------\r
228                 //  SET DEBUGGING MESSAGE\r
229                 //-------------------------------------         \r
230                 \r
231                 if ($this->debug === TRUE)\r
232                 {\r
233                         $this->debug_msg = "<!-- DEBUG INFO:\n\n".$plist."\n END DEBUG-->\n";\r
234                 }\r
235                 \r
236                 return $r;\r
237         }\r
238 \r
239         //-------------------------------------\r
240         //  Executes the Method\r
241         //-------------------------------------\r
242         \r
243         function _execute($m)\r
244         {\r
245                 $methName = $m->method_name;\r
246                 \r
247                 // Check to see if it is a system call\r
248                 $system_call = (strncmp($methName, 'system', 5) == 0) ? TRUE : FALSE;\r
249                 \r
250                 //-------------------------------------\r
251                 //  Valid Method\r
252                 //-------------------------------------\r
253                 \r
254                 if ( ! isset($this->methods[$methName]['function']))\r
255                 {\r
256                         return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);\r
257                 }\r
258                 \r
259                 //-------------------------------------\r
260                 //  Check for Method (and Object)\r
261                 //-------------------------------------\r
262                         \r
263                 $method_parts = explode(".", $this->methods[$methName]['function']);\r
264                 $objectCall = (isset($method_parts['1']) && $method_parts['1'] != "") ? TRUE : FALSE;\r
265                 \r
266                 if ($system_call === TRUE)\r
267                 {\r
268                         if ( ! is_callable(array($this,$method_parts['1'])))\r
269                         {\r
270                                 return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);\r
271                         }\r
272                 }\r
273                 else\r
274                 {\r
275                         if ($objectCall && ! is_callable(array($method_parts['0'],$method_parts['1'])))\r
276                         {\r
277                                 return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);\r
278                         }\r
279                         elseif ( ! $objectCall && ! is_callable($this->methods[$methName]['function']))\r
280                         {\r
281                                 return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);\r
282                         }\r
283                 }\r
284                 \r
285                 //-------------------------------------\r
286                 //  Checking Methods Signature\r
287                 //-------------------------------------\r
288                 \r
289                 if (isset($this->methods[$methName]['signature']))\r
290                 {\r
291                         $sig = $this->methods[$methName]['signature'];\r
292                         for($i=0; $i<sizeof($sig); $i++)\r
293                         {\r
294                                 $current_sig = $sig[$i];\r
295                 \r
296                                 if (sizeof($current_sig) == sizeof($m->params)+1)\r
297                                 {\r
298                                         for($n=0; $n < sizeof($m->params); $n++)\r
299                                         {\r
300                                                 $p = $m->params[$n];\r
301                                                 $pt = ($p->kindOf() == 'scalar') ? $p->scalarval() : $p->kindOf();\r
302                                                 \r
303                                                 if ($pt != $current_sig[$n+1])\r
304                                                 {\r
305                                                         $pno = $n+1;\r
306                                                         $wanted = $current_sig[$n+1];\r
307                                                         \r
308                                                         return new XML_RPC_Response(0,\r
309                                                                 $this->xmlrpcerr['incorrect_params'],\r
310                                                                 $this->xmlrpcstr['incorrect_params'] .\r
311                                                                 ": Wanted {$wanted}, got {$pt} at param {$pno})");\r
312                                                 }\r
313                                         }\r
314                                 }\r
315                         }\r
316                 }\r
317 \r
318                 //-------------------------------------\r
319                 //  Calls the Function\r
320                 //-------------------------------------\r
321 \r
322                 if ($objectCall === TRUE)\r
323                 {\r
324                         if ($method_parts[0] == "this" && $system_call == TRUE)\r
325                         {\r
326                                 return call_user_func(array($this, $method_parts[1]), $m);\r
327                         }\r
328                         else\r
329                         {\r
330                                 if ($this->object === FALSE)\r
331                                 {\r
332                                         $CI =& get_instance();\r
333                                         return $CI->$method_parts['1']($m);\r
334                                 }\r
335                                 else\r
336                                 {\r
337                                         return $this->object->$method_parts['1']($m);\r
338                                         //return call_user_func(array(&$method_parts['0'],$method_parts['1']), $m);\r
339                                 }\r
340                         }\r
341                 }\r
342                 else\r
343                 {\r
344                         return call_user_func($this->methods[$methName]['function'], $m);\r
345                 }\r
346         }\r
347         \r
348         \r
349         //-------------------------------------\r
350         //  Server Function:  List Methods\r
351         //-------------------------------------\r
352         \r
353         function listMethods($m)\r
354         {\r
355                 $v = new XML_RPC_Values();\r
356                 $output = array();\r
357                 \r
358                 foreach($this->methods as $key => $value)\r
359                 {\r
360                         $output[] = new XML_RPC_Values($key, 'string');\r
361                 }\r
362                 \r
363                 foreach($this->system_methods as $key => $value)\r
364                 {\r
365                         $output[]= new XML_RPC_Values($key, 'string');\r
366                 }\r
367 \r
368                 $v->addArray($output);\r
369                 return new XML_RPC_Response($v);\r
370         }\r
371         \r
372         //-------------------------------------\r
373         //  Server Function:  Return Signature for Method\r
374         //-------------------------------------\r
375                 \r
376         function methodSignature($m)\r
377         {\r
378                 $parameters = $m->output_parameters();\r
379                 $method_name = $parameters[0];\r
380                 \r
381                 if (isset($this->methods[$method_name]))\r
382                 {\r
383                         if ($this->methods[$method_name]['signature'])\r
384                         {\r
385                                 $sigs = array();\r
386                                 $signature = $this->methods[$method_name]['signature'];\r
387                                 \r
388                                 for($i=0; $i < sizeof($signature); $i++)\r
389                                 {\r
390                                         $cursig = array();\r
391                                         $inSig = $signature[$i];\r
392                                         for($j=0; $j<sizeof($inSig); $j++)\r
393                                         {\r
394                                                 $cursig[]= new XML_RPC_Values($inSig[$j], 'string');\r
395                                         }\r
396                                         $sigs[]= new XML_RPC_Values($cursig, 'array');\r
397                                 }\r
398                                 $r = new XML_RPC_Response(new XML_RPC_Values($sigs, 'array'));\r
399                         }\r
400                         else\r
401                         {\r
402                                 $r = new XML_RPC_Response(new XML_RPC_Values('undef', 'string'));\r
403                         }\r
404                 }\r
405                 else\r
406                 {\r
407                         $r = new XML_RPC_Response(0,$this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);\r
408                 }\r
409                 return $r;\r
410         }\r
411         \r
412         //-------------------------------------\r
413         //  Server Function:  Doc String for Method\r
414         //-------------------------------------\r
415         \r
416         function methodHelp($m)\r
417         {\r
418                 $parameters = $m->output_parameters();\r
419                 $method_name = $parameters[0];\r
420         \r
421                 if (isset($this->methods[$method_name]))\r
422                 {\r
423                         $docstring = isset($this->methods[$method_name]['docstring']) ? $this->methods[$method_name]['docstring'] : '';\r
424                         \r
425                         return new XML_RPC_Response(new XML_RPC_Values($docstring, 'string'));\r
426                 }\r
427                 else\r
428                 {\r
429                         return new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);\r
430                 }\r
431         }\r
432 \r
433         //-------------------------------------\r
434         //  Server Function:  Multi-call\r
435         //-------------------------------------\r
436 \r
437         function multicall($m)\r
438         {\r
439                 // Disabled\r
440                 return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);\r
441                 \r
442                 $parameters = $m->output_parameters();\r
443                 $calls = $parameters[0];\r
444 \r
445                 $result = array();\r
446 \r
447                 foreach ($calls as $value)\r
448                 {\r
449                         //$attempt = $this->_execute(new XML_RPC_Message($value[0], $value[1]));\r
450                         \r
451                         $m = new XML_RPC_Message($value[0]);\r
452                         $plist='';\r
453                         \r
454                         for($i=0; $i < sizeof($value[1]); $i++)\r
455                         {\r
456                                 $m->addParam(new XML_RPC_Values($value[1][$i], 'string'));\r
457                         }\r
458                         \r
459                         $attempt = $this->_execute($m);\r
460 \r
461                         if ($attempt->faultCode() != 0)\r
462                         {\r
463                                 return $attempt;\r
464                         }\r
465 \r
466                         $result[] = new XML_RPC_Values(array($attempt->value()), 'array');\r
467                 }\r
468 \r
469                 return new XML_RPC_Response(new XML_RPC_Values($result, 'array'));\r
470         }\r
471         \r
472         \r
473         //-------------------------------------\r
474         //  Multi-call Function:  Error Handling\r
475         //-------------------------------------\r
476 \r
477         function multicall_error($err)\r
478         {\r
479                 $str  = is_string($err) ? $this->xmlrpcstr["multicall_${err}"] : $err->faultString();\r
480                 $code = is_string($err) ? $this->xmlrpcerr["multicall_${err}"] : $err->faultCode();\r
481                 \r
482                 $struct['faultCode'] = new XML_RPC_Values($code, 'int');\r
483                 $struct['faultString'] = new XML_RPC_Values($str, 'string');\r
484         \r
485                 return new XML_RPC_Values($struct, 'struct');\r
486         }\r
487         \r
488         \r
489         //-------------------------------------\r
490         //  Multi-call Function:  Processes method\r
491         //-------------------------------------\r
492         \r
493         function do_multicall($call)\r
494         {\r
495                 if ($call->kindOf() != 'struct')\r
496                         return $this->multicall_error('notstruct');\r
497                 elseif ( ! $methName = $call->me['struct']['methodName'])\r
498                         return $this->multicall_error('nomethod');\r
499                 \r
500                 list($scalar_type,$scalar_value)=each($methName->me);\r
501                 $scalar_type = $scalar_type == $this->xmlrpcI4 ? $this->xmlrpcInt : $scalar_type;\r
502                         \r
503                 if ($methName->kindOf() != 'scalar' OR $scalar_type != 'string')\r
504                         return $this->multicall_error('notstring');\r
505                 elseif ($scalar_value == 'system.multicall')\r
506                         return $this->multicall_error('recursion');\r
507                 elseif ( ! $params = $call->me['struct']['params'])\r
508                         return $this->multicall_error('noparams');\r
509                 elseif ($params->kindOf() != 'array')\r
510                         return $this->multicall_error('notarray');\r
511                         \r
512                 list($a,$b)=each($params->me);\r
513                 $numParams = sizeof($b);\r
514 \r
515                 $msg = new XML_RPC_Message($scalar_value);\r
516                 for ($i = 0; $i < $numParams; $i++)\r
517                 {\r
518                         $msg->params[] = $params->me['array'][$i];\r
519                 }\r
520 \r
521                 $result = $this->_execute($msg);\r
522 \r
523                 if ($result->faultCode() != 0)\r
524                 {\r
525                         return $this->multicall_error($result);\r
526                 }\r
527 \r
528                 return new XML_RPC_Values(array($result->value()), 'array');\r
529         }       \r
530         \r
531 }\r
532 // END XML_RPC_Server class\r
533 \r
534 \r
535 /* End of file Xmlrpcs.php */\r
536 /* Location: ./system/libraries/Xmlrpcs.php */