Subversion-Projekte lars-tiefland.ci

Revision

Revision 2257 | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
68 lars 1
<?php
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
2414 lars 9
 * Copyright (c) 2014 - 2019, British Columbia Institute of Technology
68 lars 10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package	CodeIgniter
30
 * @author	EllisLab Dev Team
31
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
2414 lars 32
 * @copyright	Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
33
 * @license	https://opensource.org/licenses/MIT	MIT License
68 lars 34
 * @link	https://codeigniter.com
35
 * @since	Version 1.0.0
36
 * @filesource
37
 */
38
defined('BASEPATH') OR exit('No direct script access allowed');
39
 
40
if ( ! function_exists('xml_parser_create'))
41
{
42
	show_error('Your PHP installation does not support XML');
43
}
44
 
45
// ------------------------------------------------------------------------
46
 
47
/**
48
 * XML-RPC request handler class
49
 *
50
 * @package		CodeIgniter
51
 * @subpackage	Libraries
52
 * @category	XML-RPC
53
 * @author		EllisLab Dev Team
54
 * @link		https://codeigniter.com/user_guide/libraries/xmlrpc.html
55
 */
56
class CI_Xmlrpc {
57
 
58
	/**
59
	 * Debug flag
60
	 *
61
	 * @var	bool
62
	 */
63
	public $debug		= FALSE;
64
 
65
	/**
66
	 * I4 data type
67
	 *
68
	 * @var	string
69
	 */
70
	public $xmlrpcI4	= 'i4';
71
 
72
	/**
73
	 * Integer data type
74
	 *
75
	 * @var	string
76
	 */
77
	public $xmlrpcInt	= 'int';
78
 
79
	/**
80
	 * Boolean data type
81
	 *
82
	 * @var	string
83
	 */
84
	public $xmlrpcBoolean	= 'boolean';
85
 
86
	/**
87
	 * Double data type
88
	 *
89
	 * @var	string
90
	 */
91
	public $xmlrpcDouble	= 'double';
92
 
93
	/**
94
	 * String data type
95
	 *
96
	 * @var	string
97
	 */
98
	public $xmlrpcString	= 'string';
99
 
100
	/**
101
	 * DateTime format
102
	 *
103
	 * @var	string
104
	 */
105
	public $xmlrpcDateTime	= 'dateTime.iso8601';
106
 
107
	/**
108
	 * Base64 data type
109
	 *
110
	 * @var	string
111
	 */
112
	public $xmlrpcBase64	= 'base64';
113
 
114
	/**
115
	 * Array data type
116
	 *
117
	 * @var	string
118
	 */
119
	public $xmlrpcArray	= 'array';
120
 
121
	/**
122
	 * Struct data type
123
	 *
124
	 * @var	string
125
	 */
126
	public $xmlrpcStruct	= 'struct';
127
 
128
	/**
129
	 * Data types list
130
	 *
131
	 * @var	array
132
	 */
133
	public $xmlrpcTypes	= array();
134
 
135
	/**
136
	 * Valid parents list
137
	 *
138
	 * @var	array
139
	 */
140
	public $valid_parents	= array();
141
 
142
	/**
143
	 * Response error numbers list
144
	 *
145
	 * @var	array
146
	 */
147
	public $xmlrpcerr		= array();
148
 
149
	/**
150
	 * Response error messages list
151
	 *
152
	 * @var	string[]
153
	 */
154
	public $xmlrpcstr		= array();
155
 
156
	/**
157
	 * Encoding charset
158
	 *
159
	 * @var	string
160
	 */
161
	public $xmlrpc_defencoding	= 'UTF-8';
162
 
163
	/**
164
	 * XML-RPC client name
165
	 *
166
	 * @var	string
167
	 */
168
	public $xmlrpcName		= 'XML-RPC for CodeIgniter';
169
 
170
	/**
171
	 * XML-RPC version
172
	 *
173
	 * @var	string
174
	 */
175
	public $xmlrpcVersion		= '1.1';
176
 
177
	/**
178
	 * Start of user errors
179
	 *
180
	 * @var	int
181
	 */
182
	public $xmlrpcerruser		= 800;
183
 
184
	/**
185
	 * Start of XML parse errors
186
	 *
187
	 * @var	int
188
	 */
189
	public $xmlrpcerrxml		= 100;
190
 
191
	/**
192
	 * Backslash replacement value
193
	 *
194
	 * @var	string
195
	 */
196
	public $xmlrpc_backslash	= '';
197
 
198
	/**
199
	 * XML-RPC Client object
200
	 *
201
	 * @var	object
202
	 */
203
	public $client;
204
 
205
	/**
206
	 * XML-RPC Method name
207
	 *
208
	 * @var	string
209
	 */
210
	public $method;
211
 
212
	/**
213
	 * XML-RPC Data
214
	 *
215
	 * @var	array
216
	 */
217
	public $data;
218
 
219
	/**
220
	 * XML-RPC Message
221
	 *
222
	 * @var	string
223
	 */
224
	public $message			= '';
225
 
226
	/**
227
	 * Request error message
228
	 *
229
	 * @var	string
230
	 */
231
	public $error			= '';
232
 
233
	/**
234
	 * XML-RPC result object
235
	 *
236
	 * @var	object
237
	 */
238
	public $result;
239
 
240
	/**
2107 lars 241
	 * XML-RPC Response
68 lars 242
	 *
243
	 * @var	array
244
	 */
245
	public $response		= array(); // Response from remote server
246
 
247
	/**
248
	 * XSS Filter flag
249
	 *
250
	 * @var	bool
251
	 */
252
	public $xss_clean		= TRUE;
253
 
254
	// --------------------------------------------------------------------
255
 
256
	/**
257
	 * Constructor
258
	 *
259
	 * Initializes property default values
260
	 *
261
	 * @param	array	$config
262
	 * @return	void
263
	 */
264
	public function __construct($config = array())
265
	{
266
		$this->xmlrpc_backslash = chr(92).chr(92);
267
 
268
		// Types for info sent back and forth
269
		$this->xmlrpcTypes = array(
270
			$this->xmlrpcI4	 		=> '1',
271
			$this->xmlrpcInt		=> '1',
272
			$this->xmlrpcBoolean	=> '1',
273
			$this->xmlrpcString		=> '1',
274
			$this->xmlrpcDouble		=> '1',
275
			$this->xmlrpcDateTime	=> '1',
276
			$this->xmlrpcBase64		=> '1',
277
			$this->xmlrpcArray		=> '2',
278
			$this->xmlrpcStruct		=> '3'
279
		);
280
 
281
		// Array of Valid Parents for Various XML-RPC elements
282
		$this->valid_parents = array('BOOLEAN' => array('VALUE'),
283
			'I4'				=> array('VALUE'),
284
			'INT'				=> array('VALUE'),
285
			'STRING'			=> array('VALUE'),
286
			'DOUBLE'			=> array('VALUE'),
287
			'DATETIME.ISO8601'	=> array('VALUE'),
288
			'BASE64'			=> array('VALUE'),
289
			'ARRAY'			=> array('VALUE'),
290
			'STRUCT'			=> array('VALUE'),
291
			'PARAM'			=> array('PARAMS'),
292
			'METHODNAME'		=> array('METHODCALL'),
293
			'PARAMS'			=> array('METHODCALL', 'METHODRESPONSE'),
294
			'MEMBER'			=> array('STRUCT'),
295
			'NAME'				=> array('MEMBER'),
296
			'DATA'				=> array('ARRAY'),
297
			'FAULT'			=> array('METHODRESPONSE'),
298
			'VALUE'			=> array('MEMBER', 'DATA', 'PARAM', 'FAULT')
299
		);
300
 
301
		// XML-RPC Responses
302
		$this->xmlrpcerr['unknown_method'] = '1';
303
		$this->xmlrpcstr['unknown_method'] = 'This is not a known method for this XML-RPC Server';
304
		$this->xmlrpcerr['invalid_return'] = '2';
305
		$this->xmlrpcstr['invalid_return'] = 'The XML data received was either invalid or not in the correct form for XML-RPC. Turn on debugging to examine the XML data further.';
306
		$this->xmlrpcerr['incorrect_params'] = '3';
307
		$this->xmlrpcstr['incorrect_params'] = 'Incorrect parameters were passed to method';
308
		$this->xmlrpcerr['introspect_unknown'] = '4';
309
		$this->xmlrpcstr['introspect_unknown'] = 'Cannot inspect signature for request: method unknown';
310
		$this->xmlrpcerr['http_error'] = '5';
311
		$this->xmlrpcstr['http_error'] = "Did not receive a '200 OK' response from remote server.";
312
		$this->xmlrpcerr['no_data'] = '6';
313
		$this->xmlrpcstr['no_data'] = 'No data received from server.';
314
 
315
		$this->initialize($config);
316
 
317
		log_message('info', 'XML-RPC Class Initialized');
318
	}
319
 
320
	// --------------------------------------------------------------------
321
 
322
	/**
323
	 * Initialize
324
	 *
325
	 * @param	array	$config
326
	 * @return	void
327
	 */
328
	public function initialize($config = array())
329
	{
330
		if (count($config) > 0)
331
		{
332
			foreach ($config as $key => $val)
333
			{
334
				if (isset($this->$key))
335
				{
336
					$this->$key = $val;
337
				}
338
			}
339
		}
340
	}
341
 
342
	// --------------------------------------------------------------------
343
 
344
	/**
345
	 * Parse server URL
346
	 *
347
	 * @param	string	$url
348
	 * @param	int	$port
349
	 * @param	string	$proxy
350
	 * @param	int	$proxy_port
351
	 * @return	void
352
	 */
353
	public function server($url, $port = 80, $proxy = FALSE, $proxy_port = 8080)
354
	{
1257 lars 355
		if (stripos($url, 'http') !== 0)
68 lars 356
		{
357
			$url = 'http://'.$url;
358
		}
359
 
360
		$parts = parse_url($url);
361
 
362
		if (isset($parts['user'], $parts['pass']))
363
		{
364
			$parts['host'] = $parts['user'].':'.$parts['pass'].'@'.$parts['host'];
365
		}
366
 
367
		$path = isset($parts['path']) ? $parts['path'] : '/';
368
 
369
		if ( ! empty($parts['query']))
370
		{
371
			$path .= '?'.$parts['query'];
372
		}
373
 
374
		$this->client = new XML_RPC_Client($path, $parts['host'], $port, $proxy, $proxy_port);
375
	}
376
 
377
	// --------------------------------------------------------------------
378
 
379
	/**
380
	 * Set Timeout
381
	 *
382
	 * @param	int	$seconds
383
	 * @return	void
384
	 */
385
	public function timeout($seconds = 5)
386
	{
387
		if ($this->client !== NULL && is_int($seconds))
388
		{
389
			$this->client->timeout = $seconds;
390
		}
391
	}
392
 
393
	// --------------------------------------------------------------------
394
 
395
	/**
396
	 * Set Methods
397
	 *
398
	 * @param	string	$function	Method name
399
	 * @return	void
400
	 */
401
	public function method($function)
402
	{
403
		$this->method = $function;
404
	}
405
 
406
	// --------------------------------------------------------------------
407
 
408
	/**
409
	 * Take Array of Data and Create Objects
410
	 *
411
	 * @param	array	$incoming
412
	 * @return	void
413
	 */
414
	public function request($incoming)
415
	{
416
		if ( ! is_array($incoming))
417
		{
418
			// Send Error
419
			return;
420
		}
421
 
422
		$this->data = array();
423
 
424
		foreach ($incoming as $key => $value)
425
		{
426
			$this->data[$key] = $this->values_parsing($value);
427
		}
428
	}
429
 
430
	// --------------------------------------------------------------------
431
 
432
	/**
433
	 * Set Debug
434
	 *
435
	 * @param	bool	$flag
436
	 * @return	void
437
	 */
438
	public function set_debug($flag = TRUE)
439
	{
440
		$this->debug = ($flag === TRUE);
441
	}
442
 
443
	// --------------------------------------------------------------------
444
 
445
	/**
446
	 * Values Parsing
447
	 *
448
	 * @param	mixed	$value
449
	 * @return	object
450
	 */
451
	public function values_parsing($value)
452
	{
453
		if (is_array($value) && array_key_exists(0, $value))
454
		{
455
			if ( ! isset($value[1], $this->xmlrpcTypes[$value[1]]))
456
			{
457
				$temp = new XML_RPC_Values($value[0], (is_array($value[0]) ? 'array' : 'string'));
458
			}
459
			else
460
			{
461
				if (is_array($value[0]) && ($value[1] === 'struct' OR $value[1] === 'array'))
462
				{
2107 lars 463
					foreach (array_keys($value[0]) as $k)
68 lars 464
					{
465
						$value[0][$k] = $this->values_parsing($value[0][$k]);
466
					}
467
				}
468
 
469
				$temp = new XML_RPC_Values($value[0], $value[1]);
470
			}
471
		}
472
		else
473
		{
474
			$temp = new XML_RPC_Values($value, 'string');
475
		}
476
 
477
		return $temp;
478
	}
479
 
480
	// --------------------------------------------------------------------
481
 
482
	/**
483
	 * Sends XML-RPC Request
484
	 *
485
	 * @return	bool
486
	 */
487
	public function send_request()
488
	{
489
		$this->message = new XML_RPC_Message($this->method, $this->data);
490
		$this->message->debug = $this->debug;
491
 
492
		if ( ! $this->result = $this->client->send($this->message) OR ! is_object($this->result->val))
493
		{
494
			$this->error = $this->result->errstr;
495
			return FALSE;
496
		}
497
 
498
		$this->response = $this->result->decode();
499
		return TRUE;
500
	}
501
 
502
	// --------------------------------------------------------------------
503
 
504
	/**
505
	 * Returns Error
506
	 *
507
	 * @return	string
508
	 */
509
	public function display_error()
510
	{
511
		return $this->error;
512
	}
513
 
514
	// --------------------------------------------------------------------
515
 
516
	/**
517
	 * Returns Remote Server Response
518
	 *
519
	 * @return	string
520
	 */
521
	public function display_response()
522
	{
523
		return $this->response;
524
	}
525
 
526
	// --------------------------------------------------------------------
527
 
528
	/**
529
	 * Sends an Error Message for Server Request
530
	 *
531
	 * @param	int	$number
532
	 * @param	string	$message
533
	 * @return	object
534
	 */
535
	public function send_error_message($number, $message)
536
	{
537
		return new XML_RPC_Response(0, $number, $message);
538
	}
539
 
540
	// --------------------------------------------------------------------
541
 
542
	/**
543
	 * Send Response for Server Request
544
	 *
545
	 * @param	array	$response
546
	 * @return	object
547
	 */
548
	public function send_response($response)
549
	{
550
		// $response should be array of values, which will be parsed
551
		// based on their data and type into a valid group of XML-RPC values
552
		return new XML_RPC_Response($this->values_parsing($response));
553
	}
554
 
555
} // END XML_RPC Class
556
 
