Subversion-Projekte lars-tiefland.ci

Revision

Revision 2049 | Revision 2242 | Zur aktuellen Revision | 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
 *
2049 lars 9
 * Copyright (c) 2014 - 2017, 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/)
2049 lars 32
 * @copyright	Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/)
68 lars 33
 * @license	http://opensource.org/licenses/MIT	MIT License
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
if ( ! class_exists('CI_Xmlrpc', FALSE))
46
{
47
	show_error('You must load the Xmlrpc class before loading the Xmlrpcs class in order to create a server.');
48
}
49
 
50
// ------------------------------------------------------------------------
51
 
52
/**
53
 * XML-RPC server class
54
 *
55
 * @package		CodeIgniter
56
 * @subpackage	Libraries
57
 * @category	XML-RPC
58
 * @author		EllisLab Dev Team
59
 * @link		https://codeigniter.com/user_guide/libraries/xmlrpc.html
60
 */
61
class CI_Xmlrpcs extends CI_Xmlrpc {
62
 
63
	/**
64
	 * Array of methods mapped to function names and signatures
65
	 *
66
	 * @var array
67
	 */
68
	public $methods = array();
69
 
70
	/**
71
	 * Debug Message
72
	 *
73
	 * @var string
74
	 */
75
	public $debug_msg = '';
76
 
77
	/**
78
	 * XML RPC Server methods
79
	 *
80
	 * @var array
81
	 */
82
	public $system_methods	= array();
83
 
84
	/**
85
	 * Configuration object
86
	 *
87
	 * @var object
88
	 */
89
	public $object = FALSE;
90
 
91
	/**
92
	 * Initialize XMLRPC class
93
	 *
94
	 * @param	array	$config
95
	 * @return	void
96
	 */
97
	public function __construct($config = array())
98
	{
99
		parent::__construct();
100
		$this->set_system_methods();
101
 
102
		if (isset($config['functions']) && is_array($config['functions']))
103
		{
104
			$this->methods = array_merge($this->methods, $config['functions']);
105
		}
106
 
107
		log_message('info', 'XML-RPC Server Class Initialized');
108
	}
109
 
110
	// --------------------------------------------------------------------
111
 
112
	/**
113
	 * Initialize Prefs and Serve
114
	 *
115
	 * @param	mixed
116
	 * @return	void
117
	 */
118
	public function initialize($config = array())
119
	{
120
		if (isset($config['functions']) && is_array($config['functions']))
121
		{
122
			$this->methods = array_merge($this->methods, $config['functions']);
123
		}
124
 
125
		if (isset($config['debug']))
126
		{
127
			$this->debug = $config['debug'];
128
		}
129
 
130
		if (isset($config['object']) && is_object($config['object']))
131
		{
132
			$this->object = $config['object'];
133
		}
134
 
135
		if (isset($config['xss_clean']))
136
		{
137
			$this->xss_clean = $config['xss_clean'];
138
		}
139
	}
140
 
141
	// --------------------------------------------------------------------
142
 
143
	/**
144
	 * Setting of System Methods
145
	 *
146
	 * @return	void
147
	 */
148
	public function set_system_methods()
149
	{
150
		$this->methods = array(
151
					'system.listMethods'	 => array(
152
										'function' => 'this.listMethods',
153
										'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString), array($this->xmlrpcArray)),
154
										'docstring' => 'Returns an array of available methods on this server'),
155
					'system.methodHelp'	 => array(
156
										'function' => 'this.methodHelp',
157
										'signature' => array(array($this->xmlrpcString, $this->xmlrpcString)),
158
										'docstring' => 'Returns a documentation string for the specified method'),
159
					'system.methodSignature' => array(
160
										'function' => 'this.methodSignature',
161
										'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString)),
162
										'docstring' => 'Returns an array describing the return type and required parameters of a method'),
163
					'system.multicall'	 => array(
164
										'function' => 'this.multicall',
165
										'signature' => array(array($this->xmlrpcArray, $this->xmlrpcArray)),
166
										'docstring' => 'Combine multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details')
167
				);
168
	}
169
 
170
	// --------------------------------------------------------------------
171
 
172
	/**
173
	 * Main Server Function
174
	 *
175
	 * @return	void
176
	 */
177
	public function serve()
178
	{
179
		$r = $this->parseRequest();
180
		$payload = '<?xml version="1.0" encoding="'.$this->xmlrpc_defencoding.'"?'.'>'."\n".$this->debug_msg.$r->prepare_response();
181
 
182
		header('Content-Type: text/xml');
183
		header('Content-Length: '.strlen($payload));
184
		exit($payload);
185
	}
186
 
187
	// --------------------------------------------------------------------
188
 
189
	/**
190
	 * Add Method to Class
191
	 *
192
	 * @param	string	method name
193
	 * @param	string	function
194
	 * @param	string	signature
195
	 * @param	string	docstring
196
	 * @return	void
197
	 */
198
	public function add_to_map($methodname, $function, $sig, $doc)
199
	{
200
		$this->methods[$methodname] = array(
201
			'function'	=> $function,
202
			'signature'	=> $sig,
203
			'docstring'	=> $doc
204
		);
205
	}
206
 
207
	// --------------------------------------------------------------------
208
 
209
	/**
210
	 * Parse Server Request
211
	 *
212
	 * @param	string	data
213
	 * @return	object	xmlrpc response
214
	 */
215
	public function parseRequest($data = '')
216
	{
217
		//-------------------------------------
218
		//  Get Data
219
		//-------------------------------------
220
 
221
		if ($data === '')
222
		{
223
			$CI =& get_instance();
224
			if ($CI->input->method() === 'post')
225
			{
226
				$data = $CI->input->raw_input_stream;
227
			}
228
		}
229
 
230
		//-------------------------------------
231
		//  Set up XML Parser
232
		//-------------------------------------
233
 
234
		$parser = xml_parser_create($this->xmlrpc_defencoding);
235
		$parser_object = new XML_RPC_Message('filler');
236
		$pname = (string) $parser;
237
 
238
		$parser_object->xh[$pname] = array(
239
			'isf' => 0,
240
			'isf_reason' => '',
241
			'params' => array(),
242
			'stack' => array(),
243
			'valuestack' => array(),
244
			'method' => ''
245
		);
246
 
247
		xml_set_object($parser, $parser_object);
248
		xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, TRUE);
249
		xml_set_element_handler($parser, 'open_tag', 'closing_tag');
250
		xml_set_character_data_handler($parser, 'character_data');
251
		//xml_set_default_handler($parser, 'default_handler');
252
 
253
		//-------------------------------------
254
		// PARSE + PROCESS XML DATA
255
		//-------------------------------------
256
 
257
		if ( ! xml_parse($parser, $data, 1))
258
		{
259
			// Return XML error as a faultCode
260
			$r = new XML_RPC_Response(0,
261
				$this->xmlrpcerrxml + xml_get_error_code($parser),
262
				sprintf('XML error: %s at line %d',
263
				xml_error_string(xml_get_error_code($parser)),
264
				xml_get_current_line_number($parser)));
265
			xml_parser_free($parser);
266
		}
267
		elseif ($parser_object->xh[$pname]['isf'])
268
		{
269
			return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']);
270
		}
271
		else
272
		{
273
			xml_parser_free($parser);
274
 
275
			$m = new XML_RPC_Message($parser_object->xh[$pname]['method']);
276
			$plist = '';
277
 
278
			for ($i = 0, $c = count($parser_object->xh[$pname]['params']); $i < $c; $i++)
279
			{
280
				if ($this->debug === TRUE)
281
				{
282
					$plist .= $i.' - '.print_r(get_object_vars($parser_object->xh[$pname]['params'][$i]), TRUE).";\n";
283
				}
284
 
285
				$m->addParam($parser_object->xh[$pname]['params'][$i]);
286
			}
287
 
288
			if ($this->debug === TRUE)
289
			{
290
				echo "<pre>---PLIST---\n".$plist."\n---PLIST END---\n\n</pre>";
291
			}
292
 
293
			$r = $this->_execute($m);
294
		}
295
 
296
		//-------------------------------------
297
		// SET DEBUGGING MESSAGE
298
		//-------------------------------------
299
 
300
		if ($this->debug === TRUE)
301
		{
302
			$this->debug_msg = "<!-- DEBUG INFO:\n\n".$plist."\n END DEBUG-->\n";
303
		}
304
 
305
		return $r;
306
	}
