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