557
/**
558
 * XML-RPC Client class
559
 *
560
 * @category	XML-RPC
561
 * @author		EllisLab Dev Team
562
 * @link		https://codeigniter.com/user_guide/libraries/xmlrpc.html
563
 */
564
class XML_RPC_Client extends CI_Xmlrpc
565
{
566
	/**
567
	 * Path
568
	 *
569
	 * @var	string
570
	 */
571
	public $path			= '';
572
 
573
	/**
574
	 * Server hostname
575
	 *
576
	 * @var	string
577
	 */
578
	public $server			= '';
579
 
580
	/**
581
	 * Server port
582
	 *
583
	 * @var	int
584
	 */
585
	public $port			= 80;
586
 
587
	/**
588
	 *
589
	 * Server username
590
	 *
591
	 * @var	string
592
	 */
593
	public $username;
594
 
595
	/**
596
	 * Server password
597
	 *
598
	 * @var	string
599
	 */
600
	public $password;
601
 
602
	/**
603
	 * Proxy hostname
604
	 *
605
	 * @var	string
606
	 */
607
	public $proxy			= FALSE;
608
 
609
	/**
610
	 * Proxy port
611
	 *
612
	 * @var	int
613
	 */
614
	public $proxy_port		= 8080;
615
 
616
	/**
617
	 * Error number
618
	 *
619
	 * @var	string
620
	 */
621
	public $errno			= '';
622
 
623
	/**
624
	 * Error message
625
	 *
626
	 * @var	string
627
	 */
628
	public $errstring		= '';
629
 
630
	/**
631
	 * Timeout in seconds
632
	 *
633
	 * @var	int
634
	 */
635
	public $timeout		= 5;
636
 
637
	/**
638
	 * No Multicall flag
639
	 *
640
	 * @var	bool
641
	 */
642
	public $no_multicall	= FALSE;
643
 
644
	// --------------------------------------------------------------------
645
 
646
	/**
647
	 * Constructor
648
	 *
649
	 * @param	string	$path
650
	 * @param	object	$server
651
	 * @param	int	$port
652
	 * @param	string	$proxy
653
	 * @param	int	$proxy_port
654
	 * @return	void
655
	 */
656
	public function __construct($path, $server, $port = 80, $proxy = FALSE, $proxy_port = 8080)
657
	{
658
		parent::__construct();
659
 
660
		$url = parse_url('http://'.$server);
661
 
662
		if (isset($url['user'], $url['pass']))
663
		{
664
			$this->username = $url['user'];
665
			$this->password = $url['pass'];
666
		}
667
 
668
		$this->port = $port;
669
		$this->server = $url['host'];
670
		$this->path = $path;
671
		$this->proxy = $proxy;
672
		$this->proxy_port = $proxy_port;
673
	}
674
 
675
	// --------------------------------------------------------------------
676
 
677
	/**
678
	 * Send message
679
	 *
680
	 * @param	mixed	$msg
681
	 * @return	object
682
	 */
683
	public function send($msg)
684
	{
685
		if (is_array($msg))
686
		{
687
			// Multi-call disabled
688
			return new XML_RPC_Response(0, $this->xmlrpcerr['multicall_recursion'], $this->xmlrpcstr['multicall_recursion']);
689
		}
690
 
691
		return $this->sendPayload($msg);
692
	}
693
 
694
	// --------------------------------------------------------------------
695
 
696
	/**
697
	 * Send payload
698
	 *
699
	 * @param	object	$msg
700
	 * @return	object
701
	 */
702
	public function sendPayload($msg)
703
	{
704
		if ($this->proxy === FALSE)
705
		{
706
			$server = $this->server;
707
			$port = $this->port;
708
		}
709
		else
710
		{
711
			$server = $this->proxy;
712
			$port = $this->proxy_port;
713
		}
714
 
715
		$fp = @fsockopen($server, $port, $this->errno, $this->errstring, $this->timeout);
716
 
717
		if ( ! is_resource($fp))
718
		{
719
			error_log($this->xmlrpcstr['http_error']);
720
			return new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']);
721
		}
722
 
723
		if (empty($msg->payload))
724
		{
725
			// $msg = XML_RPC_Messages
726
			$msg->createPayload();
727
		}
728
 
729
		$r = "\r\n";
730
		$op = 'POST '.$this->path.' HTTP/1.0'.$r
731
			.'Host: '.$this->server.$r
732
			.'Content-Type: text/xml'.$r
733
			.(isset($this->username, $this->password) ? 'Authorization: Basic '.base64_encode($this->username.':'.$this->password).$r : '')
734
			.'User-Agent: '.$this->xmlrpcName.$r
735
			.'Content-Length: '.strlen($msg->payload).$r.$r
736
			.$msg->payload;
737
 
1257 lars 738
		stream_set_timeout($fp, $this->timeout); // set timeout for subsequent operations
739
 
68 lars 740
		for ($written = $timestamp = 0, $length = strlen($op); $written < $length; $written += $result)
741
		{
742
			if (($result = fwrite($fp, substr($op, $written))) === FALSE)
743
			{
744
				break;
745
			}
746
			// See https://bugs.php.net/bug.php?id=39598 and http://php.net/manual/en/function.fwrite.php#96951
747
			elseif ($result === 0)
748
			{
749
				if ($timestamp === 0)
750
				{
751
					$timestamp = time();
752
				}
753
				elseif ($timestamp < (time() - $this->timeout))
754
				{
755
					$result = FALSE;
756
					break;
757
				}
758
			}
759
			else
760
			{
761
				$timestamp = 0;
762
			}
763
		}
764
 
765
		if ($result === FALSE)
766
		{
767
			error_log($this->xmlrpcstr['http_error']);
768
			return new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']);
769
		}
770
 
771
		$resp = $msg->parseResponse($fp);
772
		fclose($fp);
773
		return $resp;
774
	}
775
 
776
} // END XML_RPC_Client Class
777
 
