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