307
 
308
	// --------------------------------------------------------------------
309
 
310
	/**
311
	 * Executes the Method
312
	 *
313
	 * @param	object
314
	 * @return	mixed
315
	 */
316
	protected function _execute($m)
317
	{
318
		$methName = $m->method_name;
319
 
320
		// Check to see if it is a system call
321
		$system_call = (strpos($methName, 'system') === 0);
322
 
323
		if ($this->xss_clean === FALSE)
324
		{
325
			$m->xss_clean = FALSE;
326
		}
327
 
328
		//-------------------------------------
329
		// Valid Method
330
		//-------------------------------------
331
 
332
		if ( ! isset($this->methods[$methName]['function']))
333
		{
334
			return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
335
		}
336
 
337
		//-------------------------------------
338
		// Check for Method (and Object)
339
		//-------------------------------------
340
 
341
		$method_parts = explode('.', $this->methods[$methName]['function']);
2049 lars 342
		$objectCall   = ! empty($method_parts[1]);
68 lars 343
 
344
		if ($system_call === TRUE)
345
		{
2049 lars 346
			if ( ! is_callable(array($this, $method_parts[1])))
68 lars 347
			{
348
				return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
349
			}
350
		}
351
		elseif (($objectCall && ! is_callable(array($method_parts[0], $method_parts[1])))
352
			OR ( ! $objectCall && ! is_callable($this->methods[$methName]['function']))
353
		)
354
		{
355
			return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
356
		}
357
 
358
		//-------------------------------------
359
		// Checking Methods Signature
360
		//-------------------------------------
361
 
362
		if (isset($this->methods[$methName]['signature']))
363
		{
364
			$sig = $this->methods[$methName]['signature'];
365
			for ($i = 0, $c = count($sig); $i < $c; $i++)
366
			{
367
				$current_sig = $sig[$i];
368
 
369
				if (count($current_sig) === count($m->params)+1)
370
				{
371
					for ($n = 0, $mc = count($m->params); $n < $mc; $n++)
372
					{
373
						$p = $m->params[$n];
374
						$pt = ($p->kindOf() === 'scalar') ? $p->scalarval() : $p->kindOf();
375
 
376
						if ($pt !== $current_sig[$n+1])
377
						{
378
							$pno = $n+1;
379
							$wanted = $current_sig[$n+1];
380
 
381
							return new XML_RPC_Response(0,
382
								$this->xmlrpcerr['incorrect_params'],
383
								$this->xmlrpcstr['incorrect_params'] .
384
								': Wanted '.$wanted.', got '.$pt.' at param '.$pno.')');
385
						}
386
					}
387
				}
388
			}
389
		}
390
 
391
		//-------------------------------------
392
		// Calls the Function
393
		//-------------------------------------
394
 
395
		if ($objectCall === TRUE)
396
		{
397
			if ($method_parts[0] === 'this' && $system_call === TRUE)
398
			{
399
				return call_user_func(array($this, $method_parts[1]), $m);
400
			}
401
			elseif ($this->object === FALSE)
402
			{
2049 lars 403
				return get_instance()->{$method_parts[1]}($m);
68 lars 404
			}
405
			else
406
			{
2049 lars 407
				return $this->object->{$method_parts[1]}($m);
68 lars 408
			}
409
		}
410
		else
411
		{
412
			return call_user_func($this->methods[$methName]['function'], $m);
413
		}
414
	}
