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