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