778
/**
779
 * XML-RPC Response class
780
 *
781
 * @category	XML-RPC
782
 * @author		EllisLab Dev Team
783
 * @link		https://codeigniter.com/user_guide/libraries/xmlrpc.html
784
 */
785
class XML_RPC_Response
786
{
787
 
788
	/**
789
	 * Value
790
	 *
791
	 * @var	mixed
792
	 */
793
	public $val		= 0;
794
 
795
	/**
796
	 * Error number
797
	 *
798
	 * @var	int
799
	 */
800
	public $errno		= 0;
801
 
802
	/**
803
	 * Error message
804
	 *
805
	 * @var	string
806
	 */
807
	public $errstr		= '';
808
 
809
	/**
810
	 * Headers list
811
	 *
812
	 * @var	array
813
	 */
814
	public $headers		= array();
815
 
816
	/**
817
	 * XSS Filter flag
818
	 *
819
	 * @var	bool
820
	 */
821
	public $xss_clean	= TRUE;
822
 
823
	// --------------------------------------------------------------------
824
 
825
	/**
826
	 * Constructor
827
	 *
828
	 * @param	mixed	$val
829
	 * @param	int	$code
830
	 * @param	string	$fstr
831
	 * @return	void
832
	 */
833
	public function __construct($val, $code = 0, $fstr = '')
834
	{
835
		if ($code !== 0)
836
		{
837
			// error
838
			$this->errno = $code;
839
			$this->errstr = htmlspecialchars($fstr,
840
							(is_php('5.4') ? ENT_XML1 | ENT_NOQUOTES : ENT_NOQUOTES),
841
							'UTF-8');
842
		}
843
		elseif ( ! is_object($val))
844
		{
845
			// programmer error, not an object
846
			error_log("Invalid type '".gettype($val)."' (value: ".$val.') passed to XML_RPC_Response. Defaulting to empty value.');
847
			$this->val = new XML_RPC_Values();
848
		}
849
		else
850
		{
851
			$this->val = $val;
852
		}
853
	}
854
 
855
	// --------------------------------------------------------------------
856
 
857
	/**
858
	 * Fault code
859
	 *
860
	 * @return	int
861
	 */
862
	public function faultCode()
863
	{
864
		return $this->errno;
865
	}
866
 
867
	// --------------------------------------------------------------------
868
 
869
	/**
870
	 * Fault string
871
	 *
872
	 * @return	string
873
	 */
874
	public function faultString()
875
	{
876
		return $this->errstr;
877
	}
878
 
879
	// --------------------------------------------------------------------
880
 
881
	/**
882
	 * Value
883
	 *
884
	 * @return	mixed
885
	 */
886
	public function value()
887
	{
888
		return $this->val;
889
	}
890
 
891
	// --------------------------------------------------------------------
892
 
893
	/**
894
	 * Prepare response
895
	 *
896
	 * @return	string	xml
897
	 */
898
	public function prepare_response()
899
	{
900
		return "<methodResponse>\n"
901
			.($this->errno
902
				? '<fault>
903
	<value>
904
		<struct>
905
			<member>
906
				<name>faultCode</name>
907
				<value><int>'.$this->errno.'</int></value>
908
			</member>
909
			<member>
910
				<name>faultString</name>
911
				<value><string>'.$this->errstr.'</string></value>
912
			</member>
913
		</struct>
914
	</value>
915
</fault>'
916
				: "<params>\n<param>\n".$this->val->serialize_class()."</param>\n</params>")
917
			."\n</methodResponse>";
918
	}
919
 
920
	// --------------------------------------------------------------------
921
 
922
	/**
923
	 * Decode
924
	 *
925
	 * @param	mixed	$array
926
	 * @return	array
927
	 */
928
	public function decode($array = NULL)
929
	{
930
		$CI =& get_instance();
931
 
932
		if (is_array($array))
933
		{
2107 lars 934
			foreach ($array as $key => &$value)
68 lars 935
			{
2107 lars 936
				if (is_array($value))
68 lars 937
				{
2107 lars 938
					$array[$key] = $this->decode($value);
68 lars 939
				}
940
				elseif ($this->xss_clean)
941
				{
2107 lars 942
					$array[$key] = $CI->security->xss_clean($value);
68 lars 943
				}
944
			}
945
 
946
			return $array;
947
		}
948
 
949
		$result = $this->xmlrpc_decoder($this->val);
950
 
951
		if (is_array($result))
952
		{
953
			$result = $this->decode($result);
954
		}
955
		elseif ($this->xss_clean)
956
		{
957
			$result = $CI->security->xss_clean($result);
958
		}
959
 
960
		return $result;
961
	}
962
 
963
	// --------------------------------------------------------------------
964
 
965
	/**
966
	 * XML-RPC Object to PHP Types
967
	 *
968
	 * @param	object
969
	 * @return	array
970
	 */
971
	public function xmlrpc_decoder($xmlrpc_val)
972
	{
973
		$kind = $xmlrpc_val->kindOf();
974
 
975
		if ($kind === 'scalar')
976
		{
977
			return $xmlrpc_val->scalarval();
978
		}
979
		elseif ($kind === 'array')
980
		{
981
			reset($xmlrpc_val->me);
982
			$b = current($xmlrpc_val->me);
983
			$arr = array();
984
 
985
			for ($i = 0, $size = count($b); $i < $size; $i++)
986
			{
987
				$arr[] = $this->xmlrpc_decoder($xmlrpc_val->me['array'][$i]);
988
			}
989
			return $arr;
990
		}
991
		elseif ($kind === 'struct')
992
		{
993
			reset($xmlrpc_val->me['struct']);
994
			$arr = array();
995
 
2107 lars 996
			foreach ($xmlrpc_val->me['struct'] as $key => &$value)
68 lars 997
			{
998
				$arr[$key] = $this->xmlrpc_decoder($value);
999
			}
2107 lars 1000
 
68 lars 1001
			return $arr;
1002
		}
1003
	}
1004
 
1005
	// --------------------------------------------------------------------
1006
 
1007
	/**
1008
	 * ISO-8601 time to server or UTC time
1009
	 *
1010
	 * @param	string
1011
	 * @param	bool
1012
	 * @return	int	unix timestamp
1013
	 */
1014
	public function iso8601_decode($time, $utc = FALSE)
1015
	{
1016
		// Return a time in the localtime, or UTC
1017
		$t = 0;
1018
		if (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $time, $regs))
1019
		{
1020
			$fnc = ($utc === TRUE) ? 'gmmktime' : 'mktime';
1021
			$t = $fnc($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
1022
		}
1023
		return $t;
1024
	}
1025
 
1026
} // END XML_RPC_Response Class
1027
 
