Completed
Push — php51 ( 3c9909...bb9991 )
by Gaetano
10:12
created
lib/xmlrpc_wrappers.inc 1 patch
Indentation   +916 added lines, -916 removed lines patch added patch discarded remove patch
@@ -14,920 +14,920 @@
 block discarded – undo
14 14
  * @todo implement self-parsing of php code for PHP <= 4
15 15
  */
16 16
 
17
-	// requires: xmlrpc.inc
18
-
19
-	/**
20
-	* Given a string defining a php type or phpxmlrpc type (loosely defined: strings
21
-	* accepted come from javadoc blocks), return corresponding phpxmlrpc type.
22
-	* NB: for php 'resource' types returns empty string, since resources cannot be serialized;
23
-	* for php class names returns 'struct', since php objects can be serialized as xmlrpc structs
24
-	* for php arrays always return array, even though arrays sometiles serialize as json structs
25
-	* @param string $phptype
26
-	* @return string
27
-	*/
28
-	function php_2_xmlrpc_type($phptype)
29
-	{
30
-		switch(strtolower($phptype))
31
-		{
32
-			case 'string':
33
-				return $GLOBALS['xmlrpcString'];
34
-			case 'integer':
35
-			case $GLOBALS['xmlrpcInt']: // 'int'
36
-			case $GLOBALS['xmlrpcI4']:
37
-				return $GLOBALS['xmlrpcInt'];
38
-			case 'double':
39
-				return $GLOBALS['xmlrpcDouble'];
40
-			case 'boolean':
41
-				return $GLOBALS['xmlrpcBoolean'];
42
-			case 'array':
43
-				return $GLOBALS['xmlrpcArray'];
44
-			case 'object':
45
-				return $GLOBALS['xmlrpcStruct'];
46
-			case $GLOBALS['xmlrpcBase64']:
47
-			case $GLOBALS['xmlrpcStruct']:
48
-				return strtolower($phptype);
49
-			case 'resource':
50
-				return '';
51
-			default:
52
-				if(class_exists($phptype))
53
-				{
54
-					return $GLOBALS['xmlrpcStruct'];
55
-				}
56
-				else
57
-				{
58
-					// unknown: might be any 'extended' xmlrpc type
59
-					return $GLOBALS['xmlrpcValue'];
60
-				}
61
-		}
62
-	}
63
-
64
-	/**
65
-	* Given a string defining a phpxmlrpc type return corresponding php type.
66
-	* @param string $xmlrpctype
67
-	* @return string
68
-	*/
69
-	function xmlrpc_2_php_type($xmlrpctype)
70
-	{
71
-		switch(strtolower($xmlrpctype))
72
-		{
73
-			case 'base64':
74
-			case 'datetime.iso8601':
75
-			case 'string':
76
-				return $GLOBALS['xmlrpcString'];
77
-			case 'int':
78
-			case 'i4':
79
-				return 'integer';
80
-			case 'struct':
81
-			case 'array':
82
-				return 'array';
83
-			case 'double':
84
-				return 'float';
85
-			case 'undefined':
86
-				return 'mixed';
87
-			case 'boolean':
88
-			case 'null':
89
-			default:
90
-				// unknown: might be any xmlrpc type
91
-				return strtolower($xmlrpctype);
92
-		}
93
-	}
94
-
95
-	/**
96
-	* Given a user-defined PHP function, create a PHP 'wrapper' function that can
97
-	* be exposed as xmlrpc method from an xmlrpc_server object and called from remote
98
-	* clients (as well as its corresponding signature info).
99
-	*
100
-	* Since php is a typeless language, to infer types of input and output parameters,
101
-	* it relies on parsing the javadoc-style comment block associated with the given
102
-	* function. Usage of xmlrpc native types (such as datetime.dateTime.iso8601 and base64)
103
-	* in the @param tag is also allowed, if you need the php function to receive/send
104
-	* data in that particular format (note that base64 encoding/decoding is transparently
105
-	* carried out by the lib, while datetime vals are passed around as strings)
106
-	*
107
-	* Known limitations:
108
-	* - only works for user-defined functions, not for PHP internal functions
109
-	*   (reflection does not support retrieving number/type of params for those)
110
-	* - functions returning php objects will generate special xmlrpc responses:
111
-	*   when the xmlrpc decoding of those responses is carried out by this same lib, using
112
-	*   the appropriate param in php_xmlrpc_decode, the php objects will be rebuilt.
113
-	*   In short: php objects can be serialized, too (except for their resource members),
114
-	*   using this function.
115
-	*   Other libs might choke on the very same xml that will be generated in this case
116
-	*   (i.e. it has a nonstandard attribute on struct element tags)
117
-	* - usage of javadoc @param tags using param names in a different order from the
118
-	*   function prototype is not considered valid (to be fixed?)
119
-	*
120
-	* Note that since rel. 2.0RC3 the preferred method to have the server call 'standard'
121
-	* php functions (ie. functions not expecting a single xmlrpcmsg obj as parameter)
122
-	* is by making use of the functions_parameters_type class member.
123
-	*
124
-	* @param string $funcname the name of the PHP user function to be exposed as xmlrpc method; array($obj, 'methodname') and array('class', 'methodname') are ok too
125
-	* @param string $newfuncname (optional) name for function to be created
126
-	* @param array $extra_options (optional) array of options for conversion. valid values include:
127
-	*        bool  return_source when true, php code w. function definition will be returned, not evaluated
128
-	*        bool  encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
129
-	*        bool  decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
130
-	*        bool  suppress_warnings  remove from produced xml any runtime warnings due to the php function being invoked
131
-	* @return false on error, or an array containing the name of the new php function,
132
-	*         its signature and docs, to be used in the server dispatch map
133
-	*
134
-	* @todo decide how to deal with params passed by ref: bomb out or allow?
135
-	* @todo finish using javadoc info to build method sig if all params are named but out of order
136
-	* @todo add a check for params of 'resource' type
137
-	* @todo add some trigger_errors / error_log when returning false?
138
-	* @todo what to do when the PHP function returns NULL? we are currently returning an empty string value...
139
-	* @todo add an option to suppress php warnings in invocation of user function, similar to server debug level 3?
140
-	* @todo if $newfuncname is empty, we could use create_user_func instead of eval, as it is possibly faster
141
-	* @todo add a verbatim_object_copy parameter to allow avoiding the same obj instance?
142
-	*/
143
-	function wrap_php_function($funcname, $newfuncname='', $extra_options=array())
144
-	{
145
-		$buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
146
-		$prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
147
-		$encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
148
-		$decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
149
-		$catch_warnings = isset($extra_options['suppress_warnings']) && $extra_options['suppress_warnings'] ? '@' : '';
150
-
151
-		$exists = false;
152
-		if (is_string($funcname) && strpos($funcname, '::') !== false)
153
-		{
154
-			$funcname = explode('::', $funcname);
155
-		}
156
-		if(is_array($funcname))
157
-		{
158
-			if(count($funcname) < 2 || (!is_string($funcname[0]) && !is_object($funcname[0])))
159
-			{
160
-				error_log('XML-RPC: syntax for function to be wrapped is wrong');
161
-				return false;
162
-			}
163
-			if(is_string($funcname[0]))
164
-			{
165
-				$plainfuncname = implode('::', $funcname);
166
-			}
167
-			elseif(is_object($funcname[0]))
168
-			{
169
-				$plainfuncname = get_class($funcname[0]) . '->' . $funcname[1];
170
-			}
171
-			$exists = method_exists($funcname[0], $funcname[1]);
172
-		}
173
-		else
174
-		{
175
-			$plainfuncname = $funcname;
176
-			$exists = function_exists($funcname);
177
-		}
178
-
179
-		if(!$exists)
180
-		{
181
-			error_log('XML-RPC: function to be wrapped is not defined: '.$plainfuncname);
182
-			return false;
183
-		}
184
-		else
185
-		{
186
-			// determine name of new php function
187
-			if($newfuncname == '')
188
-			{
189
-				if(is_array($funcname))
190
-				{
191
-					if(is_string($funcname[0]))
192
-						$xmlrpcfuncname = "{$prefix}_".implode('_', $funcname);
193
-					else
194
-						$xmlrpcfuncname = "{$prefix}_".get_class($funcname[0]) . '_' . $funcname[1];
195
-				}
196
-				else
197
-				{
198
-					$xmlrpcfuncname = "{$prefix}_$funcname";
199
-				}
200
-			}
201
-			else
202
-			{
203
-				$xmlrpcfuncname = $newfuncname;
204
-			}
205
-			while($buildit && function_exists($xmlrpcfuncname))
206
-			{
207
-				$xmlrpcfuncname .= 'x';
208
-			}
209
-
210
-			// start to introspect PHP code
211
-			if(is_array($funcname))
212
-			{
213
-				$func = new ReflectionMethod($funcname[0], $funcname[1]);
214
-				if($func->isPrivate())
215
-				{
216
-					error_log('XML-RPC: method to be wrapped is private: '.$plainfuncname);
217
-					return false;
218
-				}
219
-				if($func->isProtected())
220
-				{
221
-					error_log('XML-RPC: method to be wrapped is protected: '.$plainfuncname);
222
-					return false;
223
-				}
224
-	 			if($func->isConstructor())
225
-				{
226
-					error_log('XML-RPC: method to be wrapped is the constructor: '.$plainfuncname);
227
-					return false;
228
-				}
229
-				if($func->isDestructor())
230
-				{
231
-					error_log('XML-RPC: method to be wrapped is the destructor: '.$plainfuncname);
232
-					return false;
233
-				}
234
-				if($func->isAbstract())
235
-				{
236
-					error_log('XML-RPC: method to be wrapped is abstract: '.$plainfuncname);
237
-					return false;
238
-				}
239
-				/// @todo add more checks for static vs. nonstatic?
240
-			}
241
-			else
242
-			{
243
-				$func = new ReflectionFunction($funcname);
244
-			}
245
-			if($func->isInternal())
246
-			{
247
-				// Note: from PHP 5.1.0 onward, we will possibly be able to use invokeargs
248
-				// instead of getparameters to fully reflect internal php functions ?
249
-				error_log('XML-RPC: function to be wrapped is internal: '.$plainfuncname);
250
-				return false;
251
-			}
252
-
253
-			// retrieve parameter names, types and description from javadoc comments
254
-
255
-			// function description
256
-			$desc = '';
257
-			// type of return val: by default 'any'
258
-			$returns = $GLOBALS['xmlrpcValue'];
259
-			// desc of return val
260
-			$returnsDocs = '';
261
-			// type + name of function parameters
262
-			$paramDocs = array();
263
-
264
-			$docs = $func->getDocComment();
265
-			if($docs != '')
266
-			{
267
-				$docs = explode("\n", $docs);
268
-				$i = 0;
269
-				foreach($docs as $doc)
270
-				{
271
-					$doc = trim($doc, " \r\t/*");
272
-					if(strlen($doc) && strpos($doc, '@') !== 0 && !$i)
273
-					{
274
-						if($desc)
275
-						{
276
-							$desc .= "\n";
277
-						}
278
-						$desc .= $doc;
279
-					}
280
-					elseif(strpos($doc, '@param') === 0)
281
-					{
282
-						// syntax: @param type [$name] desc
283
-						if(preg_match('/@param\s+(\S+)(\s+\$\S+)?\s+(.+)/', $doc, $matches))
284
-						{
285
-							if(strpos($matches[1], '|'))
286
-							{
287
-								//$paramDocs[$i]['type'] = explode('|', $matches[1]);
288
-								$paramDocs[$i]['type'] = 'mixed';
289
-							}
290
-							else
291
-							{
292
-								$paramDocs[$i]['type'] = $matches[1];
293
-							}
294
-							$paramDocs[$i]['name'] = trim($matches[2]);
295
-							$paramDocs[$i]['doc'] = $matches[3];
296
-						}
297
-						$i++;
298
-					}
299
-					elseif(strpos($doc, '@return') === 0)
300
-					{
301
-						// syntax: @return type desc
302
-						//$returns = preg_split('/\s+/', $doc);
303
-						if(preg_match('/@return\s+(\S+)\s+(.+)/', $doc, $matches))
304
-						{
305
-							$returns = php_2_xmlrpc_type($matches[1]);
306
-							if(isset($matches[2]))
307
-							{
308
-								$returnsDocs = $matches[2];
309
-							}
310
-						}
311
-					}
312
-				}
313
-			}
314
-
315
-			// execute introspection of actual function prototype
316
-			$params = array();
317
-			$i = 0;
318
-			foreach($func->getParameters() as $paramobj)
319
-			{
320
-				$params[$i] = array();
321
-				$params[$i]['name'] = '$'.$paramobj->getName();
322
-				$params[$i]['isoptional'] = $paramobj->isOptional();
323
-				$i++;
324
-			}
325
-
326
-			// start  building of PHP code to be eval'd
327
-			$innercode = '';
328
-			$i = 0;
329
-			$parsvariations = array();
330
-			$pars = array();
331
-			$pnum = count($params);
332
-			foreach($params as $param)
333
-			{
334
-				if (isset($paramDocs[$i]['name']) && $paramDocs[$i]['name'] && strtolower($paramDocs[$i]['name']) != strtolower($param['name']))
335
-				{
336
-					// param name from phpdoc info does not match param definition!
337
-					$paramDocs[$i]['type'] = 'mixed';
338
-				}
339
-
340
-				if($param['isoptional'])
341
-				{
342
-					// this particular parameter is optional. save as valid previous list of parameters
343
-					$innercode .= "if (\$paramcount > $i) {\n";
344
-					$parsvariations[] = $pars;
345
-				}
346
-				$innercode .= "\$p$i = \$msg->getParam($i);\n";
347
-				if ($decode_php_objects)
348
-				{
349
-					$innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i, array('decode_php_objs'));\n";
350
-				}
351
-				else
352
-				{
353
-					$innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i);\n";
354
-				}
355
-
356
-				$pars[] = "\$p$i";
357
-				$i++;
358
-				if($param['isoptional'])
359
-				{
360
-					$innercode .= "}\n";
361
-				}
362
-				if($i == $pnum)
363
-				{
364
-					// last allowed parameters combination
365
-					$parsvariations[] = $pars;
366
-				}
367
-			}
368
-
369
-			$sigs = array();
370
-			$psigs = array();
371
-			if(count($parsvariations) == 0)
372
-			{
373
-				// only known good synopsis = no parameters
374
-				$parsvariations[] = array();
375
-				$minpars = 0;
376
-			}
377
-			else
378
-			{
379
-				$minpars = count($parsvariations[0]);
380
-			}
381
-
382
-			if($minpars)
383
-			{
384
-				// add to code the check for min params number
385
-				// NB: this check needs to be done BEFORE decoding param values
386
-				$innercode = "\$paramcount = \$msg->getNumParams();\n" .
387
-				"if (\$paramcount < $minpars) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}');\n" . $innercode;
388
-			}
389
-			else
390
-			{
391
-				$innercode = "\$paramcount = \$msg->getNumParams();\n" . $innercode;
392
-			}
393
-
394
-			$innercode .= "\$np = false;\n";
395
-			// since there are no closures in php, if we are given an object instance,
396
-			// we store a pointer to it in a global var...
397
-			if ( is_array($funcname) && is_object($funcname[0]) )
398
-			{
399
-				$GLOBALS['xmlrpcWPFObjHolder'][$xmlrpcfuncname] =& $funcname[0];
400
-				$innercode .= "\$obj =& \$GLOBALS['xmlrpcWPFObjHolder']['$xmlrpcfuncname'];\n";
401
-				$realfuncname = '$obj->'.$funcname[1];
402
-			}
403
-			else
404
-			{
405
-				$realfuncname = $plainfuncname;
406
-			}
407
-			foreach($parsvariations as $pars)
408
-			{
409
-				$innercode .= "if (\$paramcount == " . count($pars) . ") \$retval = {$catch_warnings}$realfuncname(" . implode(',', $pars) . "); else\n";
410
-				// build a 'generic' signature (only use an appropriate return type)
411
-				$sig = array($returns);
412
-				$psig = array($returnsDocs);
413
-				for($i=0; $i < count($pars); $i++)
414
-				{
415
-					if (isset($paramDocs[$i]['type']))
416
-					{
417
-						$sig[] = php_2_xmlrpc_type($paramDocs[$i]['type']);
418
-					}
419
-					else
420
-					{
421
-						$sig[] = $GLOBALS['xmlrpcValue'];
422
-					}
423
-					$psig[] = isset($paramDocs[$i]['doc']) ? $paramDocs[$i]['doc'] : '';
424
-				}
425
-				$sigs[] = $sig;
426
-				$psigs[] = $psig;
427
-			}
428
-			$innercode .= "\$np = true;\n";
429
-			$innercode .= "if (\$np) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}'); else {\n";
430
-			//$innercode .= "if (\$_xmlrpcs_error_occurred) return new xmlrpcresp(0, $GLOBALS['xmlrpcerr']user, \$_xmlrpcs_error_occurred); else\n";
431
-			$innercode .= "if (is_a(\$retval, '{$prefix}resp')) return \$retval; else\n";
432
-			if($returns == $GLOBALS['xmlrpcDateTime'] || $returns == $GLOBALS['xmlrpcBase64'])
433
-			{
434
-				$innercode .= "return new {$prefix}resp(new {$prefix}val(\$retval, '$returns'));";
435
-			}
436
-			else
437
-			{
438
-				if ($encode_php_objects)
439
-					$innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval, array('encode_php_objs')));\n";
440
-				else
441
-					$innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval));\n";
442
-			}
443
-			// shall we exclude functions returning by ref?
444
-			// if($func->returnsReference())
445
-			// 	return false;
446
-			$code = "function $xmlrpcfuncname(\$msg) {\n" . $innercode . "}\n}";
447
-			//print_r($code);
448
-			if ($buildit)
449
-			{
450
-				$allOK = 0;
451
-				eval($code.'$allOK=1;');
452
-				// alternative
453
-				//$xmlrpcfuncname = create_function('$m', $innercode);
454
-
455
-				if(!$allOK)
456
-				{
457
-					error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap php function '.$plainfuncname);
458
-					return false;
459
-				}
460
-			}
461
-
462
-			/// @todo examine if $paramDocs matches $parsvariations and build array for
463
-			/// usage as method signature, plus put together a nice string for docs
464
-
465
-			$ret = array('function' => $xmlrpcfuncname, 'signature' => $sigs, 'docstring' => $desc, 'signature_docs' => $psigs, 'source' => $code);
466
-			return $ret;
467
-		}
468
-	}
469
-
470
-	/**
471
-	* Given a user-defined PHP class or php object, map its methods onto a list of
472
-	* PHP 'wrapper' functions that can be exposed as xmlrpc methods from an xmlrpc_server
473
-	* object and called from remote clients (as well as their corresponding signature info).
474
-	*
475
-	* @param mixed $classname the name of the class whose methods are to be exposed as xmlrpc methods, or an object instance of that class
476
-	* @param array $extra_options see the docs for wrap_php_method for more options
477
-	*        string method_type 'static', 'nonstatic', 'all' and 'auto' (default); the latter will switch between static and non-static depending on wheter $classname is a class name or object instance
478
-	* @return array or false on failure
479
-	*
480
-	* @todo get_class_methods will return both static and non-static methods.
481
-	*       we have to differentiate the action, depending on whether we received a class name or object
482
-	*/
483
-	function wrap_php_class($classname, $extra_options=array())
484
-	{
485
-		$methodfilter = isset($extra_options['method_filter']) ? $extra_options['method_filter'] : '';
486
-		$methodtype = isset($extra_options['method_type']) ? $extra_options['method_type'] : 'auto';
487
-
488
-		$result = array();
489
-		$mlist = get_class_methods($classname);
490
-		foreach($mlist as $mname)
491
-		{
492
-			if ($methodfilter == '' || preg_match($methodfilter, $mname))
493
-			{
494
-				// echo $mlist."\n";
495
-				$func = new ReflectionMethod($classname, $mname);
496
-				if(!$func->isPrivate() && !$func->isProtected() && !$func->isConstructor() && !$func->isDestructor() && !$func->isAbstract())
497
-				{
498
-					if(($func->isStatic && ($methodtype == 'all' || $methodtype == 'static' || ($methodtype == 'auto' && is_string($classname)))) ||
499
-						(!$func->isStatic && ($methodtype == 'all' || $methodtype == 'nonstatic' || ($methodtype == 'auto' && is_object($classname)))))
500
-					{
501
-						$methodwrap = wrap_php_function(array($classname, $mname), '', $extra_options);
502
-						if ( $methodwrap )
503
-						{
504
-							$result[$methodwrap['function']] = $methodwrap['function'];
505
-						}
506
-					}
507
-				}
508
-			}
509
-		}
510
-		return $result;
511
-	}
512
-
513
-	/**
514
-	* Given an xmlrpc client and a method name, register a php wrapper function
515
-	* that will call it and return results using native php types for both
516
-	* params and results. The generated php function will return an xmlrpcresp
517
-	* object for failed xmlrpc calls
518
-	*
519
-	* Known limitations:
520
-	* - server must support system.methodsignature for the wanted xmlrpc method
521
-	* - for methods that expose many signatures, only one can be picked (we
522
-	*   could in principle check if signatures differ only by number of params
523
-	*   and not by type, but it would be more complication than we can spare time)
524
-	* - nested xmlrpc params: the caller of the generated php function has to
525
-	*   encode on its own the params passed to the php function if these are structs
526
-	*   or arrays whose (sub)members include values of type datetime or base64
527
-	*
528
-	* Notes: the connection properties of the given client will be copied
529
-	* and reused for the connection used during the call to the generated
530
-	* php function.
531
-	* Calling the generated php function 'might' be slow: a new xmlrpc client
532
-	* is created on every invocation and an xmlrpc-connection opened+closed.
533
-	* An extra 'debug' param is appended to param list of xmlrpc method, useful
534
-	* for debugging purposes.
535
-	*
536
-	* @param xmlrpc_client $client     an xmlrpc client set up correctly to communicate with target server
537
-	* @param string        $methodname the xmlrpc method to be mapped to a php function
538
-	* @param array         $extra_options array of options that specify conversion details. valid options include
539
-	*        integer       signum      the index of the method signature to use in mapping (if method exposes many sigs)
540
-	*        integer       timeout     timeout (in secs) to be used when executing function/calling remote method
541
-	*        string        protocol    'http' (default), 'http11' or 'https'
542
-	*        string        new_function_name the name of php function to create. If unspecified, lib will pick an appropriate name
543
-	*        string        return_source if true return php code w. function definition instead fo function name
544
-	*        bool          encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
545
-	*        bool          decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
546
-	*        mixed         return_on_fault a php value to be returned when the xmlrpc call fails/returns a fault response (by default the xmlrpcresp object is returned in this case). If a string is used, '%faultCode%' and '%faultString%' tokens will be substituted with actual error values
547
-	*        bool          debug        set it to 1 or 2 to see debug results of querying server for method synopsis
548
-	* @return string                   the name of the generated php function (or false) - OR AN ARRAY...
549
-	*/
550
-	function wrap_xmlrpc_method($client, $methodname, $extra_options=0, $timeout=0, $protocol='', $newfuncname='')
551
-	{
552
-		// mind numbing: let caller use sane calling convention (as per javadoc, 3 params),
553
-		// OR the 2.0 calling convention (no options) - we really love backward compat, don't we?
554
-		if (!is_array($extra_options))
555
-		{
556
-			$signum = $extra_options;
557
-			$extra_options = array();
558
-		}
559
-		else
560
-		{
561
-			$signum = isset($extra_options['signum']) ? (int)$extra_options['signum'] : 0;
562
-			$timeout = isset($extra_options['timeout']) ? (int)$extra_options['timeout'] : 0;
563
-			$protocol = isset($extra_options['protocol']) ? $extra_options['protocol'] : '';
564
-			$newfuncname = isset($extra_options['new_function_name']) ? $extra_options['new_function_name'] : '';
565
-		}
566
-		//$encode_php_objects = in_array('encode_php_objects', $extra_options);
567
-		//$verbatim_client_copy = in_array('simple_client_copy', $extra_options) ? 1 :
568
-		//	in_array('build_class_code', $extra_options) ? 2 : 0;
569
-
570
-		$encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
571
-		$decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
572
-		// it seems like the meaning of 'simple_client_copy' here is swapped wrt client_copy_mode later on...
573
-		$simple_client_copy = isset($extra_options['simple_client_copy']) ? (int)($extra_options['simple_client_copy']) : 0;
574
-		$buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
575
-		$prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
576
-		if (isset($extra_options['return_on_fault']))
577
-		{
578
-			$decode_fault = true;
579
-			$fault_response = $extra_options['return_on_fault'];
580
-		}
581
-		else
582
-		{
583
-			$decode_fault = false;
584
-			$fault_response = '';
585
-		}
586
-		$debug = isset($extra_options['debug']) ? ($extra_options['debug']) : 0;
587
-
588
-		$msgclass = $prefix.'msg';
589
-		$valclass = $prefix.'val';
590
-		$decodefunc = 'php_'.$prefix.'_decode';
591
-
592
-		$msg = new $msgclass('system.methodSignature');
593
-		$msg->addparam(new $valclass($methodname));
594
-		$client->setDebug($debug);
595
-		$response =& $client->send($msg, $timeout, $protocol);
596
-		if($response->faultCode())
597
-		{
598
-			error_log('XML-RPC: could not retrieve method signature from remote server for method '.$methodname);
599
-			return false;
600
-		}
601
-		else
602
-		{
603
-			$msig = $response->value();
604
-			if ($client->return_type != 'phpvals')
605
-			{
606
-				$msig = $decodefunc($msig);
607
-			}
608
-			if(!is_array($msig) || count($msig) <= $signum)
609
-			{
610
-				error_log('XML-RPC: could not retrieve method signature nr.'.$signum.' from remote server for method '.$methodname);
611
-				return false;
612
-			}
613
-			else
614
-			{
615
-				// pick a suitable name for the new function, avoiding collisions
616
-				if($newfuncname != '')
617
-				{
618
-					$xmlrpcfuncname = $newfuncname;
619
-				}
620
-				else
621
-				{
622
-					// take care to insure that methodname is translated to valid
623
-					// php function name
624
-					$xmlrpcfuncname = $prefix.'_'.preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
625
-						array('_', ''), $methodname);
626
-				}
627
-				while($buildit && function_exists($xmlrpcfuncname))
628
-				{
629
-					$xmlrpcfuncname .= 'x';
630
-				}
631
-
632
-				$msig = $msig[$signum];
633
-				$mdesc = '';
634
-				// if in 'offline' mode, get method description too.
635
-				// in online mode, favour speed of operation
636
-				if(!$buildit)
637
-				{
638
-					$msg = new $msgclass('system.methodHelp');
639
-					$msg->addparam(new $valclass($methodname));
640
-					$response =& $client->send($msg, $timeout, $protocol);
641
-					if (!$response->faultCode())
642
-					{
643
-						$mdesc = $response->value();
644
-						if ($client->return_type != 'phpvals')
645
-						{
646
-							$mdesc = $mdesc->scalarval();
647
-						}
648
-					}
649
-				}
650
-
651
-				$results = build_remote_method_wrapper_code($client, $methodname,
652
-					$xmlrpcfuncname, $msig, $mdesc, $timeout, $protocol, $simple_client_copy,
653
-					$prefix, $decode_php_objects, $encode_php_objects, $decode_fault,
654
-					$fault_response);
655
-
656
-				//print_r($code);
657
-				if ($buildit)
658
-				{
659
-					$allOK = 0;
660
-					eval($results['source'].'$allOK=1;');
661
-					// alternative
662
-					//$xmlrpcfuncname = create_function('$m', $innercode);
663
-					if($allOK)
664
-					{
665
-						return $xmlrpcfuncname;
666
-					}
667
-					else
668
-					{
669
-						error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap remote method '.$methodname);
670
-						return false;
671
-					}
672
-				}
673
-				else
674
-				{
675
-					$results['function'] = $xmlrpcfuncname;
676
-					return $results;
677
-				}
678
-			}
679
-		}
680
-	}
681
-
682
-	/**
683
-	* Similar to wrap_xmlrpc_method, but will generate a php class that wraps
684
-	* all xmlrpc methods exposed by the remote server as own methods.
685
-	* For more details see wrap_xmlrpc_method.
686
-	* @param xmlrpc_client $client the client obj all set to query the desired server
687
-	* @param array $extra_options list of options for wrapped code
688
-	* @return mixed false on error, the name of the created class if all ok or an array with code, class name and comments (if the appropriatevoption is set in extra_options)
689
-	*/
690
-	function wrap_xmlrpc_server($client, $extra_options=array())
691
-	{
692
-		$methodfilter = isset($extra_options['method_filter']) ? $extra_options['method_filter'] : '';
693
-		//$signum = isset($extra_options['signum']) ? (int)$extra_options['signum'] : 0;
694
-		$timeout = isset($extra_options['timeout']) ? (int)$extra_options['timeout'] : 0;
695
-		$protocol = isset($extra_options['protocol']) ? $extra_options['protocol'] : '';
696
-		$newclassname = isset($extra_options['new_class_name']) ? $extra_options['new_class_name'] : '';
697
-		$encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
698
-		$decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
699
-		$verbatim_client_copy = isset($extra_options['simple_client_copy']) ? !($extra_options['simple_client_copy']) : true;
700
-		$buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
701
-		$prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
702
-
703
-		$msgclass = $prefix.'msg';
704
-		//$valclass = $prefix.'val';
705
-		$decodefunc = 'php_'.$prefix.'_decode';
706
-
707
-		$msg = new $msgclass('system.listMethods');
708
-		$response =& $client->send($msg, $timeout, $protocol);
709
-		if($response->faultCode())
710
-		{
711
-			error_log('XML-RPC: could not retrieve method list from remote server');
712
-			return false;
713
-		}
714
-		else
715
-		{
716
-			$mlist = $response->value();
717
-			if ($client->return_type != 'phpvals')
718
-			{
719
-				$mlist = $decodefunc($mlist);
720
-			}
721
-			if(!is_array($mlist) || !count($mlist))
722
-			{
723
-				error_log('XML-RPC: could not retrieve meaningful method list from remote server');
724
-				return false;
725
-			}
726
-			else
727
-			{
728
-				// pick a suitable name for the new function, avoiding collisions
729
-				if($newclassname != '')
730
-				{
731
-					$xmlrpcclassname = $newclassname;
732
-				}
733
-				else
734
-				{
735
-					$xmlrpcclassname = $prefix.'_'.preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
736
-						array('_', ''), $client->server).'_client';
737
-				}
738
-				while($buildit && class_exists($xmlrpcclassname))
739
-				{
740
-					$xmlrpcclassname .= 'x';
741
-				}
742
-
743
-				/// @todo add function setdebug() to new class, to enable/disable debugging
744
-				$source = "class $xmlrpcclassname\n{\nvar \$client;\n\n";
745
-				$source .= "function $xmlrpcclassname()\n{\n";
746
-				$source .= build_client_wrapper_code($client, $verbatim_client_copy, $prefix);
747
-				$source .= "\$this->client =& \$client;\n}\n\n";
748
-				$opts = array('simple_client_copy' => 2, 'return_source' => true,
749
-					'timeout' => $timeout, 'protocol' => $protocol,
750
-					'encode_php_objs' => $encode_php_objects, 'prefix' => $prefix,
751
-					'decode_php_objs' => $decode_php_objects
752
-					);
753
-				/// @todo build javadoc for class definition, too
754
-				foreach($mlist as $mname)
755
-				{
756
-					if ($methodfilter == '' || preg_match($methodfilter, $mname))
757
-					{
758
-						$opts['new_function_name'] = preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
759
-							array('_', ''), $mname);
760
-						$methodwrap = wrap_xmlrpc_method($client, $mname, $opts);
761
-						if ($methodwrap)
762
-						{
763
-							if (!$buildit)
764
-							{
765
-								$source .= $methodwrap['docstring'];
766
-							}
767
-							$source .= $methodwrap['source']."\n";
768
-						}
769
-						else
770
-						{
771
-							error_log('XML-RPC: will not create class method to wrap remote method '.$mname);
772
-						}
773
-					}
774
-				}
775
-				$source .= "}\n";
776
-				if ($buildit)
777
-				{
778
-					$allOK = 0;
779
-					eval($source.'$allOK=1;');
780
-					// alternative
781
-					//$xmlrpcfuncname = create_function('$m', $innercode);
782
-					if($allOK)
783
-					{
784
-						return $xmlrpcclassname;
785
-					}
786
-					else
787
-					{
788
-						error_log('XML-RPC: could not create class '.$xmlrpcclassname.' to wrap remote server '.$client->server);
789
-						return false;
790
-					}
791
-				}
792
-				else
793
-				{
794
-					return array('class' => $xmlrpcclassname, 'code' => $source, 'docstring' => '');
795
-				}
796
-			}
797
-		}
798
-	}
799
-
800
-	/**
801
-	* Given the necessary info, build php code that creates a new function to
802
-	* invoke a remote xmlrpc method.
803
-	* Take care that no full checking of input parameters is done to ensure that
804
-	* valid php code is emitted.
805
-	* Note: real spaghetti code follows...
806
-	* @access private
807
-	*/
808
-	function build_remote_method_wrapper_code($client, $methodname, $xmlrpcfuncname,
809
-		$msig, $mdesc='', $timeout=0, $protocol='', $client_copy_mode=0, $prefix='xmlrpc',
810
-		$decode_php_objects=false, $encode_php_objects=false, $decode_fault=false,
811
-		$fault_response='')
812
-	{
813
-		$code = "function $xmlrpcfuncname (";
814
-		if ($client_copy_mode < 2)
815
-		{
816
-			// client copy mode 0 or 1 == partial / full client copy in emitted code
817
-			$innercode = build_client_wrapper_code($client, $client_copy_mode, $prefix);
818
-			$innercode .= "\$client->setDebug(\$debug);\n";
819
-			$this_ = '';
820
-		}
821
-		else
822
-		{
823
-			// client copy mode 2 == no client copy in emitted code
824
-			$innercode = '';
825
-			$this_ = 'this->';
826
-		}
827
-		$innercode .= "\$msg = new {$prefix}msg('$methodname');\n";
828
-
829
-		if ($mdesc != '')
830
-		{
831
-			// take care that PHP comment is not terminated unwillingly by method description
832
-			$mdesc = "/**\n* ".str_replace('*/', '* /', $mdesc)."\n";
833
-		}
834
-		else
835
-		{
836
-			$mdesc = "/**\nFunction $xmlrpcfuncname\n";
837
-		}
838
-
839
-		// param parsing
840
-		$plist = array();
841
-		$pcount = count($msig);
842
-		for($i = 1; $i < $pcount; $i++)
843
-		{
844
-			$plist[] = "\$p$i";
845
-			$ptype = $msig[$i];
846
-			if($ptype == 'i4' || $ptype == 'int' || $ptype == 'boolean' || $ptype == 'double' ||
847
-				$ptype == 'string' || $ptype == 'dateTime.iso8601' || $ptype == 'base64' || $ptype == 'null')
848
-			{
849
-				// only build directly xmlrpcvals when type is known and scalar
850
-				$innercode .= "\$p$i = new {$prefix}val(\$p$i, '$ptype');\n";
851
-			}
852
-			else
853
-			{
854
-				if ($encode_php_objects)
855
-				{
856
-					$innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i, array('encode_php_objs'));\n";
857
-				}
858
-				else
859
-				{
860
-					$innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i);\n";
861
-				}
862
-			}
863
-			$innercode .= "\$msg->addparam(\$p$i);\n";
864
-			$mdesc .= '* @param '.xmlrpc_2_php_type($ptype)." \$p$i\n";
865
-		}
866
-		if ($client_copy_mode < 2)
867
-		{
868
-			$plist[] = '$debug=0';
869
-			$mdesc .= "* @param int \$debug when 1 (or 2) will enable debugging of the underlying {$prefix} call (defaults to 0)\n";
870
-		}
871
-		$plist = implode(', ', $plist);
872
-		$mdesc .= '* @return '.xmlrpc_2_php_type($msig[0])." (or an {$prefix}resp obj instance if call fails)\n*/\n";
873
-
874
-		$innercode .= "\$res =& \${$this_}client->send(\$msg, $timeout, '$protocol');\n";
875
-		if ($decode_fault)
876
-		{
877
-			if (is_string($fault_response) && ((strpos($fault_response, '%faultCode%') !== false) || (strpos($fault_response, '%faultString%') !== false)))
878
-			{
879
-				$respcode = "str_replace(array('%faultCode%', '%faultString%'), array(\$res->faultCode(), \$res->faultString()), '".str_replace("'", "''", $fault_response)."')";
880
-			}
881
-			else
882
-			{
883
-				$respcode = var_export($fault_response, true);
884
-			}
885
-		}
886
-		else
887
-		{
888
-			$respcode = '$res';
889
-		}
890
-		if ($decode_php_objects)
891
-		{
892
-			$innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value(), array('decode_php_objs'));";
893
-		}
894
-		else
895
-		{
896
-			$innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value());";
897
-		}
898
-
899
-		$code = $code . $plist. ") {\n" . $innercode . "\n}\n";
900
-
901
-		return array('source' => $code, 'docstring' => $mdesc);
902
-	}
903
-
904
-	/**
905
-	* Given necessary info, generate php code that will rebuild a client object
906
-	* Take care that no full checking of input parameters is done to ensure that
907
-	* valid php code is emitted.
908
-	* @access private
909
-	*/
910
-	function build_client_wrapper_code($client, $verbatim_client_copy, $prefix='xmlrpc')
911
-	{
912
-		$code = "\$client = new {$prefix}_client('".str_replace("'", "\'", $client->path).
913
-			"', '" . str_replace("'", "\'", $client->server) . "', $client->port);\n";
914
-
915
-		// copy all client fields to the client that will be generated runtime
916
-		// (this provides for future expansion or subclassing of client obj)
917
-		if ($verbatim_client_copy)
918
-		{
919
-			foreach($client as $fld => $val)
920
-			{
921
-				if($fld != 'debug' && $fld != 'return_type')
922
-				{
923
-					$val = var_export($val, true);
924
-					$code .= "\$client->$fld = $val;\n";
925
-				}
926
-			}
927
-		}
928
-		// only make sure that client always returns the correct data type
929
-		$code .= "\$client->return_type = '{$prefix}vals';\n";
930
-		//$code .= "\$client->setDebug(\$debug);\n";
931
-		return $code;
932
-	}
17
+    // requires: xmlrpc.inc
18
+
19
+    /**
20
+     * Given a string defining a php type or phpxmlrpc type (loosely defined: strings
21
+     * accepted come from javadoc blocks), return corresponding phpxmlrpc type.
22
+     * NB: for php 'resource' types returns empty string, since resources cannot be serialized;
23
+     * for php class names returns 'struct', since php objects can be serialized as xmlrpc structs
24
+     * for php arrays always return array, even though arrays sometiles serialize as json structs
25
+     * @param string $phptype
26
+     * @return string
27
+     */
28
+    function php_2_xmlrpc_type($phptype)
29
+    {
30
+        switch(strtolower($phptype))
31
+        {
32
+            case 'string':
33
+                return $GLOBALS['xmlrpcString'];
34
+            case 'integer':
35
+            case $GLOBALS['xmlrpcInt']: // 'int'
36
+            case $GLOBALS['xmlrpcI4']:
37
+                return $GLOBALS['xmlrpcInt'];
38
+            case 'double':
39
+                return $GLOBALS['xmlrpcDouble'];
40
+            case 'boolean':
41
+                return $GLOBALS['xmlrpcBoolean'];
42
+            case 'array':
43
+                return $GLOBALS['xmlrpcArray'];
44
+            case 'object':
45
+                return $GLOBALS['xmlrpcStruct'];
46
+            case $GLOBALS['xmlrpcBase64']:
47
+            case $GLOBALS['xmlrpcStruct']:
48
+                return strtolower($phptype);
49
+            case 'resource':
50
+                return '';
51
+            default:
52
+                if(class_exists($phptype))
53
+                {
54
+                    return $GLOBALS['xmlrpcStruct'];
55
+                }
56
+                else
57
+                {
58
+                    // unknown: might be any 'extended' xmlrpc type
59
+                    return $GLOBALS['xmlrpcValue'];
60
+                }
61
+        }
62
+    }
63
+
64
+    /**
65
+     * Given a string defining a phpxmlrpc type return corresponding php type.
66
+     * @param string $xmlrpctype
67
+     * @return string
68
+     */
69
+    function xmlrpc_2_php_type($xmlrpctype)
70
+    {
71
+        switch(strtolower($xmlrpctype))
72
+        {
73
+            case 'base64':
74
+            case 'datetime.iso8601':
75
+            case 'string':
76
+                return $GLOBALS['xmlrpcString'];
77
+            case 'int':
78
+            case 'i4':
79
+                return 'integer';
80
+            case 'struct':
81
+            case 'array':
82
+                return 'array';
83
+            case 'double':
84
+                return 'float';
85
+            case 'undefined':
86
+                return 'mixed';
87
+            case 'boolean':
88
+            case 'null':
89
+            default:
90
+                // unknown: might be any xmlrpc type
91
+                return strtolower($xmlrpctype);
92
+        }
93
+    }
94
+
95
+    /**
96
+     * Given a user-defined PHP function, create a PHP 'wrapper' function that can
97
+     * be exposed as xmlrpc method from an xmlrpc_server object and called from remote
98
+     * clients (as well as its corresponding signature info).
99
+     *
100
+     * Since php is a typeless language, to infer types of input and output parameters,
101
+     * it relies on parsing the javadoc-style comment block associated with the given
102
+     * function. Usage of xmlrpc native types (such as datetime.dateTime.iso8601 and base64)
103
+     * in the @param tag is also allowed, if you need the php function to receive/send
104
+     * data in that particular format (note that base64 encoding/decoding is transparently
105
+     * carried out by the lib, while datetime vals are passed around as strings)
106
+     *
107
+     * Known limitations:
108
+     * - only works for user-defined functions, not for PHP internal functions
109
+     *   (reflection does not support retrieving number/type of params for those)
110
+     * - functions returning php objects will generate special xmlrpc responses:
111
+     *   when the xmlrpc decoding of those responses is carried out by this same lib, using
112
+     *   the appropriate param in php_xmlrpc_decode, the php objects will be rebuilt.
113
+     *   In short: php objects can be serialized, too (except for their resource members),
114
+     *   using this function.
115
+     *   Other libs might choke on the very same xml that will be generated in this case
116
+     *   (i.e. it has a nonstandard attribute on struct element tags)
117
+     * - usage of javadoc @param tags using param names in a different order from the
118
+     *   function prototype is not considered valid (to be fixed?)
119
+     *
120
+     * Note that since rel. 2.0RC3 the preferred method to have the server call 'standard'
121
+     * php functions (ie. functions not expecting a single xmlrpcmsg obj as parameter)
122
+     * is by making use of the functions_parameters_type class member.
123
+     *
124
+     * @param string $funcname the name of the PHP user function to be exposed as xmlrpc method; array($obj, 'methodname') and array('class', 'methodname') are ok too
125
+     * @param string $newfuncname (optional) name for function to be created
126
+     * @param array $extra_options (optional) array of options for conversion. valid values include:
127
+     *        bool  return_source when true, php code w. function definition will be returned, not evaluated
128
+     *        bool  encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
129
+     *        bool  decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
130
+     *        bool  suppress_warnings  remove from produced xml any runtime warnings due to the php function being invoked
131
+     * @return false on error, or an array containing the name of the new php function,
132
+     *         its signature and docs, to be used in the server dispatch map
133
+     *
134
+     * @todo decide how to deal with params passed by ref: bomb out or allow?
135
+     * @todo finish using javadoc info to build method sig if all params are named but out of order
136
+     * @todo add a check for params of 'resource' type
137
+     * @todo add some trigger_errors / error_log when returning false?
138
+     * @todo what to do when the PHP function returns NULL? we are currently returning an empty string value...
139
+     * @todo add an option to suppress php warnings in invocation of user function, similar to server debug level 3?
140
+     * @todo if $newfuncname is empty, we could use create_user_func instead of eval, as it is possibly faster
141
+     * @todo add a verbatim_object_copy parameter to allow avoiding the same obj instance?
142
+     */
143
+    function wrap_php_function($funcname, $newfuncname='', $extra_options=array())
144
+    {
145
+        $buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
146
+        $prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
147
+        $encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
148
+        $decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
149
+        $catch_warnings = isset($extra_options['suppress_warnings']) && $extra_options['suppress_warnings'] ? '@' : '';
150
+
151
+        $exists = false;
152
+        if (is_string($funcname) && strpos($funcname, '::') !== false)
153
+        {
154
+            $funcname = explode('::', $funcname);
155
+        }
156
+        if(is_array($funcname))
157
+        {
158
+            if(count($funcname) < 2 || (!is_string($funcname[0]) && !is_object($funcname[0])))
159
+            {
160
+                error_log('XML-RPC: syntax for function to be wrapped is wrong');
161
+                return false;
162
+            }
163
+            if(is_string($funcname[0]))
164
+            {
165
+                $plainfuncname = implode('::', $funcname);
166
+            }
167
+            elseif(is_object($funcname[0]))
168
+            {
169
+                $plainfuncname = get_class($funcname[0]) . '->' . $funcname[1];
170
+            }
171
+            $exists = method_exists($funcname[0], $funcname[1]);
172
+        }
173
+        else
174
+        {
175
+            $plainfuncname = $funcname;
176
+            $exists = function_exists($funcname);
177
+        }
178
+
179
+        if(!$exists)
180
+        {
181
+            error_log('XML-RPC: function to be wrapped is not defined: '.$plainfuncname);
182
+            return false;
183
+        }
184
+        else
185
+        {
186
+            // determine name of new php function
187
+            if($newfuncname == '')
188
+            {
189
+                if(is_array($funcname))
190
+                {
191
+                    if(is_string($funcname[0]))
192
+                        $xmlrpcfuncname = "{$prefix}_".implode('_', $funcname);
193
+                    else
194
+                        $xmlrpcfuncname = "{$prefix}_".get_class($funcname[0]) . '_' . $funcname[1];
195
+                }
196
+                else
197
+                {
198
+                    $xmlrpcfuncname = "{$prefix}_$funcname";
199
+                }
200
+            }
201
+            else
202
+            {
203
+                $xmlrpcfuncname = $newfuncname;
204
+            }
205
+            while($buildit && function_exists($xmlrpcfuncname))
206
+            {
207
+                $xmlrpcfuncname .= 'x';
208
+            }
209
+
210
+            // start to introspect PHP code
211
+            if(is_array($funcname))
212
+            {
213
+                $func = new ReflectionMethod($funcname[0], $funcname[1]);
214
+                if($func->isPrivate())
215
+                {
216
+                    error_log('XML-RPC: method to be wrapped is private: '.$plainfuncname);
217
+                    return false;
218
+                }
219
+                if($func->isProtected())
220
+                {
221
+                    error_log('XML-RPC: method to be wrapped is protected: '.$plainfuncname);
222
+                    return false;
223
+                }
224
+                    if($func->isConstructor())
225
+                {
226
+                    error_log('XML-RPC: method to be wrapped is the constructor: '.$plainfuncname);
227
+                    return false;
228
+                }
229
+                if($func->isDestructor())
230
+                {
231
+                    error_log('XML-RPC: method to be wrapped is the destructor: '.$plainfuncname);
232
+                    return false;
233
+                }
234
+                if($func->isAbstract())
235
+                {
236
+                    error_log('XML-RPC: method to be wrapped is abstract: '.$plainfuncname);
237
+                    return false;
238
+                }
239
+                /// @todo add more checks for static vs. nonstatic?
240
+            }
241
+            else
242
+            {
243
+                $func = new ReflectionFunction($funcname);
244
+            }
245
+            if($func->isInternal())
246
+            {
247
+                // Note: from PHP 5.1.0 onward, we will possibly be able to use invokeargs
248
+                // instead of getparameters to fully reflect internal php functions ?
249
+                error_log('XML-RPC: function to be wrapped is internal: '.$plainfuncname);
250
+                return false;
251
+            }
252
+
253
+            // retrieve parameter names, types and description from javadoc comments
254
+
255
+            // function description
256
+            $desc = '';
257
+            // type of return val: by default 'any'
258
+            $returns = $GLOBALS['xmlrpcValue'];
259
+            // desc of return val
260
+            $returnsDocs = '';
261
+            // type + name of function parameters
262
+            $paramDocs = array();
263
+
264
+            $docs = $func->getDocComment();
265
+            if($docs != '')
266
+            {
267
+                $docs = explode("\n", $docs);
268
+                $i = 0;
269
+                foreach($docs as $doc)
270
+                {
271
+                    $doc = trim($doc, " \r\t/*");
272
+                    if(strlen($doc) && strpos($doc, '@') !== 0 && !$i)
273
+                    {
274
+                        if($desc)
275
+                        {
276
+                            $desc .= "\n";
277
+                        }
278
+                        $desc .= $doc;
279
+                    }
280
+                    elseif(strpos($doc, '@param') === 0)
281
+                    {
282
+                        // syntax: @param type [$name] desc
283
+                        if(preg_match('/@param\s+(\S+)(\s+\$\S+)?\s+(.+)/', $doc, $matches))
284
+                        {
285
+                            if(strpos($matches[1], '|'))
286
+                            {
287
+                                //$paramDocs[$i]['type'] = explode('|', $matches[1]);
288
+                                $paramDocs[$i]['type'] = 'mixed';
289
+                            }
290
+                            else
291
+                            {
292
+                                $paramDocs[$i]['type'] = $matches[1];
293
+                            }
294
+                            $paramDocs[$i]['name'] = trim($matches[2]);
295
+                            $paramDocs[$i]['doc'] = $matches[3];
296
+                        }
297
+                        $i++;
298
+                    }
299
+                    elseif(strpos($doc, '@return') === 0)
300
+                    {
301
+                        // syntax: @return type desc
302
+                        //$returns = preg_split('/\s+/', $doc);
303
+                        if(preg_match('/@return\s+(\S+)\s+(.+)/', $doc, $matches))
304
+                        {
305
+                            $returns = php_2_xmlrpc_type($matches[1]);
306
+                            if(isset($matches[2]))
307
+                            {
308
+                                $returnsDocs = $matches[2];
309
+                            }
310
+                        }
311
+                    }
312
+                }
313
+            }
314
+
315
+            // execute introspection of actual function prototype
316
+            $params = array();
317
+            $i = 0;
318
+            foreach($func->getParameters() as $paramobj)
319
+            {
320
+                $params[$i] = array();
321
+                $params[$i]['name'] = '$'.$paramobj->getName();
322
+                $params[$i]['isoptional'] = $paramobj->isOptional();
323
+                $i++;
324
+            }
325
+
326
+            // start  building of PHP code to be eval'd
327
+            $innercode = '';
328
+            $i = 0;
329
+            $parsvariations = array();
330
+            $pars = array();
331
+            $pnum = count($params);
332
+            foreach($params as $param)
333
+            {
334
+                if (isset($paramDocs[$i]['name']) && $paramDocs[$i]['name'] && strtolower($paramDocs[$i]['name']) != strtolower($param['name']))
335
+                {
336
+                    // param name from phpdoc info does not match param definition!
337
+                    $paramDocs[$i]['type'] = 'mixed';
338
+                }
339
+
340
+                if($param['isoptional'])
341
+                {
342
+                    // this particular parameter is optional. save as valid previous list of parameters
343
+                    $innercode .= "if (\$paramcount > $i) {\n";
344
+                    $parsvariations[] = $pars;
345
+                }
346
+                $innercode .= "\$p$i = \$msg->getParam($i);\n";
347
+                if ($decode_php_objects)
348
+                {
349
+                    $innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i, array('decode_php_objs'));\n";
350
+                }
351
+                else
352
+                {
353
+                    $innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i);\n";
354
+                }
355
+
356
+                $pars[] = "\$p$i";
357
+                $i++;
358
+                if($param['isoptional'])
359
+                {
360
+                    $innercode .= "}\n";
361
+                }
362
+                if($i == $pnum)
363
+                {
364
+                    // last allowed parameters combination
365
+                    $parsvariations[] = $pars;
366
+                }
367
+            }
368
+
369
+            $sigs = array();
370
+            $psigs = array();
371
+            if(count($parsvariations) == 0)
372
+            {
373
+                // only known good synopsis = no parameters
374
+                $parsvariations[] = array();
375
+                $minpars = 0;
376
+            }
377
+            else
378
+            {
379
+                $minpars = count($parsvariations[0]);
380
+            }
381
+
382
+            if($minpars)
383
+            {
384
+                // add to code the check for min params number
385
+                // NB: this check needs to be done BEFORE decoding param values
386
+                $innercode = "\$paramcount = \$msg->getNumParams();\n" .
387
+                "if (\$paramcount < $minpars) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}');\n" . $innercode;
388
+            }
389
+            else
390
+            {
391
+                $innercode = "\$paramcount = \$msg->getNumParams();\n" . $innercode;
392
+            }
393
+
394
+            $innercode .= "\$np = false;\n";
395
+            // since there are no closures in php, if we are given an object instance,
396
+            // we store a pointer to it in a global var...
397
+            if ( is_array($funcname) && is_object($funcname[0]) )
398
+            {
399
+                $GLOBALS['xmlrpcWPFObjHolder'][$xmlrpcfuncname] =& $funcname[0];
400
+                $innercode .= "\$obj =& \$GLOBALS['xmlrpcWPFObjHolder']['$xmlrpcfuncname'];\n";
401
+                $realfuncname = '$obj->'.$funcname[1];
402
+            }
403
+            else
404
+            {
405
+                $realfuncname = $plainfuncname;
406
+            }
407
+            foreach($parsvariations as $pars)
408
+            {
409
+                $innercode .= "if (\$paramcount == " . count($pars) . ") \$retval = {$catch_warnings}$realfuncname(" . implode(',', $pars) . "); else\n";
410
+                // build a 'generic' signature (only use an appropriate return type)
411
+                $sig = array($returns);
412
+                $psig = array($returnsDocs);
413
+                for($i=0; $i < count($pars); $i++)
414
+                {
415
+                    if (isset($paramDocs[$i]['type']))
416
+                    {
417
+                        $sig[] = php_2_xmlrpc_type($paramDocs[$i]['type']);
418
+                    }
419
+                    else
420
+                    {
421
+                        $sig[] = $GLOBALS['xmlrpcValue'];
422
+                    }
423
+                    $psig[] = isset($paramDocs[$i]['doc']) ? $paramDocs[$i]['doc'] : '';
424
+                }
425
+                $sigs[] = $sig;
426
+                $psigs[] = $psig;
427
+            }
428
+            $innercode .= "\$np = true;\n";
429
+            $innercode .= "if (\$np) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}'); else {\n";
430
+            //$innercode .= "if (\$_xmlrpcs_error_occurred) return new xmlrpcresp(0, $GLOBALS['xmlrpcerr']user, \$_xmlrpcs_error_occurred); else\n";
431
+            $innercode .= "if (is_a(\$retval, '{$prefix}resp')) return \$retval; else\n";
432
+            if($returns == $GLOBALS['xmlrpcDateTime'] || $returns == $GLOBALS['xmlrpcBase64'])
433
+            {
434
+                $innercode .= "return new {$prefix}resp(new {$prefix}val(\$retval, '$returns'));";
435
+            }
436
+            else
437
+            {
438
+                if ($encode_php_objects)
439
+                    $innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval, array('encode_php_objs')));\n";
440
+                else
441
+                    $innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval));\n";
442
+            }
443
+            // shall we exclude functions returning by ref?
444
+            // if($func->returnsReference())
445
+            // 	return false;
446
+            $code = "function $xmlrpcfuncname(\$msg) {\n" . $innercode . "}\n}";
447
+            //print_r($code);
448
+            if ($buildit)
449
+            {
450
+                $allOK = 0;
451
+                eval($code.'$allOK=1;');
452
+                // alternative
453
+                //$xmlrpcfuncname = create_function('$m', $innercode);
454
+
455
+                if(!$allOK)
456
+                {
457
+                    error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap php function '.$plainfuncname);
458
+                    return false;
459
+                }
460
+            }
461
+
462
+            /// @todo examine if $paramDocs matches $parsvariations and build array for
463
+            /// usage as method signature, plus put together a nice string for docs
464
+
465
+            $ret = array('function' => $xmlrpcfuncname, 'signature' => $sigs, 'docstring' => $desc, 'signature_docs' => $psigs, 'source' => $code);
466
+            return $ret;
467
+        }
468
+    }
469
+
470
+    /**
471
+     * Given a user-defined PHP class or php object, map its methods onto a list of
472
+     * PHP 'wrapper' functions that can be exposed as xmlrpc methods from an xmlrpc_server
473
+     * object and called from remote clients (as well as their corresponding signature info).
474
+     *
475
+     * @param mixed $classname the name of the class whose methods are to be exposed as xmlrpc methods, or an object instance of that class
476
+     * @param array $extra_options see the docs for wrap_php_method for more options
477
+     *        string method_type 'static', 'nonstatic', 'all' and 'auto' (default); the latter will switch between static and non-static depending on wheter $classname is a class name or object instance
478
+     * @return array or false on failure
479
+     *
480
+     * @todo get_class_methods will return both static and non-static methods.
481
+     *       we have to differentiate the action, depending on whether we received a class name or object
482
+     */
483
+    function wrap_php_class($classname, $extra_options=array())
484
+    {
485
+        $methodfilter = isset($extra_options['method_filter']) ? $extra_options['method_filter'] : '';
486
+        $methodtype = isset($extra_options['method_type']) ? $extra_options['method_type'] : 'auto';
487
+
488
+        $result = array();
489
+        $mlist = get_class_methods($classname);
490
+        foreach($mlist as $mname)
491
+        {
492
+            if ($methodfilter == '' || preg_match($methodfilter, $mname))
493
+            {
494
+                // echo $mlist."\n";
495
+                $func = new ReflectionMethod($classname, $mname);
496
+                if(!$func->isPrivate() && !$func->isProtected() && !$func->isConstructor() && !$func->isDestructor() && !$func->isAbstract())
497
+                {
498
+                    if(($func->isStatic && ($methodtype == 'all' || $methodtype == 'static' || ($methodtype == 'auto' && is_string($classname)))) ||
499
+                        (!$func->isStatic && ($methodtype == 'all' || $methodtype == 'nonstatic' || ($methodtype == 'auto' && is_object($classname)))))
500
+                    {
501
+                        $methodwrap = wrap_php_function(array($classname, $mname), '', $extra_options);
502
+                        if ( $methodwrap )
503
+                        {
504
+                            $result[$methodwrap['function']] = $methodwrap['function'];
505
+                        }
506
+                    }
507
+                }
508
+            }
509
+        }
510
+        return $result;
511
+    }
512
+
513
+    /**
514
+     * Given an xmlrpc client and a method name, register a php wrapper function
515
+     * that will call it and return results using native php types for both
516
+     * params and results. The generated php function will return an xmlrpcresp
517
+     * object for failed xmlrpc calls
518
+     *
519
+     * Known limitations:
520
+     * - server must support system.methodsignature for the wanted xmlrpc method
521
+     * - for methods that expose many signatures, only one can be picked (we
522
+     *   could in principle check if signatures differ only by number of params
523
+     *   and not by type, but it would be more complication than we can spare time)
524
+     * - nested xmlrpc params: the caller of the generated php function has to
525
+     *   encode on its own the params passed to the php function if these are structs
526
+     *   or arrays whose (sub)members include values of type datetime or base64
527
+     *
528
+     * Notes: the connection properties of the given client will be copied
529
+     * and reused for the connection used during the call to the generated
530
+     * php function.
531
+     * Calling the generated php function 'might' be slow: a new xmlrpc client
532
+     * is created on every invocation and an xmlrpc-connection opened+closed.
533
+     * An extra 'debug' param is appended to param list of xmlrpc method, useful
534
+     * for debugging purposes.
535
+     *
536
+     * @param xmlrpc_client $client     an xmlrpc client set up correctly to communicate with target server
537
+     * @param string        $methodname the xmlrpc method to be mapped to a php function
538
+     * @param array         $extra_options array of options that specify conversion details. valid options include
539
+     *        integer       signum      the index of the method signature to use in mapping (if method exposes many sigs)
540
+     *        integer       timeout     timeout (in secs) to be used when executing function/calling remote method
541
+     *        string        protocol    'http' (default), 'http11' or 'https'
542
+     *        string        new_function_name the name of php function to create. If unspecified, lib will pick an appropriate name
543
+     *        string        return_source if true return php code w. function definition instead fo function name
544
+     *        bool          encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
545
+     *        bool          decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
546
+     *        mixed         return_on_fault a php value to be returned when the xmlrpc call fails/returns a fault response (by default the xmlrpcresp object is returned in this case). If a string is used, '%faultCode%' and '%faultString%' tokens will be substituted with actual error values
547
+     *        bool          debug        set it to 1 or 2 to see debug results of querying server for method synopsis
548
+     * @return string                   the name of the generated php function (or false) - OR AN ARRAY...
549
+     */
550
+    function wrap_xmlrpc_method($client, $methodname, $extra_options=0, $timeout=0, $protocol='', $newfuncname='')
551
+    {
552
+        // mind numbing: let caller use sane calling convention (as per javadoc, 3 params),
553
+        // OR the 2.0 calling convention (no options) - we really love backward compat, don't we?
554
+        if (!is_array($extra_options))
555
+        {
556
+            $signum = $extra_options;
557
+            $extra_options = array();
558
+        }
559
+        else
560
+        {
561
+            $signum = isset($extra_options['signum']) ? (int)$extra_options['signum'] : 0;
562
+            $timeout = isset($extra_options['timeout']) ? (int)$extra_options['timeout'] : 0;
563
+            $protocol = isset($extra_options['protocol']) ? $extra_options['protocol'] : '';
564
+            $newfuncname = isset($extra_options['new_function_name']) ? $extra_options['new_function_name'] : '';
565
+        }
566
+        //$encode_php_objects = in_array('encode_php_objects', $extra_options);
567
+        //$verbatim_client_copy = in_array('simple_client_copy', $extra_options) ? 1 :
568
+        //	in_array('build_class_code', $extra_options) ? 2 : 0;
569
+
570
+        $encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
571
+        $decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
572
+        // it seems like the meaning of 'simple_client_copy' here is swapped wrt client_copy_mode later on...
573
+        $simple_client_copy = isset($extra_options['simple_client_copy']) ? (int)($extra_options['simple_client_copy']) : 0;
574
+        $buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
575
+        $prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
576
+        if (isset($extra_options['return_on_fault']))
577
+        {
578
+            $decode_fault = true;
579
+            $fault_response = $extra_options['return_on_fault'];
580
+        }
581
+        else
582
+        {
583
+            $decode_fault = false;
584
+            $fault_response = '';
585
+        }
586
+        $debug = isset($extra_options['debug']) ? ($extra_options['debug']) : 0;
587
+
588
+        $msgclass = $prefix.'msg';
589
+        $valclass = $prefix.'val';
590
+        $decodefunc = 'php_'.$prefix.'_decode';
591
+
592
+        $msg = new $msgclass('system.methodSignature');
593
+        $msg->addparam(new $valclass($methodname));
594
+        $client->setDebug($debug);
595
+        $response =& $client->send($msg, $timeout, $protocol);
596
+        if($response->faultCode())
597
+        {
598
+            error_log('XML-RPC: could not retrieve method signature from remote server for method '.$methodname);
599
+            return false;
600
+        }
601
+        else
602
+        {
603
+            $msig = $response->value();
604
+            if ($client->return_type != 'phpvals')
605
+            {
606
+                $msig = $decodefunc($msig);
607
+            }
608
+            if(!is_array($msig) || count($msig) <= $signum)
609
+            {
610
+                error_log('XML-RPC: could not retrieve method signature nr.'.$signum.' from remote server for method '.$methodname);
611
+                return false;
612
+            }
613
+            else
614
+            {
615
+                // pick a suitable name for the new function, avoiding collisions
616
+                if($newfuncname != '')
617
+                {
618
+                    $xmlrpcfuncname = $newfuncname;
619
+                }
620
+                else
621
+                {
622
+                    // take care to insure that methodname is translated to valid
623
+                    // php function name
624
+                    $xmlrpcfuncname = $prefix.'_'.preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
625
+                        array('_', ''), $methodname);
626
+                }
627
+                while($buildit && function_exists($xmlrpcfuncname))
628
+                {
629
+                    $xmlrpcfuncname .= 'x';
630
+                }
631
+
632
+                $msig = $msig[$signum];
633
+                $mdesc = '';
634
+                // if in 'offline' mode, get method description too.
635
+                // in online mode, favour speed of operation
636
+                if(!$buildit)
637
+                {
638
+                    $msg = new $msgclass('system.methodHelp');
639
+                    $msg->addparam(new $valclass($methodname));
640
+                    $response =& $client->send($msg, $timeout, $protocol);
641
+                    if (!$response->faultCode())
642
+                    {
643
+                        $mdesc = $response->value();
644
+                        if ($client->return_type != 'phpvals')
645
+                        {
646
+                            $mdesc = $mdesc->scalarval();
647
+                        }
648
+                    }
649
+                }
650
+
651
+                $results = build_remote_method_wrapper_code($client, $methodname,
652
+                    $xmlrpcfuncname, $msig, $mdesc, $timeout, $protocol, $simple_client_copy,
653
+                    $prefix, $decode_php_objects, $encode_php_objects, $decode_fault,
654
+                    $fault_response);
655
+
656
+                //print_r($code);
657
+                if ($buildit)
658
+                {
659
+                    $allOK = 0;
660
+                    eval($results['source'].'$allOK=1;');
661
+                    // alternative
662
+                    //$xmlrpcfuncname = create_function('$m', $innercode);
663
+                    if($allOK)
664
+                    {
665
+                        return $xmlrpcfuncname;
666
+                    }
667
+                    else
668
+                    {
669
+                        error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap remote method '.$methodname);
670
+                        return false;
671
+                    }
672
+                }
673
+                else
674
+                {
675
+                    $results['function'] = $xmlrpcfuncname;
676
+                    return $results;
677
+                }
678
+            }
679
+        }
680
+    }
681
+
682
+    /**
683
+     * Similar to wrap_xmlrpc_method, but will generate a php class that wraps
684
+     * all xmlrpc methods exposed by the remote server as own methods.
685
+     * For more details see wrap_xmlrpc_method.
686
+     * @param xmlrpc_client $client the client obj all set to query the desired server
687
+     * @param array $extra_options list of options for wrapped code
688
+     * @return mixed false on error, the name of the created class if all ok or an array with code, class name and comments (if the appropriatevoption is set in extra_options)
689
+     */
690
+    function wrap_xmlrpc_server($client, $extra_options=array())
691
+    {
692
+        $methodfilter = isset($extra_options['method_filter']) ? $extra_options['method_filter'] : '';
693
+        //$signum = isset($extra_options['signum']) ? (int)$extra_options['signum'] : 0;
694
+        $timeout = isset($extra_options['timeout']) ? (int)$extra_options['timeout'] : 0;
695
+        $protocol = isset($extra_options['protocol']) ? $extra_options['protocol'] : '';
696
+        $newclassname = isset($extra_options['new_class_name']) ? $extra_options['new_class_name'] : '';
697
+        $encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
698
+        $decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
699
+        $verbatim_client_copy = isset($extra_options['simple_client_copy']) ? !($extra_options['simple_client_copy']) : true;
700
+        $buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
701
+        $prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
702
+
703
+        $msgclass = $prefix.'msg';
704
+        //$valclass = $prefix.'val';
705
+        $decodefunc = 'php_'.$prefix.'_decode';
706
+
707
+        $msg = new $msgclass('system.listMethods');
708
+        $response =& $client->send($msg, $timeout, $protocol);
709
+        if($response->faultCode())
710
+        {
711
+            error_log('XML-RPC: could not retrieve method list from remote server');
712
+            return false;
713
+        }
714
+        else
715
+        {
716
+            $mlist = $response->value();
717
+            if ($client->return_type != 'phpvals')
718
+            {
719
+                $mlist = $decodefunc($mlist);
720
+            }
721
+            if(!is_array($mlist) || !count($mlist))
722
+            {
723
+                error_log('XML-RPC: could not retrieve meaningful method list from remote server');
724
+                return false;
725
+            }
726
+            else
727
+            {
728
+                // pick a suitable name for the new function, avoiding collisions
729
+                if($newclassname != '')
730
+                {
731
+                    $xmlrpcclassname = $newclassname;
732
+                }
733
+                else
734
+                {
735
+                    $xmlrpcclassname = $prefix.'_'.preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
736
+                        array('_', ''), $client->server).'_client';
737
+                }
738
+                while($buildit && class_exists($xmlrpcclassname))
739
+                {
740
+                    $xmlrpcclassname .= 'x';
741
+                }
742
+
743
+                /// @todo add function setdebug() to new class, to enable/disable debugging
744
+                $source = "class $xmlrpcclassname\n{\nvar \$client;\n\n";
745
+                $source .= "function $xmlrpcclassname()\n{\n";
746
+                $source .= build_client_wrapper_code($client, $verbatim_client_copy, $prefix);
747
+                $source .= "\$this->client =& \$client;\n}\n\n";
748
+                $opts = array('simple_client_copy' => 2, 'return_source' => true,
749
+                    'timeout' => $timeout, 'protocol' => $protocol,
750
+                    'encode_php_objs' => $encode_php_objects, 'prefix' => $prefix,
751
+                    'decode_php_objs' => $decode_php_objects
752
+                    );
753
+                /// @todo build javadoc for class definition, too
754
+                foreach($mlist as $mname)
755
+                {
756
+                    if ($methodfilter == '' || preg_match($methodfilter, $mname))
757
+                    {
758
+                        $opts['new_function_name'] = preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
759
+                            array('_', ''), $mname);
760
+                        $methodwrap = wrap_xmlrpc_method($client, $mname, $opts);
761
+                        if ($methodwrap)
762
+                        {
763
+                            if (!$buildit)
764
+                            {
765
+                                $source .= $methodwrap['docstring'];
766
+                            }
767
+                            $source .= $methodwrap['source']."\n";
768
+                        }
769
+                        else
770
+                        {
771
+                            error_log('XML-RPC: will not create class method to wrap remote method '.$mname);
772
+                        }
773
+                    }
774
+                }
775
+                $source .= "}\n";
776
+                if ($buildit)
777
+                {
778
+                    $allOK = 0;
779
+                    eval($source.'$allOK=1;');
780
+                    // alternative
781
+                    //$xmlrpcfuncname = create_function('$m', $innercode);
782
+                    if($allOK)
783
+                    {
784
+                        return $xmlrpcclassname;
785
+                    }
786
+                    else
787
+                    {
788
+                        error_log('XML-RPC: could not create class '.$xmlrpcclassname.' to wrap remote server '.$client->server);
789
+                        return false;
790
+                    }
791
+                }
792
+                else
793
+                {
794
+                    return array('class' => $xmlrpcclassname, 'code' => $source, 'docstring' => '');
795
+                }
796
+            }
797
+        }
798
+    }
799
+
800
+    /**
801
+     * Given the necessary info, build php code that creates a new function to
802
+     * invoke a remote xmlrpc method.
803
+     * Take care that no full checking of input parameters is done to ensure that
804
+     * valid php code is emitted.
805
+     * Note: real spaghetti code follows...
806
+     * @access private
807
+     */
808
+    function build_remote_method_wrapper_code($client, $methodname, $xmlrpcfuncname,
809
+        $msig, $mdesc='', $timeout=0, $protocol='', $client_copy_mode=0, $prefix='xmlrpc',
810
+        $decode_php_objects=false, $encode_php_objects=false, $decode_fault=false,
811
+        $fault_response='')
812
+    {
813
+        $code = "function $xmlrpcfuncname (";
814
+        if ($client_copy_mode < 2)
815
+        {
816
+            // client copy mode 0 or 1 == partial / full client copy in emitted code
817
+            $innercode = build_client_wrapper_code($client, $client_copy_mode, $prefix);
818
+            $innercode .= "\$client->setDebug(\$debug);\n";
819
+            $this_ = '';
820
+        }
821
+        else
822
+        {
823
+            // client copy mode 2 == no client copy in emitted code
824
+            $innercode = '';
825
+            $this_ = 'this->';
826
+        }
827
+        $innercode .= "\$msg = new {$prefix}msg('$methodname');\n";
828
+
829
+        if ($mdesc != '')
830
+        {
831
+            // take care that PHP comment is not terminated unwillingly by method description
832
+            $mdesc = "/**\n* ".str_replace('*/', '* /', $mdesc)."\n";
833
+        }
834
+        else
835
+        {
836
+            $mdesc = "/**\nFunction $xmlrpcfuncname\n";
837
+        }
838
+
839
+        // param parsing
840
+        $plist = array();
841
+        $pcount = count($msig);
842
+        for($i = 1; $i < $pcount; $i++)
843
+        {
844
+            $plist[] = "\$p$i";
845
+            $ptype = $msig[$i];
846
+            if($ptype == 'i4' || $ptype == 'int' || $ptype == 'boolean' || $ptype == 'double' ||
847
+                $ptype == 'string' || $ptype == 'dateTime.iso8601' || $ptype == 'base64' || $ptype == 'null')
848
+            {
849
+                // only build directly xmlrpcvals when type is known and scalar
850
+                $innercode .= "\$p$i = new {$prefix}val(\$p$i, '$ptype');\n";
851
+            }
852
+            else
853
+            {
854
+                if ($encode_php_objects)
855
+                {
856
+                    $innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i, array('encode_php_objs'));\n";
857
+                }
858
+                else
859
+                {
860
+                    $innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i);\n";
861
+                }
862
+            }
863
+            $innercode .= "\$msg->addparam(\$p$i);\n";
864
+            $mdesc .= '* @param '.xmlrpc_2_php_type($ptype)." \$p$i\n";
865
+        }
866
+        if ($client_copy_mode < 2)
867
+        {
868
+            $plist[] = '$debug=0';
869
+            $mdesc .= "* @param int \$debug when 1 (or 2) will enable debugging of the underlying {$prefix} call (defaults to 0)\n";
870
+        }
871
+        $plist = implode(', ', $plist);
872
+        $mdesc .= '* @return '.xmlrpc_2_php_type($msig[0])." (or an {$prefix}resp obj instance if call fails)\n*/\n";
873
+
874
+        $innercode .= "\$res =& \${$this_}client->send(\$msg, $timeout, '$protocol');\n";
875
+        if ($decode_fault)
876
+        {
877
+            if (is_string($fault_response) && ((strpos($fault_response, '%faultCode%') !== false) || (strpos($fault_response, '%faultString%') !== false)))
878
+            {
879
+                $respcode = "str_replace(array('%faultCode%', '%faultString%'), array(\$res->faultCode(), \$res->faultString()), '".str_replace("'", "''", $fault_response)."')";
880
+            }
881
+            else
882
+            {
883
+                $respcode = var_export($fault_response, true);
884
+            }
885
+        }
886
+        else
887
+        {
888
+            $respcode = '$res';
889
+        }
890
+        if ($decode_php_objects)
891
+        {
892
+            $innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value(), array('decode_php_objs'));";
893
+        }
894
+        else
895
+        {
896
+            $innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value());";
897
+        }
898
+
899
+        $code = $code . $plist. ") {\n" . $innercode . "\n}\n";
900
+
901
+        return array('source' => $code, 'docstring' => $mdesc);
902
+    }
903
+
904
+    /**
905
+     * Given necessary info, generate php code that will rebuild a client object
906
+     * Take care that no full checking of input parameters is done to ensure that
907
+     * valid php code is emitted.
908
+     * @access private
909
+     */
910
+    function build_client_wrapper_code($client, $verbatim_client_copy, $prefix='xmlrpc')
911
+    {
912
+        $code = "\$client = new {$prefix}_client('".str_replace("'", "\'", $client->path).
913
+            "', '" . str_replace("'", "\'", $client->server) . "', $client->port);\n";
914
+
915
+        // copy all client fields to the client that will be generated runtime
916
+        // (this provides for future expansion or subclassing of client obj)
917
+        if ($verbatim_client_copy)
918
+        {
919
+            foreach($client as $fld => $val)
920
+            {
921
+                if($fld != 'debug' && $fld != 'return_type')
922
+                {
923
+                    $val = var_export($val, true);
924
+                    $code .= "\$client->$fld = $val;\n";
925
+                }
926
+            }
927
+        }
928
+        // only make sure that client always returns the correct data type
929
+        $code .= "\$client->return_type = '{$prefix}vals';\n";
930
+        //$code .= "\$client->setDebug(\$debug);\n";
931
+        return $code;
932
+    }
933 933
 ?>
