Passed
Push — php51 ( 29fddb...e2e917 )
by Gaetano
09:58
created

xmlrpcmsg::parseResponseHeaders()   F

Complexity

Conditions 44
Paths > 20000

Size

Total Lines 228
Code Lines 106

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 44
eloc 106
c 0
b 0
f 0
nc 28527
nop 2
dl 0
loc 228
rs 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
// by Edd Dumbill (C) 1999-2002
3
// <[email protected]>
4
5
// Copyright (c) 1999,2000,2002 Edd Dumbill.
6
// All rights reserved.
7
//
8
// Redistribution and use in source and binary forms, with or without
9
// modification, are permitted provided that the following conditions
10
// are met:
11
//
12
//    * Redistributions of source code must retain the above copyright
13
//      notice, this list of conditions and the following disclaimer.
14
//
15
//    * Redistributions in binary form must reproduce the above
16
//      copyright notice, this list of conditions and the following
17
//      disclaimer in the documentation and/or other materials provided
18
//      with the distribution.
19
//
20
//    * Neither the name of the "XML-RPC for PHP" nor the names of its
21
//      contributors may be used to endorse or promote products derived
22
//      from this software without specific prior written permission.
23
//
24
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28
// REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35
// OF THE POSSIBILITY OF SUCH DAMAGE.
36
37
	if(!function_exists('xml_parser_create'))
38
	{
39
		// For PHP 4 onward, XML functionality is always compiled-in on windows:
40
		// no more need to dl-open it. It might have been compiled out on *nix...
41
		if(strtoupper(substr(PHP_OS, 0, 3) != 'WIN'))
42
		{
43
			dl('xml.so');
0 ignored issues
show
Deprecated Code introduced by
The function dl() has been deprecated: 5.3.0 since 5.3.0 Loads a PHP extension at runtime ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

43
			/** @scrutinizer ignore-deprecated */ dl('xml.so');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
44
		}
45
	}
46
47
	// G. Giunta 2005/01/29: declare global these variables,
48
	// so that xmlrpc.inc will work even if included from within a function
49
	// Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used.
50
	$GLOBALS['xmlrpcI4']='i4';
51
	$GLOBALS['xmlrpcInt']='int';
52
	$GLOBALS['xmlrpcBoolean']='boolean';
53
	$GLOBALS['xmlrpcDouble']='double';
54
	$GLOBALS['xmlrpcString']='string';
55
	$GLOBALS['xmlrpcDateTime']='dateTime.iso8601';
56
	$GLOBALS['xmlrpcBase64']='base64';
57
	$GLOBALS['xmlrpcArray']='array';
58
	$GLOBALS['xmlrpcStruct']='struct';
59
	$GLOBALS['xmlrpcValue']='undefined';
60
61
	$GLOBALS['xmlrpcTypes']=array(
62
		$GLOBALS['xmlrpcI4']       => 1,
63
		$GLOBALS['xmlrpcInt']      => 1,
64
		$GLOBALS['xmlrpcBoolean']  => 1,
65
		$GLOBALS['xmlrpcString']   => 1,
66
		$GLOBALS['xmlrpcDouble']   => 1,
67
		$GLOBALS['xmlrpcDateTime'] => 1,
68
		$GLOBALS['xmlrpcBase64']   => 1,
69
		$GLOBALS['xmlrpcArray']    => 2,
70
		$GLOBALS['xmlrpcStruct']   => 3
71
	);
72
73
	$GLOBALS['xmlrpc_valid_parents'] = array(
74
		'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
75
		'BOOLEAN' => array('VALUE'),
76
		'I4' => array('VALUE'),
77
		'INT' => array('VALUE'),
78
		'STRING' => array('VALUE'),
79
		'DOUBLE' => array('VALUE'),
80
		'DATETIME.ISO8601' => array('VALUE'),
81
		'BASE64' => array('VALUE'),
82
		'MEMBER' => array('STRUCT'),
83
		'NAME' => array('MEMBER'),
84
		'DATA' => array('ARRAY'),
85
		'ARRAY' => array('VALUE'),
86
		'STRUCT' => array('VALUE'),
87
		'PARAM' => array('PARAMS'),
88
		'METHODNAME' => array('METHODCALL'),
89
		'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
90
		'FAULT' => array('METHODRESPONSE'),
91
		'NIL' => array('VALUE'), // only used when extension activated
92
		'EX:NIL' => array('VALUE') // only used when extension activated
93
	);
94
95
	// define extra types for supporting NULL (useful for json or <NIL/>)
96
	$GLOBALS['xmlrpcNull']='null';
97
	$GLOBALS['xmlrpcTypes']['null']=1;
98
99
	// Not in use anymore since 2.0. Shall we remove it?
100
	/// @deprecated
101
	$GLOBALS['xmlEntities']=array(
102
		'amp'  => '&',
103
		'quot' => '"',
104
		'lt'   => '<',
105
		'gt'   => '>',
106
		'apos' => "'"
107
	);
108
109
	// tables used for transcoding different charsets into us-ascii xml
110
111
	$GLOBALS['xml_iso88591_Entities']=array();
112
	$GLOBALS['xml_iso88591_Entities']['in'] = array();
113
	$GLOBALS['xml_iso88591_Entities']['out'] = array();
114
	for ($i = 0; $i < 32; $i++)
115
	{
116
		$GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
117
		$GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
118
	}
119
	for ($i = 160; $i < 256; $i++)
120
	{
121
		$GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
122
		$GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
123
	}
124
125
	/// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159?
126
	/// These will NOT be present in true ISO-8859-1, but will save the unwary
127
	/// windows user from sending junk (though no luck when reciving them...)
128
  /*
129
	$GLOBALS['xml_cp1252_Entities']=array();
130
	for ($i = 128; $i < 160; $i++)
131
	{
132
		$GLOBALS['xml_cp1252_Entities']['in'][] = chr($i);
133
	}
134
	$GLOBALS['xml_cp1252_Entities']['out'] = array(
135
		'&#x20AC;', '?',        '&#x201A;', '&#x0192;',
136
		'&#x201E;', '&#x2026;', '&#x2020;', '&#x2021;',
137
		'&#x02C6;', '&#x2030;', '&#x0160;', '&#x2039;',
138
		'&#x0152;', '?',        '&#x017D;', '?',
139
		'?',        '&#x2018;', '&#x2019;', '&#x201C;',
140
		'&#x201D;', '&#x2022;', '&#x2013;', '&#x2014;',
141
		'&#x02DC;', '&#x2122;', '&#x0161;', '&#x203A;',
142
		'&#x0153;', '?',        '&#x017E;', '&#x0178;'
143
	);
144
  */
145
146
	$GLOBALS['xmlrpcerr'] = array(
147
	'unknown_method'=>1,
148
	'invalid_return'=>2,
149
	'incorrect_params'=>3,
150
	'introspect_unknown'=>4,
151
	'http_error'=>5,
152
	'no_data'=>6,
153
	'no_ssl'=>7,
154
	'curl_fail'=>8,
155
	'invalid_request'=>15,
156
	'no_curl'=>16,
157
	'server_error'=>17,
158
	'multicall_error'=>18,
159
	'multicall_notstruct'=>9,
160
	'multicall_nomethod'=>10,
161
	'multicall_notstring'=>11,
162
	'multicall_recursion'=>12,
163
	'multicall_noparams'=>13,
164
	'multicall_notarray'=>14,
165
166
	'cannot_decompress'=>103,
167
	'decompress_fail'=>104,
168
	'dechunk_fail'=>105,
169
	'server_cannot_decompress'=>106,
170
	'server_decompress_fail'=>107
171
	);
172
173
	$GLOBALS['xmlrpcstr'] = array(
174
	'unknown_method'=>'Unknown method',
175
	'invalid_return'=>'Invalid return payload: enable debugging to examine incoming payload',
176
	'incorrect_params'=>'Incorrect parameters passed to method',
177
	'introspect_unknown'=>"Can't introspect: method unknown",
178
	'http_error'=>"Didn't receive 200 OK from remote server.",
179
	'no_data'=>'No data received from server.',
180
	'no_ssl'=>'No SSL support compiled in.',
181
	'curl_fail'=>'CURL error',
182
	'invalid_request'=>'Invalid request payload',
183
	'no_curl'=>'No CURL support compiled in.',
184
	'server_error'=>'Internal server error',
185
	'multicall_error'=>'Received from server invalid multicall response',
186
	'multicall_notstruct'=>'system.multicall expected struct',
187
	'multicall_nomethod'=>'missing methodName',
188
	'multicall_notstring'=>'methodName is not a string',
189
	'multicall_recursion'=>'recursive system.multicall forbidden',
190
	'multicall_noparams'=>'missing params',
191
	'multicall_notarray'=>'params is not an array',
192
193
	'cannot_decompress'=>'Received from server compressed HTTP and cannot decompress',
194
	'decompress_fail'=>'Received from server invalid compressed HTTP',
195
	'dechunk_fail'=>'Received from server invalid chunked HTTP',
196
	'server_cannot_decompress'=>'Received from client compressed HTTP request and cannot decompress',
197
	'server_decompress_fail'=>'Received from client invalid compressed HTTP request'
198
	);
199
200
	// The charset encoding used by the server for received messages and
201
	// by the client for received responses when received charset cannot be determined
202
	// or is not supported
203
	$GLOBALS['xmlrpc_defencoding']='UTF-8';
204
205
	// The encoding used internally by PHP.
206
	// String values received as xml will be converted to this, and php strings will be converted to xml
207
	// as if having been coded with this
208
	$GLOBALS['xmlrpc_internalencoding']='ISO-8859-1';
209
210
	$GLOBALS['xmlrpcName']='XML-RPC for PHP';
211
	$GLOBALS['xmlrpcVersion']='3.1.2';
212
213
	// let user errors start at 800
214
	$GLOBALS['xmlrpcerruser']=800;
215
	// let XML parse errors start at 100
216
	$GLOBALS['xmlrpcerrxml']=100;
217
218
	// formulate backslashes for escaping regexp
219
	// Not in use anymore since 2.0. Shall we remove it?
220
	/// @deprecated
221
	$GLOBALS['xmlrpc_backslash']=chr(92).chr(92);
222
223
	// set to TRUE to enable correct decoding of <NIL/> and <EX:NIL/> values
224
	$GLOBALS['xmlrpc_null_extension']=false;
225
226
	// set to TRUE to enable encoding of php NULL values to <EX:NIL/> instead of <NIL/>
227
	$GLOBALS['xmlrpc_null_apache_encoding']=false;
228
	$GLOBALS['xmlrpc_null_apache_encoding_ns']='http://ws.apache.org/xmlrpc/namespaces/extensions';
229
230
	// used to store state during parsing
231
	// quick explanation of components:
232
	//   ac - used to accumulate values
233
	//   isf - used to indicate a parsing fault (2) or xmlrpcresp fault (1)
234
	//   isf_reason - used for storing xmlrpcresp fault string
235
	//   lv - used to indicate "looking for a value": implements
236
	//        the logic to allow values with no types to be strings
237
	//   params - used to store parameters in method calls
238
	//   method - used to store method name
239
	//   stack - array with genealogy of xml elements names:
240
	//           used to validate nesting of xmlrpc elements
241
	$GLOBALS['_xh']=null;
242
243
	/**
244
	* Convert a string to the correct XML representation in a target charset
245
	* To help correct communication of non-ascii chars inside strings, regardless
246
	* of the charset used when sending requests, parsing them, sending responses
247
	* and parsing responses, an option is to convert all non-ascii chars present in the message
248
	* into their equivalent 'charset entity'. Charset entities enumerated this way
249
	* are independent of the charset encoding used to transmit them, and all XML
250
	* parsers are bound to understand them.
251
	* Note that in the std case we are not sending a charset encoding mime type
252
	* along with http headers, so we are bound by RFC 3023 to emit strict us-ascii.
253
	*
254
	* @todo do a bit of basic benchmarking (strtr vs. str_replace)
255
	* @todo	make usage of iconv() or recode_string() or mb_string() where available
256
	*/
257
	function xmlrpc_encode_entitites($data, $src_encoding='', $dest_encoding='')
258
	{
259
		if ($src_encoding == '')
260
		{
261
			// lame, but we know no better...
262
			$src_encoding = $GLOBALS['xmlrpc_internalencoding'];
263
		}
264
265
		switch(strtoupper($src_encoding.'_'.$dest_encoding))
266
		{
267
			case 'ISO-8859-1_':
268
			case 'ISO-8859-1_US-ASCII':
269
				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
270
				$escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
271
				break;
272
			case 'ISO-8859-1_UTF-8':
273
				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
274
				$escaped_data = utf8_encode($escaped_data);
275
				break;
276
			case 'ISO-8859-1_ISO-8859-1':
277
			case 'US-ASCII_US-ASCII':
278
			case 'US-ASCII_UTF-8':
279
			case 'US-ASCII_':
280
			case 'US-ASCII_ISO-8859-1':
281
			case 'UTF-8_UTF-8':
282
			//case 'CP1252_CP1252':
283
				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
284
				break;
285
			case 'UTF-8_':
286
			case 'UTF-8_US-ASCII':
287
			case 'UTF-8_ISO-8859-1':
288
	// NB: this will choke on invalid UTF-8, going most likely beyond EOF
289
	$escaped_data = '';
290
	// be kind to users creating string xmlrpcvals out of different php types
291
	$data = (string) $data;
292
	$ns = strlen ($data);
293
	for ($nn = 0; $nn < $ns; $nn++)
294
	{
295
		$ch = $data[$nn];
296
		$ii = ord($ch);
297
		//1 7 0bbbbbbb (127)
298
		if ($ii < 128)
299
		{
300
			/// @todo shall we replace this with a (supposedly) faster str_replace?
301
			switch($ii){
302
				case 34:
303
					$escaped_data .= '&quot;';
304
					break;
305
				case 38:
306
					$escaped_data .= '&amp;';
307
					break;
308
				case 39:
309
					$escaped_data .= '&apos;';
310
					break;
311
				case 60:
312
					$escaped_data .= '&lt;';
313
					break;
314
				case 62:
315
					$escaped_data .= '&gt;';
316
					break;
317
				default:
318
					$escaped_data .= $ch;
319
			} // switch
320
		}
321
		//2 11 110bbbbb 10bbbbbb (2047)
322
		else if ($ii>>5 == 6)
323
		{
324
			$b1 = ($ii & 31);
325
			$ii = ord($data[$nn+1]);
326
			$b2 = ($ii & 63);
327
			$ii = ($b1 * 64) + $b2;
328
			$ent = sprintf ('&#%d;', $ii);
329
			$escaped_data .= $ent;
330
			$nn += 1;
331
		}
332
		//3 16 1110bbbb 10bbbbbb 10bbbbbb
333
		else if ($ii>>4 == 14)
334
		{
335
			$b1 = ($ii & 15);
336
			$ii = ord($data[$nn+1]);
337
			$b2 = ($ii & 63);
338
			$ii = ord($data[$nn+2]);
339
			$b3 = ($ii & 63);
340
			$ii = ((($b1 * 64) + $b2) * 64) + $b3;
341
			$ent = sprintf ('&#%d;', $ii);
342
			$escaped_data .= $ent;
343
			$nn += 2;
344
		}
345
		//4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
346
		else if ($ii>>3 == 30)
347
		{
348
			$b1 = ($ii & 7);
349
			$ii = ord($data[$nn+1]);
350
			$b2 = ($ii & 63);
351
			$ii = ord($data[$nn+2]);
352
			$b3 = ($ii & 63);
353
			$ii = ord($data[$nn+3]);
354
			$b4 = ($ii & 63);
355
			$ii = ((((($b1 * 64) + $b2) * 64) + $b3) * 64) + $b4;
356
			$ent = sprintf ('&#%d;', $ii);
357
			$escaped_data .= $ent;
358
			$nn += 3;
359
		}
360
	}
361
				break;
362
/*
363
			case 'CP1252_':
364
			case 'CP1252_US-ASCII':
365
				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
366
				$escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
367
				$escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
368
				break;
369
			case 'CP1252_UTF-8':
370
				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
371
				/// @todo we could use real UTF8 chars here instead of xml entities... (note that utf_8 encode all allone will NOT convert them)
372
				$escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
373
				$escaped_data = utf8_encode($escaped_data);
374
				break;
375
			case 'CP1252_ISO-8859-1':
376
				$escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
377
				// we might as well replave all funky chars with a '?' here, but we are kind and leave it to the receiving application layer to decide what to do with these weird entities...
378
				$escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
379
				break;
380
*/
381
			default:
382
				$escaped_data = '';
383
				error_log("Converting from $src_encoding to $dest_encoding: not supported...");
384
		}
385
		return $escaped_data;
386
	}
387
388
	/// xml parser handler function for opening element tags
389
	function xmlrpc_se($parser, $name, $attrs, $accept_single_vals=false)
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

389
	function xmlrpc_se(/** @scrutinizer ignore-unused */ $parser, $name, $attrs, $accept_single_vals=false)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
