CIS-LB: Randomized suboptimal load balancing implemented without low timeout features.
[living-lab-site.git] / system / libraries / Xmlrpc.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
22 // ------------------------------------------------------------------------
23
24 /**
25  * XML-RPC request handler class
26  *
27  * @package             CodeIgniter
28  * @subpackage  Libraries
29  * @category    XML-RPC
30  * @author              ExpressionEngine Dev Team
31  * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
32  */
33 class CI_Xmlrpc {
34
35         var $debug                      = FALSE;        // Debugging on or off
36         var $xmlrpcI4           = 'i4';
37         var $xmlrpcInt          = 'int';
38         var $xmlrpcBoolean      = 'boolean';
39         var $xmlrpcDouble       = 'double';
40         var $xmlrpcString       = 'string';
41         var $xmlrpcDateTime     = 'dateTime.iso8601';
42         var $xmlrpcBase64       = 'base64';
43         var $xmlrpcArray        = 'array';
44         var $xmlrpcStruct       = 'struct';
45
46         var $xmlrpcTypes        = array();
47         var $valid_parents      = array();
48         var $xmlrpcerr          = array();      // Response numbers
49         var $xmlrpcstr          = array();  // Response strings
50
51         var $xmlrpc_defencoding = 'UTF-8';
52         var $xmlrpcName                 = 'XML-RPC for CodeIgniter';
53         var $xmlrpcVersion              = '1.1';
54         var $xmlrpcerruser              = 800; // Start of user errors
55         var $xmlrpcerrxml               = 100; // Start of XML Parse errors
56         var $xmlrpc_backslash   = ''; // formulate backslashes for escaping regexp
57
58         var $client;
59         var $method;
60         var $data;
61         var $message                    = '';
62         var $error                              = '';           // Error string for request
63         var $result;
64         var $response                   = array();  // Response from remote server
65
66         var $xss_clean                  = TRUE;
67
68         //-------------------------------------
69         //  VALUES THAT MULTIPLE CLASSES NEED
70         //-------------------------------------
71
72         public function __construct($config = array())
73         {
74                 $this->xmlrpcName               = $this->xmlrpcName;
75                 $this->xmlrpc_backslash = chr(92).chr(92);
76
77                 // Types for info sent back and forth
78                 $this->xmlrpcTypes = array(
79                         $this->xmlrpcI4                 => '1',
80                         $this->xmlrpcInt                => '1',
81                         $this->xmlrpcBoolean    => '1',
82                         $this->xmlrpcString             => '1',
83                         $this->xmlrpcDouble             => '1',
84                         $this->xmlrpcDateTime   => '1',
85                         $this->xmlrpcBase64             => '1',
86                         $this->xmlrpcArray              => '2',
87                         $this->xmlrpcStruct             => '3'
88                         );
89
90                 // Array of Valid Parents for Various XML-RPC elements
91                 $this->valid_parents = array('BOOLEAN'                  => array('VALUE'),
92                                                                          'I4'                           => array('VALUE'),
93                                                                          'INT'                          => array('VALUE'),
94                                                                          'STRING'                       => array('VALUE'),
95                                                                          'DOUBLE'                       => array('VALUE'),
96                                                                          'DATETIME.ISO8601'     => array('VALUE'),
97                                                                          'BASE64'                       => array('VALUE'),
98                                                                          'ARRAY'                        => array('VALUE'),
99                                                                          'STRUCT'                       => array('VALUE'),
100                                                                          'PARAM'                        => array('PARAMS'),
101                                                                          'METHODNAME'           => array('METHODCALL'),
102                                                                          'PARAMS'                       => array('METHODCALL', 'METHODRESPONSE'),
103                                                                          'MEMBER'                       => array('STRUCT'),
104                                                                          'NAME'                         => array('MEMBER'),
105                                                                          'DATA'                         => array('ARRAY'),
106                                                                          'FAULT'                        => array('METHODRESPONSE'),
107                                                                          'VALUE'                        => array('MEMBER', 'DATA', 'PARAM', 'FAULT')
108                                                                          );
109
110
111                 // XML-RPC Responses
112                 $this->xmlrpcerr['unknown_method'] = '1';
113                 $this->xmlrpcstr['unknown_method'] = 'This is not a known method for this XML-RPC Server';
114                 $this->xmlrpcerr['invalid_return'] = '2';
115                 $this->xmlrpcstr['invalid_return'] = 'The XML data received was either invalid or not in the correct form for XML-RPC.  Turn on debugging to examine the XML data further.';
116                 $this->xmlrpcerr['incorrect_params'] = '3';
117                 $this->xmlrpcstr['incorrect_params'] = 'Incorrect parameters were passed to method';
118                 $this->xmlrpcerr['introspect_unknown'] = '4';
119                 $this->xmlrpcstr['introspect_unknown'] = "Cannot inspect signature for request: method unknown";
120                 $this->xmlrpcerr['http_error'] = '5';
121                 $this->xmlrpcstr['http_error'] = "Did not receive a '200 OK' response from remote server.";
122                 $this->xmlrpcerr['no_data'] = '6';
123                 $this->xmlrpcstr['no_data'] ='No data received from server.';
124
125                 $this->initialize($config);
126
127                 log_message('debug', "XML-RPC Class Initialized");
128         }
129
130
131         //-------------------------------------
132         //  Initialize Prefs
133         //-------------------------------------
134
135         function initialize($config = array())
136         {
137                 if (count($config) > 0)
138                 {
139                         foreach ($config as $key => $val)
140                         {
141                                 if (isset($this->$key))
142                                 {
143                                         $this->$key = $val;
144                                 }
145                         }
146                 }
147         }
148         // END
149
150         //-------------------------------------
151         //  Take URL and parse it
152         //-------------------------------------
153
154         function server($url, $port=80)
155         {
156                 if (substr($url, 0, 4) != "http")
157                 {
158                         $url = "http://".$url;
159                 }
160
161                 $parts = parse_url($url);
162
163                 $path = ( ! isset($parts['path'])) ? '/' : $parts['path'];
164
165                 if (isset($parts['query']) && $parts['query'] != '')
166                 {
167                         $path .= '?'.$parts['query'];
168                 }
169
170                 $this->client = new XML_RPC_Client($path, $parts['host'], $port);
171         }
172         // END
173
174         //-------------------------------------
175         //  Set Timeout
176         //-------------------------------------
177
178         function timeout($seconds=5)
179         {
180                 if ( ! is_null($this->client) && is_int($seconds))
181                 {
182                         $this->client->timeout = $seconds;
183                 }
184         }
185         // END
186
187         //-------------------------------------
188         //  Set Methods
189         //-------------------------------------
190
191         function method($function)
192         {
193                 $this->method = $function;
194         }
195         // END
196
197         //-------------------------------------
198         //  Take Array of Data and Create Objects
199         //-------------------------------------
200
201         function request($incoming)
202         {
203                 if ( ! is_array($incoming))
204                 {
205                         // Send Error
206                 }
207
208                 $this->data = array();
209
210                 foreach ($incoming as $key => $value)
211                 {
212                         $this->data[$key] = $this->values_parsing($value);
213                 }
214         }
215         // END
216
217
218         //-------------------------------------
219         //  Set Debug
220         //-------------------------------------
221
222         function set_debug($flag = TRUE)
223         {
224                 $this->debug = ($flag == TRUE) ? TRUE : FALSE;
225         }
226
227         //-------------------------------------
228         //  Values Parsing
229         //-------------------------------------
230
231         function values_parsing($value, $return = FALSE)
232         {
233                 if (is_array($value) && array_key_exists(0, $value))
234                 {
235                         if ( ! isset($value['1']) OR ( ! isset($this->xmlrpcTypes[$value['1']])))
236                         {
237                                 if (is_array($value[0]))
238                                 {
239                                         $temp = new XML_RPC_Values($value['0'], 'array');
240                                 }
241                                 else
242                                 {
243                                         $temp = new XML_RPC_Values($value['0'], 'string');
244                                 }
245                         }
246                         elseif (is_array($value['0']) && ($value['1'] == 'struct' OR $value['1'] == 'array'))
247                         {
248                                 while (list($k) = each($value['0']))
249                                 {
250                                         $value['0'][$k] = $this->values_parsing($value['0'][$k], TRUE);
251                                 }
252
253                                 $temp = new XML_RPC_Values($value['0'], $value['1']);
254                         }
255                         else
256                         {
257                                 $temp = new XML_RPC_Values($value['0'], $value['1']);
258                         }
259                 }
260                 else
261                 {
262                         $temp = new XML_RPC_Values($value, 'string');
263                 }
264
265                 return $temp;
266         }
267         // END
268
269
270         //-------------------------------------
271         //  Sends XML-RPC Request
272         //-------------------------------------
273
274         function send_request()
275         {
276                 $this->message = new XML_RPC_Message($this->method,$this->data);
277                 $this->message->debug = $this->debug;
278
279                 if ( ! $this->result = $this->client->send($this->message))
280                 {
281                         $this->error = $this->result->errstr;
282                         return FALSE;
283                 }
284                 elseif ( ! is_object($this->result->val))
285                 {
286                         $this->error = $this->result->errstr;
287                         return FALSE;
288                 }
289
290                 $this->response = $this->result->decode();
291
292                 return TRUE;
293         }
294         // END
295
296         //-------------------------------------
297         //  Returns Error
298         //-------------------------------------
299
300         function display_error()
301         {
302                 return $this->error;
303         }
304         // END
305
306         //-------------------------------------
307         //  Returns Remote Server Response
308         //-------------------------------------
309
310         function display_response()
311         {
312                 return $this->response;
313         }
314         // END
315
316         //-------------------------------------
317         //  Sends an Error Message for Server Request
318         //-------------------------------------
319
320         function send_error_message($number, $message)
321         {
322                 return new XML_RPC_Response('0',$number, $message);
323         }
324         // END
325
326
327         //-------------------------------------
328         //  Send Response for Server Request
329         //-------------------------------------
330
331         function send_response($response)
332         {
333                 // $response should be array of values, which will be parsed
334                 // based on their data and type into a valid group of XML-RPC values
335
336                 $response = $this->values_parsing($response);
337
338                 return new XML_RPC_Response($response);
339         }
340         // END
341
342 } // END XML_RPC Class
343
344
345
346 /**
347  * XML-RPC Client class
348  *
349  * @category    XML-RPC
350  * @author              ExpressionEngine Dev Team
351  * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
352  */
353 class XML_RPC_Client extends CI_Xmlrpc
354 {
355         var $path                       = '';
356         var $server                     = '';
357         var $port                       = 80;
358         var $errno                      = '';
359         var $errstring          = '';
360         var $timeout            = 5;
361         var $no_multicall       = FALSE;
362
363         public function __construct($path, $server, $port=80)
364         {
365                 parent::__construct();
366
367                 $this->port = $port;
368                 $this->server = $server;
369                 $this->path = $path;
370         }
371
372         function send($msg)
373         {
374                 if (is_array($msg))
375                 {
376                         // Multi-call disabled
377                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['multicall_recursion'],$this->xmlrpcstr['multicall_recursion']);
378                         return $r;
379                 }
380
381                 return $this->sendPayload($msg);
382         }
383
384         function sendPayload($msg)
385         {
386                 $fp = @fsockopen($this->server, $this->port,$this->errno, $this->errstr, $this->timeout);
387
388                 if ( ! is_resource($fp))
389                 {
390                         error_log($this->xmlrpcstr['http_error']);
391                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'],$this->xmlrpcstr['http_error']);
392                         return $r;
393                 }
394
395                 if (empty($msg->payload))
396                 {
397                         // $msg = XML_RPC_Messages
398                         $msg->createPayload();
399                 }
400
401                 $r = "\r\n";
402                 $op  = "POST {$this->path} HTTP/1.0$r";
403                 $op .= "Host: {$this->server}$r";
404                 $op .= "Content-Type: text/xml$r";
405                 $op .= "User-Agent: {$this->xmlrpcName}$r";
406                 $op .= "Content-Length: ".strlen($msg->payload). "$r$r";
407                 $op .= $msg->payload;
408
409
410                 if ( ! fputs($fp, $op, strlen($op)))
411                 {
412                         error_log($this->xmlrpcstr['http_error']);
413                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']);
414                         return $r;
415                 }
416                 $resp = $msg->parseResponse($fp);
417                 fclose($fp);
418                 return $resp;
419         }
420
421 } // end class XML_RPC_Client
422
423
424 /**
425  * XML-RPC Response class
426  *
427  * @category    XML-RPC
428  * @author              ExpressionEngine Dev Team
429  * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
430  */
431 class XML_RPC_Response
432 {
433         var $val = 0;
434         var $errno = 0;
435         var $errstr = '';
436         var $headers = array();
437         var $xss_clean = TRUE;
438
439         public function __construct($val, $code = 0, $fstr = '')
440         {
441                 if ($code != 0)
442                 {
443                         // error
444                         $this->errno = $code;
445                         $this->errstr = htmlentities($fstr);
446                 }
447                 else if ( ! is_object($val))
448                 {
449                         // programmer error, not an object
450                         error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to XML_RPC_Response.  Defaulting to empty value.");
451                         $this->val = new XML_RPC_Values();
452                 }
453                 else
454                 {
455                         $this->val = $val;
456                 }
457         }
458
459         function faultCode()
460         {
461                 return $this->errno;
462         }
463
464         function faultString()
465         {
466                 return $this->errstr;
467         }
468
469         function value()
470         {
471                 return $this->val;
472         }
473
474         function prepare_response()
475         {
476                 $result = "<methodResponse>\n";
477                 if ($this->errno)
478                 {
479                         $result .= '<fault>
480         <value>
481                 <struct>
482                         <member>
483                                 <name>faultCode</name>
484                                 <value><int>' . $this->errno . '</int></value>
485                         </member>
486                         <member>
487                                 <name>faultString</name>
488                                 <value><string>' . $this->errstr . '</string></value>
489                         </member>
490                 </struct>
491         </value>
492 </fault>';
493                 }
494                 else
495                 {
496                         $result .= "<params>\n<param>\n" .
497                                         $this->val->serialize_class() .
498                                         "</param>\n</params>";
499                 }
500                 $result .= "\n</methodResponse>";
501                 return $result;
502         }
503
504         function decode($array=FALSE)
505         {
506                 $CI =& get_instance();
507                 
508                 if ($array !== FALSE && is_array($array))
509                 {
510                         while (list($key) = each($array))
511                         {
512                                 if (is_array($array[$key]))
513                                 {
514                                         $array[$key] = $this->decode($array[$key]);
515                                 }
516                                 else
517                                 {
518                                         $array[$key] = ($this->xss_clean) ? $CI->security->xss_clean($array[$key]) : $array[$key];
519                                 }
520                         }
521
522                         $result = $array;
523                 }
524                 else
525                 {
526                         $result = $this->xmlrpc_decoder($this->val);
527
528                         if (is_array($result))
529                         {
530                                 $result = $this->decode($result);
531                         }
532                         else
533                         {
534                                 $result = ($this->xss_clean) ? $CI->security->xss_clean($result) : $result;
535                         }
536                 }
537
538                 return $result;
539         }
540
541
542
543         //-------------------------------------
544         //  XML-RPC Object to PHP Types
545         //-------------------------------------
546
547         function xmlrpc_decoder($xmlrpc_val)
548         {
549                 $kind = $xmlrpc_val->kindOf();
550
551                 if ($kind == 'scalar')
552                 {
553                         return $xmlrpc_val->scalarval();
554                 }
555                 elseif ($kind == 'array')
556                 {
557                         reset($xmlrpc_val->me);
558                         list($a,$b) = each($xmlrpc_val->me);
559                         $size = count($b);
560
561                         $arr = array();
562
563                         for ($i = 0; $i < $size; $i++)
564                         {
565                                 $arr[] = $this->xmlrpc_decoder($xmlrpc_val->me['array'][$i]);
566                         }
567                         return $arr;
568                 }
569                 elseif ($kind == 'struct')
570                 {
571                         reset($xmlrpc_val->me['struct']);
572                         $arr = array();
573
574                         while (list($key,$value) = each($xmlrpc_val->me['struct']))
575                         {
576                                 $arr[$key] = $this->xmlrpc_decoder($value);
577                         }
578                         return $arr;
579                 }
580         }
581
582
583         //-------------------------------------
584         //  ISO-8601 time to server or UTC time
585         //-------------------------------------
586
587         function iso8601_decode($time, $utc=0)
588         {
589                 // return a timet in the localtime, or UTC
590                 $t = 0;
591                 if (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $time, $regs))
592                 {
593                         $fnc = ($utc == 1) ? 'gmmktime' : 'mktime';
594                         $t = $fnc($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
595                 }
596                 return $t;
597         }
598
599 } // End Response Class
600
601
602
603 /**
604  * XML-RPC Message class
605  *
606  * @category    XML-RPC
607  * @author              ExpressionEngine Dev Team
608  * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
609  */
610 class XML_RPC_Message extends CI_Xmlrpc
611 {
612         var $payload;
613         var $method_name;
614         var $params                     = array();
615         var $xh                         = array();
616
617         public function __construct($method, $pars=0)
618         {
619                 parent::__construct();
620
621                 $this->method_name = $method;
622                 if (is_array($pars) && count($pars) > 0)
623                 {
624                         for ($i=0; $i<count($pars); $i++)
625                         {
626                                 // $pars[$i] = XML_RPC_Values
627                                 $this->params[] = $pars[$i];
628                         }
629                 }
630         }
631
632         //-------------------------------------
633         //  Create Payload to Send
634         //-------------------------------------
635
636         function createPayload()
637         {
638                 $this->payload = "<?xml version=\"1.0\"?".">\r\n<methodCall>\r\n";
639                 $this->payload .= '<methodName>' . $this->method_name . "</methodName>\r\n";
640                 $this->payload .= "<params>\r\n";
641
642                 for ($i=0; $i<count($this->params); $i++)
643                 {
644                         // $p = XML_RPC_Values
645                         $p = $this->params[$i];
646                         $this->payload .= "<param>\r\n".$p->serialize_class()."</param>\r\n";
647                 }
648
649                 $this->payload .= "</params>\r\n</methodCall>\r\n";
650         }
651
652         //-------------------------------------
653         //  Parse External XML-RPC Server's Response
654         //-------------------------------------
655
656         function parseResponse($fp)
657         {
658                 $data = '';
659
660                 while ($datum = fread($fp, 4096))
661                 {
662                         $data .= $datum;
663                 }
664
665                 //-------------------------------------
666                 //  DISPLAY HTTP CONTENT for DEBUGGING
667                 //-------------------------------------
668
669                 if ($this->debug === TRUE)
670                 {
671                         echo "<pre>";
672                         echo "---DATA---\n" . htmlspecialchars($data) . "\n---END DATA---\n\n";
673                         echo "</pre>";
674                 }
675
676                 //-------------------------------------
677                 //  Check for data
678                 //-------------------------------------
679
680                 if ($data == "")
681                 {
682                         error_log($this->xmlrpcstr['no_data']);
683                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['no_data'], $this->xmlrpcstr['no_data']);
684                         return $r;
685                 }
686
687
688                 //-------------------------------------
689                 //  Check for HTTP 200 Response
690                 //-------------------------------------
691
692                 if (strncmp($data, 'HTTP', 4) == 0 && ! preg_match('/^HTTP\/[0-9\.]+ 200 /', $data))
693                 {
694                         $errstr= substr($data, 0, strpos($data, "\n")-1);
695                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']. ' (' . $errstr . ')');
696                         return $r;
697                 }
698
699                 //-------------------------------------
700                 //  Create and Set Up XML Parser
701                 //-------------------------------------
702
703                 $parser = xml_parser_create($this->xmlrpc_defencoding);
704
705                 $this->xh[$parser]                                      = array();
706                 $this->xh[$parser]['isf']                       = 0;
707                 $this->xh[$parser]['ac']                        = '';
708                 $this->xh[$parser]['headers']           = array();
709                 $this->xh[$parser]['stack']                     = array();
710                 $this->xh[$parser]['valuestack']        = array();
711                 $this->xh[$parser]['isf_reason']        = 0;
712
713                 xml_set_object($parser, $this);
714                 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
715                 xml_set_element_handler($parser, 'open_tag', 'closing_tag');
716                 xml_set_character_data_handler($parser, 'character_data');
717                 //xml_set_default_handler($parser, 'default_handler');
718
719
720                 //-------------------------------------
721                 //  GET HEADERS
722                 //-------------------------------------
723
724                 $lines = explode("\r\n", $data);
725                 while (($line = array_shift($lines)))
726                 {
727                         if (strlen($line) < 1)
728                         {
729                                 break;
730                         }
731                         $this->xh[$parser]['headers'][] = $line;
732                 }
733                 $data = implode("\r\n", $lines);
734
735
736                 //-------------------------------------
737                 //  PARSE XML DATA
738                 //-------------------------------------
739
740                 if ( ! xml_parse($parser, $data, count($data)))
741                 {
742                         $errstr = sprintf('XML error: %s at line %d',
743                                         xml_error_string(xml_get_error_code($parser)),
744                                         xml_get_current_line_number($parser));
745                         //error_log($errstr);
746                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']);
747                         xml_parser_free($parser);
748                         return $r;
749                 }
750                 xml_parser_free($parser);
751
752                 // ---------------------------------------
753                 //  Got Ourselves Some Badness, It Seems
754                 // ---------------------------------------
755
756                 if ($this->xh[$parser]['isf'] > 1)
757                 {
758                         if ($this->debug === TRUE)
759                         {
760                                 echo "---Invalid Return---\n";
761                                 echo $this->xh[$parser]['isf_reason'];
762                                 echo "---Invalid Return---\n\n";
763                         }
764
765                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'],$this->xmlrpcstr['invalid_return'].' '.$this->xh[$parser]['isf_reason']);
766                         return $r;
767                 }
768                 elseif ( ! is_object($this->xh[$parser]['value']))
769                 {
770                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'],$this->xmlrpcstr['invalid_return'].' '.$this->xh[$parser]['isf_reason']);
771                         return $r;
772                 }
773
774                 //-------------------------------------
775                 //  DISPLAY XML CONTENT for DEBUGGING
776                 //-------------------------------------
777
778                 if ($this->debug === TRUE)
779                 {
780                         echo "<pre>";
781
782                         if (count($this->xh[$parser]['headers'] > 0))
783                         {
784                                 echo "---HEADERS---\n";
785                                 foreach ($this->xh[$parser]['headers'] as $header)
786                                 {
787                                         echo "$header\n";
788                                 }
789                                 echo "---END HEADERS---\n\n";
790                         }
791
792                         echo "---DATA---\n" . htmlspecialchars($data) . "\n---END DATA---\n\n";
793
794                         echo "---PARSED---\n" ;
795                         var_dump($this->xh[$parser]['value']);
796                         echo "\n---END PARSED---</pre>";
797                 }
798
799                 //-------------------------------------
800                 //  SEND RESPONSE
801                 //-------------------------------------
802
803                 $v = $this->xh[$parser]['value'];
804
805                 if ($this->xh[$parser]['isf'])
806                 {
807                         $errno_v = $v->me['struct']['faultCode'];
808                         $errstr_v = $v->me['struct']['faultString'];
809                         $errno = $errno_v->scalarval();
810
811                         if ($errno == 0)
812                         {
813                                 // FAULT returned, errno needs to reflect that
814                                 $errno = -1;
815                         }
816
817                         $r = new XML_RPC_Response($v, $errno, $errstr_v->scalarval());
818                 }
819                 else
820                 {
821                         $r = new XML_RPC_Response($v);
822                 }
823
824                 $r->headers = $this->xh[$parser]['headers'];
825                 return $r;
826         }
827
828         // ------------------------------------
829         //  Begin Return Message Parsing section
830         // ------------------------------------
831
832         // quick explanation of components:
833         //   ac - used to accumulate values
834         //   isf - used to indicate a fault
835         //   lv - used to indicate "looking for a value": implements
836         //              the logic to allow values with no types to be strings
837         //   params - used to store parameters in method calls
838         //   method - used to store method name
839         //       stack - array with parent tree of the xml element,
840         //                       used to validate the nesting of elements
841
842         //-------------------------------------
843         //  Start Element Handler
844         //-------------------------------------
845
846         function open_tag($the_parser, $name, $attrs)
847         {
848                 // If invalid nesting, then return
849                 if ($this->xh[$the_parser]['isf'] > 1) return;
850
851                 // Evaluate and check for correct nesting of XML elements
852
853                 if (count($this->xh[$the_parser]['stack']) == 0)
854                 {
855                         if ($name != 'METHODRESPONSE' && $name != 'METHODCALL')
856                         {
857                                 $this->xh[$the_parser]['isf'] = 2;
858                                 $this->xh[$the_parser]['isf_reason'] = 'Top level XML-RPC element is missing';
859                                 return;
860                         }
861                 }
862                 else
863                 {
864                         // not top level element: see if parent is OK
865                         if ( ! in_array($this->xh[$the_parser]['stack'][0], $this->valid_parents[$name], TRUE))
866                         {
867                                 $this->xh[$the_parser]['isf'] = 2;
868                                 $this->xh[$the_parser]['isf_reason'] = "XML-RPC element $name cannot be child of ".$this->xh[$the_parser]['stack'][0];
869                                 return;
870                         }
871                 }
872
873                 switch($name)
874                 {
875                         case 'STRUCT':
876                         case 'ARRAY':
877                                 // Creates array for child elements
878
879                                 $cur_val = array('value' => array(),
880                                                                  'type'  => $name);
881
882                                 array_unshift($this->xh[$the_parser]['valuestack'], $cur_val);
883                         break;
884                         case 'METHODNAME':
885                         case 'NAME':
886                                 $this->xh[$the_parser]['ac'] = '';
887                         break;
888                         case 'FAULT':
889                                 $this->xh[$the_parser]['isf'] = 1;
890                         break;
891                         case 'PARAM':
892                                 $this->xh[$the_parser]['value'] = NULL;
893                         break;
894                         case 'VALUE':
895                                 $this->xh[$the_parser]['vt'] = 'value';
896                                 $this->xh[$the_parser]['ac'] = '';
897                                 $this->xh[$the_parser]['lv'] = 1;
898                         break;
899                         case 'I4':
900                         case 'INT':
901                         case 'STRING':
902                         case 'BOOLEAN':
903                         case 'DOUBLE':
904                         case 'DATETIME.ISO8601':
905                         case 'BASE64':
906                                 if ($this->xh[$the_parser]['vt'] != 'value')
907                                 {
908                                         //two data elements inside a value: an error occurred!
909                                         $this->xh[$the_parser]['isf'] = 2;
910                                         $this->xh[$the_parser]['isf_reason'] = "'Twas a $name element following a ".$this->xh[$the_parser]['vt']." element inside a single value";
911                                         return;
912                                 }
913
914                                 $this->xh[$the_parser]['ac'] = '';
915                         break;
916                         case 'MEMBER':
917                                 // Set name of <member> to nothing to prevent errors later if no <name> is found
918                                 $this->xh[$the_parser]['valuestack'][0]['name'] = '';
919
920                                 // Set NULL value to check to see if value passed for this param/member
921                                 $this->xh[$the_parser]['value'] = NULL;
922                         break;
923                         case 'DATA':
924                         case 'METHODCALL':
925                         case 'METHODRESPONSE':
926                         case 'PARAMS':
927                                 // valid elements that add little to processing
928                         break;
929                         default:
930                                 /// An Invalid Element is Found, so we have trouble
931                                 $this->xh[$the_parser]['isf'] = 2;
932                                 $this->xh[$the_parser]['isf_reason'] = "Invalid XML-RPC element found: $name";
933                         break;
934                 }
935
936                 // Add current element name to stack, to allow validation of nesting
937                 array_unshift($this->xh[$the_parser]['stack'], $name);
938
939                 if ($name != 'VALUE') $this->xh[$the_parser]['lv'] = 0;
940         }
941         // END
942
943
944         //-------------------------------------
945         //  End Element Handler
946         //-------------------------------------
947
948         function closing_tag($the_parser, $name)
949         {
950                 if ($this->xh[$the_parser]['isf'] > 1) return;
951
952                 // Remove current element from stack and set variable
953                 // NOTE: If the XML validates, then we do not have to worry about
954                 // the opening and closing of elements.  Nesting is checked on the opening
955                 // tag so we be safe there as well.
956
957                 $curr_elem = array_shift($this->xh[$the_parser]['stack']);
958
959                 switch($name)
960                 {
961                         case 'STRUCT':
962                         case 'ARRAY':
963                                 $cur_val = array_shift($this->xh[$the_parser]['valuestack']);
964                                 $this->xh[$the_parser]['value'] = ( ! isset($cur_val['values'])) ? array() : $cur_val['values'];
965                                 $this->xh[$the_parser]['vt']    = strtolower($name);
966                         break;
967                         case 'NAME':
968                                 $this->xh[$the_parser]['valuestack'][0]['name'] = $this->xh[$the_parser]['ac'];
969                         break;
970                         case 'BOOLEAN':
971                         case 'I4':
972                         case 'INT':
973                         case 'STRING':
974                         case 'DOUBLE':
975                         case 'DATETIME.ISO8601':
976                         case 'BASE64':
977                                 $this->xh[$the_parser]['vt'] = strtolower($name);
978
979                                 if ($name == 'STRING')
980                                 {
981                                         $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
982                                 }
983                                 elseif ($name=='DATETIME.ISO8601')
984                                 {
985                                         $this->xh[$the_parser]['vt']    = $this->xmlrpcDateTime;
986                                         $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
987                                 }
988                                 elseif ($name=='BASE64')
989                                 {
990                                         $this->xh[$the_parser]['value'] = base64_decode($this->xh[$the_parser]['ac']);
991                                 }
992                                 elseif ($name=='BOOLEAN')
993                                 {
994                                         // Translated BOOLEAN values to TRUE AND FALSE
995                                         if ($this->xh[$the_parser]['ac'] == '1')
996                                         {
997                                                 $this->xh[$the_parser]['value'] = TRUE;
998                                         }
999                                         else
1000                                         {
1001                                                 $this->xh[$the_parser]['value'] = FALSE;
1002                                         }
1003                                 }
1004                                 elseif ($name=='DOUBLE')
1005                                 {
1006                                         // we have a DOUBLE
1007                                         // we must check that only 0123456789-.<space> are characters here
1008                                         if ( ! preg_match('/^[+-]?[eE0-9\t \.]+$/', $this->xh[$the_parser]['ac']))
1009                                         {
1010                                                 $this->xh[$the_parser]['value'] = 'ERROR_NON_NUMERIC_FOUND';
1011                                         }
1012                                         else
1013                                         {
1014                                                 $this->xh[$the_parser]['value'] = (double)$this->xh[$the_parser]['ac'];
1015                                         }
1016                                 }
1017                                 else
1018                                 {
1019                                         // we have an I4/INT
1020                                         // we must check that only 0123456789-<space> are characters here
1021                                         if ( ! preg_match('/^[+-]?[0-9\t ]+$/', $this->xh[$the_parser]['ac']))
1022                                         {
1023                                                 $this->xh[$the_parser]['value'] = 'ERROR_NON_NUMERIC_FOUND';
1024                                         }
1025                                         else
1026                                         {
1027                                                 $this->xh[$the_parser]['value'] = (int)$this->xh[$the_parser]['ac'];
1028                                         }
1029                                 }
1030                                 $this->xh[$the_parser]['ac'] = '';
1031                                 $this->xh[$the_parser]['lv'] = 3; // indicate we've found a value
1032                         break;
1033                         case 'VALUE':
1034                                 // This if() detects if no scalar was inside <VALUE></VALUE>
1035                                 if ($this->xh[$the_parser]['vt']=='value')
1036                                 {
1037                                         $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
1038                                         $this->xh[$the_parser]['vt']    = $this->xmlrpcString;
1039                                 }
1040
1041                                 // build the XML-RPC value out of the data received, and substitute it
1042                                 $temp = new XML_RPC_Values($this->xh[$the_parser]['value'], $this->xh[$the_parser]['vt']);
1043
1044                                 if (count($this->xh[$the_parser]['valuestack']) && $this->xh[$the_parser]['valuestack'][0]['type'] == 'ARRAY')
1045                                 {
1046                                         // Array
1047                                         $this->xh[$the_parser]['valuestack'][0]['values'][] = $temp;
1048                                 }
1049                                 else
1050                                 {
1051                                         // Struct
1052                                         $this->xh[$the_parser]['value'] = $temp;
1053                                 }
1054                         break;
1055                         case 'MEMBER':
1056                                 $this->xh[$the_parser]['ac']='';
1057
1058                                 // If value add to array in the stack for the last element built
1059                                 if ($this->xh[$the_parser]['value'])
1060                                 {
1061                                         $this->xh[$the_parser]['valuestack'][0]['values'][$this->xh[$the_parser]['valuestack'][0]['name']] = $this->xh[$the_parser]['value'];
1062                                 }
1063                         break;
1064                         case 'DATA':
1065                                 $this->xh[$the_parser]['ac']='';
1066                         break;
1067                         case 'PARAM':
1068                                 if ($this->xh[$the_parser]['value'])
1069                                 {
1070                                         $this->xh[$the_parser]['params'][] = $this->xh[$the_parser]['value'];
1071                                 }
1072                         break;
1073                         case 'METHODNAME':
1074                                 $this->xh[$the_parser]['method'] = ltrim($this->xh[$the_parser]['ac']);
1075                         break;
1076                         case 'PARAMS':
1077                         case 'FAULT':
1078                         case 'METHODCALL':
1079                         case 'METHORESPONSE':
1080                                 // We're all good kids with nuthin' to do
1081                         break;
1082                         default:
1083                                 // End of an Invalid Element.  Taken care of during the opening tag though
1084                         break;
1085                 }
1086         }
1087
1088         //-------------------------------------
1089         //  Parses Character Data
1090         //-------------------------------------
1091
1092         function character_data($the_parser, $data)
1093         {
1094                 if ($this->xh[$the_parser]['isf'] > 1) return; // XML Fault found already
1095
1096                 // If a value has not been found
1097                 if ($this->xh[$the_parser]['lv'] != 3)
1098                 {
1099                         if ($this->xh[$the_parser]['lv'] == 1)
1100                         {
1101                                 $this->xh[$the_parser]['lv'] = 2; // Found a value
1102                         }
1103
1104                         if ( ! @isset($this->xh[$the_parser]['ac']))
1105                         {
1106                                 $this->xh[$the_parser]['ac'] = '';
1107                         }
1108
1109                         $this->xh[$the_parser]['ac'] .= $data;
1110                 }
1111         }
1112
1113
1114         function addParam($par) { $this->params[]=$par; }
1115
1116         function output_parameters($array=FALSE)
1117         {
1118                 $CI =& get_instance();
1119                 
1120                 if ($array !== FALSE && is_array($array))
1121                 {
1122                         while (list($key) = each($array))
1123                         {
1124                                 if (is_array($array[$key]))
1125                                 {
1126                                         $array[$key] = $this->output_parameters($array[$key]);
1127                                 }
1128                                 else
1129                                 {
1130                                         // 'bits' is for the MetaWeblog API image bits
1131                                         // @todo - this needs to be made more general purpose
1132                                         $array[$key] = ($key == 'bits' OR $this->xss_clean == FALSE) ? $array[$key] : $CI->security->xss_clean($array[$key]);
1133                                 }
1134                         }
1135
1136                         $parameters = $array;
1137                 }
1138                 else
1139                 {
1140                         $parameters = array();
1141
1142                         for ($i = 0; $i < count($this->params); $i++)
1143                         {
1144                                 $a_param = $this->decode_message($this->params[$i]);
1145
1146                                 if (is_array($a_param))
1147                                 {
1148                                         $parameters[] = $this->output_parameters($a_param);
1149                                 }
1150                                 else
1151                                 {
1152                                         $parameters[] = ($this->xss_clean) ? $CI->security->xss_clean($a_param) : $a_param;
1153                                 }
1154                         }
1155                 }
1156
1157                 return $parameters;
1158         }
1159
1160
1161         function decode_message($param)
1162         {
1163                 $kind = $param->kindOf();
1164
1165                 if ($kind == 'scalar')
1166                 {
1167                         return $param->scalarval();
1168                 }
1169                 elseif ($kind == 'array')
1170                 {
1171                         reset($param->me);
1172                         list($a,$b) = each($param->me);
1173
1174                         $arr = array();
1175
1176                         for($i = 0; $i < count($b); $i++)
1177                         {
1178                                 $arr[] = $this->decode_message($param->me['array'][$i]);
1179                         }
1180
1181                         return $arr;
1182                 }
1183                 elseif ($kind == 'struct')
1184                 {
1185                         reset($param->me['struct']);
1186
1187                         $arr = array();
1188
1189                         while (list($key,$value) = each($param->me['struct']))
1190                         {
1191                                 $arr[$key] = $this->decode_message($value);
1192                         }
1193
1194                         return $arr;
1195                 }
1196         }
1197
1198 } // End XML_RPC_Messages class
1199
1200
1201
1202 /**
1203  * XML-RPC Values class
1204  *
1205  * @category    XML-RPC
1206  * @author              ExpressionEngine Dev Team
1207  * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
1208  */
1209 class XML_RPC_Values extends CI_Xmlrpc
1210 {
1211         var $me         = array();
1212         var $mytype     = 0;
1213
1214         public function __construct($val=-1, $type='')
1215         {
1216                 parent::__construct();
1217
1218                 if ($val != -1 OR $type != '')
1219                 {
1220                         $type = $type == '' ? 'string' : $type;
1221
1222                         if ($this->xmlrpcTypes[$type] == 1)
1223                         {
1224                                 $this->addScalar($val,$type);
1225                         }
1226                         elseif ($this->xmlrpcTypes[$type] == 2)
1227                         {
1228                                 $this->addArray($val);
1229                         }
1230                         elseif ($this->xmlrpcTypes[$type] == 3)
1231                         {
1232                                 $this->addStruct($val);
1233                         }
1234                 }
1235         }
1236
1237         function addScalar($val, $type='string')
1238         {
1239                 $typeof = $this->xmlrpcTypes[$type];
1240
1241                 if ($this->mytype==1)
1242                 {
1243                         echo '<strong>XML_RPC_Values</strong>: scalar can have only one value<br />';
1244                         return 0;
1245                 }
1246
1247                 if ($typeof != 1)
1248                 {
1249                         echo '<strong>XML_RPC_Values</strong>: not a scalar type (${typeof})<br />';
1250                         return 0;
1251                 }
1252
1253                 if ($type == $this->xmlrpcBoolean)
1254                 {
1255                         if (strcasecmp($val,'true')==0 OR $val==1 OR ($val==true && strcasecmp($val,'false')))
1256                         {
1257                                 $val = 1;
1258                         }
1259                         else
1260                         {
1261                                 $val=0;
1262                         }
1263                 }
1264
1265                 if ($this->mytype == 2)
1266                 {
1267                         // adding to an array here
1268                         $ar = $this->me['array'];
1269                         $ar[] = new XML_RPC_Values($val, $type);
1270                         $this->me['array'] = $ar;
1271                 }
1272                 else
1273                 {
1274                         // a scalar, so set the value and remember we're scalar
1275                         $this->me[$type] = $val;
1276                         $this->mytype = $typeof;
1277                 }
1278                 return 1;
1279         }
1280
1281         function addArray($vals)
1282         {
1283                 if ($this->mytype != 0)
1284                 {
1285                         echo '<strong>XML_RPC_Values</strong>: already initialized as a [' . $this->kindOf() . ']<br />';
1286                         return 0;
1287                 }
1288
1289                 $this->mytype = $this->xmlrpcTypes['array'];
1290                 $this->me['array'] = $vals;
1291                 return 1;
1292         }
1293
1294         function addStruct($vals)
1295         {
1296                 if ($this->mytype != 0)
1297                 {
1298                         echo '<strong>XML_RPC_Values</strong>: already initialized as a [' . $this->kindOf() . ']<br />';
1299                         return 0;
1300                 }
1301                 $this->mytype = $this->xmlrpcTypes['struct'];
1302                 $this->me['struct'] = $vals;
1303                 return 1;
1304         }
1305
1306         function kindOf()
1307         {
1308                 switch($this->mytype)
1309                 {
1310                         case 3:
1311                                 return 'struct';
1312                                 break;
1313                         case 2:
1314                                 return 'array';
1315                                 break;
1316                         case 1:
1317                                 return 'scalar';
1318                                 break;
1319                         default:
1320                                 return 'undef';
1321                 }
1322         }
1323
1324         function serializedata($typ, $val)
1325         {
1326                 $rs = '';
1327
1328                 switch($this->xmlrpcTypes[$typ])
1329                 {
1330                         case 3:
1331                                 // struct
1332                                 $rs .= "<struct>\n";
1333                                 reset($val);
1334                                 while (list($key2, $val2) = each($val))
1335                                 {
1336                                         $rs .= "<member>\n<name>{$key2}</name>\n";
1337                                         $rs .= $this->serializeval($val2);
1338                                         $rs .= "</member>\n";
1339                                 }
1340                                 $rs .= '</struct>';
1341                         break;
1342                         case 2:
1343                                 // array
1344                                 $rs .= "<array>\n<data>\n";
1345                                 for($i=0; $i < count($val); $i++)
1346                                 {
1347                                         $rs .= $this->serializeval($val[$i]);
1348                                 }
1349                                 $rs.="</data>\n</array>\n";
1350                                 break;
1351                         case 1:
1352                                 // others
1353                                 switch ($typ)
1354                                 {
1355                                         case $this->xmlrpcBase64:
1356                                                 $rs .= "<{$typ}>" . base64_encode((string)$val) . "</{$typ}>\n";
1357                                         break;
1358                                         case $this->xmlrpcBoolean:
1359                                                 $rs .= "<{$typ}>" . ((bool)$val ? '1' : '0') . "</{$typ}>\n";
1360                                         break;
1361                                         case $this->xmlrpcString:
1362                                                 $rs .= "<{$typ}>" . htmlspecialchars((string)$val). "</{$typ}>\n";
1363                                         break;
1364                                         default:
1365                                                 $rs .= "<{$typ}>{$val}</{$typ}>\n";
1366                                         break;
1367                                 }
1368                         default:
1369                         break;
1370                 }
1371                 return $rs;
1372         }
1373
1374         function serialize_class()
1375         {
1376                 return $this->serializeval($this);
1377         }
1378
1379         function serializeval($o)
1380         {
1381                 $ar = $o->me;
1382                 reset($ar);
1383
1384                 list($typ, $val) = each($ar);
1385                 $rs = "<value>\n".$this->serializedata($typ, $val)."</value>\n";
1386                 return $rs;
1387         }
1388
1389         function scalarval()
1390         {
1391                 reset($this->me);
1392                 list($a,$b) = each($this->me);
1393                 return $b;
1394         }
1395
1396
1397         //-------------------------------------
1398         // Encode time in ISO-8601 form.
1399         //-------------------------------------
1400
1401         // Useful for sending time in XML-RPC
1402
1403         function iso8601_encode($time, $utc=0)
1404         {
1405                 if ($utc == 1)
1406                 {
1407                         $t = strftime("%Y%m%dT%H:%M:%S", $time);
1408                 }
1409                 else
1410                 {
1411                         if (function_exists('gmstrftime'))
1412                                 $t = gmstrftime("%Y%m%dT%H:%M:%S", $time);
1413                         else
1414                                 $t = strftime("%Y%m%dT%H:%M:%S", $time - date('Z'));
1415                 }
1416                 return $t;
1417         }
1418
1419 }
1420 // END XML_RPC_Values Class
1421
1422 /* End of file Xmlrpc.php */
1423 /* Location: ./system/libraries/Xmlrpc.php */