415
 
416
	// --------------------------------------------------------------------
417
 
418
	/**
419
	 * Server Function: List Methods
420
	 *
421
	 * @param	mixed
422
	 * @return	object
423
	 */
424
	public function listMethods($m)
425
	{
426
		$v = new XML_RPC_Values();
427
		$output = array();
428
 
429
		foreach ($this->methods as $key => $value)
430
		{
431
			$output[] = new XML_RPC_Values($key, 'string');
432
		}
433
 
434
		foreach ($this->system_methods as $key => $value)
435
		{
436
			$output[] = new XML_RPC_Values($key, 'string');
437
		}
438
 
439
		$v->addArray($output);
440
		return new XML_RPC_Response($v);
441
	}
442
 
443
	// --------------------------------------------------------------------
444
 
445
	/**
446
	 * Server Function: Return Signature for Method
447
	 *
448
	 * @param	mixed
449
	 * @return	object
450
	 */
451
	public function methodSignature($m)
452
	{
453
		$parameters = $m->output_parameters();
454
		$method_name = $parameters[0];
455
 
456
		if (isset($this->methods[$method_name]))
457
		{
458
			if ($this->methods[$method_name]['signature'])
459
			{
460
				$sigs = array();
461
				$signature = $this->methods[$method_name]['signature'];
462
 
463
				for ($i = 0, $c = count($signature); $i < $c; $i++)
464
				{
465
					$cursig = array();
466
					$inSig = $signature[$i];
467
					for ($j = 0, $jc = count($inSig); $j < $jc; $j++)
468
					{
469
						$cursig[]= new XML_RPC_Values($inSig[$j], 'string');
470
					}
471
					$sigs[] = new XML_RPC_Values($cursig, 'array');
472
				}
473
 
474
				return new XML_RPC_Response(new XML_RPC_Values($sigs, 'array'));
475
			}
476
 
477
			return new XML_RPC_Response(new XML_RPC_Values('undef', 'string'));
478
		}
479
 
480
		return new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);