390
	{
391
		// if invalid xmlrpc already detected, skip all processing
392
		if ($GLOBALS['_xh']['isf'] < 2)
393
		{
394
			// check for correct element nesting
395
			// top level element can only be of 2 types
396
			/// @todo optimization creep: save this check into a bool variable, instead of using count() every time:
397
			///       there is only a single top level element in xml anyway
398
			if (count($GLOBALS['_xh']['stack']) == 0)
399
			{
400
				if ($name != 'METHODRESPONSE' && $name != 'METHODCALL' && (
401
					$name != 'VALUE' && !$accept_single_vals))
402
				{
403
					$GLOBALS['_xh']['isf'] = 2;
404
					$GLOBALS['_xh']['isf_reason'] = 'missing top level xmlrpc element';
405
					return;
406
				}
407
				else
408
				{
409
					$GLOBALS['_xh']['rt'] = strtolower($name);
410
					$GLOBALS['_xh']['rt'] = strtolower($name);
411
				}
412
			}
413
			else
414
			{
415
				// not top level element: see if parent is OK
416
				$parent = end($GLOBALS['_xh']['stack']);
417
				if (!array_key_exists($name, $GLOBALS['xmlrpc_valid_parents']) || !in_array($parent, $GLOBALS['xmlrpc_valid_parents'][$name]))
418
				{
419
					$GLOBALS['_xh']['isf'] = 2;
420
					$GLOBALS['_xh']['isf_reason'] = "xmlrpc element $name cannot be child of $parent";
421
					return;
422
				}
423
			}
424
425
			switch($name)
426
			{
427
				// optimize for speed switch cases: most common cases first
428
				case 'VALUE':
429
					/// @todo we could check for 2 VALUE elements inside a MEMBER or PARAM element
430
					$GLOBALS['_xh']['vt']='value'; // indicator: no value found yet
431
					$GLOBALS['_xh']['ac']='';
432
					$GLOBALS['_xh']['lv']=1;
433
					$GLOBALS['_xh']['php_class']=null;
434
					break;
435
				case 'I4':
436
				case 'INT':
437
				case 'STRING':
438
				case 'BOOLEAN':
439
				case 'DOUBLE':
440
				case 'DATETIME.ISO8601':
441
				case 'BASE64':
442
					if ($GLOBALS['_xh']['vt']!='value')
443
					{
444
						//two data elements inside a value: an error occurred!
445
						$GLOBALS['_xh']['isf'] = 2;
446
						$GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
447
						return;
448
					}
449
					$GLOBALS['_xh']['ac']=''; // reset the accumulator
450
					break;
451
				case 'STRUCT':
452
				case 'ARRAY':
453
					if ($GLOBALS['_xh']['vt']!='value')
454
					{
455
						//two data elements inside a value: an error occurred!
456
						$GLOBALS['_xh']['isf'] = 2;
457
						$GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
458
						return;
459
					}
460
					// create an empty array to hold child values, and push it onto appropriate stack
461
					$cur_val = array();
462
					$cur_val['values'] = array();
463
					$cur_val['type'] = $name;
464
					// check for out-of-band information to rebuild php objs
465
					// and in case it is found, save it
466
					if (@isset($attrs['PHP_CLASS']))
467
					{
468
						$cur_val['php_class'] = $attrs['PHP_CLASS'];
469
					}
470
					$GLOBALS['_xh']['valuestack'][] = $cur_val;
471
					$GLOBALS['_xh']['vt']='data'; // be prepared for a data element next
472
					break;
473
				case 'DATA':
474
					if ($GLOBALS['_xh']['vt']!='data')
475
					{
476
						//two data elements inside a value: an error occurred!
477
						$GLOBALS['_xh']['isf'] = 2;
478
						$GLOBALS['_xh']['isf_reason'] = "found two data elements inside an array element";
479
						return;
480
					}
481
				case 'METHODCALL':
482
				case 'METHODRESPONSE':
483
				case 'PARAMS':
484
					// valid elements that add little to processing
485
					break;
486
				case 'METHODNAME':
487
				case 'NAME':
488
					/// @todo we could check for 2 NAME elements inside a MEMBER element
489
					$GLOBALS['_xh']['ac']='';
490
					break;
491
				case 'FAULT':
492
					$GLOBALS['_xh']['isf']=1;
493
					break;
494
				case 'MEMBER':
495
					$GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name']=''; // set member name to null, in case we do not find in the xml later on
496
					//$GLOBALS['_xh']['ac']='';
497
					// Drop trough intentionally
498
				case 'PARAM':
499
					// clear value type, so we can check later if no value has been passed for this param/member
500
					$GLOBALS['_xh']['vt']=null;
501
					break;
502
				case 'NIL':
503
				case 'EX:NIL':
504
					if ($GLOBALS['xmlrpc_null_extension'])
505
					{
506
						if ($GLOBALS['_xh']['vt']!='value')
507
						{
508
							//two data elements inside a value: an error occurred!
509
							$GLOBALS['_xh']['isf'] = 2;
510
							$GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
511
							return;
512
						}
513
						$GLOBALS['_xh']['ac']=''; // reset the accumulator
514
						break;
515
					}
516
					// we do not support the <NIL/> extension, so
517
					// drop through intentionally
518
				default:
519
					/// INVALID ELEMENT: RAISE ISF so that it is later recognized!!!
520
					$GLOBALS['_xh']['isf'] = 2;
521
					$GLOBALS['_xh']['isf_reason'] = "found not-xmlrpc xml element $name";
522
					break;
523
			}
524
525
			// Save current element name to stack, to validate nesting
526
			$GLOBALS['_xh']['stack'][] = $name;
527
528
			/// @todo optimization creep: move this inside the big switch() above
529
			if($name!='VALUE')
530
			{
531
				$GLOBALS['_xh']['lv']=0;
532
			}
533
		}
534
	}
535
536
	/// Used in decoding xml chunks that might represent single xmlrpc values
537
	function xmlrpc_se_any($parser, $name, $attrs)
538
	{
539
		xmlrpc_se($parser, $name, $attrs, true);
540
	}
541
542
	/// xml parser handler function for close element tags
543
	function xmlrpc_ee($parser, $name, $rebuild_xmlrpcvals = true)
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

543
	function xmlrpc_ee(/** @scrutinizer ignore-unused */ $parser, $name, $rebuild_xmlrpcvals = true)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
544
	{
545
		if ($GLOBALS['_xh']['isf'] < 2)
546
		{
547
			// push this element name from stack
548
			// NB: if XML validates, correct opening/closing is guaranteed and
549
			// we do not have to check for $name == $curr_elem.
550
			// we also checked for proper nesting at start of elements...
551
			$curr_elem = array_pop($GLOBALS['_xh']['stack']);
0 ignored issues
show
Unused Code introduced by
The assignment to $curr_elem is dead and can be removed.
Loading history...
552
553
			switch($name)
554
			{
555
				case 'VALUE':
556
					// This if() detects if no scalar was inside <VALUE></VALUE>
557
					if ($GLOBALS['_xh']['vt']=='value')
558
					{
559
						$GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
560
						$GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcString'];
561
					}
562
563
					if ($rebuild_xmlrpcvals)
564
					{
565
						// build the xmlrpc val out of the data received, and substitute it
566
						$temp = new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']);
567
						// in case we got info about underlying php class, save it
568
						// in the object we're rebuilding
569
						if (isset($GLOBALS['_xh']['php_class']))
570
							$temp->_php_class = $GLOBALS['_xh']['php_class'];
571
						// check if we are inside an array or struct:
572
						// if value just built is inside an array, let's move it into array on the stack
573
						$vscount = count($GLOBALS['_xh']['valuestack']);
574
						if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
575
						{
576
							$GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $temp;
577
						}
578
						else
579
						{
580
							$GLOBALS['_xh']['value'] = $temp;
581
						}
582
					}
583
					else
584
					{
585
						/// @todo this needs to treat correctly php-serialized objects,
586
						/// since std deserializing is done by php_xmlrpc_decode,
587
						/// which we will not be calling...
588
						if (isset($GLOBALS['_xh']['php_class']))
589
						{
590
						}
591
592
						// check if we are inside an array or struct:
593
						// if value just built is inside an array, let's move it into array on the stack
594
						$vscount = count($GLOBALS['_xh']['valuestack']);
595
						if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
596
						{
597
							$GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $GLOBALS['_xh']['value'];
598
						}
599
					}
600
					break;
601
				case 'BOOLEAN':
602
				case 'I4':
603
				case 'INT':
604
				case 'STRING':
605
				case 'DOUBLE':
606
				case 'DATETIME.ISO8601':
607
				case 'BASE64':
608
					$GLOBALS['_xh']['vt']=strtolower($name);
609
					/// @todo: optimization creep - remove the if/elseif cycle below
610
					/// since the case() in which we are already did that
611
					if ($name=='STRING')
612
					{
613
						$GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
614
					}
615
					elseif ($name=='DATETIME.ISO8601')
616
					{
617
						if (!preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $GLOBALS['_xh']['ac']))
618
						{
619
							error_log('XML-RPC: invalid value received in DATETIME: '.$GLOBALS['_xh']['ac']);
620
						}
621
						$GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcDateTime'];
622
						$GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
623
					}
624
					elseif ($name=='BASE64')
625
					{
626
						/// @todo check for failure of base64 decoding / catch warnings
627
						$GLOBALS['_xh']['value']=base64_decode($GLOBALS['_xh']['ac']);
628
					}
629
					elseif ($name=='BOOLEAN')
630
					{
631
						// special case here: we translate boolean 1 or 0 into PHP
632
						// constants true or false.
633
						// Strings 'true' and 'false' are accepted, even though the
634
						// spec never mentions them (see eg. Blogger api docs)
635
						// NB: this simple checks helps a lot sanitizing input, ie no
636
						// security problems around here
637
						if ($GLOBALS['_xh']['ac']=='1' || strcasecmp($GLOBALS['_xh']['ac'], 'true') == 0)
638
						{
639
							$GLOBALS['_xh']['value']=true;
640
						}
641
						else
642
						{
643
							// log if receiveing something strange, even though we set the value to false anyway
644
							if ($GLOBALS['_xh']['ac']!='0' && strcasecmp($GLOBALS['_xh']['ac'], 'false') != 0)
645
								error_log('XML-RPC: invalid value received in BOOLEAN: '.$GLOBALS['_xh']['ac']);
646
							$GLOBALS['_xh']['value']=false;
647
						}
648
					}
649
					elseif ($name=='DOUBLE')
650
					{
651
						// we have a DOUBLE
652
						// we must check that only 0123456789-.<space> are characters here
653
						// NOTE: regexp could be much stricter than this...
654
						if (!preg_match('/^[+-eE0123456789 \t.]+$/', $GLOBALS['_xh']['ac']))
655
						{
656
							/// @todo: find a better way of throwing an error than this!
657
							error_log('XML-RPC: non numeric value received in DOUBLE: '.$GLOBALS['_xh']['ac']);
658
							$GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
659
						}
660
						else
661
						{
662
							// it's ok, add it on
663
							$GLOBALS['_xh']['value']=(double)$GLOBALS['_xh']['ac'];
664
						}
665
					}
666
					else
667
					{
668
						// we have an I4/INT
669
						// we must check that only 0123456789-<space> are characters here
670
						if (!preg_match('/^[+-]?[0123456789 \t]+$/', $GLOBALS['_xh']['ac']))
671
						{
672
							/// @todo find a better way of throwing an error than this!
673
							error_log('XML-RPC: non numeric value received in INT: '.$GLOBALS['_xh']['ac']);
674
							$GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
675
						}
676
						else
677
						{
678
							// it's ok, add it on
679
							$GLOBALS['_xh']['value']=(int)$GLOBALS['_xh']['ac'];
680
						}
681
					}
682
					//$GLOBALS['_xh']['ac']=''; // is this necessary?
683
					$GLOBALS['_xh']['lv']=3; // indicate we've found a value
684
					break;
685
				case 'NAME':
686
					$GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name'] = $GLOBALS['_xh']['ac'];
687
					break;
688
				case 'MEMBER':
689
					//$GLOBALS['_xh']['ac']=''; // is this necessary?
690
					// add to array in the stack the last element built,
691
					// unless no VALUE was found
692
					if ($GLOBALS['_xh']['vt'])
693
					{
694
						$vscount = count($GLOBALS['_xh']['valuestack']);
695
						$GLOBALS['_xh']['valuestack'][$vscount-1]['values'][$GLOBALS['_xh']['valuestack'][$vscount-1]['name']] = $GLOBALS['_xh']['value'];
696
					} else
697
						error_log('XML-RPC: missing VALUE inside STRUCT in received xml');
698
					break;
699
				case 'DATA':
700
					//$GLOBALS['_xh']['ac']=''; // is this necessary?
701
					$GLOBALS['_xh']['vt']=null; // reset this to check for 2 data elements in a row - even if they're empty
702
					break;
703
				case 'STRUCT':
704
				case 'ARRAY':
705
					// fetch out of stack array of values, and promote it to current value
706
					$curr_val = array_pop($GLOBALS['_xh']['valuestack']);
707
					$GLOBALS['_xh']['value'] = $curr_val['values'];
708
					$GLOBALS['_xh']['vt']=strtolower($name);
709
					if (isset($curr_val['php_class']))
710
					{
711
						$GLOBALS['_xh']['php_class'] = $curr_val['php_class'];
712
					}
713
					break;
714
				case 'PARAM':
715
					// add to array of params the current value,
716
					// unless no VALUE was found
717
					if ($GLOBALS['_xh']['vt'])
718
					{
719
						$GLOBALS['_xh']['params'][]=$GLOBALS['_xh']['value'];
720
						$GLOBALS['_xh']['pt'][]=$GLOBALS['_xh']['vt'];
721
					}
722
					else
723
						error_log('XML-RPC: missing VALUE inside PARAM in received xml');
724
					break;
725
				case 'METHODNAME':
726
					$GLOBALS['_xh']['method']=preg_replace('/^[\n\r\t ]+/', '', $GLOBALS['_xh']['ac']);
727
					break;
728
				case 'NIL':
729
				case 'EX:NIL':
730
					if ($GLOBALS['xmlrpc_null_extension'])
731
					{
732
						$GLOBALS['_xh']['vt']='null';
733
						$GLOBALS['_xh']['value']=null;
734
						$GLOBALS['_xh']['lv']=3;
735
						break;
736
					}
737
					// drop through intentionally if nil extension not enabled
738
				case 'PARAMS':
739
				case 'FAULT':
740
				case 'METHODCALL':
741
				case 'METHORESPONSE':
742
					break;
743
				default:
744
					// End of INVALID ELEMENT!
745
					// shall we add an assert here for unreachable code???
746
					break;
747
			}
748
		}
749
	}
750
751
	/// Used in decoding xmlrpc requests/responses without rebuilding xmlrpc values
752
	function xmlrpc_ee_fast($parser, $name)
753
	{
754
		xmlrpc_ee($parser, $name, false);
755
	}
756
757
	/// xml parser handler function for character data
758
	function xmlrpc_cd($parser, $data)
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

758
	function xmlrpc_cd(/** @scrutinizer ignore-unused */ $parser, $data)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
759
	{
760
		// skip processing if xml fault already detected
761
		if ($GLOBALS['_xh']['isf'] < 2)
762
		{
763
			// "lookforvalue==3" means that we've found an entire value
764
			// and should discard any further character data
765
			if($GLOBALS['_xh']['lv']!=3)
766
			{
767
				// G. Giunta 2006-08-23: useless change of 'lv' from 1 to 2
768
				//if($GLOBALS['_xh']['lv']==1)
769
				//{
770
					// if we've found text and we're just in a <value> then
771
					// say we've found a value
772
					//$GLOBALS['_xh']['lv']=2;
773
				//}
774
				// we always initialize the accumulator before starting parsing, anyway...
775
				//if(!@isset($GLOBALS['_xh']['ac']))
776
				//{
777
				//	$GLOBALS['_xh']['ac'] = '';
778
				//}
779
				$GLOBALS['_xh']['ac'].=$data;
780
			}
781
		}
782
	}
783
784
	/// xml parser handler function for 'other stuff', ie. not char data or
785
	/// element start/end tag. In fact it only gets called on unknown entities...
786
	function xmlrpc_dh($parser, $data)
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

786
	function xmlrpc_dh(/** @scrutinizer ignore-unused */ $parser, $data)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
787
	{
788
		// skip processing if xml fault already detected
789
		if ($GLOBALS['_xh']['isf'] < 2)
790
		{
791
			if(substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';')
792
			{
793
				// G. Giunta 2006-08-25: useless change of 'lv' from 1 to 2
794
				//if($GLOBALS['_xh']['lv']==1)
795
				//{
796
				//	$GLOBALS['_xh']['lv']=2;
797
				//}
798
				$GLOBALS['_xh']['ac'].=$data;
799
			}
800
		}
801
		return true;
802
	}
803
804
	class xmlrpc_client