Please login to merge, or discard this patch.
lib/xmlrpcs.inc 1 patch
Indentation   +1208 added lines, -1208 removed lines patch added patch discarded remove patch
@@ -34,1215 +34,1215 @@
 block discarded – undo
34 34
 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35 35
 // OF THE POSSIBILITY OF SUCH DAMAGE.
36 36
 
37
-	// XML RPC Server class
38
-	// requires: xmlrpc.inc
39
-
40
-	$GLOBALS['xmlrpcs_capabilities'] = array(
41
-		// xmlrpc spec: always supported
42
-		'xmlrpc' => new xmlrpcval(array(
43
-			'specUrl' => new xmlrpcval('http://www.xmlrpc.com/spec', 'string'),
44
-			'specVersion' => new xmlrpcval(1, 'int')
45
-		), 'struct'),
46
-		// if we support system.xxx functions, we always support multicall, too...
47
-		// Note that, as of 2006/09/17, the following URL does not respond anymore
48
-		'system.multicall' => new xmlrpcval(array(
49
-			'specUrl' => new xmlrpcval('http://www.xmlrpc.com/discuss/msgReader$1208', 'string'),
50
-			'specVersion' => new xmlrpcval(1, 'int')
51
-		), 'struct'),
52
-		// introspection: version 2! we support 'mixed', too
53
-		'introspection' => new xmlrpcval(array(
54
-			'specUrl' => new xmlrpcval('http://phpxmlrpc.sourceforge.net/doc-2/ch10.html', 'string'),
55
-			'specVersion' => new xmlrpcval(2, 'int')
56
-		), 'struct')
57
-	);
58
-
59
-	/* Functions that implement system.XXX methods of xmlrpc servers */
60
-	$_xmlrpcs_getCapabilities_sig=array(array($GLOBALS['xmlrpcStruct']));
61
-	$_xmlrpcs_getCapabilities_doc='This method lists all the capabilites that the XML-RPC server has: the (more or less standard) extensions to the xmlrpc spec that it adheres to';
62
-	$_xmlrpcs_getCapabilities_sdoc=array(array('list of capabilities, described as structs with a version number and url for the spec'));
63
-	function _xmlrpcs_getCapabilities($server, $m=null)
64
-	{
65
-		$outAr = $GLOBALS['xmlrpcs_capabilities'];
66
-		// NIL extension
67
-		if ($GLOBALS['xmlrpc_null_extension']) {
68
-			$outAr['nil'] = new xmlrpcval(array(
69
-				'specUrl' => new xmlrpcval('http://www.ontosys.com/xml-rpc/extensions.php', 'string'),
70
-				'specVersion' => new xmlrpcval(1, 'int')
71
-			), 'struct');
72
-		}
73
-		return new xmlrpcresp(new xmlrpcval($outAr, 'struct'));
74
-	}
75
-
76
-	// listMethods: signature was either a string, or nothing.
77
-	// The useless string variant has been removed
78
-	$_xmlrpcs_listMethods_sig=array(array($GLOBALS['xmlrpcArray']));
79
-	$_xmlrpcs_listMethods_doc='This method lists all the methods that the XML-RPC server knows how to dispatch';
80
-	$_xmlrpcs_listMethods_sdoc=array(array('list of method names'));
81
-	function _xmlrpcs_listMethods($server, $m=null) // if called in plain php values mode, second param is missing
82
-	{
83
-
84
-		$outAr=array();
85
-		foreach($server->dmap as $key => $val)
86
-		{
87
-			$outAr[]=new xmlrpcval($key, 'string');
88
-		}
89
-		if($server->allow_system_funcs)
90
-		{
91
-			foreach($GLOBALS['_xmlrpcs_dmap'] as $key => $val)
92
-			{
93
-				$outAr[]=new xmlrpcval($key, 'string');
94
-			}
95
-		}
96
-		return new xmlrpcresp(new xmlrpcval($outAr, 'array'));
97
-	}
98
-
99
-	$_xmlrpcs_methodSignature_sig=array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcString']));
100
-	$_xmlrpcs_methodSignature_doc='Returns an array of known signatures (an array of arrays) for the method name passed. If no signatures are known, returns a none-array (test for type != array to detect missing signature)';
101
-	$_xmlrpcs_methodSignature_sdoc=array(array('list of known signatures, each sig being an array of xmlrpc type names', 'name of method to be described'));
102
-	function _xmlrpcs_methodSignature($server, $m)
103
-	{
104
-		// let accept as parameter both an xmlrpcval or string
105
-		if (is_object($m))
106
-		{
107
-			$methName=$m->getParam(0);
108
-			$methName=$methName->scalarval();
109
-		}
110
-		else
111
-		{
112
-			$methName=$m;
113
-		}
114
-		if(strpos($methName, "system.") === 0)
115
-		{
116
-			$dmap=$GLOBALS['_xmlrpcs_dmap']; //$sysCall=1;
117
-		}
118
-		else
119
-		{
120
-			$dmap=$server->dmap; //$sysCall=0;
121
-		}
122
-		if(isset($dmap[$methName]))
123
-		{
124
-			if(isset($dmap[$methName]['signature']))
125
-			{
126
-				$sigs=array();
127
-				foreach($dmap[$methName]['signature'] as $inSig)
128
-				{
129
-					$cursig=array();
130
-					foreach($inSig as $sig)
131
-					{
132
-						$cursig[]=new xmlrpcval($sig, 'string');
133
-					}
134
-					$sigs[]=new xmlrpcval($cursig, 'array');
135
-				}
136
-				$r=new xmlrpcresp(new xmlrpcval($sigs, 'array'));
137
-			}
138
-			else
139
-			{
140
-				// NB: according to the official docs, we should be returning a
141
-				// "none-array" here, which means not-an-array
142
-				$r=new xmlrpcresp(new xmlrpcval('undef', 'string'));
143
-			}
144
-		}
145
-		else
146
-		{
147
-			$r=new xmlrpcresp(0,$GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
148
-		}
149
-		return $r;
150
-	}
151
-
152
-	$_xmlrpcs_methodHelp_sig=array(array($GLOBALS['xmlrpcString'], $GLOBALS['xmlrpcString']));
153
-	$_xmlrpcs_methodHelp_doc='Returns help text if defined for the method passed, otherwise returns an empty string';
154
-	$_xmlrpcs_methodHelp_sdoc=array(array('method description', 'name of the method to be described'));
155
-	function _xmlrpcs_methodHelp($server, $m)
156
-	{
157
-		// let accept as parameter both an xmlrpcval or string
158
-		if (is_object($m))
159
-		{
160
-			$methName=$m->getParam(0);
161
-			$methName=$methName->scalarval();
162
-		}
163
-		else
164
-		{
165
-			$methName=$m;
166
-		}
167
-		if(strpos($methName, "system.") === 0)
168
-		{
169
-			$dmap=$GLOBALS['_xmlrpcs_dmap']; //$sysCall=1;
170
-		}
171
-		else
172
-		{
173
-			$dmap=$server->dmap; //$sysCall=0;
174
-		}
175
-		if(isset($dmap[$methName]))
176
-		{
177
-			if(isset($dmap[$methName]['docstring']))
178
-			{
179
-				$r=new xmlrpcresp(new xmlrpcval($dmap[$methName]['docstring']), 'string');
180
-			}
181
-			else
182
-			{
183
-				$r=new xmlrpcresp(new xmlrpcval('', 'string'));
184
-			}
185
-		}
186
-		else
187
-		{
188
-			$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
189
-		}
190
-		return $r;
191
-	}
192
-
193
-	$_xmlrpcs_multicall_sig = array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcArray']));
194
-	$_xmlrpcs_multicall_doc = 'Boxcar multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details';
195
-	$_xmlrpcs_multicall_sdoc = array(array('list of response structs, where each struct has the usual members', 'list of calls, with each call being represented as a struct, with members "methodname" and "params"'));
196
-	function _xmlrpcs_multicall_error($err)
197
-	{
198
-		if(is_string($err))
199
-		{
200
-			$str = $GLOBALS['xmlrpcstr']["multicall_${err}"];
201
-			$code = $GLOBALS['xmlrpcerr']["multicall_${err}"];
202
-		}
203
-		else
204
-		{
205
-			$code = $err->faultCode();
206
-			$str = $err->faultString();
207
-		}
208
-		$struct = array();
209
-		$struct['faultCode'] = new xmlrpcval($code, 'int');
210
-		$struct['faultString'] = new xmlrpcval($str, 'string');
211
-		return new xmlrpcval($struct, 'struct');
212
-	}
213
-
214
-	function _xmlrpcs_multicall_do_call($server, $call)
215
-	{
216
-		if($call->kindOf() != 'struct')
217
-		{
218
-			return _xmlrpcs_multicall_error('notstruct');
219
-		}
220
-		$methName = @$call->structmem('methodName');
221
-		if(!$methName)
222
-		{
223
-			return _xmlrpcs_multicall_error('nomethod');
224
-		}
225
-		if($methName->kindOf() != 'scalar' || $methName->scalartyp() != 'string')
226
-		{
227
-			return _xmlrpcs_multicall_error('notstring');
228
-		}
229
-		if($methName->scalarval() == 'system.multicall')
230
-		{
231
-			return _xmlrpcs_multicall_error('recursion');
232
-		}
233
-
234
-		$params = @$call->structmem('params');
235
-		if(!$params)
236
-		{
237
-			return _xmlrpcs_multicall_error('noparams');
238
-		}
239
-		if($params->kindOf() != 'array')
240
-		{
241
-			return _xmlrpcs_multicall_error('notarray');
242
-		}
243
-		$numParams = $params->arraysize();
244
-
245
-		$msg = new xmlrpcmsg($methName->scalarval());
246
-		for($i = 0; $i < $numParams; $i++)
247
-		{
248
-			if(!$msg->addParam($params->arraymem($i)))
249
-			{
250
-				$i++;
251
-				return _xmlrpcs_multicall_error(new xmlrpcresp(0,
252
-					$GLOBALS['xmlrpcerr']['incorrect_params'],
253
-					$GLOBALS['xmlrpcstr']['incorrect_params'] . ": probable xml error in param " . $i));
254
-			}
255
-		}
256
-
257
-		$result = $server->execute($msg);
258
-
259
-		if($result->faultCode() != 0)
260
-		{
261
-			return _xmlrpcs_multicall_error($result);		// Method returned fault.
262
-		}
263
-
264
-		return new xmlrpcval(array($result->value()), 'array');
265
-	}
266
-
267
-	function _xmlrpcs_multicall_do_call_phpvals($server, $call)
268
-	{
269
-		if(!is_array($call))
270
-		{
271
-			return _xmlrpcs_multicall_error('notstruct');
272
-		}
273
-		if(!array_key_exists('methodName', $call))
274
-		{
275
-			return _xmlrpcs_multicall_error('nomethod');
276
-		}
277
-		if (!is_string($call['methodName']))
278
-		{
279
-			return _xmlrpcs_multicall_error('notstring');
280
-		}
281
-		if($call['methodName'] == 'system.multicall')
282
-		{
283
-			return _xmlrpcs_multicall_error('recursion');
284
-		}
285
-		if(!array_key_exists('params', $call))
286
-		{
287
-			return _xmlrpcs_multicall_error('noparams');
288
-		}
289
-		if(!is_array($call['params']))
290
-		{
291
-			return _xmlrpcs_multicall_error('notarray');
292
-		}
293
-
294
-		// this is a real dirty and simplistic hack, since we might have received a
295
-		// base64 or datetime values, but they will be listed as strings here...
296
-		//$numParams = count($call['params']);
297
-		$pt = array();
298
-		foreach($call['params'] as $val)
299
-			$pt[] = php_2_xmlrpc_type(gettype($val));
300
-
301
-		$result = $server->execute($call['methodName'], $call['params'], $pt);
302
-
303
-		if($result->faultCode() != 0)
304
-		{
305
-			return _xmlrpcs_multicall_error($result);		// Method returned fault.
306
-		}
307
-
308
-		return new xmlrpcval(array($result->value()), 'array');
309
-	}
310
-
311
-	function _xmlrpcs_multicall($server, $m)
312
-	{
313
-		$result = array();
314
-		// let accept a plain list of php parameters, beside a single xmlrpc msg object
315
-		if (is_object($m))
316
-		{
317
-			$calls = $m->getParam(0);
318
-			$numCalls = $calls->arraysize();
319
-			for($i = 0; $i < $numCalls; $i++)
320
-			{
321
-				$call = $calls->arraymem($i);
322
-				$result[$i] = _xmlrpcs_multicall_do_call($server, $call);
323
-			}
324
-		}
325
-		else
326
-		{
327
-			$numCalls=count($m);
328
-			for($i = 0; $i < $numCalls; $i++)
329
-			{
330
-				$result[$i] = _xmlrpcs_multicall_do_call_phpvals($server, $m[$i]);
331
-			}
332
-		}
333
-
334
-		return new xmlrpcresp(new xmlrpcval($result, 'array'));
335
-	}
336
-
337
-	$GLOBALS['_xmlrpcs_dmap']=array(
338
-		'system.listMethods' => array(
339
-			'function' => '_xmlrpcs_listMethods',
340
-			'signature' => $_xmlrpcs_listMethods_sig,
341
-			'docstring' => $_xmlrpcs_listMethods_doc,
342
-			'signature_docs' => $_xmlrpcs_listMethods_sdoc),
343
-		'system.methodHelp' => array(
344
-			'function' => '_xmlrpcs_methodHelp',
345
-			'signature' => $_xmlrpcs_methodHelp_sig,
346
-			'docstring' => $_xmlrpcs_methodHelp_doc,
347
-			'signature_docs' => $_xmlrpcs_methodHelp_sdoc),
348
-		'system.methodSignature' => array(
349
-			'function' => '_xmlrpcs_methodSignature',
350
-			'signature' => $_xmlrpcs_methodSignature_sig,
351
-			'docstring' => $_xmlrpcs_methodSignature_doc,
352
-			'signature_docs' => $_xmlrpcs_methodSignature_sdoc),
353
-		'system.multicall' => array(
354
-			'function' => '_xmlrpcs_multicall',
355
-			'signature' => $_xmlrpcs_multicall_sig,
356
-			'docstring' => $_xmlrpcs_multicall_doc,
357
-			'signature_docs' => $_xmlrpcs_multicall_sdoc),
358
-		'system.getCapabilities' => array(
359
-			'function' => '_xmlrpcs_getCapabilities',
360
-			'signature' => $_xmlrpcs_getCapabilities_sig,
361
-			'docstring' => $_xmlrpcs_getCapabilities_doc,
362
-			'signature_docs' => $_xmlrpcs_getCapabilities_sdoc)
363
-	);
364
-
365
-	$GLOBALS['_xmlrpcs_occurred_errors'] = '';
366
-	$GLOBALS['_xmlrpcs_prev_ehandler'] = '';
367
-
368
-	/**
369
-	* Error handler used to track errors that occur during server-side execution of PHP code.
370
-	* This allows to report back to the client whether an internal error has occurred or not
371
-	* using an xmlrpc response object, instead of letting the client deal with the html junk
372
-	* that a PHP execution error on the server generally entails.
373
-	*
374
-	* NB: in fact a user defined error handler can only handle WARNING, NOTICE and USER_* errors.
375
-	*
376
-	*/
377
-	function _xmlrpcs_errorHandler($errcode, $errstring, $filename=null, $lineno=null, $context=null)
378
-	{
379
-		// obey the @ protocol
380
-		if (error_reporting() == 0)
381
-			return;
382
-
383
-		//if($errcode != E_NOTICE && $errcode != E_WARNING && $errcode != E_USER_NOTICE && $errcode != E_USER_WARNING)
384
-		if($errcode != E_STRICT)
385
-		{
386
-			$GLOBALS['_xmlrpcs_occurred_errors'] = $GLOBALS['_xmlrpcs_occurred_errors'] . $errstring . "\n";
387
-		}
388
-		// Try to avoid as much as possible disruption to the previous error handling
389
-		// mechanism in place
390
-		if($GLOBALS['_xmlrpcs_prev_ehandler'] == '')
391
-		{
392
-			// The previous error handler was the default: all we should do is log error
393
-			// to the default error log (if level high enough)
394
-			if(ini_get('log_errors') && (intval(ini_get('error_reporting')) & $errcode))
395
-			{
396
-				error_log($errstring);
397
-			}
398
-		}
399
-		else
400
-		{
401
-			// Pass control on to previous error handler, trying to avoid loops...
402
-			if($GLOBALS['_xmlrpcs_prev_ehandler'] != '_xmlrpcs_errorHandler')
403
-			{
404
-				// NB: this code will NOT work on php < 4.0.2: only 2 params were used for error handlers
405
-				if(is_array($GLOBALS['_xmlrpcs_prev_ehandler']))
406
-				{
407
-					// the following works both with static class methods and plain object methods as error handler
408
-					call_user_func_array($GLOBALS['_xmlrpcs_prev_ehandler'], array($errcode, $errstring, $filename, $lineno, $context));
409
-				}
410
-				else
411
-				{
412
-					$GLOBALS['_xmlrpcs_prev_ehandler']($errcode, $errstring, $filename, $lineno, $context);
413
-				}
414
-			}
415
-		}
416
-	}
417
-
418
-	$GLOBALS['_xmlrpc_debuginfo']='';
419
-
420
-	/**
421
-	* Add a string to the debug info that can be later seralized by the server
422
-	* as part of the response message.
423
-	* Note that for best compatibility, the debug string should be encoded using
424
-	* the $GLOBALS['xmlrpc_internalencoding'] character set.
425
-	* @param string $m
426
-	* @access public
427
-	*/
428
-	function xmlrpc_debugmsg($m)
429
-	{
430
-		$GLOBALS['_xmlrpc_debuginfo'] .= $m . "\n";
431
-	}
432
-
433
-	class xmlrpc_server
434
-	{
435
-		/**
436
-		* Array defining php functions exposed as xmlrpc methods by this server
437
-		* @access private
438
-		*/
439
-		var $dmap=array();
440
-		/**
441
-		* Defines how functions in dmap will be invoked: either using an xmlrpc msg object
442
-		* or plain php values.
443
-		* valid strings are 'xmlrpcvals', 'phpvals' or 'epivals'
444
-		*/
445
-		var $functions_parameters_type='xmlrpcvals';
446
-		/**
447
-		* Option used for fine-tuning the encoding the php values returned from
448
-		* functions registered in the dispatch map when the functions_parameters_types
449
-		* member is set to 'phpvals'
450
-		* @see php_xmlrpc_encode for a list of values
451
-		*/
452
-		var $phpvals_encoding_options = array( 'auto_dates' );
453
-		/// controls whether the server is going to echo debugging messages back to the client as comments in response body. valid values: 0,1,2,3
454
-		var $debug = 1;
455
-		/**
456
-		* Controls behaviour of server when invoked user function throws an exception:
457
-		* 0 = catch it and return an 'internal error' xmlrpc response (default)
458
-		* 1 = catch it and return an xmlrpc response with the error corresponding to the exception
459
-		* 2 = allow the exception to float to the upper layers
460
-		*/
461
-		var $exception_handling = 0;
462
-		/**
463
-		* When set to true, it will enable HTTP compression of the response, in case
464
-		* the client has declared its support for compression in the request.
465
-		*/
466
-		var $compress_response = false;
467
-		/**
468
-		* List of http compression methods accepted by the server for requests.
469
-		* NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
470
-		*/
471
-		var $accepted_compression = array();
472
-		/// shall we serve calls to system.* methods?
473
-		var $allow_system_funcs = true;
474
-		/// list of charset encodings natively accepted for requests
475
-		var $accepted_charset_encodings = array();
476
-		/**
477
-		* charset encoding to be used for response.
478
-		* NB: if we can, we will convert the generated response from internal_encoding to the intended one.
479
-		* can be: a supported xml encoding (only UTF-8 and ISO-8859-1 at present, unless mbstring is enabled),
480
-		* null (leave unspecified in response, convert output stream to US_ASCII),
481
-		* 'default' (use xmlrpc library default as specified in xmlrpc.inc, convert output stream if needed),
482
-		* or 'auto' (use client-specified charset encoding or same as request if request headers do not specify it (unless request is US-ASCII: then use library default anyway).
483
-		* NB: pretty dangerous if you accept every charset and do not have mbstring enabled)
484
-		*/
485
-		var $response_charset_encoding = '';
486
-		/**
487
-		* Storage for internal debug info
488
-		* @access private
489
-		*/
490
-		var $debug_info = '';
491
-		/**
492
-		* Extra data passed at runtime to method handling functions. Used only by EPI layer
493
-		*/
494
-		var $user_data = null;
495
-
496
-		/**
497
-		* @param array $dispmap the dispatch map with definition of exposed services
498
-		* @param boolean $servicenow set to false to prevent the server from running upon construction
499
-		*/
500
-		function __construct($dispMap=null, $serviceNow=true)
501
-		{
502
-			// if ZLIB is enabled, let the server by default accept compressed requests,
503
-			// and compress responses sent to clients that support them
504
-			if(function_exists('gzinflate'))
505
-			{
506
-				$this->accepted_compression = array('gzip', 'deflate');
507
-				$this->compress_response = true;
508
-			}
509
-
510
-			// by default the xml parser can support these 3 charset encodings
511
-			$this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
512
-
513
-			// dispMap is a dispatch array of methods
514
-			// mapped to function names and signatures
515
-			// if a method
516
-			// doesn't appear in the map then an unknown
517
-			// method error is generated
518
-			/* milosch - changed to make passing dispMap optional.
37
+    // XML RPC Server class
38
+    // requires: xmlrpc.inc
39
+
40
+    $GLOBALS['xmlrpcs_capabilities'] = array(
41
+        // xmlrpc spec: always supported
42
+        'xmlrpc' => new xmlrpcval(array(
43
+            'specUrl' => new xmlrpcval('http://www.xmlrpc.com/spec', 'string'),
44
+            'specVersion' => new xmlrpcval(1, 'int')
45
+        ), 'struct'),
46
+        // if we support system.xxx functions, we always support multicall, too...
47
+        // Note that, as of 2006/09/17, the following URL does not respond anymore
48
+        'system.multicall' => new xmlrpcval(array(
49
+            'specUrl' => new xmlrpcval('http://www.xmlrpc.com/discuss/msgReader$1208', 'string'),
50
+            'specVersion' => new xmlrpcval(1, 'int')
51
+        ), 'struct'),
52
+        // introspection: version 2! we support 'mixed', too
53
+        'introspection' => new xmlrpcval(array(
54
+            'specUrl' => new xmlrpcval('http://phpxmlrpc.sourceforge.net/doc-2/ch10.html', 'string'),
55
+            'specVersion' => new xmlrpcval(2, 'int')
56
+        ), 'struct')
57
+    );
58
+
59
+    /* Functions that implement system.XXX methods of xmlrpc servers */
60
+    $_xmlrpcs_getCapabilities_sig=array(array($GLOBALS['xmlrpcStruct']));
61
+    $_xmlrpcs_getCapabilities_doc='This method lists all the capabilites that the XML-RPC server has: the (more or less standard) extensions to the xmlrpc spec that it adheres to';
62
+    $_xmlrpcs_getCapabilities_sdoc=array(array('list of capabilities, described as structs with a version number and url for the spec'));
63
+    function _xmlrpcs_getCapabilities($server, $m=null)
64
+    {
65
+        $outAr = $GLOBALS['xmlrpcs_capabilities'];
66
+        // NIL extension
67
+        if ($GLOBALS['xmlrpc_null_extension']) {
68
+            $outAr['nil'] = new xmlrpcval(array(
69
+                'specUrl' => new xmlrpcval('http://www.ontosys.com/xml-rpc/extensions.php', 'string'),
70
+                'specVersion' => new xmlrpcval(1, 'int')
71
+            ), 'struct');
72
+        }
73
+        return new xmlrpcresp(new xmlrpcval($outAr, 'struct'));
74
+    }
75
+
76
+    // listMethods: signature was either a string, or nothing.
77
+    // The useless string variant has been removed
78
+    $_xmlrpcs_listMethods_sig=array(array($GLOBALS['xmlrpcArray']));
79
+    $_xmlrpcs_listMethods_doc='This method lists all the methods that the XML-RPC server knows how to dispatch';
80
+    $_xmlrpcs_listMethods_sdoc=array(array('list of method names'));
81
+    function _xmlrpcs_listMethods($server, $m=null) // if called in plain php values mode, second param is missing
82
+    {
83
+
84
+        $outAr=array();
85
+        foreach($server->dmap as $key => $val)
86
+        {
87
+            $outAr[]=new xmlrpcval($key, 'string');
88
+        }
89
+        if($server->allow_system_funcs)
90
+        {
91
+            foreach($GLOBALS['_xmlrpcs_dmap'] as $key => $val)
92
+            {
93
+                $outAr[]=new xmlrpcval($key, 'string');
94
+            }
95
+        }
96
+        return new xmlrpcresp(new xmlrpcval($outAr, 'array'));
97
+    }
98
+
99
+    $_xmlrpcs_methodSignature_sig=array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcString']));
100
+    $_xmlrpcs_methodSignature_doc='Returns an array of known signatures (an array of arrays) for the method name passed. If no signatures are known, returns a none-array (test for type != array to detect missing signature)';
101
+    $_xmlrpcs_methodSignature_sdoc=array(array('list of known signatures, each sig being an array of xmlrpc type names', 'name of method to be described'));
102
+    function _xmlrpcs_methodSignature($server, $m)
103
+    {
104
+        // let accept as parameter both an xmlrpcval or string
105
+        if (is_object($m))
106
+        {
107
+            $methName=$m->getParam(0);
108
+            $methName=$methName->scalarval();
109
+        }
110
+        else
111
+        {
112
+            $methName=$m;
113
+        }
114
+        if(strpos($methName, "system.") === 0)
115
+        {
116
+            $dmap=$GLOBALS['_xmlrpcs_dmap']; //$sysCall=1;
117
+        }
118
+        else
119
+        {
120
+            $dmap=$server->dmap; //$sysCall=0;
121
+        }
122
+        if(isset($dmap[$methName]))
123
+        {
124
+            if(isset($dmap[$methName]['signature']))
125
+            {
126
+                $sigs=array();
127
+                foreach($dmap[$methName]['signature'] as $inSig)
128
+                {
129
+                    $cursig=array();
130
+                    foreach($inSig as $sig)
131
+                    {
132
+                        $cursig[]=new xmlrpcval($sig, 'string');
133
+                    }
134
+                    $sigs[]=new xmlrpcval($cursig, 'array');
135
+                }
136
+                $r=new xmlrpcresp(new xmlrpcval($sigs, 'array'));
137
+            }
138
+            else
139
+            {
140
+                // NB: according to the official docs, we should be returning a
141
+                // "none-array" here, which means not-an-array
142
+                $r=new xmlrpcresp(new xmlrpcval('undef', 'string'));
143
+            }
144
+        }
145
+        else
146
+        {
147
+            $r=new xmlrpcresp(0,$GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
148
+        }
149
+        return $r;
150
+    }
151
+
152
+    $_xmlrpcs_methodHelp_sig=array(array($GLOBALS['xmlrpcString'], $GLOBALS['xmlrpcString']));
153
+    $_xmlrpcs_methodHelp_doc='Returns help text if defined for the method passed, otherwise returns an empty string';
154
+    $_xmlrpcs_methodHelp_sdoc=array(array('method description', 'name of the method to be described'));
155
+    function _xmlrpcs_methodHelp($server, $m)
156
+    {
157
+        // let accept as parameter both an xmlrpcval or string
158
+        if (is_object($m))
159
+        {
160
+            $methName=$m->getParam(0);
161
+            $methName=$methName->scalarval();
162
+        }
163
+        else
164
+        {
165
+            $methName=$m;
166
+        }
167
+        if(strpos($methName, "system.") === 0)
168
+        {
169
+            $dmap=$GLOBALS['_xmlrpcs_dmap']; //$sysCall=1;
170
+        }
171
+        else
172
+        {
173
+            $dmap=$server->dmap; //$sysCall=0;
174
+        }
175
+        if(isset($dmap[$methName]))
176
+        {
177
+            if(isset($dmap[$methName]['docstring']))
178
+            {
179
+                $r=new xmlrpcresp(new xmlrpcval($dmap[$methName]['docstring']), 'string');
180
+            }
181
+            else
182
+            {
183
+                $r=new xmlrpcresp(new xmlrpcval('', 'string'));
184
+            }
185
+        }
186
+        else
187
+        {
188
+            $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
189
+        }
190
+        return $r;
191
+    }
192
+
193
+    $_xmlrpcs_multicall_sig = array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcArray']));
194
+    $_xmlrpcs_multicall_doc = 'Boxcar multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details';
195
+    $_xmlrpcs_multicall_sdoc = array(array('list of response structs, where each struct has the usual members', 'list of calls, with each call being represented as a struct, with members "methodname" and "params"'));
196
+    function _xmlrpcs_multicall_error($err)
197
+    {
198
+        if(is_string($err))
199
+        {
200
+            $str = $GLOBALS['xmlrpcstr']["multicall_${err}"];
201
+            $code = $GLOBALS['xmlrpcerr']["multicall_${err}"];
202
+        }
203
+        else
204
+        {
205
+            $code = $err->faultCode();
206
+            $str = $err->faultString();
207
+        }
208
+        $struct = array();
209
+        $struct['faultCode'] = new xmlrpcval($code, 'int');
210
+        $struct['faultString'] = new xmlrpcval($str, 'string');
211
+        return new xmlrpcval($struct, 'struct');
212
+    }
213
+
214
+    function _xmlrpcs_multicall_do_call($server, $call)
215
+    {
216
+        if($call->kindOf() != 'struct')
217
+        {
218
+            return _xmlrpcs_multicall_error('notstruct');
219
+        }
220
+        $methName = @$call->structmem('methodName');
221
+        if(!$methName)
222
+        {
223
+            return _xmlrpcs_multicall_error('nomethod');
224
+        }
225
+        if($methName->kindOf() != 'scalar' || $methName->scalartyp() != 'string')
226
+        {
227
+            return _xmlrpcs_multicall_error('notstring');
228
+        }
229
+        if($methName->scalarval() == 'system.multicall')
230
+        {
231
+            return _xmlrpcs_multicall_error('recursion');
232
+        }
233
+
234
+        $params = @$call->structmem('params');
235
+        if(!$params)
236
+        {
237
+            return _xmlrpcs_multicall_error('noparams');
238
+        }
239
+        if($params->kindOf() != 'array')
240
+        {
241
+            return _xmlrpcs_multicall_error('notarray');
242
+        }
243
+        $numParams = $params->arraysize();
244
+
245
+        $msg = new xmlrpcmsg($methName->scalarval());
246
+        for($i = 0; $i < $numParams; $i++)
247
+        {
248
+            if(!$msg->addParam($params->arraymem($i)))
249
+            {
250
+                $i++;
251
+                return _xmlrpcs_multicall_error(new xmlrpcresp(0,
252
+                    $GLOBALS['xmlrpcerr']['incorrect_params'],
253
+                    $GLOBALS['xmlrpcstr']['incorrect_params'] . ": probable xml error in param " . $i));
254
+            }
255
+        }
256
+
257
+        $result = $server->execute($msg);
258
+
259
+        if($result->faultCode() != 0)
260
+        {
261
+            return _xmlrpcs_multicall_error($result);		// Method returned fault.
262
+        }
263
+
264
+        return new xmlrpcval(array($result->value()), 'array');
265
+    }
266
+
267
+    function _xmlrpcs_multicall_do_call_phpvals($server, $call)
268
+    {
269
+        if(!is_array($call))
270
+        {
271
+            return _xmlrpcs_multicall_error('notstruct');
272
+        }
273
+        if(!array_key_exists('methodName', $call))
274
+        {
275
+            return _xmlrpcs_multicall_error('nomethod');
276
+        }
277
+        if (!is_string($call['methodName']))
278
+        {
279
+            return _xmlrpcs_multicall_error('notstring');
280
+        }
281
+        if($call['methodName'] == 'system.multicall')
282
+        {
283
+            return _xmlrpcs_multicall_error('recursion');
284
+        }
285
+        if(!array_key_exists('params', $call))
286
+        {
287
+            return _xmlrpcs_multicall_error('noparams');
288
+        }
289
+        if(!is_array($call['params']))
290
+        {
291
+            return _xmlrpcs_multicall_error('notarray');
292
+        }
293
+
294
+        // this is a real dirty and simplistic hack, since we might have received a
295
+        // base64 or datetime values, but they will be listed as strings here...
296
+        //$numParams = count($call['params']);
297
+        $pt = array();
298
+        foreach($call['params'] as $val)
299
+            $pt[] = php_2_xmlrpc_type(gettype($val));
300
+
301
+        $result = $server->execute($call['methodName'], $call['params'], $pt);
302
+
303
+        if($result->faultCode() != 0)
304
+        {
305
+            return _xmlrpcs_multicall_error($result);		// Method returned fault.
306
+        }
307
+
308
+        return new xmlrpcval(array($result->value()), 'array');
309
+    }
310
+
311
+    function _xmlrpcs_multicall($server, $m)
312
+    {
313
+        $result = array();
314
+        // let accept a plain list of php parameters, beside a single xmlrpc msg object
315
+        if (is_object($m))
316
+        {
317
+            $calls = $m->getParam(0);
318
+            $numCalls = $calls->arraysize();
319
+            for($i = 0; $i < $numCalls; $i++)
320
+            {
321
+                $call = $calls->arraymem($i);
322
+                $result[$i] = _xmlrpcs_multicall_do_call($server, $call);
323
+            }
324
+        }
325
+        else
326
+        {
327
+            $numCalls=count($m);
328
+            for($i = 0; $i < $numCalls; $i++)
329
+            {
330
+                $result[$i] = _xmlrpcs_multicall_do_call_phpvals($server, $m[$i]);
331
+            }
332
+        }
333
+
334
+        return new xmlrpcresp(new xmlrpcval($result, 'array'));
335
+    }
336
+
337
+    $GLOBALS['_xmlrpcs_dmap']=array(
338
+        'system.listMethods' => array(
339
+            'function' => '_xmlrpcs_listMethods',
340
+            'signature' => $_xmlrpcs_listMethods_sig,
341
+            'docstring' => $_xmlrpcs_listMethods_doc,
342
+            'signature_docs' => $_xmlrpcs_listMethods_sdoc),
343
+        'system.methodHelp' => array(
344
+            'function' => '_xmlrpcs_methodHelp',
345
+            'signature' => $_xmlrpcs_methodHelp_sig,
346
+            'docstring' => $_xmlrpcs_methodHelp_doc,
347
+            'signature_docs' => $_xmlrpcs_methodHelp_sdoc),
348
+        'system.methodSignature' => array(
349
+            'function' => '_xmlrpcs_methodSignature',
350
+            'signature' => $_xmlrpcs_methodSignature_sig,
351
+            'docstring' => $_xmlrpcs_methodSignature_doc,
352
+            'signature_docs' => $_xmlrpcs_methodSignature_sdoc),
353
+        'system.multicall' => array(
354
+            'function' => '_xmlrpcs_multicall',
355
+            'signature' => $_xmlrpcs_multicall_sig,
356
+            'docstring' => $_xmlrpcs_multicall_doc,
357
+            'signature_docs' => $_xmlrpcs_multicall_sdoc),
358
+        'system.getCapabilities' => array(
359
+            'function' => '_xmlrpcs_getCapabilities',
360
+            'signature' => $_xmlrpcs_getCapabilities_sig,
361
+            'docstring' => $_xmlrpcs_getCapabilities_doc,
362
+            'signature_docs' => $_xmlrpcs_getCapabilities_sdoc)
363
+    );
364
+
365
+    $GLOBALS['_xmlrpcs_occurred_errors'] = '';
366
+    $GLOBALS['_xmlrpcs_prev_ehandler'] = '';
367
+
368
+    /**
369
+     * Error handler used to track errors that occur during server-side execution of PHP code.
370
+     * This allows to report back to the client whether an internal error has occurred or not
371
+     * using an xmlrpc response object, instead of letting the client deal with the html junk
372
+     * that a PHP execution error on the server generally entails.
373
+     *
374
+     * NB: in fact a user defined error handler can only handle WARNING, NOTICE and USER_* errors.
375
+     *
376
+     */
377
+    function _xmlrpcs_errorHandler($errcode, $errstring, $filename=null, $lineno=null, $context=null)
378
+    {
379
+        // obey the @ protocol
380
+        if (error_reporting() == 0)
381
+            return;
382
+
383
+        //if($errcode != E_NOTICE && $errcode != E_WARNING && $errcode != E_USER_NOTICE && $errcode != E_USER_WARNING)
384
+        if($errcode != E_STRICT)
385
+        {
386
+            $GLOBALS['_xmlrpcs_occurred_errors'] = $GLOBALS['_xmlrpcs_occurred_errors'] . $errstring . "\n";
387
+        }
388
+        // Try to avoid as much as possible disruption to the previous error handling
389
+        // mechanism in place
390
+        if($GLOBALS['_xmlrpcs_prev_ehandler'] == '')
391
+        {
392
+            // The previous error handler was the default: all we should do is log error
393
+            // to the default error log (if level high enough)
394
+            if(ini_get('log_errors') && (intval(ini_get('error_reporting')) & $errcode))
395
+            {
396
+                error_log($errstring);
397
+            }
398
+        }
399
+        else
400
+        {
401
+            // Pass control on to previous error handler, trying to avoid loops...
402
+            if($GLOBALS['_xmlrpcs_prev_ehandler'] != '_xmlrpcs_errorHandler')
403
+            {
404
+                // NB: this code will NOT work on php < 4.0.2: only 2 params were used for error handlers
405
+                if(is_array($GLOBALS['_xmlrpcs_prev_ehandler']))
406
+                {
407
+                    // the following works both with static class methods and plain object methods as error handler
408
+                    call_user_func_array($GLOBALS['_xmlrpcs_prev_ehandler'], array($errcode, $errstring, $filename, $lineno, $context));
409
+                }
410
+                else
411
+                {
412
+                    $GLOBALS['_xmlrpcs_prev_ehandler']($errcode, $errstring, $filename, $lineno, $context);
413
+                }
414
+            }
415
+        }
416
+    }
417
+
418
+    $GLOBALS['_xmlrpc_debuginfo']='';
419
+
420
+    /**
421
+     * Add a string to the debug info that can be later seralized by the server
422
+     * as part of the response message.
423
+     * Note that for best compatibility, the debug string should be encoded using
424
+     * the $GLOBALS['xmlrpc_internalencoding'] character set.
425
+     * @param string $m
426
+     * @access public
427
+     */
428
+    function xmlrpc_debugmsg($m)
429
+    {
430
+        $GLOBALS['_xmlrpc_debuginfo'] .= $m . "\n";
431
+    }
432
+
433
+    class xmlrpc_server
434
+    {
435
+        /**
436
+         * Array defining php functions exposed as xmlrpc methods by this server
437
+         * @access private
438
+         */
439
+        var $dmap=array();
440
+        /**
441
+         * Defines how functions in dmap will be invoked: either using an xmlrpc msg object
442
+         * or plain php values.
443
+         * valid strings are 'xmlrpcvals', 'phpvals' or 'epivals'
444
+         */
445
+        var $functions_parameters_type='xmlrpcvals';
446
+        /**
447
+         * Option used for fine-tuning the encoding the php values returned from
448
+         * functions registered in the dispatch map when the functions_parameters_types
449
+         * member is set to 'phpvals'
450
+         * @see php_xmlrpc_encode for a list of values
451
+         */
452
+        var $phpvals_encoding_options = array( 'auto_dates' );
453
+        /// controls whether the server is going to echo debugging messages back to the client as comments in response body. valid values: 0,1,2,3
454
+        var $debug = 1;
455
+        /**
456
+         * Controls behaviour of server when invoked user function throws an exception:
457
+         * 0 = catch it and return an 'internal error' xmlrpc response (default)
458
+         * 1 = catch it and return an xmlrpc response with the error corresponding to the exception
459
+         * 2 = allow the exception to float to the upper layers
460
+         */
461
+        var $exception_handling = 0;
462
+        /**
463
+         * When set to true, it will enable HTTP compression of the response, in case
464
+         * the client has declared its support for compression in the request.
465
+         */
466
+        var $compress_response = false;
467
+        /**
468
+         * List of http compression methods accepted by the server for requests.
469
+         * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
470
+         */
471
+        var $accepted_compression = array();
472
+        /// shall we serve calls to system.* methods?
473
+        var $allow_system_funcs = true;
474
+        /// list of charset encodings natively accepted for requests
475
+        var $accepted_charset_encodings = array();
476
+        /**
477
+         * charset encoding to be used for response.
478
+         * NB: if we can, we will convert the generated response from internal_encoding to the intended one.
479
+         * can be: a supported xml encoding (only UTF-8 and ISO-8859-1 at present, unless mbstring is enabled),
480
+         * null (leave unspecified in response, convert output stream to US_ASCII),
481
+         * 'default' (use xmlrpc library default as specified in xmlrpc.inc, convert output stream if needed),
482
+         * or 'auto' (use client-specified charset encoding or same as request if request headers do not specify it (unless request is US-ASCII: then use library default anyway).
483
+         * NB: pretty dangerous if you accept every charset and do not have mbstring enabled)
484
+         */
485
+        var $response_charset_encoding = '';
486
+        /**
487
+         * Storage for internal debug info
488
+         * @access private
489
+         */
490
+        var $debug_info = '';
491
+        /**
492
+         * Extra data passed at runtime to method handling functions. Used only by EPI layer
493
+         */
494
+        var $user_data = null;
495
+
496
+        /**
497
+         * @param array $dispmap the dispatch map with definition of exposed services
498
+         * @param boolean $servicenow set to false to prevent the server from running upon construction
499
+         */
500
+        function __construct($dispMap=null, $serviceNow=true)
501
+        {
502
+            // if ZLIB is enabled, let the server by default accept compressed requests,
503
+            // and compress responses sent to clients that support them
504
+            if(function_exists('gzinflate'))
505
+            {
506
+                $this->accepted_compression = array('gzip', 'deflate');
507
+                $this->compress_response = true;
508
+            }
509
+
510
+            // by default the xml parser can support these 3 charset encodings
511
+            $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
512
+
513
+            // dispMap is a dispatch array of methods
514
+            // mapped to function names and signatures
515
+            // if a method
516
+            // doesn't appear in the map then an unknown
517
+            // method error is generated
518
+            /* milosch - changed to make passing dispMap optional.
519 519
 			 * instead, you can use the class add_to_map() function
520 520
 			 * to add functions manually (borrowed from SOAPX4)
521 521
 			 */
522
-			if($dispMap)
523
-			{
524
-				$this->dmap = $dispMap;
525
-				if($serviceNow)
526
-				{
527
-					$this->service();
528
-				}
529
-			}
530
-		}
531
-
532
-		/**
533
-		* @deprecated
534
-		*/
535
-		function xmlrpc_client($dispMap=null, $serviceNow=true)
536
-		{
537
-			self::__construct($dispMap, $serviceNow);
538
-		}
539
-
540
-		/**
541
-		* Set debug level of server.
542
-		* @param integer $in debug lvl: determines info added to xmlrpc responses (as xml comments)
543
-		* 0 = no debug info,
544
-		* 1 = msgs set from user with debugmsg(),
545
-		* 2 = add complete xmlrpc request (headers and body),
546
-		* 3 = add also all processing warnings happened during method processing
547
-		* (NB: this involves setting a custom error handler, and might interfere
548
-		* with the standard processing of the php function exposed as method. In
549
-		* particular, triggering an USER_ERROR level error will not halt script
550
-		* execution anymore, but just end up logged in the xmlrpc response)
551
-		* Note that info added at level 2 and 3 will be base64 encoded
552
-		* @access public
553
-		*/
554
-		function setDebug($in)
555
-		{
556
-			$this->debug=$in;
557
-		}
558
-
559
-		/**
560
-		* Return a string with the serialized representation of all debug info
561
-		* @param string $charset_encoding the target charset encoding for the serialization
562
-		* @return string an XML comment (or two)
563
-		*/
564
-		function serializeDebug($charset_encoding='')
565
-		{
566
-			// Tough encoding problem: which internal charset should we assume for debug info?
567
-			// It might contain a copy of raw data received from client, ie with unknown encoding,
568
-			// intermixed with php generated data and user generated data...
569
-			// so we split it: system debug is base 64 encoded,
570
-			// user debug info should be encoded by the end user using the INTERNAL_ENCODING
571
-			$out = '';
572
-			if ($this->debug_info != '')
573
-			{
574
-				$out .= "<!-- SERVER DEBUG INFO (BASE64 ENCODED):\n".base64_encode($this->debug_info)."\n-->\n";
575
-			}
576
-			if($GLOBALS['_xmlrpc_debuginfo']!='')
577
-			{
578
-
579
-				$out .= "<!-- DEBUG INFO:\n" . xmlrpc_encode_entitites(str_replace('--', '_-', $GLOBALS['_xmlrpc_debuginfo']), $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "\n-->\n";
580
-				// NB: a better solution MIGHT be to use CDATA, but we need to insert it
581
-				// into return payload AFTER the beginning tag
582
-				//$out .= "<![CDATA[ DEBUG INFO:\n\n" . str_replace(']]>', ']_]_>', $GLOBALS['_xmlrpc_debuginfo']) . "\n]]>\n";
583
-			}
584
-			return $out;
585
-		}
586
-
587
-		/**
588
-		* Execute the xmlrpc request, printing the response
589
-		* @param string $data the request body. If null, the http POST request will be examined
590
-		* @return xmlrpcresp the response object (usually not used by caller...)
591
-		* @access public
592
-		*/
593
-		function service($data=null, $return_payload=false)
594
-		{
595
-			if ($data === null)
596
-			{
597
-				// workaround for a known bug in php ver. 5.2.2 that broke $HTTP_RAW_POST_DATA
598
-				$data = file_get_contents('php://input');
599
-			}
600
-			$raw_data = $data;
601
-
602
-			// reset internal debug info
603
-			$this->debug_info = '';
604
-
605
-			// Echo back what we received, before parsing it
606
-			if($this->debug > 1)
607
-			{
608
-				$this->debugmsg("+++GOT+++\n" . $data . "\n+++END+++");
609
-			}
610
-
611
-			$r = $this->parseRequestHeaders($data, $req_charset, $resp_charset, $resp_encoding);
612
-			if (!$r)
613
-			{
614
-				$r=$this->parseRequest($data, $req_charset);
615
-			}
616
-
617
-			// save full body of request into response, for more debugging usages
618
-			$r->raw_data = $raw_data;
619
-
620
-			if($this->debug > 2 && $GLOBALS['_xmlrpcs_occurred_errors'])
621
-			{
622
-				$this->debugmsg("+++PROCESSING ERRORS AND WARNINGS+++\n" .
623
-					$GLOBALS['_xmlrpcs_occurred_errors'] . "+++END+++");
624
-			}
625
-
626
-			$payload=$this->xml_header($resp_charset);
627
-			if($this->debug > 0)
628
-			{
629
-				$payload = $payload . $this->serializeDebug($resp_charset);
630
-			}
631
-
632
-			// G. Giunta 2006-01-27: do not create response serialization if it has
633
-			// already happened. Helps building json magic
634
-			if (empty($r->payload))
635
-			{
636
-				$r->serialize($resp_charset);
637
-			}
638
-			$payload = $payload . $r->payload;
639
-
640
-			if ($return_payload)
641
-			{
642
-				return $payload;
643
-			}
644
-
645
-			// if we get a warning/error that has output some text before here, then we cannot
646
-			// add a new header. We cannot say we are sending xml, either...
647
-			if(!headers_sent())
648
-			{
649
-				header('Content-Type: '.$r->content_type);
650
-				// we do not know if client actually told us an accepted charset, but if he did
651
-				// we have to tell him what we did
652
-				header("Vary: Accept-Charset");
653
-
654
-				// http compression of output: only
655
-				// if we can do it, and we want to do it, and client asked us to,
656
-				// and php ini settings do not force it already
657
-				$php_no_self_compress = !ini_get('zlib.output_compression') && (ini_get('output_handler') != 'ob_gzhandler');
658
-				if($this->compress_response && function_exists('gzencode') && $resp_encoding != ''
659
-					&& $php_no_self_compress)
660
-				{
661
-					if(strpos($resp_encoding, 'gzip') !== false)
662
-					{
663
-						$payload = gzencode($payload);
664
-						header("Content-Encoding: gzip");
665
-						header("Vary: Accept-Encoding");
666
-					}
667
-					elseif (strpos($resp_encoding, 'deflate') !== false)
668
-					{
669
-						$payload = gzcompress($payload);
670
-						header("Content-Encoding: deflate");
671
-						header("Vary: Accept-Encoding");
672
-					}
673
-				}
674
-
675
-				// do not ouput content-length header if php is compressing output for us:
676
-				// it will mess up measurements
677
-				if($php_no_self_compress)
678
-				{
679
-					header('Content-Length: ' . (int)strlen($payload));
680
-				}
681
-			}
682
-			else
683
-			{
684
-				error_log('XML-RPC: '.__METHOD__.': http headers already sent before response is fully generated. Check for php warning or error messages');
685
-			}
686
-
687
-			print $payload;
688
-
689
-			// return request, in case subclasses want it
690
-			return $r;
691
-		}
692
-
693
-		/**
694
-		* Add a method to the dispatch map
695
-		* @param string $methodname the name with which the method will be made available
696
-		* @param string $function the php function that will get invoked
697
-		* @param array $sig the array of valid method signatures
698
-		* @param string $doc method documentation
699
-		* @param array $sigdoc the array of valid method signatures docs (one string per param, one for return type)
700
-		* @access public
701
-		*/
702
-		function add_to_map($methodname,$function,$sig=null,$doc=false,$sigdoc=false)
703
-		{
704
-			$this->dmap[$methodname] = array(
705
-				'function'	=> $function,
706
-				'docstring' => $doc
707
-			);
708
-			if ($sig)
709
-			{
710
-				$this->dmap[$methodname]['signature'] = $sig;
711
-			}
712
-			if ($sigdoc)
713
-			{
714
-				$this->dmap[$methodname]['signature_docs'] = $sigdoc;
715
-			}
716
-		}
717
-
718
-		/**
719
-		* Verify type and number of parameters received against a list of known signatures
720
-		* @param array $in array of either xmlrpcval objects or xmlrpc type definitions
721
-		* @param array $sig array of known signatures to match against
722
-		* @return array
723
-		* @access private
724
-		*/
725
-		function verifySignature($in, $sig)
726
-		{
727
-			// check each possible signature in turn
728
-			if (is_object($in))
729
-			{
730
-				$numParams = $in->getNumParams();
731
-			}
732
-			else
733
-			{
734
-				$numParams = count($in);
735
-			}
736
-			foreach($sig as $cursig)
737
-			{
738
-				if(count($cursig)==$numParams+1)
739
-				{
740
-					$itsOK=1;
741
-					for($n=0; $n<$numParams; $n++)
742
-					{
743
-						if (is_object($in))
744
-						{
745
-							$p=$in->getParam($n);
746
-							if($p->kindOf() == 'scalar')
747
-							{
748
-								$pt=$p->scalartyp();
749
-							}
750
-							else
751
-							{
752
-								$pt=$p->kindOf();
753
-							}
754
-						}
755
-						else
756
-						{
757
-							$pt= $in[$n] == 'i4' ? 'int' : strtolower($in[$n]); // dispatch maps never use i4...
758
-						}
759
-
760
-						// param index is $n+1, as first member of sig is return type
761
-						if($pt != $cursig[$n+1] && $cursig[$n+1] != $GLOBALS['xmlrpcValue'])
762
-						{
763
-							$itsOK=0;
764
-							$pno=$n+1;
765
-							$wanted=$cursig[$n+1];
766
-							$got=$pt;
767
-							break;
768
-						}
769
-					}
770
-					if($itsOK)
771
-					{
772
-						return array(1,'');
773
-					}
774
-				}
775
-			}
776
-			if(isset($wanted))
777
-			{
778
-				return array(0, "Wanted ${wanted}, got ${got} at param ${pno}");
779
-			}
780
-			else
781
-			{
782
-				return array(0, "No method signature matches number of parameters");
783
-			}
784
-		}
785
-
786
-		/**
787
-		* Parse http headers received along with xmlrpc request. If needed, inflate request
788
-		* @return mixed null on success or an xmlrpcresp
789
-		* @access private
790
-		*/
791
-		function parseRequestHeaders(&$data, &$req_encoding, &$resp_encoding, &$resp_compression)
792
-		{
793
-			// check if $_SERVER is populated: it might have been disabled via ini file
794
-			// (this is true even when in CLI mode)
795
-			if (count($_SERVER) == 0)
796
-			{
797
-				error_log('XML-RPC: '.__METHOD__.': cannot parse request headers as $_SERVER is not populated');
798
-			}
799
-
800
-			if($this->debug > 1)
801
-			{
802
-				if(function_exists('getallheaders'))
803
-				{
804
-					$this->debugmsg(''); // empty line
805
-					foreach(getallheaders() as $name => $val)
806
-					{
807
-						$this->debugmsg("HEADER: $name: $val");
808
-					}
809
-				}
810
-
811
-			}
812
-
813
-			if(isset($_SERVER['HTTP_CONTENT_ENCODING']))
814
-			{
815
-				$content_encoding = str_replace('x-', '', $_SERVER['HTTP_CONTENT_ENCODING']);
816
-			}
817
-			else
818
-			{
819
-				$content_encoding = '';
820
-			}
821
-
822
-			// check if request body has been compressed and decompress it
823
-			if($content_encoding != '' && strlen($data))
824
-			{
825
-				if($content_encoding == 'deflate' || $content_encoding == 'gzip')
826
-				{
827
-					// if decoding works, use it. else assume data wasn't gzencoded
828
-					if(function_exists('gzinflate') && in_array($content_encoding, $this->accepted_compression))
829
-					{
830
-						if($content_encoding == 'deflate' && $degzdata = @gzuncompress($data))
831
-						{
832
-							$data = $degzdata;
833
-							if($this->debug > 1)
834
-							{
835
-								$this->debugmsg("\n+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
836
-							}
837
-						}
838
-						elseif($content_encoding == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
839
-						{
840
-							$data = $degzdata;
841
-							if($this->debug > 1)
842
-								$this->debugmsg("+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
843
-						}
844
-						else
845
-						{
846
-							$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_decompress_fail'], $GLOBALS['xmlrpcstr']['server_decompress_fail']);
847
-							return $r;
848
-						}
849
-					}
850
-					else
851
-					{
852
-						//error_log('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
853
-						$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_cannot_decompress'], $GLOBALS['xmlrpcstr']['server_cannot_decompress']);
854
-						return $r;
855
-					}
856
-				}
857
-			}
858
-
859
-			// check if client specified accepted charsets, and if we know how to fulfill
860
-			// the request
861
-			if ($this->response_charset_encoding == 'auto')
862
-			{
863
-				$resp_encoding = '';
864
-				if (isset($_SERVER['HTTP_ACCEPT_CHARSET']))
865
-				{
866
-					// here we should check if we can match the client-requested encoding
867
-					// with the encodings we know we can generate.
868
-					/// @todo we should parse q=0.x preferences instead of getting first charset specified...
869
-					$client_accepted_charsets = explode(',', strtoupper($_SERVER['HTTP_ACCEPT_CHARSET']));
870
-					// Give preference to internal encoding
871
-					$known_charsets = array($GLOBALS['xmlrpc_internalencoding'], 'UTF-8', 'ISO-8859-1', 'US-ASCII');
872
-					foreach ($known_charsets as $charset)
873
-					{
874
-						foreach ($client_accepted_charsets as $accepted)
875
-							if (strpos($accepted, $charset) === 0)
876
-							{
877
-								$resp_encoding = $charset;
878
-								break;
879
-							}
880
-						if ($resp_encoding)
881
-							break;
882
-					}
883
-				}
884
-			}
885
-			else
886
-			{
887
-				$resp_encoding = $this->response_charset_encoding;
888
-			}
889
-
890
-			if (isset($_SERVER['HTTP_ACCEPT_ENCODING']))
891
-			{
892
-				$resp_compression = $_SERVER['HTTP_ACCEPT_ENCODING'];
893
-			}
894
-			else
895
-			{
896
-				$resp_compression = '';
897
-			}
898
-
899
-			// 'guestimate' request encoding
900
-			/// @todo check if mbstring is enabled and automagic input conversion is on: it might mingle with this check???
901
-			$req_encoding = guess_encoding(isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '',
902
-				$data);
903
-
904
-			return null;
905
-		}
906
-
907
-		/**
908
-		* Parse an xml chunk containing an xmlrpc request and execute the corresponding
909
-		* php function registered with the server
910
-		* @param string $data the xml request
911
-		* @param string $req_encoding (optional) the charset encoding of the xml request
912
-		* @return xmlrpcresp
913
-		* @access private
914
-		*/
915
-		function parseRequest($data, $req_encoding='')
916
-		{
917
-			// 2005/05/07 commented and moved into caller function code
918
-			//if($data=='')
919
-			//{
920
-			//	$data=$GLOBALS['HTTP_RAW_POST_DATA'];
921
-			//}
922
-
923
-			// G. Giunta 2005/02/13: we do NOT expect to receive html entities
924
-			// so we do not try to convert them into xml character entities
925
-			//$data = xmlrpc_html_entity_xlate($data);
926
-
927
-			$GLOBALS['_xh']=array();
928
-			$GLOBALS['_xh']['ac']='';
929
-			$GLOBALS['_xh']['stack']=array();
930
-			$GLOBALS['_xh']['valuestack'] = array();
931
-			$GLOBALS['_xh']['params']=array();
932
-			$GLOBALS['_xh']['pt']=array();
933
-			$GLOBALS['_xh']['isf']=0;
934
-			$GLOBALS['_xh']['isf_reason']='';
935
-			$GLOBALS['_xh']['method']=false; // so we can check later if we got a methodname or not
936
-			$GLOBALS['_xh']['rt']='';
937
-
938
-			// decompose incoming XML into request structure
939
-
940
-			if ($req_encoding != '')
941
-			{
942
-				// Since parsing will fail if charset is not specified in the xml prologue,
943
-				// the encoding is not UTF8 and there are non-ascii chars in the text, we try to work round that...
944
-				// The following code might be better for mb_string enabled installs, but
945
-				// makes the lib about 200% slower...
946
-				//if (!is_valid_charset($req_encoding, array('UTF-8')))
947
-				if (!in_array($req_encoding, array('UTF-8', 'US-ASCII')) && !has_encoding($data)) {
948
-					if ($req_encoding == 'ISO-8859-1') {
949
-						$data = utf8_encode($data);
950
-					} else {
951
-						if (extension_loaded('mbstring')) {
952
-							$data = mb_convert_encoding($data, 'UTF-8', $req_encoding);
953
-						} else {
954
-							error_log('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of received request: ' . $req_encoding);
955
-						}
956
-					}
957
-				}
958
-			}
959
-
960
-			$parser = xml_parser_create();
961
-			xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
962
-			// G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
963
-			// the xml parser to give us back data in the expected charset
964
-			// What if internal encoding is not in one of the 3 allowed?
965
-			// we use the broadest one, ie. utf8
966
-			// This allows to send data which is native in various charset,
967
-			// by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
968
-			if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
969
-			{
970
-				xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
971
-			}
972
-			else
973
-			{
974
-				xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
975
-			}
976
-
977
-			if ($this->functions_parameters_type != 'xmlrpcvals')
978
-				xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
979
-			else
980
-				xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
981
-			xml_set_character_data_handler($parser, 'xmlrpc_cd');
982
-			xml_set_default_handler($parser, 'xmlrpc_dh');
983
-			if(!xml_parse($parser, $data, 1))
984
-			{
985
-				// return XML error as a faultCode
986
-				$r=new xmlrpcresp(0,
987
-				$GLOBALS['xmlrpcerrxml']+xml_get_error_code($parser),
988
-				sprintf('XML error: %s at line %d, column %d',
989
-					xml_error_string(xml_get_error_code($parser)),
990
-					xml_get_current_line_number($parser), xml_get_current_column_number($parser)));
991
-				xml_parser_free($parser);
992
-			}
993
-			elseif ($GLOBALS['_xh']['isf'])
994
-			{
995
-				xml_parser_free($parser);
996
-				$r=new xmlrpcresp(0,
997
-					$GLOBALS['xmlrpcerr']['invalid_request'],
998
-					$GLOBALS['xmlrpcstr']['invalid_request'] . ' ' . $GLOBALS['_xh']['isf_reason']);
999
-			}
1000
-			else
1001
-			{
1002
-				xml_parser_free($parser);
1003
-				// small layering violation in favor of speed and memory usage:
1004
-				// we should allow the 'execute' method handle this, but in the
1005
-				// most common scenario (xmlrpcvals type server with some methods
1006
-				// registered as phpvals) that would mean a useless encode+decode pass
1007
-				if ($this->functions_parameters_type != 'xmlrpcvals' || (isset($this->dmap[$GLOBALS['_xh']['method']]['parameters_type']) && ($this->dmap[$GLOBALS['_xh']['method']]['parameters_type'] == 'phpvals')))
1008
-				{
1009
-					if($this->debug > 1)
1010
-					{
1011
-						$this->debugmsg("\n+++PARSED+++\n".var_export($GLOBALS['_xh']['params'], true)."\n+++END+++");
1012
-					}
1013
-					$r = $this->execute($GLOBALS['_xh']['method'], $GLOBALS['_xh']['params'], $GLOBALS['_xh']['pt']);
1014
-				}
1015
-				else
1016
-				{
1017
-					// build an xmlrpcmsg object with data parsed from xml
1018
-					$m=new xmlrpcmsg($GLOBALS['_xh']['method']);
1019
-					// now add parameters in
1020
-					for($i=0; $i<count($GLOBALS['_xh']['params']); $i++)
1021
-					{
1022
-						$m->addParam($GLOBALS['_xh']['params'][$i]);
1023
-					}
1024
-
1025
-					if($this->debug > 1)
1026
-					{
1027
-						$this->debugmsg("\n+++PARSED+++\n".var_export($m, true)."\n+++END+++");
1028
-					}
1029
-					$r = $this->execute($m);
1030
-				}
1031
-			}
1032
-			return $r;
1033
-		}
1034
-
1035
-		/**
1036
-		* Execute a method invoked by the client, checking parameters used
1037
-		* @param mixed $m either an xmlrpcmsg obj or a method name
1038
-		* @param array $params array with method parameters as php types (if m is method name only)
1039
-		* @param array $paramtypes array with xmlrpc types of method parameters (if m is method name only)
1040
-		* @return xmlrpcresp
1041
-		* @access private
1042
-		*/
1043
-		function execute($m, $params=null, $paramtypes=null)
1044
-		{
1045
-			if (is_object($m))
1046
-			{
1047
-				$methName = $m->method();
1048
-			}
1049
-			else
1050
-			{
1051
-				$methName = $m;
1052
-			}
1053
-			$sysCall = $this->allow_system_funcs && (strpos($methName, "system.") === 0);
1054
-			$dmap = $sysCall ? $GLOBALS['_xmlrpcs_dmap'] : $this->dmap;
1055
-
1056
-			if(!isset($dmap[$methName]['function']))
1057
-			{
1058
-				// No such method
1059
-				return new xmlrpcresp(0,
1060
-					$GLOBALS['xmlrpcerr']['unknown_method'],
1061
-					$GLOBALS['xmlrpcstr']['unknown_method']);
1062
-			}
1063
-
1064
-			// Check signature
1065
-			if(isset($dmap[$methName]['signature']))
1066
-			{
1067
-				$sig = $dmap[$methName]['signature'];
1068
-				if (is_object($m))
1069
-				{
1070
-					list($ok, $errstr) = $this->verifySignature($m, $sig);
1071
-				}
1072
-				else
1073
-				{
1074
-					list($ok, $errstr) = $this->verifySignature($paramtypes, $sig);
1075
-				}
1076
-				if(!$ok)
1077
-				{
1078
-					// Didn't match.
1079
-					return new xmlrpcresp(
1080
-						0,
1081
-						$GLOBALS['xmlrpcerr']['incorrect_params'],
1082
-						$GLOBALS['xmlrpcstr']['incorrect_params'] . ": ${errstr}"
1083
-					);
1084
-				}
1085
-			}
1086
-
1087
-			$func = $dmap[$methName]['function'];
1088
-			// let the 'class::function' syntax be accepted in dispatch maps
1089
-			if(is_string($func) && strpos($func, '::'))
1090
-			{
1091
-				$func = explode('::', $func);
1092
-			}
1093
-			// verify that function to be invoked is in fact callable
1094
-			if(!is_callable($func))
1095
-			{
1096
-				error_log("XML-RPC: ".__METHOD__.": function $func registered as method handler is not callable");
1097
-				return new xmlrpcresp(
1098
-					0,
1099
-					$GLOBALS['xmlrpcerr']['server_error'],
1100
-					$GLOBALS['xmlrpcstr']['server_error'] . ": no function matches method"
1101
-				);
1102
-			}
1103
-
1104
-			// If debug level is 3, we should catch all errors generated during
1105
-			// processing of user function, and log them as part of response
1106
-			if($this->debug > 2)
1107
-			{
1108
-				$GLOBALS['_xmlrpcs_prev_ehandler'] = set_error_handler('_xmlrpcs_errorHandler');
1109
-			}
1110
-			try
1111
-			{
1112
-				// Allow mixed-convention servers
1113
-				if (is_object($m))
1114
-				{
1115
-					if ($sysCall)
1116
-					{
1117
-						$r = call_user_func($func, $this, $m);
1118
-					}
1119
-					else
1120
-					{
1121
-						$r = call_user_func($func, $m);
1122
-					}
1123
-					if (!is_a($r, 'xmlrpcresp'))
1124
-					{
1125
-						error_log("XML-RPC: ".__METHOD__.": function $func registered as method handler does not return an xmlrpcresp object");
1126
-						if (is_a($r, 'xmlrpcval'))
1127
-						{
1128
-							$r = new xmlrpcresp($r);
1129
-						}
1130
-						else
1131
-						{
1132
-							$r = new xmlrpcresp(
1133
-								0,
1134
-								$GLOBALS['xmlrpcerr']['server_error'],
1135
-								$GLOBALS['xmlrpcstr']['server_error'] . ": function does not return xmlrpcresp object"
1136
-							);
1137
-						}
1138
-					}
1139
-				}
1140
-				else
1141
-				{
1142
-					// call a 'plain php' function
1143
-					if($sysCall)
1144
-					{
1145
-						array_unshift($params, $this);
1146
-						$r = call_user_func_array($func, $params);
1147
-					}
1148
-					else
1149
-					{
1150
-						// 3rd API convention for method-handling functions: EPI-style
1151
-						if ($this->functions_parameters_type == 'epivals')
1152
-						{
1153
-							$r = call_user_func_array($func, array($methName, $params, $this->user_data));
1154
-							// mimic EPI behaviour: if we get an array that looks like an error, make it
1155
-							// an eror response
1156
-							if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r))
1157
-							{
1158
-								$r = new xmlrpcresp(0, (integer)$r['faultCode'], (string)$r['faultString']);
1159
-							}
1160
-							else
1161
-							{
1162
-								// functions using EPI api should NOT return resp objects,
1163
-								// so make sure we encode the return type correctly
1164
-								$r = new xmlrpcresp(php_xmlrpc_encode($r, array('extension_api')));
1165
-							}
1166
-						}
1167
-						else
1168
-						{
1169
-							$r = call_user_func_array($func, $params);
1170
-						}
1171
-					}
1172
-					// the return type can be either an xmlrpcresp object or a plain php value...
1173
-					if (!is_a($r, 'xmlrpcresp'))
1174
-					{
1175
-						// what should we assume here about automatic encoding of datetimes
1176
-						// and php classes instances???
1177
-						$r = new xmlrpcresp(php_xmlrpc_encode($r, $this->phpvals_encoding_options));
1178
-					}
1179
-				}
1180
-			}
1181
-			catch(Exception $e)
1182
-			{
1183
-				// (barring errors in the lib) an uncatched exception happened
1184
-				// in the called function, we wrap it in a proper error-response
1185
-				switch($this->exception_handling)
1186
-				{
1187
-					case 2:
1188
-						throw $e;
1189
-						break;
1190
-					case 1:
1191
-						$r = new xmlrpcresp(0, $e->getCode(), $e->getMessage());
1192
-						break;
1193
-					default:
1194
-						$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_error'], $GLOBALS['xmlrpcstr']['server_error']);
1195
-				}
1196
-			}
1197
-			if($this->debug > 2)
1198
-			{
1199
-				// note: restore the error handler we found before calling the
1200
-				// user func, even if it has been changed inside the func itself
1201
-				if($GLOBALS['_xmlrpcs_prev_ehandler'])
1202
-				{
1203
-					set_error_handler($GLOBALS['_xmlrpcs_prev_ehandler']);
1204
-				}
1205
-				else
1206
-				{
1207
-					restore_error_handler();
1208
-				}
1209
-			}
1210
-			return $r;
1211
-		}
1212
-
1213
-		/**
1214
-		* add a string to the 'internal debug message' (separate from 'user debug message')
1215
-		* @param string $string
1216
-		* @access private
1217
-		*/
1218
-		function debugmsg($string)
1219
-		{
1220
-			$this->debug_info .= $string."\n";
1221
-		}
1222
-
1223
-		/**
1224
-		* @access private
1225
-		*/
1226
-		function xml_header($charset_encoding='')
1227
-		{
1228
-			if ($charset_encoding != '')
1229
-			{
1230
-				return "<?xml version=\"1.0\" encoding=\"$charset_encoding\"?" . ">\n";
1231
-			}
1232
-			else
1233
-			{
1234
-				return "<?xml version=\"1.0\"?" . ">\n";
1235
-			}
1236
-		}
1237
-
1238
-		/**
1239
-		* A debugging routine: just echoes back the input packet as a string value
1240
-		* @deprecated
1241
-		*/
1242
-		function echoInput()
1243
-		{
1244
-			$r=new xmlrpcresp(new xmlrpcval( "'Aha said I: '" . $GLOBALS['HTTP_RAW_POST_DATA'], 'string'));
1245
-			print $r->serialize();
1246
-		}
1247
-	}
522
+            if($dispMap)
523
+            {
524
+                $this->dmap = $dispMap;
525
+                if($serviceNow)
526
+                {
527
+                    $this->service();
528
+                }
529
+            }
530
+        }
531
+
532
+        /**
533
+         * @deprecated
534
+         */
535
+        function xmlrpc_client($dispMap=null, $serviceNow=true)
536
+        {
537
+            self::__construct($dispMap, $serviceNow);
538
+        }
539
+
540
+        /**
541
+         * Set debug level of server.
542
+         * @param integer $in debug lvl: determines info added to xmlrpc responses (as xml comments)
543
+         * 0 = no debug info,
544
+         * 1 = msgs set from user with debugmsg(),
545
+         * 2 = add complete xmlrpc request (headers and body),
546
+         * 3 = add also all processing warnings happened during method processing
547
+         * (NB: this involves setting a custom error handler, and might interfere
548
+         * with the standard processing of the php function exposed as method. In
549
+         * particular, triggering an USER_ERROR level error will not halt script
550
+         * execution anymore, but just end up logged in the xmlrpc response)
551
+         * Note that info added at level 2 and 3 will be base64 encoded
552
+         * @access public
553
+         */
554
+        function setDebug($in)
555
+        {
556
+            $this->debug=$in;
557
+        }
558
+
559
+        /**
560
+         * Return a string with the serialized representation of all debug info
561
+         * @param string $charset_encoding the target charset encoding for the serialization
562
+         * @return string an XML comment (or two)
563
+         */
564
+        function serializeDebug($charset_encoding='')
565
+        {
566
+            // Tough encoding problem: which internal charset should we assume for debug info?
567
+            // It might contain a copy of raw data received from client, ie with unknown encoding,
568
+            // intermixed with php generated data and user generated data...
569
+            // so we split it: system debug is base 64 encoded,
570
+            // user debug info should be encoded by the end user using the INTERNAL_ENCODING
571
+            $out = '';
572
+            if ($this->debug_info != '')
573
+            {
574
+                $out .= "<!-- SERVER DEBUG INFO (BASE64 ENCODED):\n".base64_encode($this->debug_info)."\n-->\n";
575
+            }
576
+            if($GLOBALS['_xmlrpc_debuginfo']!='')
577
+            {
578
+
579
+                $out .= "<!-- DEBUG INFO:\n" . xmlrpc_encode_entitites(str_replace('--', '_-', $GLOBALS['_xmlrpc_debuginfo']), $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "\n-->\n";
580
+                // NB: a better solution MIGHT be to use CDATA, but we need to insert it
581
+                // into return payload AFTER the beginning tag
582
+                //$out .= "<![CDATA[ DEBUG INFO:\n\n" . str_replace(']]>', ']_]_>', $GLOBALS['_xmlrpc_debuginfo']) . "\n]]>\n";
583
+            }
584
+            return $out;
585
+        }
586
+
587
+        /**
588
+         * Execute the xmlrpc request, printing the response
589
+         * @param string $data the request body. If null, the http POST request will be examined
590
+         * @return xmlrpcresp the response object (usually not used by caller...)
591
+         * @access public
592
+         */
593
+        function service($data=null, $return_payload=false)
594
+        {
595
+            if ($data === null)
596
+            {
597
+                // workaround for a known bug in php ver. 5.2.2 that broke $HTTP_RAW_POST_DATA
598
+                $data = file_get_contents('php://input');
599
+            }
600
+            $raw_data = $data;
601
+
602
+            // reset internal debug info
603
+            $this->debug_info = '';
604
+
605
+            // Echo back what we received, before parsing it
606
+            if($this->debug > 1)
607
+            {
608
+                $this->debugmsg("+++GOT+++\n" . $data . "\n+++END+++");
609
+            }
610
+
611
+            $r = $this->parseRequestHeaders($data, $req_charset, $resp_charset, $resp_encoding);
612
+            if (!$r)
613
+            {
614
+                $r=$this->parseRequest($data, $req_charset);
615
+            }
616
+
617
+            // save full body of request into response, for more debugging usages
618
+            $r->raw_data = $raw_data;
619
+
620
+            if($this->debug > 2 && $GLOBALS['_xmlrpcs_occurred_errors'])
621
+            {
622
+                $this->debugmsg("+++PROCESSING ERRORS AND WARNINGS+++\n" .
623
+                    $GLOBALS['_xmlrpcs_occurred_errors'] . "+++END+++");
624
+            }
625
+
626
+            $payload=$this->xml_header($resp_charset);
627
+            if($this->debug > 0)
628
+            {
629
+                $payload = $payload . $this->serializeDebug($resp_charset);
630
+            }
631
+
632
+            // G. Giunta 2006-01-27: do not create response serialization if it has
633
+            // already happened. Helps building json magic
634
+            if (empty($r->payload))
635
+            {
636
+                $r->serialize($resp_charset);
637
+            }
638
+            $payload = $payload . $r->payload;
639
+
640
+            if ($return_payload)
641
+            {
642
+                return $payload;
643
+            }
644
+
645
+            // if we get a warning/error that has output some text before here, then we cannot
646
+            // add a new header. We cannot say we are sending xml, either...
647
+            if(!headers_sent())
648
+            {
649
+                header('Content-Type: '.$r->content_type);
650
+                // we do not know if client actually told us an accepted charset, but if he did
651
+                // we have to tell him what we did
652
+                header("Vary: Accept-Charset");
653
+
654
+                // http compression of output: only
655
+                // if we can do it, and we want to do it, and client asked us to,
656
+                // and php ini settings do not force it already
657
+                $php_no_self_compress = !ini_get('zlib.output_compression') && (ini_get('output_handler') != 'ob_gzhandler');
658
+                if($this->compress_response && function_exists('gzencode') && $resp_encoding != ''
659
+                    && $php_no_self_compress)
660
+                {
661
+                    if(strpos($resp_encoding, 'gzip') !== false)
662
+                    {
663
+                        $payload = gzencode($payload);
664
+                        header("Content-Encoding: gzip");
665
+                        header("Vary: Accept-Encoding");
666
+                    }
667
+                    elseif (strpos($resp_encoding, 'deflate') !== false)
668
+                    {
669
+                        $payload = gzcompress($payload);
670
+                        header("Content-Encoding: deflate");
671
+                        header("Vary: Accept-Encoding");
672
+                    }
673
+                }
674
+
675
+                // do not ouput content-length header if php is compressing output for us:
676
+                // it will mess up measurements
677
+                if($php_no_self_compress)
678
+                {
679
+                    header('Content-Length: ' . (int)strlen($payload));
680
+                }
681
+            }
682
+            else
683
+            {
684
+                error_log('XML-RPC: '.__METHOD__.': http headers already sent before response is fully generated. Check for php warning or error messages');
685
+            }
686
+
687
+            print $payload;
688
+
689
+            // return request, in case subclasses want it
690
+            return $r;
691
+        }
692
+
693
+        /**
694
+         * Add a method to the dispatch map
695
+         * @param string $methodname the name with which the method will be made available
696
+         * @param string $function the php function that will get invoked
697
+         * @param array $sig the array of valid method signatures
698
+         * @param string $doc method documentation
699
+         * @param array $sigdoc the array of valid method signatures docs (one string per param, one for return type)
700
+         * @access public
701
+         */
702
+        function add_to_map($methodname,$function,$sig=null,$doc=false,$sigdoc=false)
703
+        {
704
+            $this->dmap[$methodname] = array(
705
+                'function'	=> $function,
706
+                'docstring' => $doc
707
+            );
708
+            if ($sig)
709
+            {
710
+                $this->dmap[$methodname]['signature'] = $sig;
711
+            }
712
+            if ($sigdoc)
713
+            {
714
+                $this->dmap[$methodname]['signature_docs'] = $sigdoc;
715
+            }
716
+        }
717
+
718
+        /**
719
+         * Verify type and number of parameters received against a list of known signatures
720
+         * @param array $in array of either xmlrpcval objects or xmlrpc type definitions
721
+         * @param array $sig array of known signatures to match against
722
+         * @return array
723
+         * @access private
724
+         */
725
+        function verifySignature($in, $sig)
726
+        {
727
+            // check each possible signature in turn
728
+            if (is_object($in))
729
+            {
730
+                $numParams = $in->getNumParams();
731
+            }
732
+            else
733
+            {
734
+                $numParams = count($in);
735
+            }
736
+            foreach($sig as $cursig)
737
+            {
738
+                if(count($cursig)==$numParams+1)
739
+                {
740
+                    $itsOK=1;
741
+                    for($n=0; $n<$numParams; $n++)
742
+                    {
743
+                        if (is_object($in))
744
+                        {
745
+                            $p=$in->getParam($n);
746
+                            if($p->kindOf() == 'scalar')
747
+                            {
748
+                                $pt=$p->scalartyp();
749
+                            }
750
+                            else
751
+                            {
752
+                                $pt=$p->kindOf();
753
+                            }
754
+                        }
755
+                        else
756
+                        {
757
+                            $pt= $in[$n] == 'i4' ? 'int' : strtolower($in[$n]); // dispatch maps never use i4...
758
+                        }
759
+
760
+                        // param index is $n+1, as first member of sig is return type
761
+                        if($pt != $cursig[$n+1] && $cursig[$n+1] != $GLOBALS['xmlrpcValue'])
762
+                        {
763
+                            $itsOK=0;
764
+                            $pno=$n+1;
765
+                            $wanted=$cursig[$n+1];
766
+                            $got=$pt;
767
+                            break;
768
+                        }
769
+                    }
770
+                    if($itsOK)
771
+                    {
772
+                        return array(1,'');
773
+                    }
774
+                }
775
+            }
776
+            if(isset($wanted))
777
+            {
778
+                return array(0, "Wanted ${wanted}, got ${got} at param ${pno}");
779
+            }
780
+            else
781
+            {
782
+                return array(0, "No method signature matches number of parameters");
783
+            }
784
+        }
785
+
786
+        /**
787
+         * Parse http headers received along with xmlrpc request. If needed, inflate request
788
+         * @return mixed null on success or an xmlrpcresp
789
+         * @access private
790
+         */
791
+        function parseRequestHeaders(&$data, &$req_encoding, &$resp_encoding, &$resp_compression)
792
+        {
793
+            // check if $_SERVER is populated: it might have been disabled via ini file
794
+            // (this is true even when in CLI mode)
795
+            if (count($_SERVER) == 0)
796
+            {
797
+                error_log('XML-RPC: '.__METHOD__.': cannot parse request headers as $_SERVER is not populated');
798
+            }
799
+
800
+            if($this->debug > 1)
801
+            {
802
+                if(function_exists('getallheaders'))
803
+                {
804
+                    $this->debugmsg(''); // empty line
805
+                    foreach(getallheaders() as $name => $val)
806
+                    {
807
+                        $this->debugmsg("HEADER: $name: $val");
808
+                    }
809
+                }
810
+
811
+            }
812
+
813
+            if(isset($_SERVER['HTTP_CONTENT_ENCODING']))
814
+            {
815
+                $content_encoding = str_replace('x-', '', $_SERVER['HTTP_CONTENT_ENCODING']);
816
+            }
817
+            else
818
+            {
819
+                $content_encoding = '';
820
+            }
821
+
822
+            // check if request body has been compressed and decompress it
823
+            if($content_encoding != '' && strlen($data))
824
+            {
825
+                if($content_encoding == 'deflate' || $content_encoding == 'gzip')
826
+                {
827
+                    // if decoding works, use it. else assume data wasn't gzencoded
828
+                    if(function_exists('gzinflate') && in_array($content_encoding, $this->accepted_compression))
829
+                    {
830
+                        if($content_encoding == 'deflate' && $degzdata = @gzuncompress($data))
831
+                        {
832
+                            $data = $degzdata;
833
+                            if($this->debug > 1)
834
+                            {
835
+                                $this->debugmsg("\n+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
836
+                            }
837
+                        }
838
+                        elseif($content_encoding == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
839
+                        {
840
+                            $data = $degzdata;
841
+                            if($this->debug > 1)
842
+                                $this->debugmsg("+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
843
+                        }
844
+                        else
845
+                        {
846
+                            $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_decompress_fail'], $GLOBALS['xmlrpcstr']['server_decompress_fail']);
847
+                            return $r;
848
+                        }
849
+                    }
850
+                    else
851
+                    {
852
+                        //error_log('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
853
+                        $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_cannot_decompress'], $GLOBALS['xmlrpcstr']['server_cannot_decompress']);
854
+                        return $r;
855
+                    }
856
+                }
857
+            }
858
+
859
+            // check if client specified accepted charsets, and if we know how to fulfill
860
+            // the request
861
+            if ($this->response_charset_encoding == 'auto')
862
+            {
863
+                $resp_encoding = '';
864
+                if (isset($_SERVER['HTTP_ACCEPT_CHARSET']))
865
+                {
866
+                    // here we should check if we can match the client-requested encoding
867
+                    // with the encodings we know we can generate.
868
+                    /// @todo we should parse q=0.x preferences instead of getting first charset specified...
869
+                    $client_accepted_charsets = explode(',', strtoupper($_SERVER['HTTP_ACCEPT_CHARSET']));
870
+                    // Give preference to internal encoding
871
+                    $known_charsets = array($GLOBALS['xmlrpc_internalencoding'], 'UTF-8', 'ISO-8859-1', 'US-ASCII');
872
+                    foreach ($known_charsets as $charset)
873
+                    {
874
+                        foreach ($client_accepted_charsets as $accepted)
875
+                            if (strpos($accepted, $charset) === 0)
876
+                            {
877
+                                $resp_encoding = $charset;
878
+                                break;
879
+                            }
880
+                        if ($resp_encoding)
881
+                            break;
882
+                    }
883
+                }
884
+            }
885
+            else
886
+            {
887
+                $resp_encoding = $this->response_charset_encoding;
888
+            }
889
+
890
+            if (isset($_SERVER['HTTP_ACCEPT_ENCODING']))
891
+            {
892
+                $resp_compression = $_SERVER['HTTP_ACCEPT_ENCODING'];
893
+            }
894
+            else
895
+            {
896
+                $resp_compression = '';
897
+            }
898
+
899
+            // 'guestimate' request encoding
900
+            /// @todo check if mbstring is enabled and automagic input conversion is on: it might mingle with this check???
901
+            $req_encoding = guess_encoding(isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '',
902
+                $data);
903
+
904
+            return null;
905
+        }
906
+
907
+        /**
908
+         * Parse an xml chunk containing an xmlrpc request and execute the corresponding
909
+         * php function registered with the server
910
+         * @param string $data the xml request
911
+         * @param string $req_encoding (optional) the charset encoding of the xml request
912
+         * @return xmlrpcresp
913
+         * @access private
914
+         */
915
+        function parseRequest($data, $req_encoding='')
916
+        {
917
+            // 2005/05/07 commented and moved into caller function code
918
+            //if($data=='')
919
+            //{
920
+            //	$data=$GLOBALS['HTTP_RAW_POST_DATA'];
921
+            //}
922
+
923
+            // G. Giunta 2005/02/13: we do NOT expect to receive html entities
924
+            // so we do not try to convert them into xml character entities
925
+            //$data = xmlrpc_html_entity_xlate($data);
926
+
927
+            $GLOBALS['_xh']=array();
928
+            $GLOBALS['_xh']['ac']='';
929
+            $GLOBALS['_xh']['stack']=array();
930
+            $GLOBALS['_xh']['valuestack'] = array();
931
+            $GLOBALS['_xh']['params']=array();
932
+            $GLOBALS['_xh']['pt']=array();
933
+            $GLOBALS['_xh']['isf']=0;
934
+            $GLOBALS['_xh']['isf_reason']='';
935
+            $GLOBALS['_xh']['method']=false; // so we can check later if we got a methodname or not
936
+            $GLOBALS['_xh']['rt']='';
937
+
938
+            // decompose incoming XML into request structure
939
+
940
+            if ($req_encoding != '')
941
+            {
942
+                // Since parsing will fail if charset is not specified in the xml prologue,
943
+                // the encoding is not UTF8 and there are non-ascii chars in the text, we try to work round that...
944
+                // The following code might be better for mb_string enabled installs, but
945
+                // makes the lib about 200% slower...
946
+                //if (!is_valid_charset($req_encoding, array('UTF-8')))
947
+                if (!in_array($req_encoding, array('UTF-8', 'US-ASCII')) && !has_encoding($data)) {
948
+                    if ($req_encoding == 'ISO-8859-1') {
949
+                        $data = utf8_encode($data);
950
+                    } else {
951
+                        if (extension_loaded('mbstring')) {
952
+                            $data = mb_convert_encoding($data, 'UTF-8', $req_encoding);
953
+                        } else {
954
+                            error_log('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of received request: ' . $req_encoding);
955
+                        }
956
+                    }
957
+                }
958
+            }
959
+
960
+            $parser = xml_parser_create();
961
+            xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
962
+            // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
963
+            // the xml parser to give us back data in the expected charset
964
+            // What if internal encoding is not in one of the 3 allowed?
965
+            // we use the broadest one, ie. utf8
966
+            // This allows to send data which is native in various charset,
967
+            // by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
968
+            if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
969
+            {
970
+                xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
971
+            }
972
+            else
973
+            {
974
+                xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
975
+            }
976
+
977
+            if ($this->functions_parameters_type != 'xmlrpcvals')
978
+                xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
979
+            else
980
+                xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
981
+            xml_set_character_data_handler($parser, 'xmlrpc_cd');
982
+            xml_set_default_handler($parser, 'xmlrpc_dh');
983
+            if(!xml_parse($parser, $data, 1))
984
+            {
985
+                // return XML error as a faultCode
986
+                $r=new xmlrpcresp(0,
987
+                $GLOBALS['xmlrpcerrxml']+xml_get_error_code($parser),
988
+                sprintf('XML error: %s at line %d, column %d',
989
+                    xml_error_string(xml_get_error_code($parser)),
990
+                    xml_get_current_line_number($parser), xml_get_current_column_number($parser)));
991
+                xml_parser_free($parser);
992
+            }
993
+            elseif ($GLOBALS['_xh']['isf'])
994
+            {
995
+                xml_parser_free($parser);
996
+                $r=new xmlrpcresp(0,
997
+                    $GLOBALS['xmlrpcerr']['invalid_request'],
998
+                    $GLOBALS['xmlrpcstr']['invalid_request'] . ' ' . $GLOBALS['_xh']['isf_reason']);
999
+            }
1000
+            else
1001
+            {
1002
+                xml_parser_free($parser);
1003
+                // small layering violation in favor of speed and memory usage:
1004
+                // we should allow the 'execute' method handle this, but in the
1005
+                // most common scenario (xmlrpcvals type server with some methods
1006
+                // registered as phpvals) that would mean a useless encode+decode pass
1007
+                if ($this->functions_parameters_type != 'xmlrpcvals' || (isset($this->dmap[$GLOBALS['_xh']['method']]['parameters_type']) && ($this->dmap[$GLOBALS['_xh']['method']]['parameters_type'] == 'phpvals')))
1008
+                {
1009
+                    if($this->debug > 1)
1010
+                    {
1011
+                        $this->debugmsg("\n+++PARSED+++\n".var_export($GLOBALS['_xh']['params'], true)."\n+++END+++");
1012
+                    }
1013
+                    $r = $this->execute($GLOBALS['_xh']['method'], $GLOBALS['_xh']['params'], $GLOBALS['_xh']['pt']);
1014
+                }
1015
+                else
1016
+                {
1017
+                    // build an xmlrpcmsg object with data parsed from xml
1018
+                    $m=new xmlrpcmsg($GLOBALS['_xh']['method']);
1019
+                    // now add parameters in
1020
+                    for($i=0; $i<count($GLOBALS['_xh']['params']); $i++)
1021
+                    {
1022
+                        $m->addParam($GLOBALS['_xh']['params'][$i]);
1023
+                    }
1024
+
1025
+                    if($this->debug > 1)
1026
+                    {
1027
+                        $this->debugmsg("\n+++PARSED+++\n".var_export($m, true)."\n+++END+++");
1028
+                    }
1029
+                    $r = $this->execute($m);
1030
+                }
1031
+            }
1032
+            return $r;
1033
+        }
1034
+
1035
+        /**
1036
+         * Execute a method invoked by the client, checking parameters used
1037
+         * @param mixed $m either an xmlrpcmsg obj or a method name
1038
+         * @param array $params array with method parameters as php types (if m is method name only)
1039
+         * @param array $paramtypes array with xmlrpc types of method parameters (if m is method name only)
1040
+         * @return xmlrpcresp
1041
+         * @access private
1042
+         */
1043
+        function execute($m, $params=null, $paramtypes=null)
1044
+        {
1045
+            if (is_object($m))
1046
+            {
1047
+                $methName = $m->method();
1048
+            }
1049
+            else
1050
+            {
1051
+                $methName = $m;
1052
+            }
1053
+            $sysCall = $this->allow_system_funcs && (strpos($methName, "system.") === 0);
1054
+            $dmap = $sysCall ? $GLOBALS['_xmlrpcs_dmap'] : $this->dmap;
1055
+
1056
+            if(!isset($dmap[$methName]['function']))
1057
+            {
1058
+                // No such method
1059
+                return new xmlrpcresp(0,
1060
+                    $GLOBALS['xmlrpcerr']['unknown_method'],
1061
+                    $GLOBALS['xmlrpcstr']['unknown_method']);
1062
+            }
1063
+
1064
+            // Check signature
1065
+            if(isset($dmap[$methName]['signature']))
1066
+            {
1067
+                $sig = $dmap[$methName]['signature'];
1068
+                if (is_object($m))
1069
+                {
1070
+                    list($ok, $errstr) = $this->verifySignature($m, $sig);
1071
+                }
1072
+                else
1073
+                {
1074
+                    list($ok, $errstr) = $this->verifySignature($paramtypes, $sig);
1075
+                }
1076
+                if(!$ok)
1077
+                {
1078
+                    // Didn't match.
1079
+                    return new xmlrpcresp(
1080
+                        0,
1081
+                        $GLOBALS['xmlrpcerr']['incorrect_params'],
1082
+                        $GLOBALS['xmlrpcstr']['incorrect_params'] . ": ${errstr}"
1083
+                    );
1084
+                }
1085
+            }
1086
+
1087
+            $func = $dmap[$methName]['function'];
1088
+            // let the 'class::function' syntax be accepted in dispatch maps
1089
+            if(is_string($func) && strpos($func, '::'))
1090
+            {
1091
+                $func = explode('::', $func);
1092
+            }
1093
+            // verify that function to be invoked is in fact callable
1094
+            if(!is_callable($func))
1095
+            {
1096
+                error_log("XML-RPC: ".__METHOD__.": function $func registered as method handler is not callable");
1097
+                return new xmlrpcresp(
1098
+                    0,
1099
+                    $GLOBALS['xmlrpcerr']['server_error'],
1100
+                    $GLOBALS['xmlrpcstr']['server_error'] . ": no function matches method"
1101
+                );
1102
+            }
1103
+
1104
+            // If debug level is 3, we should catch all errors generated during
1105
+            // processing of user function, and log them as part of response
1106
+            if($this->debug > 2)
1107
+            {
1108
+                $GLOBALS['_xmlrpcs_prev_ehandler'] = set_error_handler('_xmlrpcs_errorHandler');
1109
+            }
1110
+            try
1111
+            {
1112
+                // Allow mixed-convention servers
1113
+                if (is_object($m))
1114
+                {
1115
+                    if ($sysCall)
1116
+                    {
1117
+                        $r = call_user_func($func, $this, $m);
1118
+                    }
1119
+                    else
1120
+                    {
1121
+                        $r = call_user_func($func, $m);
1122
+                    }
1123
+                    if (!is_a($r, 'xmlrpcresp'))
1124
+                    {
1125
+                        error_log("XML-RPC: ".__METHOD__.": function $func registered as method handler does not return an xmlrpcresp object");
1126
+                        if (is_a($r, 'xmlrpcval'))
1127
+                        {
1128
+                            $r = new xmlrpcresp($r);
1129
+                        }
1130
+                        else
1131
+                        {
1132
+                            $r = new xmlrpcresp(
1133
+                                0,
1134
+                                $GLOBALS['xmlrpcerr']['server_error'],
1135
+                                $GLOBALS['xmlrpcstr']['server_error'] . ": function does not return xmlrpcresp object"
1136
+                            );
1137
+                        }
1138
+                    }
1139
+                }
1140
+                else
1141
+                {
1142
+                    // call a 'plain php' function
1143
+                    if($sysCall)
1144
+                    {
1145
+                        array_unshift($params, $this);
1146
+                        $r = call_user_func_array($func, $params);
1147
+                    }
1148
+                    else
1149
+                    {
1150
+                        // 3rd API convention for method-handling functions: EPI-style
1151
+                        if ($this->functions_parameters_type == 'epivals')
1152
+                        {
1153
+                            $r = call_user_func_array($func, array($methName, $params, $this->user_data));
1154
+                            // mimic EPI behaviour: if we get an array that looks like an error, make it
1155
+                            // an eror response
1156
+                            if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r))
1157
+                            {
1158
+                                $r = new xmlrpcresp(0, (integer)$r['faultCode'], (string)$r['faultString']);
1159
+                            }
1160
+                            else
1161
+                            {
1162
+                                // functions using EPI api should NOT return resp objects,
1163
+                                // so make sure we encode the return type correctly
1164
+                                $r = new xmlrpcresp(php_xmlrpc_encode($r, array('extension_api')));
1165
+                            }
1166
+                        }
1167
+                        else
1168
+                        {
1169
+                            $r = call_user_func_array($func, $params);
1170
+                        }
1171
+                    }
1172
+                    // the return type can be either an xmlrpcresp object or a plain php value...
1173
+                    if (!is_a($r, 'xmlrpcresp'))
1174
+                    {
1175
+                        // what should we assume here about automatic encoding of datetimes
1176
+                        // and php classes instances???
1177
+                        $r = new xmlrpcresp(php_xmlrpc_encode($r, $this->phpvals_encoding_options));
1178
+                    }
1179
+                }
1180
+            }
1181
+            catch(Exception $e)
1182
+            {
1183
+                // (barring errors in the lib) an uncatched exception happened
1184
+                // in the called function, we wrap it in a proper error-response
1185
+                switch($this->exception_handling)
1186
+                {
1187
+                    case 2:
1188
+                        throw $e;
1189
+                        break;
1190
+                    case 1:
1191
+                        $r = new xmlrpcresp(0, $e->getCode(), $e->getMessage());
1192
+                        break;
1193
+                    default:
1194
+                        $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_error'], $GLOBALS['xmlrpcstr']['server_error']);
1195
+                }
1196
+            }
1197
+            if($this->debug > 2)
1198
+            {
1199
+                // note: restore the error handler we found before calling the
1200
+                // user func, even if it has been changed inside the func itself
1201
+                if($GLOBALS['_xmlrpcs_prev_ehandler'])
1202
+                {
1203
+                    set_error_handler($GLOBALS['_xmlrpcs_prev_ehandler']);
1204
+                }
1205
+                else
1206
+                {
1207
+                    restore_error_handler();
1208
+                }
1209
+            }
1210
+            return $r;
1211
+        }
1212
+
1213
+        /**
1214
+         * add a string to the 'internal debug message' (separate from 'user debug message')
1215
+         * @param string $string
1216
+         * @access private
1217
+         */
1218
+        function debugmsg($string)
1219
+        {
1220
+            $this->debug_info .= $string."\n";
1221
+        }
1222
+
1223
+        /**
1224
+         * @access private
1225
+         */
1226
+        function xml_header($charset_encoding='')
1227
+        {
1228
+            if ($charset_encoding != '')
1229
+            {
1230
+                return "<?xml version=\"1.0\" encoding=\"$charset_encoding\"?" . ">\n";
1231
+            }
1232
+            else
1233
+            {
1234
+                return "<?xml version=\"1.0\"?" . ">\n";
1235
+            }
1236
+        }
1237
+
1238
+        /**
1239
+         * A debugging routine: just echoes back the input packet as a string value
1240
+         * @deprecated
1241
+         */
1242
+        function echoInput()
1243
+        {
1244
+            $r=new xmlrpcresp(new xmlrpcval( "'Aha said I: '" . $GLOBALS['HTTP_RAW_POST_DATA'], 'string'));
1245
+            print $r->serialize();
1246
+        }
1247
+    }
1248 1248
 ?>
Please login to merge, or discard this patch.