1028
/**
1029
 * XML-RPC Message class
1030
 *
1031
 * @category	XML-RPC
1032
 * @author		EllisLab Dev Team
1033
 * @link		https://codeigniter.com/user_guide/libraries/xmlrpc.html
1034
 */
1035
class XML_RPC_Message extends CI_Xmlrpc
1036
{
1037
 
1038
	/**
1039
	 * Payload
1040
	 *
1041
	 * @var	string
1042
	 */
1043
	public $payload;
1044
 
1045
	/**
1046
	 * Method name
1047
	 *
1048
	 * @var	string
1049
	 */
1050
	public $method_name;
1051
 
1052
	/**
1053
	 * Parameter list
1054
	 *
1055
	 * @var	array
1056
	 */
1057
	public $params		= array();
1058
 
1059
	/**
1060
	 * XH?
1061
	 *
1062
	 * @var	array
1063
	 */
1064
	public $xh		= array();
1065
 
1066
	// --------------------------------------------------------------------
1067
 
1068
	/**
1069
	 * Constructor
1070
	 *
1071
	 * @param	string	$method
1072
	 * @param	array	$pars
1073
	 * @return	void
1074
	 */
1075
	public function __construct($method, $pars = FALSE)
1076
	{
1077
		parent::__construct();
1078
 
1079
		$this->method_name = $method;
1080
		if (is_array($pars) && count($pars) > 0)
1081
		{
1082
			for ($i = 0, $c = count($pars); $i < $c; $i++)
1083
			{
1084
				// $pars[$i] = XML_RPC_Values
1085
				$this->params[] = $pars[$i];
1086
			}
1087
		}
1088
	}
1089
 
1090
	// --------------------------------------------------------------------
1091
 
1092
	/**
1093
	 * Create Payload to Send
1094
	 *
1095
	 * @return	void
1096
	 */
1097
	public function createPayload()
1098
	{
1099
		$this->payload = '<?xml version="1.0"?'.">\r\n<methodCall>\r\n"
1100
				.'<methodName>'.$this->method_name."</methodName>\r\n"
1101
				."<params>\r\n";
1102
 
1103
		for ($i = 0, $c = count($this->params); $i < $c; $i++)
1104
		{
1105
			// $p = XML_RPC_Values
1106
			$p = $this->params[$i];
1107
			$this->payload .= "<param>\r\n".$p->serialize_class()."</param>\r\n";
1108
		}
1109
 
1110
		$this->payload .= "</params>\r\n</methodCall>\r\n";
1111
	}
1112
 
1113
	// --------------------------------------------------------------------
1114
 
1115
	/**
1116
	 * Parse External XML-RPC Server's Response
1117
	 *
1118
	 * @param	resource
1119
	 * @return	object
1120
	 */
1121
	public function parseResponse($fp)
1122
	{
1123
		$data = '';
1124
 
1125
		while ($datum = fread($fp, 4096))
1126
		{
1127
			$data .= $datum;
1128
		}
1129
 
1130
		// Display HTTP content for debugging
1131
		if ($this->debug === TRUE)
1132
		{
1133
			echo "<pre>---DATA---\n".htmlspecialchars($data)."\n---END DATA---\n\n</pre>";
1134
		}
1135
 
1136
		// Check for data
1137
		if ($data === '')
1138
		{
1139
			error_log($this->xmlrpcstr['no_data']);
1140
			return new XML_RPC_Response(0, $this->xmlrpcerr['no_data'], $this->xmlrpcstr['no_data']);
1141
		}
1142
 
1143
		// Check for HTTP 200 Response
1144
		if (strpos($data, 'HTTP') === 0 && ! preg_match('/^HTTP\/[0-9\.]+ 200 /', $data))
1145
		{
1146
			$errstr = substr($data, 0, strpos($data, "\n")-1);
1147
			return new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error'].' ('.$errstr.')');
1148
		}
1149
 
1150
		//-------------------------------------
1151
		// Create and Set Up XML Parser
1152
		//-------------------------------------
1153
 
1154
		$parser = xml_parser_create($this->xmlrpc_defencoding);
1155
		$pname = (string) $parser;
1156
		$this->xh[$pname] = array(
1157
			'isf'		=> 0,
1158
			'ac'		=> '',
1159
			'headers'	=> array(),
1160
			'stack'		=> array(),
1161
			'valuestack'	=> array(),
1162
			'isf_reason'	=> 0
1163
		);
1164
 
1165
		xml_set_object($parser, $this);
1166
		xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, TRUE);
1167
		xml_set_element_handler($parser, 'open_tag', 'closing_tag');
1168
		xml_set_character_data_handler($parser, 'character_data');
1169
		//xml_set_default_handler($parser, 'default_handler');
1170
 
1171
		// Get headers
1172
		$lines = explode("\r\n", $data);
1173
		while (($line = array_shift($lines)))
1174
		{
1175
			if (strlen($line) < 1)
1176
			{
1177
				break;
1178
			}
1179
			$this->xh[$pname]['headers'][] = $line;
1180
		}
1181
		$data = implode("\r\n", $lines);
1182
 
1183
		// Parse XML data
2257 lars 1184
		if ( ! xml_parse($parser, $data, TRUE))
68 lars 1185
		{
1186
			$errstr = sprintf('XML error: %s at line %d',
1187
						xml_error_string(xml_get_error_code($parser)),
1188
						xml_get_current_line_number($parser));
1189
 
1190
			$r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']);
1191
			xml_parser_free($parser);
1192
			return $r;
1193
		}
1194
		xml_parser_free($parser);
1195
 
1196
		// Got ourselves some badness, it seems
1197
		if ($this->xh[$pname]['isf'] > 1)
1198
		{
1199
			if ($this->debug === TRUE)
1200
			{
1201
				echo "---Invalid Return---\n".$this->xh[$pname]['isf_reason']."---Invalid Return---\n\n";
1202
			}
1203
 
1204
			return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return'].' '.$this->xh[$pname]['isf_reason']);
1205
		}
1206
		elseif ( ! is_object($this->xh[$pname]['value']))
1207
		{
1208
			return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return'].' '.$this->xh[$pname]['isf_reason']);
1209
		}
1210
 
1211
		// Display XML content for debugging
1212
		if ($this->debug === TRUE)
1213
		{
1214
			echo '<pre>';
1215
 
2414 lars 1216
			if (count($this->xh[$pname]['headers']) > 0)
68 lars 1217
			{
1218
				echo "---HEADERS---\n";
1219
				foreach ($this->xh[$pname]['headers'] as $header)
1220
				{
1221
					echo $header."\n";
1222
				}
1223
				echo "---END HEADERS---\n\n";
1224
			}
1225
 
1226
			echo "---DATA---\n".htmlspecialchars($data)."\n---END DATA---\n\n---PARSED---\n";
1227
			var_dump($this->xh[$pname]['value']);
1228
			echo "\n---END PARSED---</pre>";
1229
		}
1230
 
1231
		// Send response
1232
		$v = $this->xh[$pname]['value'];
1233
		if ($this->xh[$pname]['isf'])
1234
		{
1235
			$errno_v = $v->me['struct']['faultCode'];
1236
			$errstr_v = $v->me['struct']['faultString'];
1237
			$errno = $errno_v->scalarval();
1238
 
1239
			if ($errno === 0)
1240
			{
1241
				// FAULT returned, errno needs to reflect that
1242
				$errno = -1;
1243
			}
1244
 
1245
			$r = new XML_RPC_Response($v, $errno, $errstr_v->scalarval());
1246
		}
1247
		else
1248
		{
1249
			$r = new XML_RPC_Response($v);
1250
		}
1251
 
1252
		$r->headers = $this->xh[$pname]['headers'];
1253
		return $r;
1254
	}