805
	{
806
		var $path;
807
		var $server;
808
		var $port=0;
809
		var $method='http';
810
		var $errno;
811
		var $errstr;
812
		var $debug=0;
813
		var $username='';
814
		var $password='';
815
		var $authtype=1;
816
		var $cert='';
817
		var $certpass='';
818
		var $cacert='';
819
		var $cacertdir='';
820
		var $key='';
821
		var $keypass='';
822
		var $verifypeer=true;
823
		var $verifyhost=1;
824
		var $sslversion=0; // corresponds to CURL_SSLVERSION_DEFAULT
825
		var $no_multicall=false;
826
		var $proxy='';
827
		var $proxyport=0;
828
		var $proxy_user='';
829
		var $proxy_pass='';
830
		var $proxy_authtype=1;
831
		var $cookies=array();
832
		var $extracurlopts=array();
833
834
		/**
835
		* List of http compression methods accepted by the client for responses.
836
		* NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
837
		*
838
		* NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since
839
		* in those cases it will be up to CURL to decide the compression methods
840
		* it supports. You might check for the presence of 'zlib' in the output of
841
		* curl_version() to determine wheter compression is supported or not
842
		*/
843
		var $accepted_compression = array();
844
		/**
845
		* Name of compression scheme to be used for sending requests.
846
		* Either null, gzip or deflate
847
		*/
848
		var $request_compression = '';
849
		/**
850
		* CURL handle: used for keep-alive connections (PHP 4.3.8 up, see:
851
		* http://curl.haxx.se/docs/faq.html#7.3)
852
		*/
853
		var $xmlrpc_curl_handle = null;
854
		/// Whether to use persistent connections for http 1.1 and https
855
		var $keepalive = false;
856
		/// Charset encodings that can be decoded without problems by the client
857
		var $accepted_charset_encodings = array();
858
		/// Charset encoding to be used in serializing request. NULL = use ASCII
859
		var $request_charset_encoding = '';
860
		/**
861
		* Decides the content of xmlrpcresp objects returned by calls to send()
862
		* valid strings are 'xmlrpcvals', 'phpvals' or 'xml'
863
		*/
864
		var $return_type = 'xmlrpcvals';
865
		/**
866
		* Sent to servers in http headers
867
		*/
868
		var $user_agent;
869
870
		/**
871
		* @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php
872
		* @param string $server the server name / ip address
873
		* @param integer $port the port the server is listening on, defaults to 80 or 443 depending on protocol used
874
		* @param string $method the http protocol variant: defaults to 'http', 'https' and 'http11' can be used if CURL is installed
875
		*/
876
		function __construct($path, $server='', $port='', $method='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
877
		{
878
			// allow user to specify all params in $path
879
			if($server == '' and $port == '' and $method == '')
880
			{
881
				$parts = parse_url($path);
882
				$server = $parts['host'];
883
				$path = isset($parts['path']) ? $parts['path'] : '';
884
				if(isset($parts['query']))
885
				{
886
					$path .= '?'.$parts['query'];
887
				}
888
				if(isset($parts['fragment']))
889
				{
890
					$path .= '#'.$parts['fragment'];
891
				}
892
				if(isset($parts['port']))
893
				{
894
					$port = $parts['port'];
895
				}
896
				if(isset($parts['scheme']))
897
				{
898
					$method = $parts['scheme'];
899
				}
900
				if(isset($parts['user']))
901
				{
902
					$this->username = $parts['user'];
903
				}
904
				if(isset($parts['pass']))
905
				{
906
					$this->password = $parts['pass'];
907
				}
908
			}
909
			if($path == '' || $path[0] != '/')
910
			{
911
				$this->path='/'.$path;
912
			}
913
			else
914
			{
915
				$this->path=$path;
916
			}
917
			$this->server=$server;
918
			if($port != '')
919
			{
920
				$this->port=$port;
921
			}
922
			if($method != '')
923
			{
924
				$this->method=$method;
925
			}
926
927
			// if ZLIB is enabled, let the client by default accept compressed responses
928
			if(function_exists('gzinflate') || (
929
				function_exists('curl_init') && (($info = curl_version()) &&
930
				((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version'])))
931
			))
932
			{
933
				$this->accepted_compression = array('gzip', 'deflate');
934
			}
935
936
			// keepalives: enabled by default
937
			$this->keepalive = true;
938
939
			// by default the xml parser can support these 3 charset encodings
940
			$this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
941
942
			// initialize user_agent string
943
			$this->user_agent = $GLOBALS['xmlrpcName'] . ' ' . $GLOBALS['xmlrpcVersion'];
944
		}
945
946
		/**
947
		* @deprecated
948
		*/
949
		function xmlrpc_client($path, $server='', $port='', $method='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
950
		{
951
			self::__construct($path, $server, $port, $method);
0 ignored issues
show
Bug Best Practice introduced by
The method xmlrpc_client::__construct() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

951
			self::/** @scrutinizer ignore-call */ 
952
         __construct($path, $server, $port, $method);
Loading history...
952
		}
953
954
		/**
955
		* Enables/disables the echoing to screen of the xmlrpc responses received
956
		* @param integer $in values 0, 1 and 2 are supported (2 = echo sent msg too, before received response)
957
		* @access public
958
		*/
959
		function setDebug($in)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
960
		{
961
			$this->debug=$in;
962
		}
963
964
		/**
965
		* Add some http BASIC AUTH credentials, used by the client to authenticate
966
		* @param string $u username
967
		* @param string $p password
968
		* @param integer $t auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC (basic auth)
969
		* @access public
970
		*/
971
		function setCredentials($u, $p, $t=1)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
972
		{
973
			$this->username=$u;
974
			$this->password=$p;
975
			$this->authtype=$t;
976
		}
977
978
		/**
979
		* Add a client-side https certificate
980
		* @param string $cert
981
		* @param string $certpass
982
		* @access public
983
		*/
984
		function setCertificate($cert, $certpass)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
985
		{
986
			$this->cert = $cert;
987
			$this->certpass = $certpass;
988
		}
989
990
		/**
991
		* Add a CA certificate to verify server with (see man page about
992
		* CURLOPT_CAINFO for more details)
993
		* @param string $cacert certificate file name (or dir holding certificates)
994
		* @param bool $is_dir set to true to indicate cacert is a dir. defaults to false
995
		* @access public
996
		*/
997
		function setCaCertificate($cacert, $is_dir=false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
998
		{
999
			if ($is_dir)
1000
			{
1001
				$this->cacertdir = $cacert;
1002
			}
1003
			else
1004
			{
1005
				$this->cacert = $cacert;
1006
			}
1007
		}
1008
1009
		/**
1010
		* Set attributes for SSL communication: private SSL key
1011
		* NB: does not work in older php/curl installs
1012
		* Thanks to Daniel Convissor
1013
		* @param string $key The name of a file containing a private SSL key
1014
		* @param string $keypass The secret password needed to use the private SSL key
1015
		* @access public
1016
		*/
1017
		function setKey($key, $keypass)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1018
		{
1019
			$this->key = $key;
1020
			$this->keypass = $keypass;
1021
		}
1022
1023
		/**
1024
		* Set attributes for SSL communication: verify server certificate
1025
		* @param bool $i enable/disable verification of peer certificate
1026
		* @access public
1027
		*/
1028
		function setSSLVerifyPeer($i)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1029
		{
1030
			$this->verifypeer = $i;
1031
		}
1032
1033
		/**
1034
		* Set attributes for SSL communication: verify match of server cert w. hostname
1035
		* @param int $i
1036
		* @access public
1037
		*/
1038
		function setSSLVerifyHost($i)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1039
		{
1040
			$this->verifyhost = $i;
1041
		}
1042
1043
		/**
1044
		* Set attributes for SSL communication: SSL version to use. Best left at 0 (default value ): let cURL decide
1045
		*
1046
		* @param int $i
1047
		*/
1048
		public function setSSLVersion($i)
1049
		{
1050
			$this->sslversion = $i;
1051
		}
1052
1053
		/**
1054
		* Set proxy info
1055
		* @param string $proxyhost
1056
		* @param string $proxyport Defaults to 8080 for HTTP and 443 for HTTPS
1057
		* @param string $proxyusername Leave blank if proxy has public access
1058
		* @param string $proxypassword Leave blank if proxy has public access
1059
		* @param int $proxyauthtype set to constant CURLAUTH_NTLM to use NTLM auth with proxy
1060
		* @access public
1061
		*/
1062
		function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1063
		{
1064
			$this->proxy = $proxyhost;
1065
			$this->proxyport = $proxyport;
1066
			$this->proxy_user = $proxyusername;
1067
			$this->proxy_pass = $proxypassword;
1068
			$this->proxy_authtype = $proxyauthtype;
1069
		}
1070
1071
		/**
1072
		* Enables/disables reception of compressed xmlrpc responses.
1073
		* Note that enabling reception of compressed responses merely adds some standard
1074
		* http headers to xmlrpc requests. It is up to the xmlrpc server to return
1075
		* compressed responses when receiving such requests.
1076
		* @param string $compmethod either 'gzip', 'deflate', 'any' or ''
1077
		* @access public
1078
		*/
1079
		function setAcceptedCompression($compmethod)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1080
		{
1081
			if ($compmethod == 'any')
1082
				$this->accepted_compression = array('gzip', 'deflate');
1083
			else
1084
				if ($compmethod == false )
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $compmethod of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
1085
					$this->accepted_compression = array();
1086
				else
1087
					$this->accepted_compression = array($compmethod);
1088
		}
1089
1090
		/**
1091
		* Enables/disables http compression of xmlrpc request.
1092
		* Take care when sending compressed requests: servers might not support them
1093
		* (and automatic fallback to uncompressed requests is not yet implemented)
1094
		* @param string $compmethod either 'gzip', 'deflate' or ''
1095
		* @access public
1096
		*/
1097
		function setRequestCompression($compmethod)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1098
		{
1099
			$this->request_compression = $compmethod;
1100
		}
1101
1102
		/**
1103
		* Adds a cookie to list of cookies that will be sent to server.
1104
		* NB: setting any param but name and value will turn the cookie into a 'version 1' cookie:
1105
		* do not do it unless you know what you are doing
1106
		* @param string $name
1107
		* @param string $value
1108
		* @param string $path
1109
		* @param string $domain
1110
		* @param int $port
1111
		* @access public
1112
		*
1113
		* @todo check correctness of urlencoding cookie value (copied from php way of doing it...)
1114
		*/
1115
		function setCookie($name, $value='', $path='', $domain='', $port=null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1116
		{
1117
			$this->cookies[$name]['value'] = urlencode($value);
1118
			if ($path || $domain || $port)
0 ignored issues
show
Bug Best Practice introduced by
The expression $port of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1119
			{
1120
				$this->cookies[$name]['path'] = $path;
1121
				$this->cookies[$name]['domain'] = $domain;
1122
				$this->cookies[$name]['port'] = $port;
1123
				$this->cookies[$name]['version'] = 1;
1124
			}
1125
			else
1126
			{
1127
				$this->cookies[$name]['version'] = 0;
1128
			}
1129
		}
1130
1131
		/**
1132
		* Directly set cURL options, for extra flexibility
1133
		* It allows eg. to bind client to a specific IP interface / address
1134
		* @param array $options
1135
		*/
1136
		function SetCurlOptions( $options )
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1137
		{
1138
			$this->extracurlopts = $options;
1139
		}
1140
1141
		/**
1142
		* Set user-agent string that will be used by this client instance
1143
		* in http headers sent to the server
1144
		*/
1145
		function SetUserAgent( $agentstring )
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1146
		{
1147
			$this->user_agent = $agentstring;
1148
		}
1149
1150
		/**
1151
		* Send an xmlrpc request
1152
		* @param mixed $msg The message object, or an array of messages for using multicall, or the complete xml representation of a request
1153
		* @param integer $timeout Connection timeout, in seconds, If unspecified, a platform specific timeout will apply
1154
		* @param string $method if left unspecified, the http protocol chosen during creation of the object will be used
1155
		* @return xmlrpcresp
1156
		* @access public
1157
		*/
1158
		function& send($msg, $timeout=0, $method='')
1159
		{
1160
			// if user deos not specify http protocol, use native method of this client
1161
			// (i.e. method set during call to constructor)
1162
			if($method == '')
1163
			{
1164
				$method = $this->method;
1165
			}
1166
1167
			if(is_array($msg))
1168
			{
1169
				// $msg is an array of xmlrpcmsg's
1170
				$r = $this->multicall($msg, $timeout, $method);
1171
				return $r;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $r returns the type array|xmlrpcresp[] which is incompatible with the documented return type xmlrpcresp.
Loading history...
1172
			}
1173
			elseif(is_string($msg))
1174
			{
1175
				$n = new xmlrpcmsg('');
1176
				$n->payload = $msg;
1177
				$msg = $n;
1178
			}
1179
1180
			// where msg is an xmlrpcmsg
1181
			$msg->debug=$this->debug;
1182
1183
			if($method == 'https')
1184
			{
1185
				$r =& $this->sendPayloadHTTPS(
1186
					$msg,
1187
					$this->server,
1188
					$this->port,
1189
					$timeout,
1190
					$this->username,
1191
					$this->password,
1192
					$this->authtype,
1193
					$this->cert,
1194
					$this->certpass,
1195
					$this->cacert,
1196
					$this->cacertdir,
1197
					$this->proxy,
1198
					$this->proxyport,
1199
					$this->proxy_user,
1200
					$this->proxy_pass,
1201
					$this->proxy_authtype,
1202
					$this->keepalive,
1203
					$this->key,
1204
					$this->keypass,
1205
					$this->sslversion
1206
				);
1207
			}
1208
			elseif($method == 'http11')
1209
			{
1210
				$r =& $this->sendPayloadCURL(
1211
					$msg,
1212
					$this->server,
1213
					$this->port,
1214
					$timeout,
1215
					$this->username,
1216
					$this->password,
1217
					$this->authtype,
1218
					null,
1219
					null,
1220
					null,
1221
					null,
1222
					$this->proxy,
1223
					$this->proxyport,
1224
					$this->proxy_user,
1225
					$this->proxy_pass,
1226
					$this->proxy_authtype,
1227
					'http',
1228
					$this->keepalive
1229
				);
1230
			}
1231
			else
1232
			{
1233
				$r =& $this->sendPayloadHTTP10(
1234
					$msg,
1235
					$this->server,
1236
					$this->port,
1237
					$timeout,
1238
					$this->username,
1239
					$this->password,
1240
					$this->authtype,
1241
					$this->proxy,
1242
					$this->proxyport,
1243
					$this->proxy_user,
1244
					$this->proxy_pass,
1245
					$this->proxy_authtype
1246
				);
1247
			}
1248
1249
			return $r;
1250
		}
1251
1252
		/**
1253
		* @access private
1254
		*/
1255
		function &sendPayloadHTTP10($msg, $server, $port, $timeout=0,
1256
			$username='', $password='', $authtype=1, $proxyhost='',
1257
			$proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1)
1258
		{
1259
			if($port==0)
1260
			{
1261
				$port=80;
1262
			}
1263
1264
			// Only create the payload if it was not created previously
1265
			if(empty($msg->payload))
1266
			{
1267
				$msg->createPayload($this->request_charset_encoding);
1268
			}
1269
1270
			$payload = $msg->payload;
1271
			// Deflate request body and set appropriate request headers
1272
			if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1273
			{
1274
				if($this->request_compression == 'gzip')
1275
				{
1276
					$a = @gzencode($payload);
1277
					if($a)
1278
					{
1279
						$payload = $a;
1280
						$encoding_hdr = "Content-Encoding: gzip\r\n";
1281
					}
1282
				}
1283
				else
1284
				{
1285
					$a = @gzcompress($payload);
1286
					if($a)
1287
					{
1288
						$payload = $a;
1289
						$encoding_hdr = "Content-Encoding: deflate\r\n";
1290
					}
1291
				}
1292
			}
1293
			else
1294
			{
1295
				$encoding_hdr = '';
1296
			}
1297
1298
			// thanks to Grant Rauscher <[email protected]> for this
1299
			$credentials='';
1300
			if($username!='')
1301
			{
1302
				$credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
1303
				if ($authtype != 1)
1304
				{
1305
					error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported with HTTP 1.0');
1306
				}
1307
			}
1308
1309
			$accepted_encoding = '';
1310
			if(is_array($this->accepted_compression) && count($this->accepted_compression))
1311
			{
1312
				$accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n";
1313
			}
1314
1315
			$proxy_credentials = '';
1316
			if($proxyhost)
1317
			{
1318
				if($proxyport == 0)
1319
				{
1320
					$proxyport = 8080;
1321
				}
1322
				$connectserver = $proxyhost;
1323
				$connectport = $proxyport;
1324
				$uri = 'http://'.$server.':'.$port.$this->path;
1325
				if($proxyusername != '')
1326
				{
1327
					if ($proxyauthtype != 1)
1328
					{
1329
						error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported with HTTP 1.0');
1330
					}
1331
					$proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n";
1332
				}
1333
			}
1334
			else
1335
			{
1336
				$connectserver = $server;
1337
				$connectport = $port;
1338
				$uri = $this->path;
1339
			}
1340
1341
			// Cookie generation, as per rfc2965 (version 1 cookies) or
1342
			// netscape's rules (version 0 cookies)
1343
			$cookieheader='';
1344
			if (count($this->cookies))
1345
			{
1346
				$version = '';
1347
				foreach ($this->cookies as $name => $cookie)
1348
				{
1349
					if ($cookie['version'])
1350
					{
1351
						$version = ' $Version="' . $cookie['version'] . '";';
1352
						$cookieheader .= ' ' . $name . '="' . $cookie['value'] . '";';
1353
						if ($cookie['path'])
1354
							$cookieheader .= ' $Path="' . $cookie['path'] . '";';
1355
						if ($cookie['domain'])
1356
							$cookieheader .= ' $Domain="' . $cookie['domain'] . '";';
1357
						if ($cookie['port'])
1358
							$cookieheader .= ' $Port="' . $cookie['port'] . '";';
1359
					}
1360
					else
1361
					{
1362
						$cookieheader .= ' ' . $name . '=' . $cookie['value'] . ";";
1363
					}
1364
				}
1365
				$cookieheader = 'Cookie:' . $version . substr($cookieheader, 0, -1) . "\r\n";
1366
			}
1367
1368
			// omit port if 80
1369
			$port = ($port == 80) ? '' : (':' . $port);
1370
1371
			$op= 'POST ' . $uri. " HTTP/1.0\r\n" .
1372
				'User-Agent: ' . $this->user_agent . "\r\n" .
1373
				'Host: '. $server . $port . "\r\n" .
1374
				$credentials .
1375
				$proxy_credentials .
1376
				$accepted_encoding .
1377
				$encoding_hdr .
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $encoding_hdr does not seem to be defined for all execution paths leading up to this point.
Loading history...
1378
				'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings) . "\r\n" .
1379
				$cookieheader .
1380
				'Content-Type: ' . $msg->content_type . "\r\nContent-Length: " .
1381
				strlen($payload) . "\r\n\r\n" .
1382
				$payload;
1383
1384
			if($this->debug > 1)
1385
			{
1386
				print "<PRE>\n---SENDING---\n" . htmlentities($op) . "\n---END---\n</PRE>";
1387
				// let the client see this now in case http times out...
1388
				flush();
1389
			}
1390
1391
			if($timeout>0)
1392
			{
1393
				$fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout);
1394
			}
1395
			else
1396
			{
1397
				$fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr);
1398
			}
1399
			if($fp)
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
1400
			{
1401
				if($timeout>0 && function_exists('stream_set_timeout'))
1402
				{
1403
					stream_set_timeout($fp, $timeout);
1404
				}
1405
			}
1406
			else
1407
			{
1408
				$this->errstr='Connect error: '.$this->errstr;
1409
				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')');
1410
				return $r;
1411
			}
1412
1413
			if(!fputs($fp, $op, strlen($op)))
1414
			{
1415
				fclose($fp);
1416
				$this->errstr='Write error';
1417
				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr);
1418
				return $r;
1419
			}
1420
			else
1421
			{
1422
				// reset errno and errstr on successful socket connection
1423
				$this->errstr = '';
1424
			}
1425
			// G. Giunta 2005/10/24: close socket before parsing.
1426
			// should yield slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
1427
			$ipd='';
1428
			do
1429
			{
1430
				// shall we check for $data === FALSE?
1431
				// as per the manual, it signals an error
1432
				$ipd.=fread($fp, 32768);
1433
			} while(!feof($fp));
1434
			fclose($fp);
1435
			$r =& $msg->parseResponse($ipd, false, $this->return_type);
1436
			return $r;
1437
1438
		}
1439
1440
		/**
1441
		* @access private
1442
		*/
1443
		function &sendPayloadHTTPS($msg, $server, $port, $timeout=0, $username='',
1444
			$password='', $authtype=1, $cert='',$certpass='', $cacert='', $cacertdir='',
1445
			$proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1,
1446
			$keepalive=false, $key='', $keypass='', $sslVersion = 0)
1447
		{
1448
			$r =& $this->sendPayloadCURL($msg, $server, $port, $timeout, $username,
1449
				$password, $authtype, $cert, $certpass, $cacert, $cacertdir, $proxyhost, $proxyport,
1450
				$proxyusername, $proxypassword, $proxyauthtype, 'https', $keepalive, $key, $keypass, $sslVersion);
1451
			return $r;
1452
		}
1453
1454
		/**
1455
		* Contributed by Justin Miller <[email protected]>
1456
		* Requires curl to be built into PHP
1457
		* NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
1458
		* @access private
1459
		*/
1460
		function &sendPayloadCURL($msg, $server, $port, $timeout=0, $username='',
1461
			$password='', $authtype=1, $cert='', $certpass='', $cacert='', $cacertdir='',
1462
			$proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1, $method='https',
1463
			$keepalive=false, $key='', $keypass='', $sslVersion = 0)
1464
		{
1465
			if(!function_exists('curl_init'))
1466
			{
1467
				$this->errstr='CURL unavailable on this install';
1468
				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']);
1469
				return $r;
1470
			}
1471
			if($method == 'https')
1472
			{
1473
				if(($info = curl_version()) &&
1474
					((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version']))))
0 ignored issues
show
introduced by
The condition is_string($info) is always false.
Loading history...
1475
				{
1476
					$this->errstr='SSL unavailable on this install';
1477
					$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']);
1478
					return $r;
1479
				}
1480
			}
1481
1482
			if($port == 0)
1483
			{
1484
				if($method == 'http')
1485
				{
1486
					$port = 80;
1487
				}
1488
				else
1489
				{
1490
					$port = 443;
1491
				}
1492
			}
1493
1494
			// Only create the payload if it was not created previously
1495
			if(empty($msg->payload))
1496
			{
1497
				$msg->createPayload($this->request_charset_encoding);
1498
			}
1499
1500
			// Deflate request body and set appropriate request headers
1501
			$payload = $msg->payload;
1502
			if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1503
			{
1504
				if($this->request_compression == 'gzip')
1505
				{
1506
					$a = @gzencode($payload);
1507
					if($a)
1508
					{
1509
						$payload = $a;
1510
						$encoding_hdr = 'Content-Encoding: gzip';
1511
					}
1512
				}
1513
				else
1514
				{
1515
					$a = @gzcompress($payload);
1516
					if($a)
1517
					{
1518
						$payload = $a;
1519
						$encoding_hdr = 'Content-Encoding: deflate';
1520
					}
1521
				}
1522
			}
1523
			else
1524
			{
1525
				$encoding_hdr = '';
1526
			}
1527
1528
			if($this->debug > 1)
1529
			{
1530
				print "<PRE>\n---SENDING---\n" . htmlentities($payload) . "\n---END---\n</PRE>";
1531
				// let the client see this now in case http times out...
1532
				flush();
1533
			}
1534
1535
			if(!$keepalive || !$this->xmlrpc_curl_handle)
1536
			{
1537
				$curl = curl_init($method . '://' . $server . ':' . $port . $this->path);
1538
				if($keepalive)
1539
				{
1540
					$this->xmlrpc_curl_handle = $curl;
1541
				}
1542
			}
1543
			else
1544
			{
1545
				$curl = $this->xmlrpc_curl_handle;
1546
			}
1547
1548
			// results into variable
1549
			curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1550
1551
			if($this->debug)
1552
			{
1553
				curl_setopt($curl, CURLOPT_VERBOSE, 1);
1554
			}
1555
			curl_setopt($curl, CURLOPT_USERAGENT, $this->user_agent);
1556
			// required for XMLRPC: post the data
1557
			curl_setopt($curl, CURLOPT_POST, 1);
1558
			// the data
1559
			curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
1560
1561
			// return the header too
1562
			curl_setopt($curl, CURLOPT_HEADER, 1);
1563
1564
			// NB: if we set an empty string, CURL will add http header indicating
1565
			// ALL methods it is supporting. This is possibly a better option than
1566
			// letting the user tell what curl can / cannot do...
1567
			if(is_array($this->accepted_compression) && count($this->accepted_compression))
1568
			{
1569
				//curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression));
1570
				// empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1571
				if (count($this->accepted_compression) == 1)
1572
				{
1573
					curl_setopt($curl, CURLOPT_ENCODING, $this->accepted_compression[0]);
1574
				}
1575
				else
1576
					curl_setopt($curl, CURLOPT_ENCODING, '');
1577
			}
1578
			// extra headers
1579
			$headers = array('Content-Type: ' . $msg->content_type , 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings));
1580
			// if no keepalive is wanted, let the server know it in advance
1581
			if(!$keepalive)
1582
			{
1583
				$headers[] = 'Connection: close';
1584
			}
1585
			// request compression header
1586
			if($encoding_hdr)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $encoding_hdr does not seem to be defined for all execution paths leading up to this point.
Loading history...
1587
			{
1588
				$headers[] = $encoding_hdr;
1589
			}
1590
1591
			curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
1592
			// timeout is borked
1593
			if($timeout)
1594
			{
1595
				curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
1596
			}
1597
1598
			if($username && $password)
1599
			{
1600
				curl_setopt($curl, CURLOPT_USERPWD, $username.':'.$password);
1601
				if (defined('CURLOPT_HTTPAUTH'))
1602
				{
1603
					curl_setopt($curl, CURLOPT_HTTPAUTH, $authtype);
1604
				}
1605
				else if ($authtype != 1)
1606
				{
1607
					error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported by the current PHP/curl install');
1608
				}
1609
			}
1610
1611
			if($method == 'https')
1612
			{
1613
				// set cert file
1614
				if($cert)
1615
				{
1616
					curl_setopt($curl, CURLOPT_SSLCERT, $cert);
1617
				}
1618
				// set cert password
1619
				if($certpass)
1620
				{
1621
					curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass);
1622
				}
1623
				// whether to verify remote host's cert
1624
				curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
1625
				// set ca certificates file/dir
1626
				if($cacert)
1627
				{
1628
					curl_setopt($curl, CURLOPT_CAINFO, $cacert);
1629
				}
1630
				if($cacertdir)
1631
				{
1632
					curl_setopt($curl, CURLOPT_CAPATH, $cacertdir);
1633
				}
1634
				// set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1635
				if($key)
1636
				{
1637
					curl_setopt($curl, CURLOPT_SSLKEY, $key);
1638
				}
1639
				// set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1640
				if($keypass)
1641
				{
1642
					curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $keypass);
1643
				}
1644
1645
				// Upgrade transparently to more stringent check for versions of php which do not support otherwise.
1646
				// Doing it in constructor would be cleaner; doing it here saves us a couple of function calls
1647
				if($this->verifyhost == 1 && $info = curl_version() && version_compare($info['version'], '7.28.1') >= 0)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $info does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility introduced by
Consider adding parentheses for clarity. Current Interpretation: $info = (curl_version() ...sion'], '7.28.1') >= 0), Probably Intended Meaning: ($info = curl_version())...rsion'], '7.28.1') >= 0
Loading history...
Unused Code introduced by
The assignment to $info is dead and can be removed.
Loading history...
1648
				{
1649
					$this->verifyhost = 2;
1650
				}
1651
				// whether to verify cert's common name (CN); 0 for no, 1 to verify that it exists, and 2 to verify that it matches the hostname used
1652
				curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
1653
				// allow usage of different SSL versions
1654
				curl_setopt($curl, CURLOPT_SSLVERSION, $sslVersion);
1655
			}
1656
1657
			// proxy info
1658
			if($proxyhost)
1659
			{
1660
				if($proxyport == 0)
1661
				{
1662
					$proxyport = 8080; // NB: even for HTTPS, local connection is on port 8080
1663
				}
1664
				curl_setopt($curl, CURLOPT_PROXY, $proxyhost.':'.$proxyport);
1665
				//curl_setopt($curl, CURLOPT_PROXYPORT,$proxyport);
1666
				if($proxyusername)
1667
				{
1668
					curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyusername.':'.$proxypassword);
1669
					if (defined('CURLOPT_PROXYAUTH'))
1670
					{
1671
						curl_setopt($curl, CURLOPT_PROXYAUTH, $proxyauthtype);
1672
					}
1673
					else if ($proxyauthtype != 1)
1674
					{
1675
						error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported by the current PHP/curl install');
1676
					}
1677
				}
1678
			}
1679
1680
			// NB: should we build cookie http headers by hand rather than let CURL do it?
1681
			// the following code does not honour 'expires', 'path' and 'domain' cookie attributes
1682
			// set to client obj the the user...
1683
			if (count($this->cookies))
1684
			{
1685
				$cookieheader = '';
1686
				foreach ($this->cookies as $name => $cookie)
1687
				{
1688
					$cookieheader .= $name . '=' . $cookie['value'] . '; ';
1689
				}
1690
				curl_setopt($curl, CURLOPT_COOKIE, substr($cookieheader, 0, -2));
1691
			}
1692
1693
			foreach ($this->extracurlopts as $opt => $val)
1694
			{
1695
				curl_setopt($curl, $opt, $val);
1696
			}
1697
1698
			$result = curl_exec($curl);
1699
1700
			if ($this->debug > 1)
1701
			{
1702
				print "<PRE>\n---CURL INFO---\n";
1703
				foreach(curl_getinfo($curl) as $name => $val)
1704
				{
1705
					if (is_array($val))
1706
					{
1707
						$val = implode("\n", $val);
1708
					}
1709
					print $name . ': ' . htmlentities($val) . "\n";
1710
				}
1711
1712
				print "---END---\n</PRE>";
1713
			}
1714
1715
			if(!$result) /// @todo we should use a better check here - what if we get back '' or '0'?
1716
			{
1717
				$this->errstr='no response';
1718
				$resp=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['curl_fail'], $GLOBALS['xmlrpcstr']['curl_fail']. ': '. curl_error($curl));
1719
				curl_close($curl);
1720
				if($keepalive)
1721
				{
1722
					$this->xmlrpc_curl_handle = null;
1723
				}
1724
			}
1725
			else
1726
			{
1727
				if(!$keepalive)
1728
				{
1729
					curl_close($curl);
1730
				}
1731
				$resp =& $msg->parseResponse($result, true, $this->return_type);
1732
				// if we got back a 302, we can not reuse the curl handle for later calls
1733
				if($resp->faultCode() == $GLOBALS['xmlrpcerr']['http_error'] && $keepalive)
1734
				{
1735
					curl_close($curl);
1736
					$this->xmlrpc_curl_handle = null;
1737
				}
1738
			}
1739
			return $resp;
1740
		}
1741
1742
		/**
1743
		* Send an array of request messages and return an array of responses.
1744
		* Unless $this->no_multicall has been set to true, it will try first
1745
		* to use one single xmlrpc call to server method system.multicall, and
1746
		* revert to sending many successive calls in case of failure.
1747
		* This failure is also stored in $this->no_multicall for subsequent calls.
1748
		* Unfortunately, there is no server error code universally used to denote
1749
		* the fact that multicall is unsupported, so there is no way to reliably
1750
		* distinguish between that and a temporary failure.
1751
		* If you are sure that server supports multicall and do not want to
1752
		* fallback to using many single calls, set the fourth parameter to FALSE.
1753
		*
1754
		* NB: trying to shoehorn extra functionality into existing syntax has resulted
1755
		* in pretty much convoluted code...
1756
		*
1757
		* @param array $msgs an array of xmlrpcmsg objects
1758
		* @param integer $timeout connection timeout (in seconds)
1759
		* @param string $method the http protocol variant to be used
1760
		* @param boolean fallback When true, upon receiving an error during multicall, multiple single calls will be attempted
0 ignored issues
show
Bug introduced by
The type fallback was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1761
		* @return array
1762
		* @access public
1763
		*/
1764
		function multicall($msgs, $timeout=0, $method='', $fallback=true)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1765
		{
1766
			if ($method == '')
1767
			{
1768
				$method = $this->method;
1769
			}
1770
			if(!$this->no_multicall)
1771
			{
1772
				$results = $this->_try_multicall($msgs, $timeout, $method);
1773
				if(is_array($results))
1774
				{
1775
					// System.multicall succeeded
1776
					return $results;
1777
				}
1778
				else
1779
				{
1780
					// either system.multicall is unsupported by server,
1781
					// or call failed for some other reason.
1782
					if ($fallback)
1783
					{
1784
						// Don't try it next time...
1785
						$this->no_multicall = true;
1786
					}
1787
					else
1788
					{
1789
						if (is_a($results, 'xmlrpcresp'))
0 ignored issues
show
Bug introduced by
It seems like $results can also be of type false; however, parameter $object of is_a() does only seem to accept object|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1789
						if (is_a(/** @scrutinizer ignore-type */ $results, 'xmlrpcresp'))
Loading history...
1790
						{
1791
							$result = $results;
1792
						}
1793
						else
1794
						{
1795
							$result = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['multicall_error'], $GLOBALS['xmlrpcstr']['multicall_error']);
1796
						}
1797
					}
1798
				}
1799
			}
1800
			else
1801
			{
1802
				// override fallback, in case careless user tries to do two
1803
				// opposite things at the same time
1804
				$fallback = true;
1805
			}
1806
1807
			$results = array();
1808
			if ($fallback)
1809
			{
1810
				// system.multicall is (probably) unsupported by server:
1811
				// emulate multicall via multiple requests
1812
				foreach($msgs as $msg)
1813
				{
1814
					$results[] =& $this->send($msg, $timeout, $method);
1815
				}
1816
			}
1817
			else
1818
			{
1819
				// user does NOT want to fallback on many single calls:
1820
				// since we should always return an array of responses,
1821
				// return an array with the same error repeated n times
1822
				foreach($msgs as $msg)
1823
				{
1824
					$results[] = $result;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.
Loading history...
1825
				}
1826
			}
1827
			return $results;
1828
		}
