upgrade to codeigniter 1.7.2 for f12
[www-register-wizard.git] / 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 4.3.2 or newer
6  *
7  * @package             CodeIgniter
8  * @author              ExpressionEngine Dev Team
9  * @copyright   Copyright (c) 2008 - 2009, 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
67         //-------------------------------------
68         //  VALUES THAT MULTIPLE CLASSES NEED
69         //-------------------------------------
70
71         function CI_Xmlrpc ($config = array())
72         {
73                 $this->xmlrpcName               = $this->xmlrpcName;
74                 $this->xmlrpc_backslash = chr(92).chr(92);
75                 
76                 // Types for info sent back and forth
77                 $this->xmlrpcTypes = array(
78                         $this->xmlrpcI4    => '1',
79                         $this->xmlrpcInt          => '1',
80                         $this->xmlrpcBoolean  => '1',
81                         $this->xmlrpcString   => '1',
82                         $this->xmlrpcDouble   => '1',
83                         $this->xmlrpcDateTime => '1',
84                         $this->xmlrpcBase64   => '1',
85                         $this->xmlrpcArray      => '2',
86                         $this->xmlrpcStruct   => '3'
87                         );
88                         
89                 // Array of Valid Parents for Various XML-RPC elements
90                 $this->valid_parents = array('BOOLEAN'                  => array('VALUE'),
91                                                                          'I4'                           => array('VALUE'),
92                                                                          'INT'                          => array('VALUE'),
93                                                                          'STRING'                       => array('VALUE'),
94                                                                          'DOUBLE'                       => array('VALUE'),
95                                                                          'DATETIME.ISO8601'     => array('VALUE'),
96                                                                          'BASE64'                       => array('VALUE'),
97                                                                          'ARRAY'                        => array('VALUE'),
98                                                                          'STRUCT'                       => array('VALUE'),
99                                                                          'PARAM'                        => array('PARAMS'),
100                                                                          'METHODNAME'           => array('METHODCALL'),
101                                                                          'PARAMS'                       => array('METHODCALL', 'METHODRESPONSE'),
102                                                                          'MEMBER'                       => array('STRUCT'),
103                                                                          'NAME'                         => array('MEMBER'),
104                                                                          'DATA'                         => array('ARRAY'),
105                                                                          'FAULT'                        => array('METHODRESPONSE'),
106                                                                          'VALUE'                        => array('MEMBER', 'DATA', 'PARAM', 'FAULT')
107                                                                          );
108                         
109                         
110                 // XML-RPC Responses
111                 $this->xmlrpcerr['unknown_method'] = '1';
112                 $this->xmlrpcstr['unknown_method'] = 'This is not a known method for this XML-RPC Server';
113                 $this->xmlrpcerr['invalid_return'] = '2';
114                 $this->xmlrpcstr['invalid_return'] = 'The XML data receieved was either invalid or not in the correct form for XML-RPC.  Turn on debugging to examine the XML data further.';
115                 $this->xmlrpcerr['incorrect_params'] = '3';
116                 $this->xmlrpcstr['incorrect_params'] = 'Incorrect parameters were passed to method';
117                 $this->xmlrpcerr['introspect_unknown'] = '4';
118                 $this->xmlrpcstr['introspect_unknown'] = "Cannot inspect signature for request: method unknown";
119                 $this->xmlrpcerr['http_error'] = '5';
120                 $this->xmlrpcstr['http_error'] = "Did not receive a '200 OK' response from remote server.";
121                 $this->xmlrpcerr['no_data'] = '6';
122                 $this->xmlrpcstr['no_data'] ='No data received from server.';
123                 
124                 $this->initialize($config);
125                 
126                 log_message('debug', "XML-RPC Class Initialized");
127         }
128         
129         
130         //-------------------------------------
131         //  Initialize Prefs
132         //-------------------------------------
133
134         function initialize($config = array())
135         {
136                 if (count($config) > 0)
137                 {
138                         foreach ($config as $key => $val)
139                         {
140                                 if (isset($this->$key))
141                                 {
142                                         $this->$key = $val;                     
143                                 }
144                         }
145                 }
146         }
147         // END
148         
149         //-------------------------------------
150         //  Take URL and parse it
151         //-------------------------------------
152
153         function server($url, $port=80)
154         {
155                 if (substr($url, 0, 4) != "http")
156                 {
157                         $url = "http://".$url;
158                 }
159                 
160                 $parts = parse_url($url);
161                 
162                 $path = ( ! isset($parts['path'])) ? '/' : $parts['path'];
163                 
164                 if (isset($parts['query']) && $parts['query'] != '')
165                 {
166                         $path .= '?'.$parts['query'];
167                 }       
168                 
169                 $this->client = new XML_RPC_Client($path, $parts['host'], $port);
170         }
171         // END
172         
173         //-------------------------------------
174         //  Set Timeout
175         //-------------------------------------
176
177         function timeout($seconds=5)
178         {
179                 if ( ! is_null($this->client) && is_int($seconds))
180                 {
181                         $this->client->timeout = $seconds;
182                 }
183         }
184         // END
185         
186         //-------------------------------------
187         //  Set Methods
188         //-------------------------------------
189
190         function method($function)
191         {
192                 $this->method = $function;
193         }
194         // END
195         
196         //-------------------------------------
197         //  Take Array of Data and Create Objects
198         //-------------------------------------
199
200         function request($incoming)
201         {
202                 if ( ! is_array($incoming))
203                 {
204                         // Send Error
205                 }
206                 
207                 $this->data = array();
208                 
209                 foreach($incoming as $key => $value)
210                 {
211                         $this->data[$key] = $this->values_parsing($value);
212                 }
213         }
214         // END
215         
216         
217         //-------------------------------------
218         //  Set Debug
219         //-------------------------------------
220
221         function set_debug($flag = TRUE)
222         {
223                 $this->debug = ($flag == TRUE) ? TRUE : FALSE;
224         }
225         
226         //-------------------------------------
227         //  Values Parsing
228         //-------------------------------------
229
230         function values_parsing($value, $return = FALSE)
231         {
232                 if (is_array($value) && isset($value['0']))
233                 {
234                         if ( ! isset($value['1']) OR (! isset($this->xmlrpcTypes[$value['1']])))
235                         {
236                                 if (is_array($value[0]))
237                                 {
238                                         $temp = new XML_RPC_Values($value['0'], 'array');
239                                 }
240                                 else
241                                 {
242                                         $temp = new XML_RPC_Values($value['0'], 'string');
243                                 }
244                         }
245                         elseif(is_array($value['0']) && ($value['1'] == 'struct' OR $value['1'] == 'array'))
246                         {
247                                 while (list($k) = each($value['0']))
248                                 {
249                                         $value['0'][$k] = $this->values_parsing($value['0'][$k], TRUE);
250                                 }
251                                 
252                                 $temp = new XML_RPC_Values($value['0'], $value['1']);
253                         }
254                         else
255                         {
256                                 $temp = new XML_RPC_Values($value['0'], $value['1']);
257                         }
258                 }
259                 else
260                 {
261                         $temp = new XML_RPC_Values($value, 'string');
262                 }
263
264                 return $temp;
265         }
266         // END
267
268
269         //-------------------------------------
270         //  Sends XML-RPC Request
271         //-------------------------------------
272
273         function send_request()
274         {
275                 $this->message = new XML_RPC_Message($this->method,$this->data);
276                 $this->message->debug = $this->debug;
277         
278                 if ( ! $this->result = $this->client->send($this->message))
279                 {
280                         $this->error = $this->result->errstr;
281                         return FALSE;
282                 }
283                 elseif( ! is_object($this->result->val))
284                 {
285                         $this->error = $this->result->errstr;
286                         return FALSE;
287                 }
288                 
289                 $this->response = $this->result->decode();
290                 
291                 return TRUE;
292         }
293         // END
294         
295         //-------------------------------------
296         //  Returns Error
297         //-------------------------------------
298
299         function display_error()
300         {
301                 return $this->error;
302         }
303         // END
304         
305         //-------------------------------------
306         //  Returns Remote Server Response
307         //-------------------------------------
308
309         function display_response()
310         {
311                 return $this->response;
312         }
313         // END
314         
315         //-------------------------------------
316         //  Sends an Error Message for Server Request
317         //-------------------------------------
318         
319         function send_error_message($number, $message)
320         {
321                 return new XML_RPC_Response('0',$number, $message);
322         }
323         // END
324         
325         
326         //-------------------------------------
327         //  Send Response for Server Request
328         //-------------------------------------
329         
330         function send_response($response)
331         {
332                 // $response should be array of values, which will be parsed
333                 // based on their data and type into a valid group of XML-RPC values
334                 
335                 $response = $this->values_parsing($response);
336         
337                 return new XML_RPC_Response($response);
338         }
339         // END
340         
341 } // END XML_RPC Class
342
343         
344         
345 /**
346  * XML-RPC Client class
347  *
348  * @category    XML-RPC
349  * @author              ExpressionEngine Dev Team
350  * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
351  */
352 class XML_RPC_Client extends CI_Xmlrpc
353 {
354         var $path                       = '';
355         var $server                     = '';
356         var $port                       = 80;
357         var $errno                      = '';
358         var $errstring          = '';
359         var $timeout            = 5;
360         var $no_multicall       = false;
361
362         function XML_RPC_Client($path, $server, $port=80)
363         {
364                 parent::CI_Xmlrpc();
365                 
366                 $this->port = $port;
367                 $this->server = $server;
368                 $this->path = $path;
369         }
370         
371         function send($msg)
372         {
373                 if (is_array($msg))
374                 {
375                         // Multi-call disabled
376                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['multicall_recursion'],$this->xmlrpcstr['multicall_recursion']);
377                         return $r;
378                 }
379
380                 return $this->sendPayload($msg);
381         }
382
383         function sendPayload($msg)
384         {       
385                 $fp = @fsockopen($this->server, $this->port,$this->errno, $this->errstr, $this->timeout);
386                 
387                 if ( ! is_resource($fp))
388                 {
389                         error_log($this->xmlrpcstr['http_error']);
390                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'],$this->xmlrpcstr['http_error']);
391                         return $r;
392                 }
393                 
394                 if(empty($msg->payload))
395                 {
396                         // $msg = XML_RPC_Messages
397                         $msg->createPayload();
398                 }
399                 
400                 $r = "\r\n";
401                 $op  = "POST {$this->path} HTTP/1.0$r";
402                 $op .= "Host: {$this->server}$r";
403                 $op .= "Content-Type: text/xml$r";
404                 $op .= "User-Agent: {$this->xmlrpcName}$r";
405                 $op .= "Content-Length: ".strlen($msg->payload). "$r$r";
406                 $op .= $msg->payload;
407                 
408
409                 if ( ! fputs($fp, $op, strlen($op)))
410                 {
411                         error_log($this->xmlrpcstr['http_error']);
412                         $r = new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']);
413                         return $r;
414                 }
415                 $resp = $msg->parseResponse($fp);
416                 fclose($fp);
417                 return $resp;
418         }
419
420 } // end class XML_RPC_Client
421
422
423 /**
424  * XML-RPC Response class
425  *
426  * @category    XML-RPC
427  * @author              ExpressionEngine Dev Team
428  * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
429  */
430 class XML_RPC_Response
431 {
432         var $val = 0;
433         var $errno = 0;
434         var $errstr = '';
435         var $headers = array();
436
437         function XML_RPC_Response($val, $code = 0, $fstr = '')
438         {       
439                 if ($code != 0)
440                 {
441                         // error
442                         $this->errno = $code;
443                         $this->errstr = htmlentities($fstr);
444                 }
445                 else if ( ! is_object($val))
446                 {
447                         // programmer error, not an object
448                         error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to XML_RPC_Response.  Defaulting to empty value.");
449                         $this->val = new XML_RPC_Values();
450                 }
451                 else
452                 {
453                         $this->val = $val;
454                 }
455         }
456
457         function faultCode()
458         {
459                 return $this->errno;
460         }
461
462         function faultString()
463         {
464                 return $this->errstr;
465         }
466
467         function value()
468         {
469                 return $this->val;
470         }
471         
472         function prepare_response()
473         {
474                 $result = "<methodResponse>\n";
475                 if ($this->errno)
476                 {
477                         $result .= '<fault>
478         <value>
479                 <struct>
480                         <member>
481                                 <name>faultCode</name>
482                                 <value><int>' . $this->errno . '</int></value>
483                         </member>
484                         <member>
485                                 <name>faultString</name>
486                                 <value><string>' . $this->errstr . '</string></value>
487                         </member>
488                 </struct>
489         </value>
490 </fault>';
491                 }
492                 else
493                 {
494                         $result .= "<params>\n<param>\n" .
495                                         $this->val->serialize_class() .
496                                         "</param>\n</params>";
497                 }
498                 $result .= "\n</methodResponse>";
499                 return $result;
500         }
501         
502         function decode($array=FALSE)
503         {
504                 $CI =& get_instance();
505
506                 if ($array !== FALSE && is_array($array))
507                 {
508                         while (list($key) = each($array))
509                         {
510                                 if (is_array($array[$key]))
511                                 {
512                                         $array[$key] = $this->decode($array[$key]);
513                                 }
514                                 else
515                                 {
516                                         $array[$key] = $CI->input->xss_clean($array[$key]);
517                                 }
518                         }
519                         
520                         $result = $array;
521                 }
522                 else
523                 {
524                         $result = $this->xmlrpc_decoder($this->val);
525                         
526                         if (is_array($result))
527                         {
528                                 $result = $this->decode($result);
529                         }
530                         else
531                         {
532                                 $result = $CI->input->xss_clean($result);
533                         }
534                 }
535                 
536                 return $result;
537         }
538
539         
540         
541         //-------------------------------------
542         //  XML-RPC Object to PHP Types
543         //-------------------------------------
544
545         function xmlrpc_decoder($xmlrpc_val)
546         {
547                 $kind = $xmlrpc_val->kindOf();
548
549                 if($kind == 'scalar')
550                 {
551                         return $xmlrpc_val->scalarval();
552                 }
553                 elseif($kind == 'array')
554                 {
555                         reset($xmlrpc_val->me);
556                         list($a,$b) = each($xmlrpc_val->me);
557                         $size = count($b);
558                         
559                         $arr = array();
560
561                         for($i = 0; $i < $size; $i++)
562                         {
563                                 $arr[] = $this->xmlrpc_decoder($xmlrpc_val->me['array'][$i]);
564                         }
565                         return $arr;
566                 }
567                 elseif($kind == 'struct')
568                 {
569                         reset($xmlrpc_val->me['struct']);
570                         $arr = array();
571
572                         while(list($key,$value) = each($xmlrpc_val->me['struct']))
573                         {
574                                 $arr[$key] = $this->xmlrpc_decoder($value);
575                         }
576                         return $arr;
577                 }
578         }
579         
580         
581         //-------------------------------------
582         //  ISO-8601 time to server or UTC time
583         //-------------------------------------
584
585         function iso8601_decode($time, $utc=0)
586         {
587                 // return a timet in the localtime, or UTC
588                 $t = 0;
589                 if (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $time, $regs))
590                 {
591                         if ($utc == 1)
592                                 $t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
593                         else
594                                 $t = mktime($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         function XML_RPC_Message($method, $pars=0)
618         {
619                 parent::CI_Xmlrpc();
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                                         $array[$key] = $CI->input->xss_clean($array[$key]);
1131                                 }
1132                         }
1133                         
1134                         $parameters = $array;
1135                 }
1136                 else
1137                 {
1138                         $parameters = array();
1139                 
1140                         for ($i = 0; $i < count($this->params); $i++)
1141                         {
1142                                 $a_param = $this->decode_message($this->params[$i]);
1143                                 
1144                                 if (is_array($a_param))
1145                                 {
1146                                         $parameters[] = $this->output_parameters($a_param);
1147                                 }
1148                                 else
1149                                 {
1150                                         $parameters[] = $CI->input->xss_clean($a_param);
1151                                 }
1152                         }       
1153                 }
1154                 
1155                 return $parameters;
1156         }
1157         
1158         
1159         function decode_message($param)
1160         {
1161                 $kind = $param->kindOf();
1162
1163                 if($kind == 'scalar')
1164                 {
1165                         return $param->scalarval();
1166                 }
1167                 elseif($kind == 'array')
1168                 {
1169                         reset($param->me);
1170                         list($a,$b) = each($param->me);
1171                         
1172                         $arr = array();
1173
1174                         for($i = 0; $i < count($b); $i++)
1175                         {
1176                                 $arr[] = $this->decode_message($param->me['array'][$i]);
1177                         }
1178                         
1179                         return $arr;
1180                 }
1181                 elseif($kind == 'struct')
1182                 {
1183                         reset($param->me['struct']);
1184                         
1185                         $arr = array();
1186
1187                         while(list($key,$value) = each($param->me['struct']))
1188                         {
1189                                 $arr[$key] = $this->decode_message($value);
1190                         }
1191                         
1192                         return $arr;
1193                 }
1194         }
1195         
1196 } // End XML_RPC_Messages class
1197
1198
1199
1200 /**
1201  * XML-RPC Values class
1202  *
1203  * @category    XML-RPC
1204  * @author              ExpressionEngine Dev Team
1205  * @link                http://codeigniter.com/user_guide/libraries/xmlrpc.html
1206  */
1207 class XML_RPC_Values extends CI_Xmlrpc
1208 {
1209         var $me         = array();
1210         var $mytype     = 0;
1211
1212         function XML_RPC_Values($val=-1, $type='')
1213         {       
1214                 parent::CI_Xmlrpc();
1215                 
1216                 if ($val != -1 OR $type != '')
1217                 {
1218                         $type = $type == '' ? 'string' : $type;
1219                         
1220                         if ($this->xmlrpcTypes[$type] == 1)
1221                         {
1222                                 $this->addScalar($val,$type);
1223                         }
1224                         elseif ($this->xmlrpcTypes[$type] == 2)
1225                         {
1226                                 $this->addArray($val);
1227                         }
1228                         elseif ($this->xmlrpcTypes[$type] == 3)
1229                         {
1230                                 $this->addStruct($val);
1231                         }
1232                 }
1233         }
1234
1235         function addScalar($val, $type='string')
1236         {
1237                 $typeof = $this->xmlrpcTypes[$type];
1238                 
1239                 if ($this->mytype==1)
1240                 {
1241                         echo '<strong>XML_RPC_Values</strong>: scalar can have only one value<br />';
1242                         return 0;
1243                 }
1244                 
1245                 if ($typeof != 1)
1246                 {
1247                         echo '<strong>XML_RPC_Values</strong>: not a scalar type (${typeof})<br />';
1248                         return 0;
1249                 }
1250
1251                 if ($type == $this->xmlrpcBoolean)
1252                 {
1253                         if (strcasecmp($val,'true')==0 OR $val==1 OR ($val==true && strcasecmp($val,'false')))
1254                         {
1255                                 $val = 1;
1256                         }
1257                         else
1258                         {
1259                                 $val=0;
1260                         }
1261                 }
1262
1263                 if ($this->mytype == 2)
1264                 {
1265                         // adding to an array here
1266                         $ar = $this->me['array'];
1267                         $ar[] = new XML_RPC_Values($val, $type);
1268                         $this->me['array'] = $ar;
1269                 }
1270                 else
1271                 {
1272                         // a scalar, so set the value and remember we're scalar
1273                         $this->me[$type] = $val;
1274                         $this->mytype = $typeof;
1275                 }
1276                 return 1;
1277         }
1278
1279         function addArray($vals)
1280         {
1281                 if ($this->mytype != 0)
1282                 {
1283                         echo '<strong>XML_RPC_Values</strong>: already initialized as a [' . $this->kindOf() . ']<br />';
1284                         return 0;
1285                 }
1286
1287                 $this->mytype = $this->xmlrpcTypes['array'];
1288                 $this->me['array'] = $vals;
1289                 return 1;
1290         }
1291
1292         function addStruct($vals)
1293         {
1294                 if ($this->mytype != 0)
1295                 {
1296                         echo '<strong>XML_RPC_Values</strong>: already initialized as a [' . $this->kindOf() . ']<br />';
1297                         return 0;
1298                 }
1299                 $this->mytype = $this->xmlrpcTypes['struct'];
1300                 $this->me['struct'] = $vals;
1301                 return 1;
1302         }
1303
1304         function kindOf()
1305         {
1306                 switch($this->mytype)
1307                 {
1308                         case 3:
1309                                 return 'struct';
1310                                 break;
1311                         case 2:
1312                                 return 'array';
1313                                 break;
1314                         case 1:
1315                                 return 'scalar';
1316                                 break;
1317                         default:
1318                                 return 'undef';
1319                 }
1320         }
1321
1322         function serializedata($typ, $val)
1323         {
1324                 $rs = '';
1325                 
1326                 switch($this->xmlrpcTypes[$typ])
1327                 {
1328                         case 3:
1329                                 // struct
1330                                 $rs .= "<struct>\n";
1331                                 reset($val);
1332                                 while(list($key2, $val2) = each($val))
1333                                 {
1334                                         $rs .= "<member>\n<name>{$key2}</name>\n";
1335                                         $rs .= $this->serializeval($val2);
1336                                         $rs .= "</member>\n";
1337                                 }
1338                                 $rs .= '</struct>';
1339                         break;
1340                         case 2:
1341                                 // array
1342                                 $rs .= "<array>\n<data>\n";
1343                                 for($i=0; $i < count($val); $i++)
1344                                 {
1345                                         $rs .= $this->serializeval($val[$i]);
1346                                 }
1347                                 $rs.="</data>\n</array>\n";
1348                                 break;
1349                         case 1:
1350                                 // others
1351                                 switch ($typ)
1352                                 {
1353                                         case $this->xmlrpcBase64:
1354                                                 $rs .= "<{$typ}>" . base64_encode((string)$val) . "</{$typ}>\n";
1355                                         break;
1356                                         case $this->xmlrpcBoolean:
1357                                                 $rs .= "<{$typ}>" . ((bool)$val ? '1' : '0') . "</{$typ}>\n";
1358                                         break;
1359                                         case $this->xmlrpcString:
1360                                                 $rs .= "<{$typ}>" . htmlspecialchars((string)$val). "</{$typ}>\n";
1361                                         break;
1362                                         default:
1363                                                 $rs .= "<{$typ}>{$val}</{$typ}>\n";
1364                                         break;
1365                                 }
1366                         default:
1367                         break;
1368                 }
1369                 return $rs;
1370         }
1371
1372         function serialize_class()
1373         {
1374                 return $this->serializeval($this);
1375         }
1376
1377         function serializeval($o)
1378         {
1379                 $ar = $o->me;
1380                 reset($ar);
1381                 
1382                 list($typ, $val) = each($ar);
1383                 $rs = "<value>\n".$this->serializedata($typ, $val)."</value>\n";
1384                 return $rs;
1385         }
1386         
1387         function scalarval()
1388         {
1389                 reset($this->me);
1390                 list($a,$b) = each($this->me);
1391                 return $b;
1392         }
1393
1394
1395         //-------------------------------------
1396         // Encode time in ISO-8601 form.
1397         //-------------------------------------
1398         
1399         // Useful for sending time in XML-RPC
1400
1401         function iso8601_encode($time, $utc=0)
1402         {       
1403                 if ($utc == 1)
1404                 {
1405                         $t = strftime("%Y%m%dT%H:%M:%S", $time);
1406                 }
1407                 else
1408                 {
1409                         if (function_exists('gmstrftime'))
1410                                 $t = gmstrftime("%Y%m%dT%H:%M:%S", $time);
1411                         else
1412                                 $t = strftime("%Y%m%dT%H:%M:%S", $time - date('Z'));
1413                 }
1414                 return $t;
1415         }
1416         
1417 }
1418 // END XML_RPC_Values Class
1419
1420 /* End of file Xmlrpc.php */
1421 /* Location: ./system/libraries/Xmlrpc.php */