1255
 
1256
	// --------------------------------------------------------------------
1257
 
1258
	// ------------------------------------
1259
	//  Begin Return Message Parsing section
1260
	// ------------------------------------
1261
 
1262
	// quick explanation of components:
1263
	//   ac - used to accumulate values
1264
	//   isf - used to indicate a fault
1265
	//   lv - used to indicate "looking for a value": implements
1266
	//		the logic to allow values with no types to be strings
1267
	//   params - used to store parameters in method calls
1268
	//   method - used to store method name
1269
	//	 stack - array with parent tree of the xml element,
1270
	//			 used to validate the nesting of elements
1271
 
1272
	// --------------------------------------------------------------------
1273
 
1274
	/**
1275
	 * Start Element Handler
1276
	 *
1277
	 * @param	string
1278
	 * @param	string
1279
	 * @return	void
1280
	 */
1281
	public function open_tag($the_parser, $name)
1282
	{
1283
		$the_parser = (string) $the_parser;
1284
 
1285
		// If invalid nesting, then return
1286
		if ($this->xh[$the_parser]['isf'] > 1) return;
1287
 
1288
		// Evaluate and check for correct nesting of XML elements
1289
		if (count($this->xh[$the_parser]['stack']) === 0)
1290
		{
1291
			if ($name !== 'METHODRESPONSE' && $name !== 'METHODCALL')
1292
			{
1293
				$this->xh[$the_parser]['isf'] = 2;
1294
				$this->xh[$the_parser]['isf_reason'] = 'Top level XML-RPC element is missing';
1295
				return;
1296
			}
1297
		}
1298
		// not top level element: see if parent is OK
1299
		elseif ( ! in_array($this->xh[$the_parser]['stack'][0], $this->valid_parents[$name], TRUE))
1300
		{
1301
			$this->xh[$the_parser]['isf'] = 2;
1302
			$this->xh[$the_parser]['isf_reason'] = 'XML-RPC element '.$name.' cannot be child of '.$this->xh[$the_parser]['stack'][0];
1303
			return;
1304
		}
1305
 
1306
		switch ($name)
1307
		{
1308
			case 'STRUCT':
1309
			case 'ARRAY':
1310
				// Creates array for child elements
1311
				$cur_val = array('value' => array(), 'type' => $name);
1312
				array_unshift($this->xh[$the_parser]['valuestack'], $cur_val);
1313
				break;
1314
			case 'METHODNAME':
1315
			case 'NAME':
1316
				$this->xh[$the_parser]['ac'] = '';
1317
				break;
1318
			case 'FAULT':
1319
				$this->xh[$the_parser]['isf'] = 1;
1320
				break;
1321
			case 'PARAM':
1322
				$this->xh[$the_parser]['value'] = NULL;
1323
				break;
1324
			case 'VALUE':
1325
				$this->xh[$the_parser]['vt'] = 'value';
1326
				$this->xh[$the_parser]['ac'] = '';
1327
				$this->xh[$the_parser]['lv'] = 1;
1328
				break;
1329
			case 'I4':
1330
			case 'INT':
1331
			case 'STRING':
1332
			case 'BOOLEAN':
1333
			case 'DOUBLE':
1334
			case 'DATETIME.ISO8601':
1335
			case 'BASE64':
1336
				if ($this->xh[$the_parser]['vt'] !== 'value')
1337
				{
1338
					//two data elements inside a value: an error occurred!
1339
					$this->xh[$the_parser]['isf'] = 2;
1340
					$this->xh[$the_parser]['isf_reason'] = 'There is a '.$name.' element following a '
1341
										.$this->xh[$the_parser]['vt'].' element inside a single value';
1342
					return;
1343
				}
1344
 
1345
				$this->xh[$the_parser]['ac'] = '';
1346
				break;
1347
			case 'MEMBER':
1348
				// Set name of <member> to nothing to prevent errors later if no <name> is found
1349
				$this->xh[$the_parser]['valuestack'][0]['name'] = '';
1350
 
1351
				// Set NULL value to check to see if value passed for this param/member
1352
				$this->xh[$the_parser]['value'] = NULL;
1353
				break;
1354
			case 'DATA':
1355
			case 'METHODCALL':
1356
			case 'METHODRESPONSE':
1357
			case 'PARAMS':
1358
				// valid elements that add little to processing
1359
				break;
1360
			default:
1361
				/// An Invalid Element is Found, so we have trouble
1362
				$this->xh[$the_parser]['isf'] = 2;
1363
				$this->xh[$the_parser]['isf_reason'] = 'Invalid XML-RPC element found: '.$name;
1364
				break;
1365
		}
1366
 
1367
		// Add current element name to stack, to allow validation of nesting
1368
		array_unshift($this->xh[$the_parser]['stack'], $name);
1369
 
1370
		$name === 'VALUE' OR $this->xh[$the_parser]['lv'] = 0;
1371
	}