1829
1830
		/**
1831
		* Attempt to boxcar $msgs via system.multicall.
1832
		* Returns either an array of xmlrpcreponses, an xmlrpc error response
1833
		* or false (when received response does not respect valid multicall syntax)
1834
		* @access private
1835
		*/
1836
		function _try_multicall($msgs, $timeout, $method)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1837
		{
1838
			// Construct multicall message
1839
			$calls = array();
1840
			foreach($msgs as $msg)
1841
			{
1842
				$call['methodName'] = new xmlrpcval($msg->method(),'string');
1843
				$numParams = $msg->getNumParams();
1844
				$params = array();
1845
				for($i = 0; $i < $numParams; $i++)
1846
				{
1847
					$params[$i] = $msg->getParam($i);
1848
				}
1849
				$call['params'] = new xmlrpcval($params, 'array');
1850
				$calls[] = new xmlrpcval($call, 'struct');
1851
			}
1852
			$multicall = new xmlrpcmsg('system.multicall');
1853
			$multicall->addParam(new xmlrpcval($calls, 'array'));
1854
1855
			// Attempt RPC call
1856
			$result =& $this->send($multicall, $timeout, $method);
1857
1858
			if($result->faultCode() != 0)
1859
			{
1860
				// call to system.multicall failed
1861
				return $result;
1862
			}
1863
1864
			// Unpack responses.
1865
			$rets = $result->value();
1866
1867
			if ($this->return_type == 'xml')
1868
			{
1869
					return $rets;
1870
			}
1871
			else if ($this->return_type == 'phpvals')
1872
			{
1873
				///@todo test this code branch...
1874
				$rets = $result->value();
1875
				if(!is_array($rets))
0 ignored issues
show
introduced by
The condition is_array($rets) is always false.
Loading history...
1876
				{
1877
					return false;		// bad return type from system.multicall
1878
				}
1879
				$numRets = count($rets);
1880
				if($numRets != count($msgs))
1881
				{
1882
					return false;		// wrong number of return values.
1883
				}
1884
1885
				$response = array();
1886
				for($i = 0; $i < $numRets; $i++)
1887
				{
1888
					$val = $rets[$i];
1889
					if (!is_array($val)) {
1890
						return false;
1891
					}
1892
					switch(count($val))
1893
					{
1894
						case 1:
1895
							if(!isset($val[0]))
1896
							{
1897
								return false;		// Bad value
1898
							}
1899
							// Normal return value
1900
							$response[$i] = new xmlrpcresp($val[0], 0, '', 'phpvals');
1901
							break;
1902
						case 2:
1903
							///	@todo remove usage of @: it is apparently quite slow
1904
							$code = @$val['faultCode'];
1905
							if(!is_int($code))
1906
							{
1907
								return false;
1908
							}
1909
							$str = @$val['faultString'];
1910
							if(!is_string($str))
1911
							{
1912
								return false;
1913
							}
1914
							$response[$i] = new xmlrpcresp(0, $code, $str);
1915
							break;
1916
						default:
1917
							return false;
1918
					}
1919
				}
1920
				return $response;
1921
			}
1922
			else // return type == 'xmlrpcvals'
1923
			{
1924
				$rets = $result->value();
1925
				if($rets->kindOf() != 'array')
0 ignored issues
show
Bug introduced by
The method kindOf() does not exist on integer. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1925
				if($rets->/** @scrutinizer ignore-call */ kindOf() != 'array')

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1926
				{
1927
					return false;		// bad return type from system.multicall
1928
				}
1929
				$numRets = $rets->arraysize();
1930
				if($numRets != count($msgs))
1931
				{
1932
					return false;		// wrong number of return values.
1933
				}
1934
1935
				$response = array();
1936
				for($i = 0; $i < $numRets; $i++)
1937
				{
1938
					$val = $rets->arraymem($i);
1939
					switch($val->kindOf())
1940
					{
1941
						case 'array':
1942
							if($val->arraysize() != 1)
1943
							{
1944
								return false;		// Bad value
1945
							}
1946
							// Normal return value
1947
							$response[$i] = new xmlrpcresp($val->arraymem(0));
1948
							break;
1949
						case 'struct':
1950
							$code = $val->structmem('faultCode');
1951
							if($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
1952
							{
1953
								return false;
1954
							}
1955
							$str = $val->structmem('faultString');
1956
							if($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
1957
							{
1958
								return false;
1959
							}
1960
							$response[$i] = new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
1961
							break;
1962
						default:
1963
							return false;
1964
					}
1965
				}
1966
				return $response;
1967
			}
1968
		}
1969
	} // end class xmlrpc_client
1970
1971
	class xmlrpcresp
1972
	{
1973
		var $val = 0;
1974
		var $valtyp;
1975
		var $errno = 0;
1976
		var $errstr = '';
1977
		var $payload;
1978
		var $hdrs = array();
1979
		var $_cookies = array();
1980
		var $content_type = 'text/xml';
1981
		var $raw_data = '';
1982
1983
		/**
1984
		* @param mixed $val either an xmlrpcval obj, a php value or the xml serialization of an xmlrpcval (a string)
1985
		* @param integer $fcode set it to anything but 0 to create an error response
1986
		* @param string $fstr the error string, in case of an error response
1987
		* @param string $valtyp either 'xmlrpcvals', 'phpvals' or 'xml'
1988
		*
1989
		* @todo add check that $val / $fcode / $fstr is of correct type???
1990
		* NB: as of now we do not do it, since it might be either an xmlrpcval or a plain
1991
		* php val, or a complete xml chunk, depending on usage of xmlrpc_client::send() inside which creator is called...
1992
		*/
1993
		function __construct($val, $fcode = 0, $fstr = '', $valtyp='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1994
		{
1995
			if($fcode != 0)
1996
			{
1997
				// error response
1998
				$this->errno = $fcode;
1999
				$this->errstr = $fstr;
2000
				//$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
2001
			}
2002
			else
2003
			{
2004
				// successful response
2005
				$this->val = $val;
2006
				if ($valtyp == '')
2007
				{
2008
					// user did not declare type of response value: try to guess it
2009
					if (is_object($this->val) && is_a($this->val, 'xmlrpcval'))
2010
					{
2011
						$this->valtyp = 'xmlrpcvals';
2012
					}
2013
					else if (is_string($this->val))
2014
					{
2015
						$this->valtyp = 'xml';
2016
2017
					}
2018
					else
2019
					{
2020
						$this->valtyp = 'phpvals';
2021
					}
2022
				}
2023
				else
2024
				{
2025
					// user declares type of resp value: believe him
2026
					$this->valtyp = $valtyp;
2027
				}
2028
			}
2029
		}
2030
2031
		/**
2032
		* @deprecated
2033
		*/
2034
		function xmlrpcresp($val, $fcode = 0, $fstr = '', $valtyp='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2035
		{
2036
			self::__construct($val, $fcode, $fstr, $valtyp);
0 ignored issues
show
Bug Best Practice introduced by
The method xmlrpcresp::__construct() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2036
			self::/** @scrutinizer ignore-call */ __construct($val, $fcode, $fstr, $valtyp);
Loading history...
2037
		}
2038
2039
		/**
2040
		* Returns the error code of the response.
2041
		* @return integer the error code of this response (0 for not-error responses)
2042
		* @access public
2043
		*/
2044
		function faultCode()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2045
		{
2046
			return $this->errno;
2047
		}
2048
2049
		/**
2050
		* Returns the error code of the response.
2051
		* @return string the error string of this response ('' for not-error responses)
2052
		* @access public
2053
		*/
2054
		function faultString()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2055
		{
2056
			return $this->errstr;
2057
		}
2058
2059
		/**
2060
		* Returns the value received by the server.
2061
		* @return mixed the xmlrpcval object returned by the server. Might be an xml string or php value if the response has been created by specially configured xmlrpc_client objects
2062
		* @access public
2063
		*/
2064
		function value()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2065
		{
2066
			return $this->val;
2067
		}
2068
2069
		/**
2070
		* Returns an array with the cookies received from the server.
2071
		* Array has the form: $cookiename => array ('value' => $val, $attr1 => $val1, $attr2 = $val2, ...)
2072
		* with attributes being e.g. 'expires', 'path', domain'.
2073
		* NB: cookies sent as 'expired' by the server (i.e. with an expiry date in the past)
2074
		* are still present in the array. It is up to the user-defined code to decide
2075
		* how to use the received cookies, and whether they have to be sent back with the next
2076
		* request to the server (using xmlrpc_client::setCookie) or not
2077
		* @return array array of cookies received from the server
2078
		* @access public
2079
		*/
2080
		function cookies()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2081
		{
2082
			return $this->_cookies;
2083
		}
2084
2085
		/**
2086
		* Returns xml representation of the response. XML prologue not included
2087
		* @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
2088
		* @return string the xml representation of the response
2089
		* @access public
2090
		*/
2091
		function serialize($charset_encoding='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2092
		{
2093
			if ($charset_encoding != '')
2094
				$this->content_type = 'text/xml; charset=' . $charset_encoding;
2095
			else
2096
				$this->content_type = 'text/xml';
2097
			if ($GLOBALS['xmlrpc_null_apache_encoding'])
2098
			{
2099
				$result = "<methodResponse xmlns:ex=\"".$GLOBALS['xmlrpc_null_apache_encoding_ns']."\">\n";
2100
			}
2101
			else
2102
			{
2103
			$result = "<methodResponse>\n";
2104
			}
2105
			if($this->errno)
2106
			{
2107
				// G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
2108
				// by xml-encoding non ascii chars
2109
				$result .= "<fault>\n" .
2110
"<value>\n<struct><member><name>faultCode</name>\n<value><int>" . $this->errno .
2111
"</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>" .
2112
xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "</string></value>\n</member>\n" .
2113
"</struct>\n</value>\n</fault>";
2114
			}
2115
			else
2116
			{
2117
				if(!is_object($this->val) || !is_a($this->val, 'xmlrpcval'))
0 ignored issues
show
introduced by
The condition is_object($this->val) is always false.
Loading history...
2118
				{
2119
					if (is_string($this->val) && $this->valtyp == 'xml')
0 ignored issues
show
introduced by
The condition is_string($this->val) is always false.
Loading history...
2120
					{
2121
						$result .= "<params>\n<param>\n" .
2122
							$this->val .
2123
							"</param>\n</params>";
2124
					}
2125
					else
2126
					{
2127
						/// @todo try to build something serializable?
2128
						die('cannot serialize xmlrpcresp objects whose content is native php values');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2129
					}
2130
				}
2131
				else
2132
				{
2133
					$result .= "<params>\n<param>\n" .
2134
						$this->val->serialize($charset_encoding) .
2135
						"</param>\n</params>";
2136
				}
2137
			}
2138
			$result .= "\n</methodResponse>";
2139
			$this->payload = $result;
2140
			return $result;
2141
		}
2142
	}
2143
2144
	class xmlrpcmsg
2145
	{
2146
		var $payload;
2147
		var $methodname;
2148
		var $params=array();
2149
		var $debug=0;
2150
		var $content_type = 'text/xml';
2151
2152
		/**
2153
		* @param string $meth the name of the method to invoke
2154
		* @param array $pars array of parameters to be passed to the method (xmlrpcval objects)
2155
		*/
2156
		function __construct($meth, $pars=0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2157
		{
2158
			$this->methodname=$meth;
2159
			if(is_array($pars) && count($pars)>0)
2160
			{
2161
				for($i=0; $i<count($pars); $i++)
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
2162
				{
2163
					$this->addParam($pars[$i]);
2164
				}
2165
			}
2166
		}
2167
2168
		/**
2169
		* @deprecated
2170
		*/
2171
		function xmlrpcmsg($meth, $pars=0)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2172
		{
2173
			self::__construct($meth, $pars);
0 ignored issues
show
Bug Best Practice introduced by
The method xmlrpcmsg::__construct() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2173
			self::/** @scrutinizer ignore-call */ 
2174
         __construct($meth, $pars);
Loading history...
2174
		}
2175
2176
		/**
2177
		* @access private
2178
		*/
2179
		function xml_header($charset_encoding='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2180
		{
2181
			if ($charset_encoding != '')
2182
			{
2183
				return "<?xml version=\"1.0\" encoding=\"$charset_encoding\" ?" . ">\n<methodCall>\n";
2184
			}
2185
			else
2186
			{
2187
				return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n";
2188
			}
2189
		}
2190
2191
		/**
2192
		* @access private
2193
		*/
2194
		function xml_footer()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2195
		{
2196
			return '</methodCall>';
2197
		}
2198
2199
		/**
2200
		* @access private
2201
		*/
2202
		function kindOf()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2203
		{
2204
			return 'msg';
2205
		}
2206
2207
		/**
2208
		* @access private
2209
		*/
2210
		function createPayload($charset_encoding='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2211
		{
2212
			if ($charset_encoding != '')
2213
				$this->content_type = 'text/xml; charset=' . $charset_encoding;
2214
			else
2215
				$this->content_type = 'text/xml';
2216
			$this->payload=$this->xml_header($charset_encoding);
2217
			$this->payload.='<methodName>' . xmlrpc_encode_entitites($this->methodname, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "</methodName>\n";
2218
			$this->payload.="<params>\n";
2219
			for($i=0; $i<count($this->params); $i++)
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
2220
			{
2221
				$p=$this->params[$i];
2222
				$this->payload.="<param>\n" . $p->serialize($charset_encoding) .
2223
				"</param>\n";
2224
			}
2225
			$this->payload.="</params>\n";
2226
			$this->payload.=$this->xml_footer();
2227
		}
2228
2229
		/**
2230
		* Gets/sets the xmlrpc method to be invoked
2231
		* @param string $meth the method to be set (leave empty not to set it)
2232
		* @return string the method that will be invoked
2233
		* @access public
2234
		*/
2235
		function method($meth='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2236
		{
2237
			if($meth!='')
2238
			{
2239
				$this->methodname=$meth;
2240
			}
2241
			return $this->methodname;
2242
		}
2243
2244
		/**
2245
		* Returns xml representation of the message. XML prologue included
2246
		* @param string $charset_encoding
2247
		* @return string the xml representation of the message, xml prologue included
2248
		* @access public
2249
		*/
2250
		function serialize($charset_encoding='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2251
		{
2252
			$this->createPayload($charset_encoding);
2253
			return $this->payload;
2254
		}
2255
2256
		/**
2257
		* Add a parameter to the list of parameters to be used upon method invocation
2258
		* @param xmlrpcval $par
2259
		* @return boolean false on failure
2260
		* @access public
2261
		*/
2262
		function addParam($par)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2263
		{
2264
			// add check: do not add to self params which are not xmlrpcvals
2265
			if(is_object($par) && is_a($par, 'xmlrpcval'))
2266
			{
2267
				$this->params[]=$par;
2268
				return true;
2269
			}
2270
			else
2271
			{
2272
				return false;
2273
			}
2274
		}
2275
2276
		/**
2277
		* Returns the nth parameter in the message. The index zero-based.
2278
		* @param integer $i the index of the parameter to fetch (zero based)
2279
		* @return xmlrpcval the i-th parameter
2280
		* @access public
2281
		*/
2282
		function getParam($i) { return $this->params[$i]; }
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2283
2284
		/**
2285
		* Returns the number of parameters in the messge.
2286
		* @return integer the number of parameters currently set
2287
		* @access public
2288
		*/
2289
		function getNumParams() { return count($this->params); }
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2290
2291
		/**
2292
		* Given an open file handle, read all data available and parse it as axmlrpc response.
2293
		* NB: the file handle is not closed by this function.
2294
		* NNB: might have trouble in rare cases to work on network streams, as we
2295
		*      check for a read of 0 bytes instead of feof($fp).
2296
		*      But since checking for feof(null) returns false, we would risk an
2297
		*      infinite loop in that case, because we cannot trust the caller
2298
		*      to give us a valid pointer to an open file...
2299
		* @access public
2300
		* @param resource $fp stream pointer
2301
		* @return xmlrpcresp
2302
		* @todo add 2nd & 3rd param to be passed to ParseResponse() ???
2303
		*/
2304
		function &parseResponseFile($fp)
2305
		{
2306
			$ipd='';
2307
			while($data=fread($fp, 32768))
2308
			{
2309
				$ipd.=$data;
2310
			}
2311
			//fclose($fp);
2312
			$r =& $this->parseResponse($ipd);
2313
			return $r;
2314
		}
2315
2316
		/**
2317
		* Parses HTTP headers and separates them from data.
2318
		* @access private
2319
		*/
2320
		function &parseResponseHeaders(&$data, $headers_processed=false)
2321
		{
2322
				// Support "web-proxy-tunelling" connections for https through proxies
2323
				if(preg_match('/^HTTP\/1\.[0-1] 200 Connection established/', $data))
2324
				{
2325
					// Look for CR/LF or simple LF as line separator,
2326
					// (even though it is not valid http)
2327
					$pos = strpos($data,"\r\n\r\n");
2328
					if($pos || is_int($pos))
0 ignored issues
show
introduced by
The condition is_int($pos) is always true.
Loading history...
2329
					{
2330
						$bd = $pos+4;
2331
					}
2332
					else
2333
					{
2334
						$pos = strpos($data,"\n\n");
2335
						if($pos || is_int($pos))
2336
						{
2337
							$bd = $pos+2;
2338
						}
2339
						else
2340
						{
2341
							// No separation between response headers and body: fault?
2342
							$bd = 0;
2343
						}
2344
					}
2345
					if ($bd)
2346
					{
2347
						// this filters out all http headers from proxy.
2348
						// maybe we could take them into account, too?
2349
						$data = substr($data, $bd);
2350
					}
2351
					else
2352
					{
2353
						error_log('XML-RPC: '.__METHOD__.': HTTPS via proxy error, tunnel connection possibly failed');
2354
						$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (HTTPS via proxy error, tunnel connection possibly failed)');
2355
						return $r;
2356
					}
2357
				}
2358
2359
				// Strip HTTP 1.1 100 Continue header if present
2360
				while(preg_match('/^HTTP\/1\.1 1[0-9]{2} /', $data))
2361
				{
2362
					$pos = strpos($data, 'HTTP', 12);
2363
					// server sent a Continue header without any (valid) content following...
2364
					// give the client a chance to know it
2365
					if(!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
2366
					{
2367
						break;
2368
					}
2369
					$data = substr($data, $pos);
2370
				}
2371
				if(!preg_match('/^HTTP\/[0-9.]+ 200 /', $data))
2372
				{
2373
					$errstr= substr($data, 0, strpos($data, "\n")-1);
2374
					error_log('XML-RPC: '.__METHOD__.': HTTP error, got response: ' .$errstr);
2375
					$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (' . $errstr . ')');
2376
					return $r;
2377
				}
2378
2379
				$GLOBALS['_xh']['headers'] = array();
2380
				$GLOBALS['_xh']['cookies'] = array();
2381
2382
				// be tolerant to usage of \n instead of \r\n to separate headers and data
2383
				// (even though it is not valid http)
2384
				$pos = strpos($data,"\r\n\r\n");
2385
				if($pos || is_int($pos))
0 ignored issues
show
introduced by
The condition is_int($pos) is always true.
Loading history...
2386
				{
2387
					$bd = $pos+4;
2388
				}
2389
				else
2390
				{
2391
					$pos = strpos($data,"\n\n");
2392
					if($pos || is_int($pos))
2393
					{
2394
						$bd = $pos+2;
2395
					}
2396
					else
2397
					{
2398
						// No separation between response headers and body: fault?
2399
						// we could take some action here instead of going on...
2400
						$bd = 0;
2401
					}
2402
				}
2403
				// be tolerant to line endings, and extra empty lines
2404
				$ar = preg_split("/\r?\n/", trim(substr($data, 0, $pos)));
2405
            	foreach($ar as $line)
2406
				{
2407
					// take care of multi-line headers and cookies
2408
					$arr = explode(':',$line,2);
2409
					if(count($arr) > 1)
2410
					{
2411
						$header_name = strtolower(trim($arr[0]));
2412
						/// @todo some other headers (the ones that allow a CSV list of values)
2413
						/// do allow many values to be passed using multiple header lines.
2414
						/// We should add content to $GLOBALS['_xh']['headers'][$header_name]
2415
						/// instead of replacing it for those...
2416
						if ($header_name == 'set-cookie' || $header_name == 'set-cookie2')
2417
						{
2418
							if ($header_name == 'set-cookie2')
2419
							{
2420
								// version 2 cookies:
2421
								// there could be many cookies on one line, comma separated
2422
								$cookies = explode(',', $arr[1]);
2423
							}
2424
							else
2425
							{
2426
								$cookies = array($arr[1]);
2427
							}
2428
							foreach ($cookies as $cookie)
2429
							{
2430
								// glue together all received cookies, using a comma to separate them
2431
								// (same as php does with getallheaders())
2432
								if (isset($GLOBALS['_xh']['headers'][$header_name]))
2433
									$GLOBALS['_xh']['headers'][$header_name] .= ', ' . trim($cookie);
2434
								else
2435
									$GLOBALS['_xh']['headers'][$header_name] = trim($cookie);
2436
								// parse cookie attributes, in case user wants to correctly honour them
2437
								// feature creep: only allow rfc-compliant cookie attributes?
2438
								// @todo support for server sending multiple time cookie with same name, but using different PATHs
2439
								$cookie = explode(';', $cookie);
2440
								foreach ($cookie as $pos => $val)
2441
								{
2442
									$val = explode('=', $val, 2);
2443
									$tag = trim($val[0]);
2444
									$val = trim(@$val[1]);
2445
									/// @todo with version 1 cookies, we should strip leading and trailing " chars
2446
									if ($pos == 0)
2447
									{
2448
										$cookiename = $tag;
2449
										$GLOBALS['_xh']['cookies'][$tag] = array();
2450
										$GLOBALS['_xh']['cookies'][$cookiename]['value'] = urldecode($val);
2451
									}
2452
									else
2453
									{
2454
										if ($tag != 'value')
2455
										{
2456
											$GLOBALS['_xh']['cookies'][$cookiename][$tag] = $val;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $cookiename does not seem to be defined for all execution paths leading up to this point.
Loading history...
2457
										}
2458
									}
2459
								}
2460
							}
2461
						}
2462
						else
2463
						{
2464
							$GLOBALS['_xh']['headers'][$header_name] = trim($arr[1]);
2465
						}
2466
					}
2467
					elseif(isset($header_name))
2468
					{
2469
						///	@todo version1 cookies might span multiple lines, thus breaking the parsing above
2470
						$GLOBALS['_xh']['headers'][$header_name] .= ' ' . trim($line);
2471
					}
2472
				}
2473
2474
				$data = substr($data, $bd);
2475
2476
				if($this->debug && count($GLOBALS['_xh']['headers']))
2477
				{
2478
					print '<PRE>';
2479
					foreach($GLOBALS['_xh']['headers'] as $header => $value)
2480
					{
2481
						print htmlentities("HEADER: $header: $value\n");
2482
					}
2483
					foreach($GLOBALS['_xh']['cookies'] as $header => $value)
2484
					{
2485
						print htmlentities("COOKIE: $header={$value['value']}\n");
2486
					}
2487
					print "</PRE>\n";
2488
				}
2489
2490
				// if CURL was used for the call, http headers have been processed,
2491
				// and dechunking + reinflating have been carried out
2492
				if(!$headers_processed)
2493
				{
2494
					// Decode chunked encoding sent by http 1.1 servers
2495
					if(isset($GLOBALS['_xh']['headers']['transfer-encoding']) && $GLOBALS['_xh']['headers']['transfer-encoding'] == 'chunked')
2496
					{
2497
						if(!$data = decode_chunked($data))
2498
						{
2499
							error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to rebuild the chunked data received from server');
2500
							$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['dechunk_fail'], $GLOBALS['xmlrpcstr']['dechunk_fail']);
2501
							return $r;
2502
						}
2503
					}
2504
2505
					// Decode gzip-compressed stuff
2506
					// code shamelessly inspired from nusoap library by Dietrich Ayala
2507
					if(isset($GLOBALS['_xh']['headers']['content-encoding']))
2508
					{
2509
						$GLOBALS['_xh']['headers']['content-encoding'] = str_replace('x-', '', $GLOBALS['_xh']['headers']['content-encoding']);
2510
						if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' || $GLOBALS['_xh']['headers']['content-encoding'] == 'gzip')
2511
						{
2512
							// if decoding works, use it. else assume data wasn't gzencoded
2513
							if(function_exists('gzinflate'))
2514
							{
2515
								if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data))
2516
								{
2517
									$data = $degzdata;
2518
									if($this->debug)
2519
									print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2520
								}
2521
								elseif($GLOBALS['_xh']['headers']['content-encoding'] == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
2522
								{
2523
									$data = $degzdata;
2524
									if($this->debug)
2525
									print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2526
								}
2527
								else
2528
								{
2529
									error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to decode the deflated data received from server');
2530
									$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']);
2531
									return $r;
2532
								}
2533
							}
2534
							else
2535
							{
2536
								error_log('XML-RPC: '.__METHOD__.': the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
2537
								$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']);
2538
								return $r;
2539
							}
2540
						}
2541
					}
2542
				} // end of 'if needed, de-chunk, re-inflate response'
2543
2544
				// real stupid hack to avoid PHP complaining about returning NULL by ref
2545
				$r = null;
2546
				$r =& $r;
2547
				return $r;
2548
		}
2549
2550
		/**
2551
		* Parse the xmlrpc response contained in the string $data and return an xmlrpcresp object.
2552
		* @param string $data the xmlrpc response, eventually including http headers
2553
		* @param bool $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and consequent decoding
2554
		* @param string $return_type decides return type, i.e. content of response->value(). Either 'xmlrpcvals', 'xml' or 'phpvals'
2555
		* @return xmlrpcresp
2556
		* @access public
2557
		*/
2558
		function &parseResponse($data='', $headers_processed=false, $return_type='xmlrpcvals')
2559
		{
2560
			if($this->debug)
2561
			{
2562
				//by maHo, replaced htmlspecialchars with htmlentities
2563
				print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
2564
			}
2565
2566
			if($data == '')
2567
			{
2568
				error_log('XML-RPC: '.__METHOD__.': no response received from server.');
2569
				$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']);
2570
				return $r;
2571
			}
2572
2573
			$GLOBALS['_xh']=array();
2574
2575
			$raw_data = $data;
2576
			// parse the HTTP headers of the response, if present, and separate them from data
2577
			if(substr($data, 0, 4) == 'HTTP')
2578
			{
2579
				$r =& $this->parseResponseHeaders($data, $headers_processed);
2580
				if ($r)
2581
				{
2582
					// failed processing of HTTP response headers
2583
					// save into response obj the full payload received, for debugging
2584
					$r->raw_data = $data;
2585
					return $r;
2586
				}
2587
			}
2588
			else
2589
			{
2590
				$GLOBALS['_xh']['headers'] = array();
2591
				$GLOBALS['_xh']['cookies'] = array();
2592
			}
2593
2594
			if($this->debug)
2595
			{
2596
				$start = strpos($data, '<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2597
				if ($start)
2598
				{
2599
					$start += strlen('<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2600
					$end = strpos($data, '-->', $start);
2601
					$comments = substr($data, $start, $end-$start);
2602
					print "<PRE>---SERVER DEBUG INFO (DECODED) ---\n\t".htmlentities(str_replace("\n", "\n\t", base64_decode($comments)))."\n---END---\n</PRE>";
2603
				}
2604
			}
2605
2606
			// be tolerant of extra whitespace in response body
2607
			$data = trim($data);
2608
2609
			/// @todo return an error msg if $data=='' ?
2610
2611
			// be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts)
2612
			// idea from Luca Mariano <[email protected]> originally in PEARified version of the lib
2613
			$pos = strrpos($data, '</methodResponse>');
2614
			if($pos !== false)
2615
			{
2616
				$data = substr($data, 0, $pos+17);
2617
			}
2618
2619
			// if user wants back raw xml, give it to him
2620
			if ($return_type == 'xml')
2621
			{
2622
				$r = new xmlrpcresp($data, 0, '', 'xml');
2623
				$r->hdrs = $GLOBALS['_xh']['headers'];
2624
				$r->_cookies = $GLOBALS['_xh']['cookies'];
2625
				$r->raw_data = $raw_data;
2626
				return $r;
2627
			}
2628
2629
			// try to 'guestimate' the character encoding of the received response
2630
			$resp_encoding = guess_encoding(@$GLOBALS['_xh']['headers']['content-type'], $data);
2631
2632
			$GLOBALS['_xh']['ac']='';
2633
			//$GLOBALS['_xh']['qt']=''; //unused...
2634
			$GLOBALS['_xh']['stack'] = array();
2635
			$GLOBALS['_xh']['valuestack'] = array();
2636
			$GLOBALS['_xh']['isf']=0; // 0 = OK, 1 for xmlrpc fault responses, 2 = invalid xmlrpc
2637
			$GLOBALS['_xh']['isf_reason']='';
2638
			$GLOBALS['_xh']['rt']=''; // 'methodcall or 'methodresponse'
2639
2640
			// Since parsing will fail if charset is not specified in the xml prologue,
2641
			// the encoding is not UTF8 and there are non-ascii chars in the text, we try to work round that...
2642
			// The following code might be better for mb_string enabled installs, but
2643
			// makes the lib about 200% slower...
2644
			//if (!is_valid_charset($resp_encoding, array('UTF-8')))
2645
			if (!in_array($resp_encoding, array('UTF-8', 'US-ASCII')) && !has_encoding($data)) {
2646
				if ($resp_encoding == 'ISO-8859-1') {
2647
					$data = utf8_encode($data);
2648
				} else {
2649
					if (extension_loaded('mbstring')) {
2650
						$data = mb_convert_encoding($data, 'UTF-8', $resp_encoding);
2651
					} else {
2652
						error_log('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of received request: ' . $resp_encoding);
2653
					}
2654
				}
2655
			}
2656
2657
			$parser = xml_parser_create();
2658
			xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
2659
			// G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
2660
			// the xml parser to give us back data in the expected charset.
2661
			// What if internal encoding is not in one of the 3 allowed?
2662
			// we use the broadest one, ie. utf8
2663
			// This allows to send data which is native in various charset,
2664
			// by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
2665
			if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2666
			{
2667
				xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
2668
			}
2669
			else
2670
			{
2671
				xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
2672
			}
2673
2674
			if ($return_type == 'phpvals')
2675
			{
2676
				xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
2677
			}
2678
			else
2679
			{
2680
				xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
2681
			}
2682
2683
			xml_set_character_data_handler($parser, 'xmlrpc_cd');
2684
			xml_set_default_handler($parser, 'xmlrpc_dh');
2685
2686
			// first error check: xml not well formed
2687
			if(!xml_parse($parser, $data, 1))
2688
			{
2689
				// thanks to Peter Kocks <[email protected]>
2690
				if((xml_get_current_line_number($parser)) == 1)
2691
				{
2692
					$errstr = 'XML error at line 1, check URL';
2693
				}
2694
				else
2695
				{
2696
					$errstr = sprintf('XML error: %s at line %d, column %d',
2697
						xml_error_string(xml_get_error_code($parser)),
2698
						xml_get_current_line_number($parser), xml_get_current_column_number($parser));
2699
				}
2700
				error_log($errstr);
2701
				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return'].' ('.$errstr.')');
2702
				xml_parser_free($parser);
2703
				if($this->debug)
2704
				{
2705
					print $errstr;
2706
				}
2707
				$r->hdrs = $GLOBALS['_xh']['headers'];
2708
				$r->_cookies = $GLOBALS['_xh']['cookies'];
2709
				$r->raw_data = $raw_data;
2710
				return $r;
2711
			}
2712
			xml_parser_free($parser);
2713
			// second error check: xml well formed but not xml-rpc compliant
2714
			if ($GLOBALS['_xh']['isf'] > 1)
2715
			{
2716
				if ($this->debug)
2717
				{
2718
					/// @todo echo something for user?
2719
				}
2720
2721
				$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2722
				$GLOBALS['xmlrpcstr']['invalid_return'] . ' ' . $GLOBALS['_xh']['isf_reason']);
2723
			}
2724
			// third error check: parsing of the response has somehow gone boink.
2725
			// NB: shall we omit this check, since we trust the parsing code?
2726
			elseif ($return_type == 'xmlrpcvals' && !is_object($GLOBALS['_xh']['value']))
2727
			{
2728
				// something odd has happened
2729
				// and it's time to generate a client side error
2730
				// indicating something odd went on
2731
				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2732
					$GLOBALS['xmlrpcstr']['invalid_return']);
2733
			}
2734
			else
2735
			{
2736
				if ($this->debug)
2737
				{
2738
					print "<PRE>---PARSED---\n";
2739
					// somehow htmlentities chokes on var_export, and some full html string...
2740
					//print htmlentitites(var_export($GLOBALS['_xh']['value'], true));
2741
					print htmlspecialchars(var_export($GLOBALS['_xh']['value'], true));
2742
					print "\n---END---</PRE>";
2743
				}
2744
2745
				// note that using =& will raise an error if $GLOBALS['_xh']['st'] does not generate an object.
2746
				$v =& $GLOBALS['_xh']['value'];
2747
2748
				if($GLOBALS['_xh']['isf'])
2749
				{
2750
					/// @todo we should test here if server sent an int and a string,
2751
					/// and/or coerce them into such...
2752
					if ($return_type == 'xmlrpcvals')
2753
					{
2754
						$errno_v = $v->structmem('faultCode');
2755
						$errstr_v = $v->structmem('faultString');
2756
						$errno = $errno_v->scalarval();
2757
						$errstr = $errstr_v->scalarval();
2758
					}
2759
					else
2760
					{
2761
						$errno = $v['faultCode'];
2762
						$errstr = $v['faultString'];
2763
					}
2764
2765
					if($errno == 0)
2766
					{
2767
						// FAULT returned, errno needs to reflect that
2768
						$errno = -1;
2769
					}
2770
2771
					$r = new xmlrpcresp(0, $errno, $errstr);
2772
				}
2773
				else
2774
				{
2775
					$r=new xmlrpcresp($v, 0, '', $return_type);
2776
				}
2777
			}
2778
2779
			$r->hdrs = $GLOBALS['_xh']['headers'];
2780
			$r->_cookies = $GLOBALS['_xh']['cookies'];
2781
			$r->raw_data = $raw_data;
2782
			return $r;
2783
		}
2784
	}
2785
2786
	class xmlrpcval
2787
	{
2788
		var $me=array();
2789
		var $mytype=0;
2790
		var $_php_class=null;
2791
2792
		/**
2793
		* @param mixed $val
2794
		* @param string $type any valid xmlrpc type name (lowercase). If null, 'string' is assumed
2795
		*/
2796
		function __construct($val=-1, $type='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2797
		{
2798
			/// @todo: optimization creep - do not call addXX, do it all inline.
2799
			/// downside: booleans will not be coerced anymore
2800
			if($val!==-1 || $type!='')
2801
			{
2802
				// optimization creep: inlined all work done by constructor
2803
				switch($type)
2804
				{
2805
					case '':
2806
						$this->mytype=1;
2807
						$this->me['string']=$val;
2808
						break;
2809
					case 'i4':
2810
					case 'int':
2811
					case 'double':
2812
					case 'string':
2813
					case 'boolean':
2814
					case 'dateTime.iso8601':
2815
					case 'base64':
2816
					case 'null':
2817
						$this->mytype=1;
2818
						$this->me[$type]=$val;
2819
						break;
2820
					case 'array':
2821
						$this->mytype=2;
2822
						$this->me['array']=$val;
2823
						break;
2824
					case 'struct':
2825
						$this->mytype=3;
2826
						$this->me['struct']=$val;
2827
						break;
2828
					default:
2829
						error_log("XML-RPC: ".__METHOD__.": not a known type ($type)");
2830
				}
2831
				/*if($type=='')
2832
				{
2833
					$type='string';
2834
				}
2835
				if($GLOBALS['xmlrpcTypes'][$type]==1)
2836
				{
2837
					$this->addScalar($val,$type);
2838
				}
2839
				elseif($GLOBALS['xmlrpcTypes'][$type]==2)
2840
				{
2841
					$this->addArray($val);
2842
				}
2843
				elseif($GLOBALS['xmlrpcTypes'][$type]==3)
2844
				{
2845
					$this->addStruct($val);
2846
				}*/
2847
			}
2848
		}
2849
2850
		/**
2851
		* @deprecated
2852
		*/
2853
		function xmlrpcval($val=-1, $type='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2854
		{
2855
			self::__construct($val, $type);
0 ignored issues
show
Bug Best Practice introduced by
The method xmlrpcval::__construct() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2855
			self::/** @scrutinizer ignore-call */ 
2856
         __construct($val, $type);
Loading history...
2856
		}
2857
2858
		/**
2859
		* Add a single php value to an (unitialized) xmlrpcval
2860
		* @param mixed $val
2861
		* @param string $type
2862
		* @return int 1 or 0 on failure
2863
		*/
2864
		function addScalar($val, $type='string')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2865
		{
2866
			$typeof=@$GLOBALS['xmlrpcTypes'][$type];
2867
			if($typeof!=1)
2868
			{
2869
				error_log("XML-RPC: ".__METHOD__.": not a scalar type ($type)");
2870
				return 0;
2871
			}
2872
2873
			// coerce booleans into correct values
2874
			// NB: we should either do it for datetimes, integers and doubles, too,
2875
			// or just plain remove this check, implemented on booleans only...
2876
			if($type==$GLOBALS['xmlrpcBoolean'])
2877
			{
2878
				if(strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
2879
				{
2880
					$val=true;
2881
				}
2882
				else
2883
				{
2884
					$val=false;
2885
				}
2886
			}
2887
2888
			switch($this->mytype)
2889
			{
2890
				case 1:
2891
					error_log('XML-RPC: '.__METHOD__.': scalar xmlrpcval can have only one value');
2892
					return 0;
2893
				case 3:
2894
					error_log('XML-RPC: '.__METHOD__.': cannot add anonymous scalar to struct xmlrpcval');
2895
					return 0;
2896
				case 2:
2897
					// we're adding a scalar value to an array here
2898
					//$ar=$this->me['array'];
2899
					//$ar[]=new xmlrpcval($val, $type);
2900
					//$this->me['array']=$ar;
2901
					// Faster (?) avoid all the costly array-copy-by-val done here...
2902
					$this->me['array'][]=new xmlrpcval($val, $type);
2903
					return 1;
2904
				default:
2905
					// a scalar, so set the value and remember we're scalar
2906
					$this->me[$type]=$val;
2907
					$this->mytype=$typeof;
2908
					return 1;
2909
			}
2910
		}
2911
2912
		/**
2913
		* Add an array of xmlrpcval objects to an xmlrpcval
2914
		* @param array $vals
2915
		* @return int 1 or 0 on failure
2916
		* @access public
2917
		*
2918
		* @todo add some checking for $vals to be an array of xmlrpcvals?
2919
		*/
2920
		function addArray($vals)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2921
		{
2922
			if($this->mytype==0)
2923
			{
2924
				$this->mytype=$GLOBALS['xmlrpcTypes']['array'];
2925
				$this->me['array']=$vals;
2926
				return 1;
2927
			}
2928
			elseif($this->mytype==2)
2929
			{
2930
				// we're adding to an array here
2931
				$this->me['array'] = array_merge($this->me['array'], $vals);
2932
				return 1;
2933
			}
2934
			else
2935
			{
2936
				error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
2937
				return 0;
2938
			}
2939
		}
2940
2941
		/**
2942
		* Add an array of named xmlrpcval objects to an xmlrpcval
2943
		* @param array $vals
2944
		* @return int 1 or 0 on failure
2945
		* @access public
2946
		*
2947
		* @todo add some checking for $vals to be an array?
2948
		*/
2949
		function addStruct($vals)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2950
		{
2951
			if($this->mytype==0)
2952
			{
2953
				$this->mytype=$GLOBALS['xmlrpcTypes']['struct'];
2954
				$this->me['struct']=$vals;
2955
				return 1;
2956
			}
2957
			elseif($this->mytype==3)
2958
			{
2959
				// we're adding to a struct here
2960
				$this->me['struct'] = array_merge($this->me['struct'], $vals);
2961
				return 1;
2962
			}
2963
			else
2964
			{
2965
				error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
2966
				return 0;
2967
			}
2968
		}
2969
2970
		// poor man's version of print_r ???
2971
		/// @deprecated
2972
		function dump($ar)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2973
		{
2974
			foreach($ar as $key => $val)
2975
			{
2976
				echo "$key => $val<br />";
2977
				if($key == 'array')
2978
				{
2979
                    foreach($val as $key2 => $val2)
2980
					{
2981
						echo "-- $key2 => $val2<br />";
2982
					}
2983
				}
2984
			}
2985
		}
2986
2987
		/**
2988
		* Returns a string containing "struct", "array" or "scalar" describing the base type of the value
2989
		* @return string
2990
		* @access public
2991
		*/
2992
		function kindOf()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2993
		{
2994
			switch($this->mytype)
2995
			{
2996
				case 3:
2997
					return 'struct';
2998
					break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2999
				case 2:
3000
					return 'array';
3001
					break;
3002
				case 1:
3003
					return 'scalar';
3004
					break;
3005
				default:
3006
					return 'undef';
3007
			}
3008
		}
3009
3010
		/**
3011
		* @access private
3012
		*/
3013
		function serializedata($typ, $val, $charset_encoding='')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
3014
		{
3015
			$rs='';
3016
			switch(@$GLOBALS['xmlrpcTypes'][$typ])
3017
			{
3018
				case 1:
3019
					switch($typ)
3020
					{
3021
						case $GLOBALS['xmlrpcBase64']:
3022
							$rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
3023
							break;
3024
						case $GLOBALS['xmlrpcBoolean']:
3025
							$rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
3026
							break;
3027
						case $GLOBALS['xmlrpcString']:
3028
							// G. Giunta 2005/2/13: do NOT use htmlentities, since
3029
							// it will produce named html entities, which are invalid xml
3030
							$rs.="<${typ}>" . xmlrpc_encode_entitites($val, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding). "</${typ}>";
3031
							break;
3032
						case $GLOBALS['xmlrpcInt']:
3033
						case $GLOBALS['xmlrpcI4']:
3034
							$rs.="<${typ}>".(int)$val."</${typ}>";
3035
							break;
3036
						case $GLOBALS['xmlrpcDouble']:
3037
							// avoid using standard conversion of float to string because it is locale-dependent,
3038
							// and also because the xmlrpc spec forbids exponential notation.
3039
							// sprintf('%F') could be most likely ok but it fails eg. on 2e-14.
3040
							// The code below tries its best at keeping max precision while avoiding exp notation,
3041
							// but there is of course no limit in the number of decimal places to be used...
3042
							$rs.="<${typ}>".preg_replace('/\\.?0+$/','',number_format((double)$val, 128, '.', ''))."</${typ}>";
3043
							break;
3044
						case $GLOBALS['xmlrpcDateTime']:
3045
							if (is_string($val))
3046
							{
3047
								$rs.="<${typ}>${val}</${typ}>";
3048
							}
3049
							else if(is_a($val, 'DateTime'))
3050
							{
3051
								$rs.="<${typ}>".$val->format('Ymd\TH:i:s')."</${typ}>";
3052
							}
3053
							else if(is_int($val))
3054
							{
3055
								$rs.="<${typ}>".strftime("%Y%m%dT%H:%M:%S", $val)."</${typ}>";
3056
							}
3057
							else
3058
							{
3059
								// not really a good idea here: but what shall we output anyway? left for backward compat...
3060
								$rs.="<${typ}>${val}</${typ}>";
3061
							}
3062
							break;
3063
						case $GLOBALS['xmlrpcNull']:
3064
							if ($GLOBALS['xmlrpc_null_apache_encoding'])
3065
							{
3066
								$rs.="<ex:nil/>";
3067
							}
3068
							else
3069
							{
3070
								$rs.="<nil/>";
3071
							}
3072
							break;
3073
						default:
3074
							// no standard type value should arrive here, but provide a possibility
3075
							// for xmlrpcvals of unknown type...
3076
							$rs.="<${typ}>${val}</${typ}>";
3077
					}
3078
					break;
3079
				case 3:
3080
					// struct
3081
					if ($this->_php_class)
3082
					{
3083
						$rs.='<struct php_class="' . $this->_php_class . "\">\n";
3084
					}
3085
					else
3086
					{
3087
						$rs.="<struct>\n";
3088
					}
3089
					foreach($val as $key2 => $val2)
3090
					{
3091
						$rs.='<member><name>'.xmlrpc_encode_entitites($key2, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding)."</name>\n";
3092
						//$rs.=$this->serializeval($val2);
3093
						$rs.=$val2->serialize($charset_encoding);
3094
						$rs.="</member>\n";
3095
					}
3096
					$rs.='</struct>';
3097
					break;
3098
				case 2:
3099
					// array
3100
					$rs.="<array>\n<data>\n";
3101
					for($i=0; $i<count($val); $i++)
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
3102
					{
3103
						//$rs.=$this->serializeval($val[$i]);
3104
						$rs.=$val[$i]->serialize($charset_encoding);
3105
					}
3106
					$rs.="</data>\n</array>";
3107
					break;
3108
				default:
3109
					break;
3110
			}
3111
			return $rs;
3112
		}
3113
3114
		/**
3115
		* Returns xml representation of the value. XML prologue not included
3116
		* @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
3117
		* @return string
3118
		* @access public
3119
		*/
3120
		function serialize($charset_encoding='')
3121
		{
3122
			// add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3123
			//if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3124
			//{
3125
				$val = reset($this->me);
3126
				$typ = key($this->me);
3127
				return '<value>' . $this->serializedata($typ, $val, $charset_encoding) . "</value>\n";
3128
			//}
3129
		}
3130
3131
		/// @deprecated
3132
		function serializeval($o)
3133
		{
3134
			// add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3135
			//if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3136
			//{
3137
				$val = reset($o->me);
3138
				$typ = key($o->me);
3139
				return '<value>' . $this->serializedata($typ, $val) . "</value>\n";
3140
			//}
3141
		}
3142
3143
		/**
3144
		* Checks whether a struct member with a given name is present.
3145
		* Works only on xmlrpcvals of type struct.
3146
		* @param string $m the name of the struct member to be looked up
3147
		* @return boolean
3148
		* @access public
3149
		*/
3150
		function structmemexists($m)
3151
		{
3152
			return array_key_exists($m, $this->me['struct']);
3153
		}
3154
3155
		/**
3156
		* Returns the value of a given struct member (an xmlrpcval object in itself).
3157
		* Will raise a php warning if struct member of given name does not exist
3158
		* @param string $m the name of the struct member to be looked up
3159
		* @return xmlrpcval
3160
		* @access public
3161
		*/
3162
		function structmem($m)
3163
		{
3164
			return $this->me['struct'][$m];
3165
		}
3166
3167
		/**
3168
		* Reset internal pointer for xmlrpcvals of type struct.
3169
		* @access public
3170
		*/
3171
		function structreset()
3172
		{
3173
			reset($this->me['struct']);
3174
		}
3175
3176
		/**
3177
		* Return next member element for xmlrpcvals of type struct.
3178
		* @return xmlrpcval
3179
		* @access public
3180
		* @deprecated this will be removed in future versions
3181
		*/
3182
		function structeach()
3183
		{
3184
			return @each($this->me['struct']);
0 ignored issues
show
Deprecated Code introduced by
The function each() has been deprecated: 7.2 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

3184
			return @/** @scrutinizer ignore-deprecated */ each($this->me['struct']);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
Bug Best Practice introduced by
The expression return @each($this->me['struct']) returns the type array which is incompatible with the documented return type xmlrpcval.
Loading history...
3185
		}
3186
3187
		// this code looks like it is very fragile and has not been fixed
3188
		// for a long long time. Shall we remove it for 2.0?
3189
		/// @deprecated
3190
		function getval()
3191
		{
3192
			// UNSTABLE
3193
			$b = reset($this->me);
3194
			// contributed by I Sofer, 2001-03-24
3195
			// add support for nested arrays to scalarval
3196
			// i've created a new method here, so as to
3197
			// preserve back compatibility
3198
3199
			if(is_array($b))
3200
			{
3201
				foreach($b as $id => $cont)
3202
				{
3203
					$b[$id] = $cont->scalarval();
3204
				}
3205
			}
3206
3207
			// add support for structures directly encoding php objects
3208
			if(is_object($b))
3209
			{
3210
				$t = get_object_vars($b);
3211
				foreach($t as $id => $cont)
3212
				{
3213
					$t[$id] = $cont->scalarval();
3214
				}
3215
                foreach($t as $id => $cont)
3216
				{
3217
					@$b->$id = $cont;
3218
				}
3219
			}
3220
			// end contrib
3221
			return $b;
3222
		}
3223
3224
		/**
3225
		* Returns the value of a scalar xmlrpcval
3226
		* @return mixed
3227
		* @access public
3228
		*/
3229
		function scalarval()
3230
		{
3231
			$b = reset($this->me);
3232
			return $b;
3233
		}
3234
3235
		/**
3236
		* Returns the type of the xmlrpcval.
3237
		* For integers, 'int' is always returned in place of 'i4'
3238
		* @return string
3239
		* @access public
3240
		*/
3241
		function scalartyp()
3242
		{
3243
			reset($this->me);
3244
            $a = key($this->me);
3245
			if($a==$GLOBALS['xmlrpcI4'])
3246
			{
3247
				$a=$GLOBALS['xmlrpcInt'];
3248
			}
3249
			return $a;
3250
		}
3251
3252
		/**
3253
		* Returns the m-th member of an xmlrpcval of struct type
3254
		* @param integer $m the index of the value to be retrieved (zero based)
3255
		* @return xmlrpcval
3256
		* @access public
3257
		*/
3258
		function arraymem($m)
3259
		{
3260
			return $this->me['array'][$m];
3261
		}
3262
3263
		/**
3264
		* Returns the number of members in an xmlrpcval of array type
3265
		* @return integer
3266
		* @access public
3267
		*/
3268
		function arraysize()
3269
		{
3270
			return count($this->me['array']);
3271
		}
3272
3273
		/**
3274
		* Returns the number of members in an xmlrpcval of struct type
3275
		* @return integer
3276
		* @access public
3277
		*/
3278
		function structsize()
3279
		{
3280
			return count($this->me['struct']);
3281
		}
3282
	}
3283
3284
3285
	// date helpers
3286
3287
	/**
3288
	* Given a timestamp, return the corresponding ISO8601 encoded string.
3289
	*
3290
	* Really, timezones ought to be supported
3291
	* but the XML-RPC spec says:
3292
	*
3293
	* "Don't assume a timezone. It should be specified by the server in its
3294
	* documentation what assumptions it makes about timezones."
3295
	*
3296
	* These routines always assume localtime unless
3297
	* $utc is set to 1, in which case UTC is assumed
3298
	* and an adjustment for locale is made when encoding
3299
	*
3300
	* @param int $timet (timestamp)
3301
	* @param int $utc (0 or 1)
3302
	* @return string
3303
	*/
3304
	function iso8601_encode($timet, $utc=0)
3305
	{
3306
		if(!$utc)
3307
		{
3308
			$t=strftime("%Y%m%dT%H:%M:%S", $timet);
3309
		}
3310
		else
3311
		{
3312
			if(function_exists('gmstrftime'))
3313
			{
3314
				// gmstrftime doesn't exist in some versions
3315
				// of PHP
3316
				$t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
3317
			}
3318
			else
3319
			{
3320
				$t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z'));
3321
			}
3322
		}
3323
		return $t;
3324
	}
3325
3326
	/**
3327
	* Given an ISO8601 date string, return a timet in the localtime, or UTC
3328
	* @param string $idate
3329
	* @param int $utc either 0 or 1
3330
	* @return int (datetime)
3331
	*/
3332
	function iso8601_decode($idate, $utc=0)
3333
	{
3334
		$t=0;
3335
		if(preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $idate, $regs))
3336
		{
3337
			if($utc)
3338
			{
3339
				$t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3340
			}
3341
			else
3342
			{
3343
				$t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3344
			}
3345
		}
3346
		return $t;
3347
	}
3348
3349
	/**
3350
	* Takes an xmlrpc value in PHP xmlrpcval object format and translates it into native PHP types.
3351
	*
3352
	* Works with xmlrpc message objects as input, too.
3353
	*
3354
	* Given proper options parameter, can rebuild generic php object instances
3355
	* (provided those have been encoded to xmlrpc format using a corresponding
3356
	* option in php_xmlrpc_encode())
3357
	* PLEASE NOTE that rebuilding php objects involves calling their constructor function.
3358
	* This means that the remote communication end can decide which php code will
3359
	* get executed on your server, leaving the door possibly open to 'php-injection'
3360
	* style of attacks (provided you have some classes defined on your server that
3361
	* might wreak havoc if instances are built outside an appropriate context).
3362
	* Make sure you trust the remote server/client before eanbling this!
3363
	*
3364
	* @author Dan Libby ([email protected])
3365
	*
3366
	* @param xmlrpcval $xmlrpc_val
3367
	* @param array $options if 'decode_php_objs' is set in the options array, xmlrpc structs can be decoded into php objects; if 'dates_as_objects' is set xmlrpc datetimes are decoded as php DateTime objects (standard is
3368
	* @return mixed
3369
	*/
3370
	function php_xmlrpc_decode($xmlrpc_val, $options=array())
3371
	{
3372
		switch($xmlrpc_val->kindOf())
3373
		{
3374
			case 'scalar':
3375
				if (in_array('extension_api', $options))
3376
				{
3377
                    $val = reset($xmlrpc_val->me);
3378
                    $typ = key($xmlrpc_val->me);
3379
					switch ($typ)
3380
					{
3381
						case 'dateTime.iso8601':
3382
							$xmlrpc_val->scalar = $val;
0 ignored issues
show
Bug introduced by
The property scalar does not seem to exist on xmlrpcval.
Loading history...
3383
							$xmlrpc_val->xmlrpc_type = 'datetime';
0 ignored issues
show
Bug introduced by
The property xmlrpc_type does not seem to exist on xmlrpcval.
Loading history...
3384
							$xmlrpc_val->timestamp = iso8601_decode($val);
0 ignored issues
show
Bug introduced by
The property timestamp does not seem to exist on xmlrpcval.
Loading history...
3385
							return $xmlrpc_val;
3386
						case 'base64':
3387
							$xmlrpc_val->scalar = $val;
3388
							$xmlrpc_val->type = $typ;
0 ignored issues
show
Bug introduced by
The property type does not seem to exist on xmlrpcval.
Loading history...
3389
							return $xmlrpc_val;
3390
						default:
3391
							return $xmlrpc_val->scalarval();
3392
					}
3393
				}
3394
				if (in_array('dates_as_objects', $options) && $xmlrpc_val->scalartyp() == 'dateTime.iso8601')
3395
				{
3396
					// we return a Datetime object instead of a string
3397
					// since now the constructor of xmlrpcval accepts safely strings, ints and datetimes,
3398
					// we cater to all 3 cases here
3399
					$out = $xmlrpc_val->scalarval();
3400
					if (is_string($out))
3401
					{
3402
						$out = strtotime($out);
3403
					}
3404
					if (is_int($out))
3405
					{
3406
						$result = new Datetime();
3407
						$result->setTimestamp($out);
3408
						return $result;
3409
					}
3410
					elseif (is_a($out, 'Datetime'))
3411
					{
3412
						return $out;
3413
					}
3414
				}
3415
				return $xmlrpc_val->scalarval();
3416
			case 'array':
3417
				$size = $xmlrpc_val->arraysize();
3418
				$arr = array();
3419
				for($i = 0; $i < $size; $i++)
3420
				{
3421
					$arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i), $options);
3422
				}
3423
				return $arr;
3424
			case 'struct':
3425
				$xmlrpc_val->structreset();
3426
				// If user said so, try to rebuild php objects for specific struct vals.
3427
				/// @todo should we raise a warning for class not found?
3428
				// shall we check for proper subclass of xmlrpcval instead of
3429
				// presence of _php_class to detect what we can do?
3430
				if (in_array('decode_php_objs', $options) && $xmlrpc_val->_php_class != ''
3431
					&& class_exists($xmlrpc_val->_php_class))
3432
				{
3433
					$obj = @new $xmlrpc_val->_php_class;
3434
					while(list($key,$value)=$xmlrpc_val->structeach())
0 ignored issues
show
Deprecated Code introduced by
The function xmlrpcval::structeach() has been deprecated: this will be removed in future versions ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

3434
					while(list($key,$value)=/** @scrutinizer ignore-deprecated */ $xmlrpc_val->structeach())

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
3435
					{
3436
						$obj->$key = php_xmlrpc_decode($value, $options);
3437
					}
3438
					return $obj;
3439
				}
3440
				else
3441
				{
3442
					$arr = array();
3443
					while(list($key,$value)=$xmlrpc_val->structeach())
0 ignored issues
show
Deprecated Code introduced by
The function xmlrpcval::structeach() has been deprecated: this will be removed in future versions ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

3443
					while(list($key,$value)=/** @scrutinizer ignore-deprecated */ $xmlrpc_val->structeach())

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
3444
					{
3445
						$arr[$key] = php_xmlrpc_decode($value, $options);
3446
					}
3447
					return $arr;
3448
				}
3449
			case 'msg':
3450
				$paramcount = $xmlrpc_val->getNumParams();
0 ignored issues
show
Bug introduced by
The method getNumParams() does not exist on xmlrpcval. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3450
				/** @scrutinizer ignore-call */ $paramcount = $xmlrpc_val->getNumParams();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
3451
				$arr = array();
3452
				for($i = 0; $i < $paramcount; $i++)
3453
				{
3454
					$arr[] = php_xmlrpc_decode($xmlrpc_val->getParam($i));
0 ignored issues
show
Bug introduced by
The method getParam() does not exist on xmlrpcval. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3454
					$arr[] = php_xmlrpc_decode($xmlrpc_val->/** @scrutinizer ignore-call */ getParam($i));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
3455
				}
3456
				return $arr;
3457
			}
3458
	}
3459
3460
	// This constant left here only for historical reasons...
3461
	// it was used to decide if we have to define xmlrpc_encode on our own, but
3462
	// we do not do it anymore
3463
	if(function_exists('xmlrpc_decode'))
3464
	{
3465
		define('XMLRPC_EPI_ENABLED','1');
3466
	}
3467
	else
3468
	{
3469
		define('XMLRPC_EPI_ENABLED','0');
3470
	}
3471
3472
	/**
3473
	* Takes native php types and encodes them into xmlrpc PHP object format.
3474
	* It will not re-encode xmlrpcval objects.
3475
	*
3476
	* Feature creep -- could support more types via optional type argument
3477
	* (string => datetime support has been added, ??? => base64 not yet)
3478
	*
3479
	* If given a proper options parameter, php object instances will be encoded
3480
	* into 'special' xmlrpc values, that can later be decoded into php objects
3481
	* by calling php_xmlrpc_decode() with a corresponding option
3482
	*
3483
	* @author Dan Libby ([email protected])
3484
	*
3485
	* @param mixed $php_val the value to be converted into an xmlrpcval object
3486
	* @param array $options	can include 'encode_php_objs', 'auto_dates', 'null_extension' or 'extension_api'
3487
	* @return xmlrpcval
3488
	*/
3489
	function php_xmlrpc_encode($php_val, $options=array())
3490
	{
3491
		$type = gettype($php_val);
3492
		switch($type)
3493
		{
3494
			case 'string':
3495
				if (in_array('auto_dates', $options) && preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $php_val))
3496
					$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDateTime']);
3497
				else
3498
					$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcString']);
3499
				break;
3500
			case 'integer':
3501
				$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcInt']);
3502
				break;
3503
			case 'double':
3504
				$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDouble']);
3505
				break;
3506
				// <G_Giunta_2001-02-29>
3507
				// Add support for encoding/decoding of booleans, since they are supported in PHP
3508
			case 'boolean':
3509
				$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcBoolean']);
3510
				break;
3511
				// </G_Giunta_2001-02-29>
3512
			case 'array':
3513
				// PHP arrays can be encoded to either xmlrpc structs or arrays,
3514
				// depending on wheter they are hashes or plain 0..n integer indexed
3515
				// A shorter one-liner would be
3516
				// $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
3517
				// but execution time skyrockets!
3518
				$j = 0;
3519
				$arr = array();
3520
				$ko = false;
3521
				foreach($php_val as $key => $val)
3522
				{
3523
					$arr[$key] = php_xmlrpc_encode($val, $options);
3524
					if(!$ko && $key !== $j)
3525
					{
3526
						$ko = true;
3527
					}
3528
					$j++;
3529
				}
3530
				if($ko)
3531
				{
3532
					$xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
3533
				}
3534
				else
3535
				{
3536
					$xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcArray']);
3537
				}
3538
				break;
3539
			case 'object':
3540
				if(is_a($php_val, 'xmlrpcval'))
3541
				{
3542
					$xmlrpc_val = $php_val;
3543
				}
3544
				else if(is_a($php_val, 'DateTime'))
3545
				{
3546
					$xmlrpc_val = new xmlrpcval($php_val->format('Ymd\TH:i:s'), $GLOBALS['xmlrpcStruct']);
3547
				}
3548
				else
3549
				{
3550
					$arr = array();
3551
                    foreach($php_val as $k => $v)
3552
					{
3553
						$arr[$k] = php_xmlrpc_encode($v, $options);
3554
					}
3555
					$xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
3556
					if (in_array('encode_php_objs', $options))
3557
					{
3558
						// let's save original class name into xmlrpcval:
3559
						// might be useful later on...
3560
						$xmlrpc_val->_php_class = get_class($php_val);
3561
					}
3562
				}
3563
				break;
3564
			case 'NULL':
3565
				if (in_array('extension_api', $options))
3566
				{
3567
					$xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcString']);
3568
				}
3569
				else if (in_array('null_extension', $options))
3570
				{
3571
					$xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcNull']);
3572
				}
3573
				else
3574
				{
3575
					$xmlrpc_val = new xmlrpcval();
3576
				}
3577
				break;
3578
			case 'resource':
3579
				if (in_array('extension_api', $options))
3580
				{
3581
					$xmlrpc_val = new xmlrpcval((int)$php_val, $GLOBALS['xmlrpcInt']);
0 ignored issues
show
Unused Code introduced by
The assignment to $xmlrpc_val is dead and can be removed.
Loading history...
3582
				}
3583
				else
3584
				{
3585
					$xmlrpc_val = new xmlrpcval();
3586
				}
3587
			// catch "user function", "unknown type"
3588
			default:
3589
				// giancarlo pinerolo <[email protected]>
3590
				// it has to return
3591
				// an empty object in case, not a boolean.
3592
				$xmlrpc_val = new xmlrpcval();
3593
				break;
3594
			}
3595
			return $xmlrpc_val;
3596
	}
3597
3598
	/**
3599
	* Convert the xml representation of a method response, method request or single
3600
	* xmlrpc value into the appropriate object (a.k.a. deserialize)
3601
	* @param string $xml_val
3602
	* @param array $options
3603
	* @return mixed false on error, or an instance of either xmlrpcval, xmlrpcmsg or xmlrpcresp
3604
	*/
3605
	function php_xmlrpc_decode_xml($xml_val, $options=array())
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

3605
	function php_xmlrpc_decode_xml($xml_val, /** @scrutinizer ignore-unused */ $options=array())

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
3606
	{
3607
		$GLOBALS['_xh'] = array();
3608
		$GLOBALS['_xh']['ac'] = '';
3609
		$GLOBALS['_xh']['stack'] = array();
3610
		$GLOBALS['_xh']['valuestack'] = array();
3611
		$GLOBALS['_xh']['params'] = array();
3612
		$GLOBALS['_xh']['pt'] = array();
3613
		$GLOBALS['_xh']['isf'] = 0;
3614
		$GLOBALS['_xh']['isf_reason'] = '';
3615
		$GLOBALS['_xh']['method'] = false;
3616
		$GLOBALS['_xh']['rt'] = '';
3617
3618
		// 'guestimate' encoding
3619
		$val_encoding = guess_encoding('', $xml_val);
3620
3621
		// Since parsing will fail if charset is not specified in the xml prologue,
3622
		// the encoding is not UTF8 and there are non-ascii chars in the text, we try to work round that...
3623
		// The following code might be better for mb_string enabled installs, but
3624
		// makes the lib about 200% slower...
3625
		//if (!is_valid_charset($val_encoding, array('UTF-8')))
3626
		if (!in_array($val_encoding, array('UTF-8', 'US-ASCII')) && !has_encoding($xml_val)) {
3627
			if ($val_encoding == 'ISO-8859-1') {
3628
				$xml_val = utf8_encode($xml_val);
3629
			} else {
3630
				if (extension_loaded('mbstring')) {
3631
					$xml_val = mb_convert_encoding($xml_val, 'UTF-8', $val_encoding);
3632
				} else {
3633
					error_log('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of received request: ' . $val_encoding);
3634
				}
3635
			}
3636
		}
3637
3638
		$parser = xml_parser_create();
3639
		xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
3640
		// What if internal encoding is not in one of the 3 allowed?
3641
		// we use the broadest one, ie. utf8!
3642
		if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
3643
		{
3644
			xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
3645
		}
3646
		else
3647
		{
3648
			xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
3649
		}
3650
		xml_set_element_handler($parser, 'xmlrpc_se_any', 'xmlrpc_ee');
3651
		xml_set_character_data_handler($parser, 'xmlrpc_cd');
3652
		xml_set_default_handler($parser, 'xmlrpc_dh');
3653
		if(!xml_parse($parser, $xml_val, 1))
3654
		{
3655
			$errstr = sprintf('XML error: %s at line %d, column %d',
3656
						xml_error_string(xml_get_error_code($parser)),
3657
						xml_get_current_line_number($parser), xml_get_current_column_number($parser));
3658
			error_log($errstr);
3659
			xml_parser_free($parser);
3660
			return false;
3661
		}
3662
		xml_parser_free($parser);
3663
		if ($GLOBALS['_xh']['isf'] > 1) // test that $GLOBALS['_xh']['value'] is an obj, too???
3664
		{
3665
			error_log($GLOBALS['_xh']['isf_reason']);
3666
			return false;
3667
		}
3668
		switch ($GLOBALS['_xh']['rt'])
3669
		{
3670
			case 'methodresponse':
3671
				$v =& $GLOBALS['_xh']['value'];
3672
				if ($GLOBALS['_xh']['isf'] == 1)
3673
				{
3674
					$vc = $v->structmem('faultCode');
3675
					$vs = $v->structmem('faultString');
3676
					$r = new xmlrpcresp(0, $vc->scalarval(), $vs->scalarval());
3677
				}
3678
				else
3679
				{
3680
					$r = new xmlrpcresp($v);
3681
				}
3682
				return $r;
3683
			case 'methodcall':
3684
				$m = new xmlrpcmsg($GLOBALS['_xh']['method']);
3685
				for($i=0; $i < count($GLOBALS['_xh']['params']); $i++)
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
3686
				{
3687
					$m->addParam($GLOBALS['_xh']['params'][$i]);
3688
				}
3689
				return $m;
3690
			case 'value':
3691
				return $GLOBALS['_xh']['value'];
3692
			default:
3693
				return false;
3694
		}
3695
	}
3696
3697
	/**
3698
	* decode a string that is encoded w/ "chunked" transfer encoding
3699
	* as defined in rfc2068 par. 19.4.6
3700
	* code shamelessly stolen from nusoap library by Dietrich Ayala
3701
	*
3702
	* @param string $buffer the string to be decoded
3703
	* @return string
3704
	*/
3705
	function decode_chunked($buffer)
3706
	{
3707
		// length := 0
3708
		$length = 0;
3709
		$new = '';
3710
3711
		// read chunk-size, chunk-extension (if any) and crlf
3712
		// get the position of the linebreak
3713
		$chunkend = strpos($buffer,"\r\n") + 2;
3714
		$temp = substr($buffer,0,$chunkend);
3715
		$chunk_size = hexdec( trim($temp) );
3716
		$chunkstart = $chunkend;
3717
		while($chunk_size > 0)
3718
		{
3719
			$chunkend = strpos($buffer, "\r\n", $chunkstart + $chunk_size);
0 ignored issues
show
Bug introduced by
$chunkstart + $chunk_size of type double is incompatible with the type integer expected by parameter $offset of strpos(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3719
			$chunkend = strpos($buffer, "\r\n", /** @scrutinizer ignore-type */ $chunkstart + $chunk_size);
Loading history...
3720
3721
			// just in case we got a broken connection
3722
			if($chunkend == false)
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $chunkend of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
3723
			{
3724
				$chunk = substr($buffer,$chunkstart);
3725
				// append chunk-data to entity-body
3726
				$new .= $chunk;
3727
				$length += strlen($chunk);
3728
				break;
3729
			}
3730
3731
			// read chunk-data and crlf
3732
			$chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3733
			// append chunk-data to entity-body
3734
			$new .= $chunk;
3735
			// length := length + chunk-size
3736
			$length += strlen($chunk);
3737
			// read chunk-size and crlf
3738
			$chunkstart = $chunkend + 2;
3739
3740
			$chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
3741
			if($chunkend == false)
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $chunkend of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
3742
			{
3743
				break; //just in case we got a broken connection
3744
			}
3745
			$temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3746
			$chunk_size = hexdec( trim($temp) );
3747
			$chunkstart = $chunkend;
3748
		}
3749
		return $new;
3750
	}
3751
3752
	/**
3753
	* xml charset encoding guessing helper function.
3754
	* Tries to determine the charset encoding of an XML chunk received over HTTP.
3755
	* NB: according to the spec (RFC 3023), if text/xml content-type is received over HTTP without a content-type,
3756
	* we SHOULD assume it is strictly US-ASCII. But we try to be more tolerant of unconforming (legacy?) clients/servers,
3757
	* which will be most probably using UTF-8 anyway...
3758
	*
3759
	* @param string $httpheader the http Content-type header
3760
	* @param string $xmlchunk xml content buffer
3761
	* @param string $encoding_prefs comma separated list of character encodings to be used as default (when mb extension is enabled)
3762
	* @return string
3763
	*
3764
	* @todo explore usage of mb_http_input(): does it detect http headers + post data? if so, use it instead of hand-detection!!!
3765
	*/
3766
	function guess_encoding($httpheader='', $xmlchunk='', $encoding_prefs=null)
3767
	{
3768
		// discussion: see http://www.yale.edu/pclt/encoding/
3769
		// 1 - test if encoding is specified in HTTP HEADERS
3770
3771
		//Details:
3772
		// LWS:           (\13\10)?( |\t)+
3773
		// token:         (any char but excluded stuff)+
3774
		// quoted string: " (any char but double quotes and cointrol chars)* "
3775
		// header:        Content-type = ...; charset=value(; ...)*
3776
		//   where value is of type token, no LWS allowed between 'charset' and value
3777
		// Note: we do not check for invalid chars in VALUE:
3778
		//   this had better be done using pure ereg as below
3779
		// Note 2: we might be removing whitespace/tabs that ought to be left in if
3780
		//   the received charset is a quoted string. But nobody uses such charset names...
3781
3782
		/// @todo this test will pass if ANY header has charset specification, not only Content-Type. Fix it?
3783
		$matches = array();
3784
		if(preg_match('/;\s*charset\s*=([^;]+)/i', $httpheader, $matches))
3785
		{
3786
			return strtoupper(trim($matches[1], " \t\""));
3787
		}
3788
3789
		// 2 - scan the first bytes of the data for a UTF-16 (or other) BOM pattern
3790
		//     (source: http://www.w3.org/TR/2000/REC-xml-20001006)
3791
		//     NOTE: actually, according to the spec, even if we find the BOM and determine
3792
		//     an encoding, we should check if there is an encoding specified
3793
		//     in the xml declaration, and verify if they match.
3794
		/// @todo implement check as described above?
3795
		/// @todo implement check for first bytes of string even without a BOM? (It sure looks harder than for cases WITH a BOM)
3796
		if(preg_match('/^(\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\x00\x00\xFF\xFE|\xFE\xFF\x00\x00)/', $xmlchunk))
3797
		{
3798
			return 'UCS-4';
3799
		}
3800
		elseif(preg_match('/^(\xFE\xFF|\xFF\xFE)/', $xmlchunk))
3801
		{
3802
			return 'UTF-16';
3803
		}
3804
		elseif(preg_match('/^(\xEF\xBB\xBF)/', $xmlchunk))
3805
		{
3806
			return 'UTF-8';
3807
		}
3808
3809
		// 3 - test if encoding is specified in the xml declaration
3810
		// Details:
3811
		// SPACE:         (#x20 | #x9 | #xD | #xA)+ === [ \x9\xD\xA]+
3812
		// EQ:            SPACE?=SPACE? === [ \x9\xD\xA]*=[ \x9\xD\xA]*
3813
		if (preg_match('/^<\?xml\s+version\s*=\s*'. "((?:\"[a-zA-Z0-9_.:-]+\")|(?:'[a-zA-Z0-9_.:-]+'))".
3814
			'\s+encoding\s*=\s*' . "((?:\"[A-Za-z][A-Za-z0-9._-]*\")|(?:'[A-Za-z][A-Za-z0-9._-]*'))/",
3815
			$xmlchunk, $matches))
3816
		{
3817
			return strtoupper(substr($matches[2], 1, -1));
3818
		}
3819
3820
		// 4 - if mbstring is available, let it do the guesswork
3821
		// NB: we favour finding an encoding that is compatible with what we can process
3822
		if(extension_loaded('mbstring'))
3823
		{
3824
			if($encoding_prefs)
3825
			{
3826
				$enc = mb_detect_encoding($xmlchunk, $encoding_prefs);
3827
			}
3828
			else
3829
			{
3830
				$enc = mb_detect_encoding($xmlchunk);
3831
			}
3832
			// NB: mb_detect likes to call it ascii, xml parser likes to call it US_ASCII...
3833
			// IANA also likes better US-ASCII, so go with it
3834
			if($enc == 'ASCII')
3835
			{
3836
				$enc = 'US-'.$enc;
3837
			}
3838
			return $enc;
3839
		}
3840
		else
3841
		{
3842
			// no encoding specified: as per HTTP1.1 assume it is iso-8859-1?
3843
			// Both RFC 2616 (HTTP 1.1) and 1945 (HTTP 1.0) clearly state that for text/xxx content types
3844
			// this should be the standard. And we should be getting text/xml as request and response.
3845
			// BUT we have to be backward compatible with the lib, which always used UTF-8 as default...
3846
			return $GLOBALS['xmlrpc_defencoding'];
3847
		}
3848
	}
3849
3850
	/**
3851
	 * Helper function: checks if an xml chunk as a charset declaration (BOM or in the xml declaration)
3852
	 *
3853
	 * @param string $xmlChunk
3854
	 * @return bool
3855
	 */
3856
	function has_encoding($xmlChunk)
3857
	{
3858
		// scan the first bytes of the data for a UTF-16 (or other) BOM pattern
3859
		//	 (source: http://www.w3.org/TR/2000/REC-xml-20001006)
3860
		if (preg_match('/^(\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\x00\x00\xFF\xFE|\xFE\xFF\x00\x00)/', $xmlChunk))
3861
		{
3862
			return true;
3863
		}
3864
		elseif (preg_match('/^(\xFE\xFF|\xFF\xFE)/', $xmlChunk))
3865
		{
3866
			return true;
3867
		}
3868
		elseif (preg_match('/^(\xEF\xBB\xBF)/', $xmlChunk))
3869
		{
3870
			return true;
3871
		}
3872
3873
		// test if encoding is specified in the xml declaration
3874
		// Details:
3875
		// SPACE:		(#x20 | #x9 | #xD | #xA)+ === [ \x9\xD\xA]+
3876
		// EQ:			SPACE?=SPACE? === [ \x9\xD\xA]*=[ \x9\xD\xA]*
3877
		if (preg_match('/^<\?xml\s+version\s*=\s*' . "((?:\"[a-zA-Z0-9_.:-]+\")|(?:'[a-zA-Z0-9_.:-]+'))" .
3878
			'\s+encoding\s*=\s*' . "((?:\"[A-Za-z][A-Za-z0-9._-]*\")|(?:'[A-Za-z][A-Za-z0-9._-]*'))/",
3879
			$xmlChunk, $matches))
3880
		{
3881
			return true;
3882
		}
3883
3884
		return false;
3885
	}
3886
3887
	/**
3888
	* Checks if a given charset encoding is present in a list of encodings or
3889
	* if it is a valid subset of any encoding in the list
3890
	* @param string $encoding charset to be tested
3891
	* @param mixed $validlist comma separated list of valid charsets (or array of charsets)
3892
	* @return bool
3893
	*/
3894
	function is_valid_charset($encoding, $validlist)
3895
	{
3896
		$charset_supersets = array(
3897
			'US-ASCII' => array ('ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
3898
				'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8',
3899
				'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-11', 'ISO-8859-12',
3900
				'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'UTF-8',
3901
				'EUC-JP', 'EUC-', 'EUC-KR', 'EUC-CN')
3902
		);
3903
		if (is_string($validlist))
3904
			$validlist = explode(',', $validlist);
3905
		if (@in_array(strtoupper($encoding), $validlist))
3906
			return true;
3907
		else
3908
		{
3909
			if (array_key_exists($encoding, $charset_supersets))
3910
				foreach ($validlist as $allowed)
3911
					if (in_array($allowed, $charset_supersets[$encoding]))
3912
						return true;
3913
			return false;
3914
		}
3915
	}
3916
3917
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...
3918