481
	}
482
 
483
	// --------------------------------------------------------------------
484
 
485
	/**
486
	 * Server Function: Doc String for Method
487
	 *
488
	 * @param	mixed
489
	 * @return	object
490
	 */
491
	public function methodHelp($m)
492
	{
493
		$parameters = $m->output_parameters();
494
		$method_name = $parameters[0];
495
 
496
		if (isset($this->methods[$method_name]))
497
		{
498
			$docstring = isset($this->methods[$method_name]['docstring']) ? $this->methods[$method_name]['docstring'] : '';
499
 
500
			return new XML_RPC_Response(new XML_RPC_Values($docstring, 'string'));
501
		}
502
		else
503
		{
504
			return new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);
505
		}
506
	}
507
 
508
	// --------------------------------------------------------------------
509
 
510
	/**
511
	 * Server Function: Multi-call
512
	 *
513
	 * @param	mixed
514
	 * @return	object
515
	 */
516
	public function multicall($m)
517
	{
518
		// Disabled
519
		return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
520
 
521
		$parameters = $m->output_parameters();
522
		$calls = $parameters[0];
523
 
524
		$result = array();
525
 
526
		foreach ($calls as $value)
527
		{
528
			$m = new XML_RPC_Message($value[0]);
529
			$plist = '';
530
 
531
			for ($i = 0, $c = count($value[1]); $i < $c; $i++)
532
			{
533
				$m->addParam(new XML_RPC_Values($value[1][$i], 'string'));
534
			}
535
 
536
			$attempt = $this->_execute($m);
537
 
538
			if ($attempt->faultCode() !== 0)
539
			{
540
				return $attempt;
541
			}
542
 
543
			$result[] = new XML_RPC_Values(array($attempt->value()), 'array');
544
		}
545
 
546
		return new XML_RPC_Response(new XML_RPC_Values($result, 'array'));
547
	}
548
 
549
	// --------------------------------------------------------------------
550
 
551
	/**
552
	 * Multi-call Function: Error Handling
553
	 *
554
	 * @param	mixed
555
	 * @return	object
556
	 */
557
	public function multicall_error($err)
558
	{
559
		$str = is_string($err) ? $this->xmlrpcstr["multicall_${err}"] : $err->faultString();
560
		$code = is_string($err) ? $this->xmlrpcerr["multicall_${err}"] : $err->faultCode();
561
 
562
		$struct['faultCode'] = new XML_RPC_Values($code, 'int');
563
		$struct['faultString'] = new XML_RPC_Values($str, 'string');
564
 
565
		return new XML_RPC_Values($struct, 'struct');
566
	}
567
 
568
	// --------------------------------------------------------------------
569
 
570
	/**
571
	 * Multi-call Function: Processes method
572
	 *
573
	 * @param	mixed
574
	 * @return	object
575
	 */
576
	public function do_multicall($call)
577
	{
578
		if ($call->kindOf() !== 'struct')
579
		{
580
			return $this->multicall_error('notstruct');
581
		}
582
		elseif ( ! $methName = $call->me['struct']['methodName'])
583
		{
584
			return $this->multicall_error('nomethod');
585
		}
586
 
2107 lars 587
		list($scalar_value, $scalar_type) = array(reset($methName->me), key($methName->me));
68 lars 588
		$scalar_type = $scalar_type === $this->xmlrpcI4 ? $this->xmlrpcInt : $scalar_type;
589
 
590
		if ($methName->kindOf() !== 'scalar' OR $scalar_type !== 'string')
591
		{
592
			return $this->multicall_error('notstring');
593
		}
594
		elseif ($scalar_value === 'system.multicall')
595
		{
596
			return $this->multicall_error('recursion');
597
		}
598
		elseif ( ! $params = $call->me['struct']['params'])
599
		{
600
			return $this->multicall_error('noparams');
601
		}
602
		elseif ($params->kindOf() !== 'array')
603
		{
604
			return $this->multicall_error('notarray');
605
		}
606
 
2107 lars 607
		list($b, $a) = array(reset($params->me), key($params->me));
68 lars 608
 
609
		$msg = new XML_RPC_Message($scalar_value);
610
		for ($i = 0, $numParams = count($b); $i < $numParams; $i++)
611
		{
612
			$msg->params[] = $params->me['array'][$i];
613
		}
614
 
615
		$result = $this->_execute($msg);
616
 
617
		if ($result->faultCode() !== 0)
618
		{
619
			return $this->multicall_error($result);
620
		}
621
 
622
		return new XML_RPC_Values(array($result->value()), 'array');
623
	}
624
 
625
}