Subversion-Projekte lars-tiefland.ci

Revision

Revision 2107 | Revision 2254 | 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
 *
2242 lars 9
 * Copyright (c) 2014 - 2018, 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/)
2242 lars 32
 * @copyright	Copyright (c) 2014 - 2018, 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
			}
2242 lars 405
 
406
			return $this->object->{$method_parts[1]}($m);
68 lars 407
		}
2242 lars 408
 
409
		return call_user_func($this->methods[$methName]['function'], $m);
68 lars 410
	}
411
 
412
	// --------------------------------------------------------------------
413
 
414
	/**
415
	 * Server Function: List Methods
416
	 *
417
	 * @param	mixed
418
	 * @return	object
419
	 */
420
	public function listMethods($m)
421
	{
422
		$v = new XML_RPC_Values();
423
		$output = array();
424
 
425
		foreach ($this->methods as $key => $value)
426
		{
427
			$output[] = new XML_RPC_Values($key, 'string');
428
		}
429
 
430
		foreach ($this->system_methods as $key => $value)
431
		{
432
			$output[] = new XML_RPC_Values($key, 'string');
433
		}
434
 
435
		$v->addArray($output);
436
		return new XML_RPC_Response($v);
437
	}
438
 
439
	// --------------------------------------------------------------------
440
 
441
	/**
442
	 * Server Function: Return Signature for Method
443
	 *
444
	 * @param	mixed
445
	 * @return	object
446
	 */
447
	public function methodSignature($m)
448
	{
449
		$parameters = $m->output_parameters();
450
		$method_name = $parameters[0];
451
 
452
		if (isset($this->methods[$method_name]))
453
		{
454
			if ($this->methods[$method_name]['signature'])
455
			{
456
				$sigs = array();
457
				$signature = $this->methods[$method_name]['signature'];
458
 
459
				for ($i = 0, $c = count($signature); $i < $c; $i++)
460
				{
461
					$cursig = array();
462
					$inSig = $signature[$i];
463
					for ($j = 0, $jc = count($inSig); $j < $jc; $j++)
464
					{
465
						$cursig[]= new XML_RPC_Values($inSig[$j], 'string');
466
					}
467
					$sigs[] = new XML_RPC_Values($cursig, 'array');
468
				}
469
 
470
				return new XML_RPC_Response(new XML_RPC_Values($sigs, 'array'));
471
			}
472
 
473
			return new XML_RPC_Response(new XML_RPC_Values('undef', 'string'));
474
		}
475
 
476
		return new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);
477
	}
478
 
479
	// --------------------------------------------------------------------
480
 
481
	/**
482
	 * Server Function: Doc String for Method
483
	 *
484
	 * @param	mixed
485
	 * @return	object
486
	 */
487
	public function methodHelp($m)
488
	{
489
		$parameters = $m->output_parameters();
490
		$method_name = $parameters[0];
491
 
492
		if (isset($this->methods[$method_name]))
493
		{
494
			$docstring = isset($this->methods[$method_name]['docstring']) ? $this->methods[$method_name]['docstring'] : '';
495
 
496
			return new XML_RPC_Response(new XML_RPC_Values($docstring, 'string'));
497
		}
2242 lars 498
 
499
		return new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);
68 lars 500
	}
501
 
502
	// --------------------------------------------------------------------
503
 
504
	/**
505
	 * Server Function: Multi-call
506
	 *
507
	 * @param	mixed
508
	 * @return	object
509
	 */
510
	public function multicall($m)
511
	{
512
		// Disabled
513
		return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
514
 
515
		$parameters = $m->output_parameters();
516
		$calls = $parameters[0];
517
 
518
		$result = array();
519
 
520
		foreach ($calls as $value)
521
		{
522
			$m = new XML_RPC_Message($value[0]);
523
			$plist = '';
524
 
525
			for ($i = 0, $c = count($value[1]); $i < $c; $i++)
526
			{
527
				$m->addParam(new XML_RPC_Values($value[1][$i], 'string'));
528
			}
529
 
530
			$attempt = $this->_execute($m);
531
 
532
			if ($attempt->faultCode() !== 0)
533
			{
534
				return $attempt;
535
			}
536
 
537
			$result[] = new XML_RPC_Values(array($attempt->value()), 'array');
538
		}
539
 
540
		return new XML_RPC_Response(new XML_RPC_Values($result, 'array'));
541
	}
542
 
543
	// --------------------------------------------------------------------
544
 
545
	/**
546
	 * Multi-call Function: Error Handling
547
	 *
548
	 * @param	mixed
549
	 * @return	object
550
	 */
551
	public function multicall_error($err)
552
	{
553
		$str = is_string($err) ? $this->xmlrpcstr["multicall_${err}"] : $err->faultString();
554
		$code = is_string($err) ? $this->xmlrpcerr["multicall_${err}"] : $err->faultCode();
555
 
556
		$struct['faultCode'] = new XML_RPC_Values($code, 'int');
557
		$struct['faultString'] = new XML_RPC_Values($str, 'string');
558
 
559
		return new XML_RPC_Values($struct, 'struct');
560
	}
561
 
562
	// --------------------------------------------------------------------
563
 
564
	/**
565
	 * Multi-call Function: Processes method
566
	 *
567
	 * @param	mixed
568
	 * @return	object
569
	 */
570
	public function do_multicall($call)
571
	{
572
		if ($call->kindOf() !== 'struct')
573
		{
574
			return $this->multicall_error('notstruct');
575
		}
576
		elseif ( ! $methName = $call->me['struct']['methodName'])
577
		{
578
			return $this->multicall_error('nomethod');
579
		}
580
 
2107 lars 581
		list($scalar_value, $scalar_type) = array(reset($methName->me), key($methName->me));
68 lars 582
		$scalar_type = $scalar_type === $this->xmlrpcI4 ? $this->xmlrpcInt : $scalar_type;
583
 
584
		if ($methName->kindOf() !== 'scalar' OR $scalar_type !== 'string')
585
		{
586
			return $this->multicall_error('notstring');
587
		}
588
		elseif ($scalar_value === 'system.multicall')
589
		{
590
			return $this->multicall_error('recursion');
591
		}
592
		elseif ( ! $params = $call->me['struct']['params'])
593
		{
594
			return $this->multicall_error('noparams');
595
		}
596
		elseif ($params->kindOf() !== 'array')
597
		{
598
			return $this->multicall_error('notarray');
599
		}
600
 
2107 lars 601
		list($b, $a) = array(reset($params->me), key($params->me));
68 lars 602
 
603
		$msg = new XML_RPC_Message($scalar_value);
604
		for ($i = 0, $numParams = count($b); $i < $numParams; $i++)
605
		{
606
			$msg->params[] = $params->me['array'][$i];
607
		}
608
 
609
		$result = $this->_execute($msg);
610
 
611
		if ($result->faultCode() !== 0)
612
		{
613
			return $this->multicall_error($result);
614
		}
615
 
616
		return new XML_RPC_Values(array($result->value()), 'array');
617
	}
618
 
619
}