1372
 
1373
	// --------------------------------------------------------------------
1374
 
1375
	/**
1376
	 * End Element Handler
1377
	 *
1378
	 * @param	string
1379
	 * @param	string
1380
	 * @return	void
1381
	 */
1382
	public function closing_tag($the_parser, $name)
1383
	{
1384
		$the_parser = (string) $the_parser;
1385
 
1386
		if ($this->xh[$the_parser]['isf'] > 1) return;
1387
 
1388
		// Remove current element from stack and set variable
1389
		// NOTE: If the XML validates, then we do not have to worry about
1390
		// the opening and closing of elements. Nesting is checked on the opening
1391
		// tag so we be safe there as well.
1392
 
1393
		$curr_elem = array_shift($this->xh[$the_parser]['stack']);
1394
 
1395
		switch ($name)
1396
		{
1397
			case 'STRUCT':
1398
			case 'ARRAY':
1399
				$cur_val = array_shift($this->xh[$the_parser]['valuestack']);
1400
				$this->xh[$the_parser]['value'] = isset($cur_val['values']) ? $cur_val['values'] : array();
1401
				$this->xh[$the_parser]['vt']	= strtolower($name);
1402
				break;
1403
			case 'NAME':
1404
				$this->xh[$the_parser]['valuestack'][0]['name'] = $this->xh[$the_parser]['ac'];
1405
				break;
1406
			case 'BOOLEAN':
1407
			case 'I4':
1408
			case 'INT':
1409
			case 'STRING':
1410
			case 'DOUBLE':
1411
			case 'DATETIME.ISO8601':
1412
			case 'BASE64':
1413
				$this->xh[$the_parser]['vt'] = strtolower($name);
1414
 
1415
				if ($name === 'STRING')
1416
				{
1417
					$this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
1418
				}
1419
				elseif ($name === 'DATETIME.ISO8601')
1420
				{
1421
					$this->xh[$the_parser]['vt']	= $this->xmlrpcDateTime;
1422
					$this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
1423
				}
1424
				elseif ($name === 'BASE64')
1425
				{
1426
					$this->xh[$the_parser]['value'] = base64_decode($this->xh[$the_parser]['ac']);
1427
				}
1428
				elseif ($name === 'BOOLEAN')
1429
				{
1430
					// Translated BOOLEAN values to TRUE AND FALSE
1431
					$this->xh[$the_parser]['value'] = (bool) $this->xh[$the_parser]['ac'];
1432
				}
1433
				elseif ($name=='DOUBLE')
1434
				{
1435
					// we have a DOUBLE
1436
					// we must check that only 0123456789-.<space> are characters here
1437
					$this->xh[$the_parser]['value'] = preg_match('/^[+-]?[eE0-9\t \.]+$/', $this->xh[$the_parser]['ac'])
1438
										? (float) $this->xh[$the_parser]['ac']
1439
										: 'ERROR_NON_NUMERIC_FOUND';
1440
				}
1441
				else
1442
				{
1443
					// we have an I4/INT
1444
					// we must check that only 0123456789-<space> are characters here
1445
					$this->xh[$the_parser]['value'] = preg_match('/^[+-]?[0-9\t ]+$/', $this->xh[$the_parser]['ac'])
1446
										? (int) $this->xh[$the_parser]['ac']
1447
										: 'ERROR_NON_NUMERIC_FOUND';
1448
				}
1449
				$this->xh[$the_parser]['ac'] = '';
1450
				$this->xh[$the_parser]['lv'] = 3; // indicate we've found a value
1451
				break;
1452
			case 'VALUE':
1453
				// This if() detects if no scalar was inside <VALUE></VALUE>
1454
				if ($this->xh[$the_parser]['vt'] == 'value')
1455
				{
1456
					$this->xh[$the_parser]['value']	= $this->xh[$the_parser]['ac'];
1457
					$this->xh[$the_parser]['vt']	= $this->xmlrpcString;
1458
				}
1459
 
1460
				// build the XML-RPC value out of the data received, and substitute it
1461
				$temp = new XML_RPC_Values($this->xh[$the_parser]['value'], $this->xh[$the_parser]['vt']);
1462
 
1463
				if (count($this->xh[$the_parser]['valuestack']) && $this->xh[$the_parser]['valuestack'][0]['type'] === 'ARRAY')
1464
				{
1465
					// Array
1466
					$this->xh[$the_parser]['valuestack'][0]['values'][] = $temp;
1467
				}
1468
				else
1469
				{
1470
					// Struct
1471
					$this->xh[$the_parser]['value'] = $temp;
1472
				}
1473
				break;
1474
			case 'MEMBER':
1475
				$this->xh[$the_parser]['ac'] = '';
1476
 
1477
				// If value add to array in the stack for the last element built
1478
				if ($this->xh[$the_parser]['value'])
1479
				{
1480
					$this->xh[$the_parser]['valuestack'][0]['values'][$this->xh[$the_parser]['valuestack'][0]['name']] = $this->xh[$the_parser]['value'];
1481
				}
1482
				break;
1483
			case 'DATA':
1484
				$this->xh[$the_parser]['ac'] = '';
1485
				break;
1486
			case 'PARAM':
1487
				if ($this->xh[$the_parser]['value'])
1488
				{
1489
					$this->xh[$the_parser]['params'][] = $this->xh[$the_parser]['value'];
1490
				}
1491
				break;
1492
			case 'METHODNAME':
1493
				$this->xh[$the_parser]['method'] = ltrim($this->xh[$the_parser]['ac']);
1494
				break;
1495
			case 'PARAMS':
1496
			case 'FAULT':
1497
			case 'METHODCALL':
1498
			case 'METHORESPONSE':
1499
				// We're all good kids with nuthin' to do
1500
				break;
1501
			default:
1502
				// End of an Invalid Element. Taken care of during the opening tag though
1503
				break;
1504
		}
1505
	}
