source: sourcecode/system/libraries/Xmlrpc.php @ 1

Last change on this file since 1 was 1, checked in by dungnv, 11 years ago
File size: 32.8 KB
Line 
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
16if ( ! 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 */
33class 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 */
353class 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 */
431class 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 */
610class 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 */
1209class 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:%i:%s", $time);
1408                }
1409                else
1410                {
1411                        if (function_exists('gmstrftime'))
1412                                $t = gmstrftime("%Y%m%dT%H:%i:%s", $time);
1413                        else
1414                                $t = strftime("%Y%m%dT%H:%i:%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 */
Note: See TracBrowser for help on using the repository browser.