1506
 
1507
	// --------------------------------------------------------------------
1508
 
1509
	/**
1510
	 * Parse character data
1511
	 *
1512
	 * @param	string
1513
	 * @param	string
1514
	 * @return	void
1515
	 */
1516
	public function character_data($the_parser, $data)
1517
	{
1518
		$the_parser = (string) $the_parser;
1519
 
1520
		if ($this->xh[$the_parser]['isf'] > 1) return; // XML Fault found already
1521
 
1522
		// If a value has not been found
1523
		if ($this->xh[$the_parser]['lv'] !== 3)
1524
		{
1525
			if ($this->xh[$the_parser]['lv'] === 1)
1526
			{
1527
				$this->xh[$the_parser]['lv'] = 2; // Found a value
1528
			}
1529
 
1530
			if ( ! isset($this->xh[$the_parser]['ac']))
1531
			{
1532
				$this->xh[$the_parser]['ac'] = '';
1533
			}
1534
 
1535
			$this->xh[$the_parser]['ac'] .= $data;
1536
		}
1537
	}
1538
 
1539
	// --------------------------------------------------------------------
1540
 
1541
	/**
1542
	 * Add parameter
1543
	 *
1544
	 * @param	mixed
1545
	 * @return	void
1546
	 */
1547
	public function addParam($par)
1548
	{
1549
		$this->params[] = $par;
1550
	}
1551
 
1552
	// --------------------------------------------------------------------
1553
 
1554
	/**
1555
	 * Output parameters
1556
	 *
1557
	 * @param	array	$array
1558
	 * @return	array
1559
	 */
1560
	public function output_parameters(array $array = array())
1561
	{
1562
		$CI =& get_instance();
1563
 
1564
		if ( ! empty($array))
1565
		{
2107 lars 1566
			foreach ($array as $key => &$value)
68 lars 1567
			{
2107 lars 1568
				if (is_array($value))
68 lars 1569
				{
2107 lars 1570
					$array[$key] = $this->output_parameters($value);
68 lars 1571
				}
1572
				elseif ($key !== 'bits' && $this->xss_clean)
1573
				{
1574
					// 'bits' is for the MetaWeblog API image bits
1575
					// @todo - this needs to be made more general purpose
2107 lars 1576
					$array[$key] = $CI->security->xss_clean($value);
68 lars 1577
				}
1578
			}
1579
 
1580
			return $array;
1581
		}
1582
 
1583
		$parameters = array();
1584
 
1585
		for ($i = 0, $c = count($this->params); $i < $c; $i++)
1586
		{
1587
			$a_param = $this->decode_message($this->params[$i]);
1588
 
1589
			if (is_array($a_param))
1590
			{
1591
				$parameters[] = $this->output_parameters($a_param);
1592
			}
1593
			else
1594
			{
1595
				$parameters[] = ($this->xss_clean) ? $CI->security->xss_clean($a_param) : $a_param;
1596
			}
1597
		}
1598
 
1599
		return $parameters;
1600
	}
1601
 
1602
	// --------------------------------------------------------------------
1603
 
1604
	/**
1605
	 * Decode message
1606
	 *
1607
	 * @param	object
1608
	 * @return	mixed
1609
	 */
1610
	public function decode_message($param)
1611
	{
1612
		$kind = $param->kindOf();
1613
 
1614
		if ($kind === 'scalar')
1615
		{
1616
			return $param->scalarval();
1617
		}
1618
		elseif ($kind === 'array')
1619
		{
1620
			reset($param->me);
1621
			$b = current($param->me);
1622
			$arr = array();
1623
 
1624
			for ($i = 0, $c = count($b); $i < $c; $i++)
1625
			{
1626
				$arr[] = $this->decode_message($param->me['array'][$i]);
1627
			}
1628
 
1629
			return $arr;
1630
		}
1631
		elseif ($kind === 'struct')
1632
		{
1633
			reset($param->me['struct']);
1634
			$arr = array();
1635
 
2107 lars 1636
			foreach ($param->me['struct'] as $key => &$value)
68 lars 1637
			{
1638
				$arr[$key] = $this->decode_message($value);
1639
			}
1640
 
1641
			return $arr;
1642
		}
1643
	}
1644
 
1645
} // END XML_RPC_Message Class
1646
 
1647
/**
1648
 * XML-RPC Values class
1649
 *
1650
 * @category	XML-RPC
1651
 * @author		EllisLab Dev Team
1652
 * @link		https://codeigniter.com/user_guide/libraries/xmlrpc.html
1653
 */
1654
class XML_RPC_Values extends CI_Xmlrpc
1655
{
1656
	/**
1657
	 * Value data
1658
	 *
1659
	 * @var	array
1660
	 */
1661
	public $me	= array();
1662
 
1663
	/**
1664
	 * Value type
1665
	 *
1666
	 * @var	int
1667
	 */
1668
	public $mytype	= 0;
1669
 
1670
	// --------------------------------------------------------------------
1671
 
1672
	/**
1673
	 * Constructor
1674
	 *
1675
	 * @param	mixed	$val
1676
	 * @param	string	$type
1677
	 * @return	void
1678
	 */
1679
	public function __construct($val = -1, $type = '')
1680
	{
1681
		parent::__construct();
1682
 
1683
		if ($val !== -1 OR $type !== '')
1684
		{
1685
			$type = $type === '' ? 'string' : $type;
1686
 
1687
			if ($this->xmlrpcTypes[$type] == 1)
1688
			{
1689
				$this->addScalar($val, $type);
1690
			}
1691
			elseif ($this->xmlrpcTypes[$type] == 2)
1692
			{
1693
				$this->addArray($val);
1694
			}
1695
			elseif ($this->xmlrpcTypes[$type] == 3)
1696
			{
1697
				$this->addStruct($val);
1698
			}
1699
		}
1700
	}
1701
 
1702
	// --------------------------------------------------------------------
1703
 
1704
	/**
1705
	 * Add scalar value
1706
	 *
1707
	 * @param	scalar
1708
	 * @param	string
1709
	 * @return	int
1710
	 */
1711
	public function addScalar($val, $type = 'string')
1712
	{
1713
		$typeof = $this->xmlrpcTypes[$type];
1714
 
1715
		if ($this->mytype === 1)
1716
		{
1717
			echo '<strong>XML_RPC_Values</strong>: scalar can have only one value<br />';
1718
			return 0;
1719
		}
1720
 
1721
		if ($typeof != 1)
1722
		{
1723
			echo '<strong>XML_RPC_Values</strong>: not a scalar type (${typeof})<br />';
1724
			return 0;
1725
		}
1726
 
1727
		if ($type === $this->xmlrpcBoolean)
1728
		{
1729
			$val = (int) (strcasecmp($val, 'true') === 0 OR $val === 1 OR ($val === TRUE && strcasecmp($val, 'false')));
1730
		}
1731
 
1732
		if ($this->mytype === 2)
1733
		{
1734
			// adding to an array here
1735
			$ar = $this->me['array'];
1736
			$ar[] = new XML_RPC_Values($val, $type);
1737
			$this->me['array'] = $ar;
1738
		}
1739
		else
1740
		{
1741
			// a scalar, so set the value and remember we're scalar
1742
			$this->me[$type] = $val;
1743
			$this->mytype = $typeof;
1744
		}
1745
 
1746
		return 1;
1747
	}
1748
 
1749
	// --------------------------------------------------------------------
1750
 
1751
	/**
1752
	 * Add array value
1753
	 *
1754
	 * @param	array
1755
	 * @return	int
1756
	 */
1757
	public function addArray($vals)
1758
	{
1759
		if ($this->mytype !== 0)
1760
		{
1761
			echo '<strong>XML_RPC_Values</strong>: already initialized as a ['.$this->kindOf().']<br />';
1762
			return 0;
1763
		}
1764
 
1765
		$this->mytype = $this->xmlrpcTypes['array'];
1766
		$this->me['array'] = $vals;
1767
		return 1;
1768
	}
1769
 
1770
	// --------------------------------------------------------------------
1771
 
1772
	/**
1773
	 * Add struct value
1774
	 *
1775
	 * @param	object
1776
	 * @return	int
1777
	 */
1778
	public function addStruct($vals)
1779
	{
1780
		if ($this->mytype !== 0)
1781
		{
1782
			echo '<strong>XML_RPC_Values</strong>: already initialized as a ['.$this->kindOf().']<br />';
1783
			return 0;
1784
		}
1785
		$this->mytype = $this->xmlrpcTypes['struct'];
1786
		$this->me['struct'] = $vals;
1787
		return 1;
1788
	}
1789
 
1790
	// --------------------------------------------------------------------
1791
 
1792
	/**
1793
	 * Get value type
1794
	 *
1795
	 * @return	string
1796
	 */
1797
	public function kindOf()
1798
	{
1799
		switch ($this->mytype)
1800
		{
1801
			case 3: return 'struct';
1802
			case 2: return 'array';
1803
			case 1: return 'scalar';
1804
			default: return 'undef';
1805
		}
1806
	}
1807
 
1808
	// --------------------------------------------------------------------
1809
 
1810
	/**
1811
	 * Serialize data
1812
	 *
1813
	 * @param	string
1814
	 * @param	mixed
1815
	 * @return	string
1816
	 */
1817
	public function serializedata($typ, $val)
1818
	{
1819
		$rs = '';
1820
 
1821
		switch ($this->xmlrpcTypes[$typ])
1822
		{
1823
			case 3:
1824
				// struct
1825
				$rs .= "<struct>\n";
1826
				reset($val);
2107 lars 1827
				foreach ($val as $key2 => &$val2)
68 lars 1828
				{
1829
					$rs .= "<member>\n<name>{$key2}</name>\n".$this->serializeval($val2)."</member>\n";
1830
				}
1831
				$rs .= '</struct>';
1832
				break;
1833
			case 2:
1834
				// array
1835
				$rs .= "<array>\n<data>\n";
1836
				for ($i = 0, $c = count($val); $i < $c; $i++)
1837
				{
1838
					$rs .= $this->serializeval($val[$i]);
1839
				}
1840
				$rs .= "</data>\n</array>\n";
1841
				break;
1842
			case 1:
1843
				// others
1844
				switch ($typ)
1845
				{
1846
					case $this->xmlrpcBase64:
1847
						$rs .= '<'.$typ.'>'.base64_encode( (string) $val).'</'.$typ.">\n";
1848
						break;
1849
					case $this->xmlrpcBoolean:
1850
						$rs .= '<'.$typ.'>'.( (bool) $val ? '1' : '0').'</'.$typ.">\n";
1851
						break;
1852
					case $this->xmlrpcString:
1853
						$rs .= '<'.$typ.'>'.htmlspecialchars( (string) $val).'</'.$typ.">\n";
1854
						break;
1855
					default:
1856
						$rs .= '<'.$typ.'>'.$val.'</'.$typ.">\n";
1857
						break;
1858
				}
1859
			default:
1860
				break;
1861
		}
1862
 
1863
		return $rs;
1864
	}
1865
 
1866
	// --------------------------------------------------------------------
1867
 
1868
	/**
1869
	 * Serialize class
1870
	 *
1871
	 * @return	string
1872
	 */
1873
	public function serialize_class()
1874
	{
1875
		return $this->serializeval($this);
1876
	}
1877
 
1878
	// --------------------------------------------------------------------
1879
 
1880
	/**
1881
	 * Serialize value
1882
	 *
1883
	 * @param	object
1884
	 * @return	string
1885
	 */
1886
	public function serializeval($o)
1887
	{
2107 lars 1888
		$array = $o->me;
1889
		list($value, $type) = array(reset($array), key($array));
1890
		return "<value>\n".$this->serializedata($type, $value)."</value>\n";
68 lars 1891
	}
1892
 
1893
	// --------------------------------------------------------------------
1894
 
1895
	/**
1896
	 * Scalar value
1897
	 *
1898
	 * @return	mixed
1899
	 */
1900
	public function scalarval()
1901
	{
2107 lars 1902
		return reset($this->me);
68 lars 1903
	}
1904
 
1905
	// --------------------------------------------------------------------
1906
 
1907
	/**
1908
	 * Encode time in ISO-8601 form.
1909
	 * Useful for sending time in XML-RPC
1910
	 *
1911
	 * @param	int	unix timestamp
1912
	 * @param	bool
1913
	 * @return	string
1914
	*/
1915
	public function iso8601_encode($time, $utc = FALSE)
1916
	{
1917
		return ($utc) ? strftime('%Y%m%dT%H:%i:%s', $time) : gmstrftime('%Y%m%dT%H:%i:%s', $time);
1918
	}
1919
 
1920
} // END XML_RPC_Values Class