Passed
Push — master ( 9698e0...1a6dcb )
by
unknown
03:06
created
Components/Klarna/transport/xmlrpc-3.0.0.beta/lib/xmlrpc.inc 1 patch
Indentation   +3682 added lines, -3682 removed lines patch added patch discarded remove patch
@@ -35,98 +35,98 @@  discard block
 block discarded – undo
35 35
 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36 36
 // OF THE POSSIBILITY OF SUCH DAMAGE.
37 37
 
38
-	if(!function_exists('xml_parser_create'))
39
-	{
40
-		// For PHP 4 onward, XML functionality is always compiled-in on windows:
41
-		// no more need to dl-open it. It might have been compiled out on *nix...
42
-		if(strtoupper(substr(PHP_OS, 0, 3) != 'WIN'))
43
-		{
44
-			dl('xml.so');
45
-		}
46
-	}
47
-
48
-	// G. Giunta 2005/01/29: declare global these variables,
49
-	// so that xmlrpc.inc will work even if included from within a function
50
-	// Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used.
51
-	$GLOBALS['xmlrpcI4']='i4';
52
-	$GLOBALS['xmlrpcInt']='int';
53
-	$GLOBALS['xmlrpcBoolean']='boolean';
54
-	$GLOBALS['xmlrpcDouble']='double';
55
-	$GLOBALS['xmlrpcString']='string';
56
-	$GLOBALS['xmlrpcDateTime']='dateTime.iso8601';
57
-	$GLOBALS['xmlrpcBase64']='base64';
58
-	$GLOBALS['xmlrpcArray']='array';
59
-	$GLOBALS['xmlrpcStruct']='struct';
60
-	$GLOBALS['xmlrpcValue']='undefined';
61
-
62
-	$GLOBALS['xmlrpcTypes']=array(
63
-		$GLOBALS['xmlrpcI4']       => 1,
64
-		$GLOBALS['xmlrpcInt']      => 1,
65
-		$GLOBALS['xmlrpcBoolean']  => 1,
66
-		$GLOBALS['xmlrpcString']   => 1,
67
-		$GLOBALS['xmlrpcDouble']   => 1,
68
-		$GLOBALS['xmlrpcDateTime'] => 1,
69
-		$GLOBALS['xmlrpcBase64']   => 1,
70
-		$GLOBALS['xmlrpcArray']    => 2,
71
-		$GLOBALS['xmlrpcStruct']   => 3
72
-	);
73
-
74
-	$GLOBALS['xmlrpc_valid_parents'] = array(
75
-		'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
76
-		'BOOLEAN' => array('VALUE'),
77
-		'I4' => array('VALUE'),
78
-		'INT' => array('VALUE'),
79
-		'STRING' => array('VALUE'),
80
-		'DOUBLE' => array('VALUE'),
81
-		'DATETIME.ISO8601' => array('VALUE'),
82
-		'BASE64' => array('VALUE'),
83
-		'MEMBER' => array('STRUCT'),
84
-		'NAME' => array('MEMBER'),
85
-		'DATA' => array('ARRAY'),
86
-		'ARRAY' => array('VALUE'),
87
-		'STRUCT' => array('VALUE'),
88
-		'PARAM' => array('PARAMS'),
89
-		'METHODNAME' => array('METHODCALL'),
90
-		'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
91
-		'FAULT' => array('METHODRESPONSE'),
92
-		'NIL' => array('VALUE'), // only used when extension activated
93
-		'EX:NIL' => array('VALUE') // only used when extension activated
94
-	);
95
-
96
-	// define extra types for supporting NULL (useful for json or <NIL/>)
97
-	$GLOBALS['xmlrpcNull']='null';
98
-	$GLOBALS['xmlrpcTypes']['null']=1;
99
-
100
-	// Not in use anymore since 2.0. Shall we remove it?
101
-	/// @deprecated
102
-	$GLOBALS['xmlEntities']=array(
103
-		'amp'  => '&',
104
-		'quot' => '"',
105
-		'lt'   => '<',
106
-		'gt'   => '>',
107
-		'apos' => "'"
108
-	);
109
-
110
-	// tables used for transcoding different charsets into us-ascii xml
111
-
112
-	$GLOBALS['xml_iso88591_Entities']=array();
113
-	$GLOBALS['xml_iso88591_Entities']['in'] = array();
114
-	$GLOBALS['xml_iso88591_Entities']['out'] = array();
115
-	for ($i = 0; $i < 32; $i++)
116
-	{
117
-		$GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
118
-		$GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
119
-	}
120
-	for ($i = 160; $i < 256; $i++)
121
-	{
122
-		$GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
123
-		$GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
124
-	}
125
-
126
-	/// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159?
127
-	/// These will NOT be present in true ISO-8859-1, but will save the unwary
128
-	/// windows user from sending junk (though no luck when reciving them...)
129
-  /*
38
+    if(!function_exists('xml_parser_create'))
39
+    {
40
+        // For PHP 4 onward, XML functionality is always compiled-in on windows:
41
+        // no more need to dl-open it. It might have been compiled out on *nix...
42
+        if(strtoupper(substr(PHP_OS, 0, 3) != 'WIN'))
43
+        {
44
+            dl('xml.so');
45
+        }
46
+    }
47
+
48
+    // G. Giunta 2005/01/29: declare global these variables,
49
+    // so that xmlrpc.inc will work even if included from within a function
50
+    // Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used.
51
+    $GLOBALS['xmlrpcI4']='i4';
52
+    $GLOBALS['xmlrpcInt']='int';
53
+    $GLOBALS['xmlrpcBoolean']='boolean';
54
+    $GLOBALS['xmlrpcDouble']='double';
55
+    $GLOBALS['xmlrpcString']='string';
56
+    $GLOBALS['xmlrpcDateTime']='dateTime.iso8601';
57
+    $GLOBALS['xmlrpcBase64']='base64';
58
+    $GLOBALS['xmlrpcArray']='array';
59
+    $GLOBALS['xmlrpcStruct']='struct';
60
+    $GLOBALS['xmlrpcValue']='undefined';
61
+
62
+    $GLOBALS['xmlrpcTypes']=array(
63
+        $GLOBALS['xmlrpcI4']       => 1,
64
+        $GLOBALS['xmlrpcInt']      => 1,
65
+        $GLOBALS['xmlrpcBoolean']  => 1,
66
+        $GLOBALS['xmlrpcString']   => 1,
67
+        $GLOBALS['xmlrpcDouble']   => 1,
68
+        $GLOBALS['xmlrpcDateTime'] => 1,
69
+        $GLOBALS['xmlrpcBase64']   => 1,
70
+        $GLOBALS['xmlrpcArray']    => 2,
71
+        $GLOBALS['xmlrpcStruct']   => 3
72
+    );
73
+
74
+    $GLOBALS['xmlrpc_valid_parents'] = array(
75
+        'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
76
+        'BOOLEAN' => array('VALUE'),
77
+        'I4' => array('VALUE'),
78
+        'INT' => array('VALUE'),
79
+        'STRING' => array('VALUE'),
80
+        'DOUBLE' => array('VALUE'),
81
+        'DATETIME.ISO8601' => array('VALUE'),
82
+        'BASE64' => array('VALUE'),
83
+        'MEMBER' => array('STRUCT'),
84
+        'NAME' => array('MEMBER'),
85
+        'DATA' => array('ARRAY'),
86
+        'ARRAY' => array('VALUE'),
87
+        'STRUCT' => array('VALUE'),
88
+        'PARAM' => array('PARAMS'),
89
+        'METHODNAME' => array('METHODCALL'),
90
+        'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
91
+        'FAULT' => array('METHODRESPONSE'),
92
+        'NIL' => array('VALUE'), // only used when extension activated
93
+        'EX:NIL' => array('VALUE') // only used when extension activated
94
+    );
95
+
96
+    // define extra types for supporting NULL (useful for json or <NIL/>)
97
+    $GLOBALS['xmlrpcNull']='null';
98
+    $GLOBALS['xmlrpcTypes']['null']=1;
99
+
100
+    // Not in use anymore since 2.0. Shall we remove it?
101
+    /// @deprecated
102
+    $GLOBALS['xmlEntities']=array(
103
+        'amp'  => '&',
104
+        'quot' => '"',
105
+        'lt'   => '<',
106
+        'gt'   => '>',
107
+        'apos' => "'"
108
+    );
109
+
110
+    // tables used for transcoding different charsets into us-ascii xml
111
+
112
+    $GLOBALS['xml_iso88591_Entities']=array();
113
+    $GLOBALS['xml_iso88591_Entities']['in'] = array();
114
+    $GLOBALS['xml_iso88591_Entities']['out'] = array();
115
+    for ($i = 0; $i < 32; $i++)
116
+    {
117
+        $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
118
+        $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
119
+    }
120
+    for ($i = 160; $i < 256; $i++)
121
+    {
122
+        $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
123
+        $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
124
+    }
125
+
126
+    /// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159?
127
+    /// These will NOT be present in true ISO-8859-1, but will save the unwary
128
+    /// windows user from sending junk (though no luck when reciving them...)
129
+    /*
130 130
 	$GLOBALS['xml_cp1252_Entities']=array();
131 131
 	for ($i = 128; $i < 160; $i++)
132 132
 	{
@@ -144,221 +144,221 @@  discard block
 block discarded – undo
144 144
 	);
145 145
   */
146 146
 
147
-	$GLOBALS['xmlrpcerr'] = array(
148
-	'unknown_method'=>1,
149
-	'invalid_return'=>2,
150
-	'incorrect_params'=>3,
151
-	'introspect_unknown'=>4,
152
-	'http_error'=>5,
153
-	'no_data'=>6,
154
-	'no_ssl'=>7,
155
-	'curl_fail'=>8,
156
-	'invalid_request'=>15,
157
-	'no_curl'=>16,
158
-	'server_error'=>17,
159
-	'multicall_error'=>18,
160
-	'multicall_notstruct'=>9,
161
-	'multicall_nomethod'=>10,
162
-	'multicall_notstring'=>11,
163
-	'multicall_recursion'=>12,
164
-	'multicall_noparams'=>13,
165
-	'multicall_notarray'=>14,
166
-
167
-	'cannot_decompress'=>103,
168
-	'decompress_fail'=>104,
169
-	'dechunk_fail'=>105,
170
-	'server_cannot_decompress'=>106,
171
-	'server_decompress_fail'=>107
172
-	);
173
-
174
-	$GLOBALS['xmlrpcstr'] = array(
175
-	'unknown_method'=>'Unknown method',
176
-	'invalid_return'=>'Invalid return payload: enable debugging to examine incoming payload',
177
-	'incorrect_params'=>'Incorrect parameters passed to method',
178
-	'introspect_unknown'=>"Can't introspect: method unknown",
179
-	'http_error'=>"Didn't receive 200 OK from remote server.",
180
-	'no_data'=>'No data received from server.',
181
-	'no_ssl'=>'No SSL support compiled in.',
182
-	'curl_fail'=>'CURL error',
183
-	'invalid_request'=>'Invalid request payload',
184
-	'no_curl'=>'No CURL support compiled in.',
185
-	'server_error'=>'Internal server error',
186
-	'multicall_error'=>'Received from server invalid multicall response',
187
-	'multicall_notstruct'=>'system.multicall expected struct',
188
-	'multicall_nomethod'=>'missing methodName',
189
-	'multicall_notstring'=>'methodName is not a string',
190
-	'multicall_recursion'=>'recursive system.multicall forbidden',
191
-	'multicall_noparams'=>'missing params',
192
-	'multicall_notarray'=>'params is not an array',
193
-
194
-	'cannot_decompress'=>'Received from server compressed HTTP and cannot decompress',
195
-	'decompress_fail'=>'Received from server invalid compressed HTTP',
196
-	'dechunk_fail'=>'Received from server invalid chunked HTTP',
197
-	'server_cannot_decompress'=>'Received from client compressed HTTP request and cannot decompress',
198
-	'server_decompress_fail'=>'Received from client invalid compressed HTTP request'
199
-	);
200
-
201
-	// The charset encoding used by the server for received messages and
202
-	// by the client for received responses when received charset cannot be determined
203
-	// or is not supported
204
-	$GLOBALS['xmlrpc_defencoding']='UTF-8';
205
-
206
-	// The encoding used internally by PHP.
207
-	// String values received as xml will be converted to this, and php strings will be converted to xml
208
-	// as if having been coded with this
209
-	$GLOBALS['xmlrpc_internalencoding']='ISO-8859-1';
210
-
211
-	$GLOBALS['xmlrpcName']='XML-RPC for PHP';
212
-	$GLOBALS['xmlrpcVersion']='3.0.0.beta';
213
-
214
-	// let user errors start at 800
215
-	$GLOBALS['xmlrpcerruser']=800;
216
-	// let XML parse errors start at 100
217
-	$GLOBALS['xmlrpcerrxml']=100;
218
-
219
-	// formulate backslashes for escaping regexp
220
-	// Not in use anymore since 2.0. Shall we remove it?
221
-	/// @deprecated
222
-	$GLOBALS['xmlrpc_backslash']=chr(92).chr(92);
223
-
224
-	// set to TRUE to enable correct decoding of <NIL/> and <EX:NIL/> values
225
-	$GLOBALS['xmlrpc_null_extension']=false;
226
-
227
-	// set to TRUE to enable encoding of php NULL values to <EX:NIL/> instead of <NIL/>
228
-	$GLOBALS['xmlrpc_null_apache_encoding']=false;
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;
147
+    $GLOBALS['xmlrpcerr'] = array(
148
+    'unknown_method'=>1,
149
+    'invalid_return'=>2,
150
+    'incorrect_params'=>3,
151
+    'introspect_unknown'=>4,
152
+    'http_error'=>5,
153
+    'no_data'=>6,
154
+    'no_ssl'=>7,
155
+    'curl_fail'=>8,
156
+    'invalid_request'=>15,
157
+    'no_curl'=>16,
158
+    'server_error'=>17,
159
+    'multicall_error'=>18,
160
+    'multicall_notstruct'=>9,
161
+    'multicall_nomethod'=>10,
162
+    'multicall_notstring'=>11,
163
+    'multicall_recursion'=>12,
164
+    'multicall_noparams'=>13,
165
+    'multicall_notarray'=>14,
166
+
167
+    'cannot_decompress'=>103,
168
+    'decompress_fail'=>104,
169
+    'dechunk_fail'=>105,
170
+    'server_cannot_decompress'=>106,
171
+    'server_decompress_fail'=>107
172
+    );
173
+
174
+    $GLOBALS['xmlrpcstr'] = array(
175
+    'unknown_method'=>'Unknown method',
176
+    'invalid_return'=>'Invalid return payload: enable debugging to examine incoming payload',
177
+    'incorrect_params'=>'Incorrect parameters passed to method',
178
+    'introspect_unknown'=>"Can't introspect: method unknown",
179
+    'http_error'=>"Didn't receive 200 OK from remote server.",
180
+    'no_data'=>'No data received from server.',
181
+    'no_ssl'=>'No SSL support compiled in.',
182
+    'curl_fail'=>'CURL error',
183
+    'invalid_request'=>'Invalid request payload',
184
+    'no_curl'=>'No CURL support compiled in.',
185
+    'server_error'=>'Internal server error',
186
+    'multicall_error'=>'Received from server invalid multicall response',
187
+    'multicall_notstruct'=>'system.multicall expected struct',
188
+    'multicall_nomethod'=>'missing methodName',
189
+    'multicall_notstring'=>'methodName is not a string',
190
+    'multicall_recursion'=>'recursive system.multicall forbidden',
191
+    'multicall_noparams'=>'missing params',
192
+    'multicall_notarray'=>'params is not an array',
193
+
194
+    'cannot_decompress'=>'Received from server compressed HTTP and cannot decompress',
195
+    'decompress_fail'=>'Received from server invalid compressed HTTP',
196
+    'dechunk_fail'=>'Received from server invalid chunked HTTP',
197
+    'server_cannot_decompress'=>'Received from client compressed HTTP request and cannot decompress',
198
+    'server_decompress_fail'=>'Received from client invalid compressed HTTP request'
199
+    );
200
+
201
+    // The charset encoding used by the server for received messages and
202
+    // by the client for received responses when received charset cannot be determined
203
+    // or is not supported
204
+    $GLOBALS['xmlrpc_defencoding']='UTF-8';
205
+
206
+    // The encoding used internally by PHP.
207
+    // String values received as xml will be converted to this, and php strings will be converted to xml
208
+    // as if having been coded with this
209
+    $GLOBALS['xmlrpc_internalencoding']='ISO-8859-1';
210
+
211
+    $GLOBALS['xmlrpcName']='XML-RPC for PHP';
212
+    $GLOBALS['xmlrpcVersion']='3.0.0.beta';
213
+
214
+    // let user errors start at 800
215
+    $GLOBALS['xmlrpcerruser']=800;
216
+    // let XML parse errors start at 100
217
+    $GLOBALS['xmlrpcerrxml']=100;
218
+
219
+    // formulate backslashes for escaping regexp
220
+    // Not in use anymore since 2.0. Shall we remove it?
221
+    /// @deprecated
222
+    $GLOBALS['xmlrpc_backslash']=chr(92).chr(92);
223
+
224
+    // set to TRUE to enable correct decoding of <NIL/> and <EX:NIL/> values
225
+    $GLOBALS['xmlrpc_null_extension']=false;
226
+
227
+    // set to TRUE to enable encoding of php NULL values to <EX:NIL/> instead of <NIL/>
228
+    $GLOBALS['xmlrpc_null_apache_encoding']=false;
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 362
 /*
363 363
 			case 'CP1252_':
364 364
 			case 'CP1252_US-ASCII':
@@ -378,2378 +378,2378 @@  discard block
 block discarded – undo
378 378
 				$escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
379 379
 				break;
380 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)
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)
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']);
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)
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)
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 $no_multicall=false;
825
-		var $proxy='';
826
-		var $proxyport=0;
827
-		var $proxy_user='';
828
-		var $proxy_pass='';
829
-		var $proxy_authtype=1;
830
-		var $cookies=array();
831
-		var $extracurlopts=array();
832
-
833
-		/**
834
-		* List of http compression methods accepted by the client for responses.
835
-		* NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
836
-		*
837
-		* NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since
838
-		* in those cases it will be up to CURL to decide the compression methods
839
-		* it supports. You might check for the presence of 'zlib' in the output of
840
-		* curl_version() to determine wheter compression is supported or not
841
-		*/
842
-		var $accepted_compression = array();
843
-		/**
844
-		* Name of compression scheme to be used for sending requests.
845
-		* Either null, gzip or deflate
846
-		*/
847
-		var $request_compression = '';
848
-		/**
849
-		* CURL handle: used for keep-alive connections (PHP 4.3.8 up, see:
850
-		* http://curl.haxx.se/docs/faq.html#7.3)
851
-		*/
852
-		var $xmlrpc_curl_handle = null;
853
-		/// Wheter to use persistent connections for http 1.1 and https
854
-		var $keepalive = false;
855
-		/// Charset encodings that can be decoded without problems by the client
856
-		var $accepted_charset_encodings = array();
857
-		/// Charset encoding to be used in serializing request. NULL = use ASCII
858
-		var $request_charset_encoding = '';
859
-		/**
860
-		* Decides the content of xmlrpcresp objects returned by calls to send()
861
-		* valid strings are 'xmlrpcvals', 'phpvals' or 'xml'
862
-		*/
863
-		var $return_type = 'xmlrpcvals';
864
-		/**
865
-		* Sent to servers in http headers
866
-		*/
867
-		var $user_agent;
868
-
869
-		/**
870
-		* @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php
871
-		* @param string $server the server name / ip address
872
-		* @param integer $port the port the server is listening on, defaults to 80 or 443 depending on protocol used
873
-		* @param string $method the http protocol variant: defaults to 'http', 'https' and 'http11' can be used if CURL is installed
874
-		*/
875
-		function xmlrpc_client($path, $server='', $port='', $method='')
876
-		{
877
-			// allow user to specify all params in $path
878
-			if($server == '' and $port == '' and $method == '')
879
-			{
880
-				$parts = parse_url($path);
881
-				$server = $parts['host'];
882
-				$path = isset($parts['path']) ? $parts['path'] : '';
883
-				if(isset($parts['query']))
884
-				{
885
-					$path .= '?'.$parts['query'];
886
-				}
887
-				if(isset($parts['fragment']))
888
-				{
889
-					$path .= '#'.$parts['fragment'];
890
-				}
891
-				if(isset($parts['port']))
892
-				{
893
-					$port = $parts['port'];
894
-				}
895
-				if(isset($parts['scheme']))
896
-				{
897
-					$method = $parts['scheme'];
898
-				}
899
-				if(isset($parts['user']))
900
-				{
901
-					$this->username = $parts['user'];
902
-				}
903
-				if(isset($parts['pass']))
904
-				{
905
-					$this->password = $parts['pass'];
906
-				}
907
-			}
908
-			if($path == '' || $path[0] != '/')
909
-			{
910
-				$this->path='/'.$path;
911
-			}
912
-			else
913
-			{
914
-				$this->path=$path;
915
-			}
916
-			$this->server=$server;
917
-			if($port != '')
918
-			{
919
-				$this->port=$port;
920
-			}
921
-			if($method != '')
922
-			{
923
-				$this->method=$method;
924
-			}
925
-
926
-			// if ZLIB is enabled, let the client by default accept compressed responses
927
-			if(function_exists('gzinflate') || (
928
-				function_exists('curl_init') && (($info = curl_version()) &&
929
-				((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version'])))
930
-			))
931
-			{
932
-				$this->accepted_compression = array('gzip', 'deflate');
933
-			}
934
-
935
-			// keepalives: enabled by default
936
-			$this->keepalive = true;
937
-
938
-			// by default the xml parser can support these 3 charset encodings
939
-			$this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
940
-
941
-			// initialize user_agent string
942
-			$this->user_agent = $GLOBALS['xmlrpcName'] . ' ' . $GLOBALS['xmlrpcVersion'];
943
-		}
944
-
945
-		/**
946
-		* Enables/disables the echoing to screen of the xmlrpc responses received
947
-		* @param integer $debug values 0, 1 and 2 are supported (2 = echo sent msg too, before received response)
948
-		* @access public
949
-		*/
950
-		function setDebug($in)
951
-		{
952
-			$this->debug=$in;
953
-		}
954
-
955
-		/**
956
-		* Add some http BASIC AUTH credentials, used by the client to authenticate
957
-		* @param string $u username
958
-		* @param string $p password
959
-		* @param integer $t auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC (basic auth)
960
-		* @access public
961
-		*/
962
-		function setCredentials($u, $p, $t=1)
963
-		{
964
-			$this->username=$u;
965
-			$this->password=$p;
966
-			$this->authtype=$t;
967
-		}
968
-
969
-		/**
970
-		* Add a client-side https certificate
971
-		* @param string $cert
972
-		* @param string $certpass
973
-		* @access public
974
-		*/
975
-		function setCertificate($cert, $certpass)
976
-		{
977
-			$this->cert = $cert;
978
-			$this->certpass = $certpass;
979
-		}
980
-
981
-		/**
982
-		* Add a CA certificate to verify server with (see man page about
983
-		* CURLOPT_CAINFO for more details
984
-		* @param string $cacert certificate file name (or dir holding certificates)
985
-		* @param bool $is_dir set to true to indicate cacert is a dir. defaults to false
986
-		* @access public
987
-		*/
988
-		function setCaCertificate($cacert, $is_dir=false)
989
-		{
990
-			if ($is_dir)
991
-			{
992
-				$this->cacertdir = $cacert;
993
-			}
994
-			else
995
-			{
996
-				$this->cacert = $cacert;
997
-			}
998
-		}
999
-
1000
-		/**
1001
-		* Set attributes for SSL communication: private SSL key
1002
-		* NB: does not work in older php/curl installs
1003
-		* Thanks to Daniel Convissor
1004
-		* @param string $key The name of a file containing a private SSL key
1005
-		* @param string $keypass The secret password needed to use the private SSL key
1006
-		* @access public
1007
-		*/
1008
-		function setKey($key, $keypass)
1009
-		{
1010
-			$this->key = $key;
1011
-			$this->keypass = $keypass;
1012
-		}
1013
-
1014
-		/**
1015
-		* Set attributes for SSL communication: verify server certificate
1016
-		* @param bool $i enable/disable verification of peer certificate
1017
-		* @access public
1018
-		*/
1019
-		function setSSLVerifyPeer($i)
1020
-		{
1021
-			$this->verifypeer = $i;
1022
-		}
1023
-
1024
-		/**
1025
-		* Set attributes for SSL communication: verify match of server cert w. hostname
1026
-		* @param int $i
1027
-		* @access public
1028
-		*/
1029
-		function setSSLVerifyHost($i)
1030
-		{
1031
-			$this->verifyhost = $i;
1032
-		}
1033
-
1034
-		/**
1035
-		* Set proxy info
1036
-		* @param string $proxyhost
1037
-		* @param string $proxyport Defaults to 8080 for HTTP and 443 for HTTPS
1038
-		* @param string $proxyusername Leave blank if proxy has public access
1039
-		* @param string $proxypassword Leave blank if proxy has public access
1040
-		* @param int $proxyauthtype set to constant CURLAUTH_NTLM to use NTLM auth with proxy
1041
-		* @access public
1042
-		*/
1043
-		function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1)
1044
-		{
1045
-			$this->proxy = $proxyhost;
1046
-			$this->proxyport = $proxyport;
1047
-			$this->proxy_user = $proxyusername;
1048
-			$this->proxy_pass = $proxypassword;
1049
-			$this->proxy_authtype = $proxyauthtype;
1050
-		}
1051
-
1052
-		/**
1053
-		* Enables/disables reception of compressed xmlrpc responses.
1054
-		* Note that enabling reception of compressed responses merely adds some standard
1055
-		* http headers to xmlrpc requests. It is up to the xmlrpc server to return
1056
-		* compressed responses when receiving such requests.
1057
-		* @param string $compmethod either 'gzip', 'deflate', 'any' or ''
1058
-		* @access public
1059
-		*/
1060
-		function setAcceptedCompression($compmethod)
1061
-		{
1062
-			if ($compmethod == 'any')
1063
-				$this->accepted_compression = array('gzip', 'deflate');
1064
-			else
1065
-				$this->accepted_compression = array($compmethod);
1066
-		}
1067
-
1068
-		/**
1069
-		* Enables/disables http compression of xmlrpc request.
1070
-		* Take care when sending compressed requests: servers might not support them
1071
-		* (and automatic fallback to uncompressed requests is not yet implemented)
1072
-		* @param string $compmethod either 'gzip', 'deflate' or ''
1073
-		* @access public
1074
-		*/
1075
-		function setRequestCompression($compmethod)
1076
-		{
1077
-			$this->request_compression = $compmethod;
1078
-		}
1079
-
1080
-		/**
1081
-		* Adds a cookie to list of cookies that will be sent to server.
1082
-		* NB: setting any param but name and value will turn the cookie into a 'version 1' cookie:
1083
-		* do not do it unless you know what you are doing
1084
-		* @param string $name
1085
-		* @param string $value
1086
-		* @param string $path
1087
-		* @param string $domain
1088
-		* @param int $port
1089
-		* @access public
1090
-		*
1091
-		* @todo check correctness of urlencoding cookie value (copied from php way of doing it...)
1092
-		*/
1093
-		function setCookie($name, $value='', $path='', $domain='', $port=null)
1094
-		{
1095
-			$this->cookies[$name]['value'] = urlencode($value);
1096
-			if ($path || $domain || $port)
1097
-			{
1098
-				$this->cookies[$name]['path'] = $path;
1099
-				$this->cookies[$name]['domain'] = $domain;
1100
-				$this->cookies[$name]['port'] = $port;
1101
-				$this->cookies[$name]['version'] = 1;
1102
-			}
1103
-			else
1104
-			{
1105
-				$this->cookies[$name]['version'] = 0;
1106
-			}
1107
-		}
1108
-
1109
-		/**
1110
-		* Directly set cURL options, for extra flexibility
1111
-		* It allows eg. to bind client to a specific IP interface / address
1112
-		* @param $options array
1113
-		*/
1114
-		function SetCurlOptions( $options )
1115
-		{
1116
-			$this->extracurlopts = $options;
1117
-		}
1118
-
1119
-		/**
1120
-		* Set user-agent string that will be used by this client instance
1121
-		* in http headers sent to the server
1122
-		*/
1123
-		function SetUserAgent( $agentstring )
1124
-		{
1125
-			$this->user_agent = $agentstring;
1126
-		}
1127
-
1128
-		/**
1129
-		* Send an xmlrpc request
1130
-		* @param mixed $msg The message object, or an array of messages for using multicall, or the complete xml representation of a request
1131
-		* @param integer $timeout Connection timeout, in seconds, If unspecified, a platform specific timeout will apply
1132
-		* @param string $method if left unspecified, the http protocol chosen during creation of the object will be used
1133
-		* @return xmlrpcresp
1134
-		* @access public
1135
-		*/
1136
-		function& send($msg, $timeout=0, $method='')
1137
-		{
1138
-			// if user deos not specify http protocol, use native method of this client
1139
-			// (i.e. method set during call to constructor)
1140
-			if($method == '')
1141
-			{
1142
-				$method = $this->method;
1143
-			}
1144
-
1145
-			if(is_array($msg))
1146
-			{
1147
-				// $msg is an array of xmlrpcmsg's
1148
-				$r = $this->multicall($msg, $timeout, $method);
1149
-				return $r;
1150
-			}
1151
-			elseif(is_string($msg))
1152
-			{
1153
-				$n = new xmlrpcmsg('');
1154
-				$n->payload = $msg;
1155
-				$msg = $n;
1156
-			}
1157
-
1158
-			// where msg is an xmlrpcmsg
1159
-			$msg->debug=$this->debug;
1160
-
1161
-			if($method == 'https')
1162
-			{
1163
-				$r =& $this->sendPayloadHTTPS(
1164
-					$msg,
1165
-					$this->server,
1166
-					$this->port,
1167
-					$timeout,
1168
-					$this->username,
1169
-					$this->password,
1170
-					$this->authtype,
1171
-					$this->cert,
1172
-					$this->certpass,
1173
-					$this->cacert,
1174
-					$this->cacertdir,
1175
-					$this->proxy,
1176
-					$this->proxyport,
1177
-					$this->proxy_user,
1178
-					$this->proxy_pass,
1179
-					$this->proxy_authtype,
1180
-					$this->keepalive,
1181
-					$this->key,
1182
-					$this->keypass
1183
-				);
1184
-			}
1185
-			elseif($method == 'http11')
1186
-			{
1187
-				$r =& $this->sendPayloadCURL(
1188
-					$msg,
1189
-					$this->server,
1190
-					$this->port,
1191
-					$timeout,
1192
-					$this->username,
1193
-					$this->password,
1194
-					$this->authtype,
1195
-					null,
1196
-					null,
1197
-					null,
1198
-					null,
1199
-					$this->proxy,
1200
-					$this->proxyport,
1201
-					$this->proxy_user,
1202
-					$this->proxy_pass,
1203
-					$this->proxy_authtype,
1204
-					'http',
1205
-					$this->keepalive
1206
-				);
1207
-			}
1208
-			else
1209
-			{
1210
-				$r =& $this->sendPayloadHTTP10(
1211
-					$msg,
1212
-					$this->server,
1213
-					$this->port,
1214
-					$timeout,
1215
-					$this->username,
1216
-					$this->password,
1217
-					$this->authtype,
1218
-					$this->proxy,
1219
-					$this->proxyport,
1220
-					$this->proxy_user,
1221
-					$this->proxy_pass,
1222
-					$this->proxy_authtype
1223
-				);
1224
-			}
1225
-
1226
-			return $r;
1227
-		}
1228
-
1229
-		/**
1230
-		* @access private
1231
-		*/
1232
-		function &sendPayloadHTTP10($msg, $server, $port, $timeout=0,
1233
-			$username='', $password='', $authtype=1, $proxyhost='',
1234
-			$proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1)
1235
-		{
1236
-			if($port==0)
1237
-			{
1238
-				$port=80;
1239
-			}
1240
-
1241
-			// Only create the payload if it was not created previously
1242
-			if(empty($msg->payload))
1243
-			{
1244
-				$msg->createPayload($this->request_charset_encoding);
1245
-			}
1246
-
1247
-			$payload = $msg->payload;
1248
-			// Deflate request body and set appropriate request headers
1249
-			if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1250
-			{
1251
-				if($this->request_compression == 'gzip')
1252
-				{
1253
-					$a = @gzencode($payload);
1254
-					if($a)
1255
-					{
1256
-						$payload = $a;
1257
-						$encoding_hdr = "Content-Encoding: gzip\r\n";
1258
-					}
1259
-				}
1260
-				else
1261
-				{
1262
-					$a = @gzcompress($payload);
1263
-					if($a)
1264
-					{
1265
-						$payload = $a;
1266
-						$encoding_hdr = "Content-Encoding: deflate\r\n";
1267
-					}
1268
-				}
1269
-			}
1270
-			else
1271
-			{
1272
-				$encoding_hdr = '';
1273
-			}
1274
-
1275
-			// thanks to Grant Rauscher <[email protected]> for this
1276
-			$credentials='';
1277
-			if($username!='')
1278
-			{
1279
-				$credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
1280
-				if ($authtype != 1)
1281
-				{
1282
-					error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported with HTTP 1.0');
1283
-				}
1284
-			}
1285
-
1286
-			$accepted_encoding = '';
1287
-			if(is_array($this->accepted_compression) && count($this->accepted_compression))
1288
-			{
1289
-				$accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n";
1290
-			}
1291
-
1292
-			$proxy_credentials = '';
1293
-			if($proxyhost)
1294
-			{
1295
-				if($proxyport == 0)
1296
-				{
1297
-					$proxyport = 8080;
1298
-				}
1299
-				$connectserver = $proxyhost;
1300
-				$connectport = $proxyport;
1301
-				$uri = 'http://'.$server.':'.$port.$this->path;
1302
-				if($proxyusername != '')
1303
-				{
1304
-					if ($proxyauthtype != 1)
1305
-					{
1306
-						error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported with HTTP 1.0');
1307
-					}
1308
-					$proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n";
1309
-				}
1310
-			}
1311
-			else
1312
-			{
1313
-				$connectserver = $server;
1314
-				$connectport = $port;
1315
-				$uri = $this->path;
1316
-			}
1317
-
1318
-			// Cookie generation, as per rfc2965 (version 1 cookies) or
1319
-			// netscape's rules (version 0 cookies)
1320
-			$cookieheader='';
1321
-			if (count($this->cookies))
1322
-			{
1323
-				$version = '';
1324
-				foreach ($this->cookies as $name => $cookie)
1325
-				{
1326
-					if ($cookie['version'])
1327
-					{
1328
-						$version = ' $Version="' . $cookie['version'] . '";';
1329
-						$cookieheader .= ' ' . $name . '="' . $cookie['value'] . '";';
1330
-						if ($cookie['path'])
1331
-							$cookieheader .= ' $Path="' . $cookie['path'] . '";';
1332
-						if ($cookie['domain'])
1333
-							$cookieheader .= ' $Domain="' . $cookie['domain'] . '";';
1334
-						if ($cookie['port'])
1335
-							$cookieheader .= ' $Port="' . $cookie['port'] . '";';
1336
-					}
1337
-					else
1338
-					{
1339
-						$cookieheader .= ' ' . $name . '=' . $cookie['value'] . ";";
1340
-					}
1341
-				}
1342
-				$cookieheader = 'Cookie:' . $version . substr($cookieheader, 0, -1) . "\r\n";
1343
-			}
1344
-
1345
-			$op= 'POST ' . $uri. " HTTP/1.0\r\n" .
1346
-				'User-Agent: ' . $this->user_agent . "\r\n" .
1347
-				'Host: '. $server . ':' . $port . "\r\n" .
1348
-				$credentials .
1349
-				$proxy_credentials .
1350
-				$accepted_encoding .
1351
-				$encoding_hdr .
1352
-				'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings) . "\r\n" .
1353
-				$cookieheader .
1354
-				'Content-Type: ' . $msg->content_type . "\r\nContent-Length: " .
1355
-				strlen($payload) . "\r\n\r\n" .
1356
-				$payload;
1357
-
1358
-			if($this->debug > 1)
1359
-			{
1360
-				print "<PRE>\n---SENDING---\n" . htmlentities($op) . "\n---END---\n</PRE>";
1361
-				// let the client see this now in case http times out...
1362
-				flush();
1363
-			}
1364
-
1365
-			if($timeout>0)
1366
-			{
1367
-				$fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout);
1368
-			}
1369
-			else
1370
-			{
1371
-				$fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr);
1372
-			}
1373
-			if($fp)
1374
-			{
1375
-				if($timeout>0 && function_exists('stream_set_timeout'))
1376
-				{
1377
-					stream_set_timeout($fp, $timeout);
1378
-				}
1379
-			}
1380
-			else
1381
-			{
1382
-				$this->errstr='Connect error: '.$this->errstr;
1383
-				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')');
1384
-				return $r;
1385
-			}
1386
-
1387
-			if(!fputs($fp, $op, strlen($op)))
1388
-			{
1389
-				fclose($fp);
1390
-				$this->errstr='Write error';
1391
-				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr);
1392
-				return $r;
1393
-			}
1394
-			else
1395
-			{
1396
-				// reset errno and errstr on succesful socket connection
1397
-				$this->errstr = '';
1398
-			}
1399
-			// G. Giunta 2005/10/24: close socket before parsing.
1400
-			// should yeld slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
1401
-			$ipd='';
1402
-			do
1403
-			{
1404
-				// shall we check for $data === FALSE?
1405
-				// as per the manual, it signals an error
1406
-				$ipd.=fread($fp, 32768);
1407
-			} while(!feof($fp));
1408
-			fclose($fp);
1409
-			$r =& $msg->parseResponse($ipd, false, $this->return_type);
1410
-			return $r;
1411
-
1412
-		}
1413
-
1414
-		/**
1415
-		* @access private
1416
-		*/
1417
-		function &sendPayloadHTTPS($msg, $server, $port, $timeout=0, $username='',
1418
-			$password='', $authtype=1, $cert='',$certpass='', $cacert='', $cacertdir='',
1419
-			$proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1,
1420
-			$keepalive=false, $key='', $keypass='')
1421
-		{
1422
-			$r =& $this->sendPayloadCURL($msg, $server, $port, $timeout, $username,
1423
-				$password, $authtype, $cert, $certpass, $cacert, $cacertdir, $proxyhost, $proxyport,
1424
-				$proxyusername, $proxypassword, $proxyauthtype, 'https', $keepalive, $key, $keypass);
1425
-			return $r;
1426
-		}
1427
-
1428
-		/**
1429
-		* Contributed by Justin Miller <[email protected]>
1430
-		* Requires curl to be built into PHP
1431
-		* NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
1432
-		* @access private
1433
-		*/
1434
-		function &sendPayloadCURL($msg, $server, $port, $timeout=0, $username='',
1435
-			$password='', $authtype=1, $cert='', $certpass='', $cacert='', $cacertdir='',
1436
-			$proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1, $method='https',
1437
-			$keepalive=false, $key='', $keypass='')
1438
-		{
1439
-			if(!function_exists('curl_init'))
1440
-			{
1441
-				$this->errstr='CURL unavailable on this install';
1442
-				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']);
1443
-				return $r;
1444
-			}
1445
-			if($method == 'https')
1446
-			{
1447
-				if(($info = curl_version()) &&
1448
-					((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version']))))
1449
-				{
1450
-					$this->errstr='SSL unavailable on this install';
1451
-					$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']);
1452
-					return $r;
1453
-				}
1454
-			}
1455
-
1456
-			if($port == 0)
1457
-			{
1458
-				if($method == 'http')
1459
-				{
1460
-					$port = 80;
1461
-				}
1462
-				else
1463
-				{
1464
-					$port = 443;
1465
-				}
1466
-			}
1467
-
1468
-			// Only create the payload if it was not created previously
1469
-			if(empty($msg->payload))
1470
-			{
1471
-				$msg->createPayload($this->request_charset_encoding);
1472
-			}
1473
-
1474
-			// Deflate request body and set appropriate request headers
1475
-			$payload = $msg->payload;
1476
-			if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1477
-			{
1478
-				if($this->request_compression == 'gzip')
1479
-				{
1480
-					$a = @gzencode($payload);
1481
-					if($a)
1482
-					{
1483
-						$payload = $a;
1484
-						$encoding_hdr = 'Content-Encoding: gzip';
1485
-					}
1486
-				}
1487
-				else
1488
-				{
1489
-					$a = @gzcompress($payload);
1490
-					if($a)
1491
-					{
1492
-						$payload = $a;
1493
-						$encoding_hdr = 'Content-Encoding: deflate';
1494
-					}
1495
-				}
1496
-			}
1497
-			else
1498
-			{
1499
-				$encoding_hdr = '';
1500
-			}
1501
-
1502
-			if($this->debug > 1)
1503
-			{
1504
-				print "<PRE>\n---SENDING---\n" . htmlentities($payload) . "\n---END---\n</PRE>";
1505
-				// let the client see this now in case http times out...
1506
-				flush();
1507
-			}
1508
-
1509
-			if(!$keepalive || !$this->xmlrpc_curl_handle)
1510
-			{
1511
-				$curl = curl_init($method . '://' . $server . ':' . $port . $this->path);
1512
-				if($keepalive)
1513
-				{
1514
-					$this->xmlrpc_curl_handle = $curl;
1515
-				}
1516
-			}
1517
-			else
1518
-			{
1519
-				$curl = $this->xmlrpc_curl_handle;
1520
-			}
1521
-
1522
-			// results into variable
1523
-			curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1524
-
1525
-			if($this->debug)
1526
-			{
1527
-				curl_setopt($curl, CURLOPT_VERBOSE, 1);
1528
-			}
1529
-			curl_setopt($curl, CURLOPT_USERAGENT, $this->user_agent);
1530
-			// required for XMLRPC: post the data
1531
-			curl_setopt($curl, CURLOPT_POST, 1);
1532
-			// the data
1533
-			curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
1534
-
1535
-			// return the header too
1536
-			curl_setopt($curl, CURLOPT_HEADER, 1);
1537
-
1538
-			// will only work with PHP >= 5.0
1539
-			// NB: if we set an empty string, CURL will add http header indicating
1540
-			// ALL methods it is supporting. This is possibly a better option than
1541
-			// letting the user tell what curl can / cannot do...
1542
-			if(is_array($this->accepted_compression) && count($this->accepted_compression))
1543
-			{
1544
-				//curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression));
1545
-				// empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1546
-				if (count($this->accepted_compression) == 1)
1547
-				{
1548
-					curl_setopt($curl, CURLOPT_ENCODING, $this->accepted_compression[0]);
1549
-				}
1550
-				else
1551
-					curl_setopt($curl, CURLOPT_ENCODING, '');
1552
-			}
1553
-			// extra headers
1554
-			$headers = array('Content-Type: ' . $msg->content_type , 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings));
1555
-			// if no keepalive is wanted, let the server know it in advance
1556
-			if(!$keepalive)
1557
-			{
1558
-				$headers[] = 'Connection: close';
1559
-			}
1560
-			// request compression header
1561
-			if($encoding_hdr)
1562
-			{
1563
-				$headers[] = $encoding_hdr;
1564
-			}
1565
-
1566
-			curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
1567
-			// timeout is borked
1568
-			if($timeout)
1569
-			{
1570
-				curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
1571
-			}
1572
-
1573
-			if($username && $password)
1574
-			{
1575
-				curl_setopt($curl, CURLOPT_USERPWD, $username.':'.$password);
1576
-				if (defined('CURLOPT_HTTPAUTH'))
1577
-				{
1578
-					curl_setopt($curl, CURLOPT_HTTPAUTH, $authtype);
1579
-				}
1580
-				else if ($authtype != 1)
1581
-				{
1582
-					error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported by the current PHP/curl install');
1583
-				}
1584
-			}
1585
-
1586
-			if($method == 'https')
1587
-			{
1588
-				// set cert file
1589
-				if($cert)
1590
-				{
1591
-					curl_setopt($curl, CURLOPT_SSLCERT, $cert);
1592
-				}
1593
-				// set cert password
1594
-				if($certpass)
1595
-				{
1596
-					curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass);
1597
-				}
1598
-				// whether to verify remote host's cert
1599
-				curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
1600
-				// set ca certificates file/dir
1601
-				if($cacert)
1602
-				{
1603
-					curl_setopt($curl, CURLOPT_CAINFO, $cacert);
1604
-				}
1605
-				if($cacertdir)
1606
-				{
1607
-					curl_setopt($curl, CURLOPT_CAPATH, $cacertdir);
1608
-				}
1609
-				// set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1610
-				if($key)
1611
-				{
1612
-					curl_setopt($curl, CURLOPT_SSLKEY, $key);
1613
-				}
1614
-				// set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1615
-				if($keypass)
1616
-				{
1617
-					curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $keypass);
1618
-				}
1619
-				// 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
1620
-				curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
1621
-			}
1622
-
1623
-			// proxy info
1624
-			if($proxyhost)
1625
-			{
1626
-				if($proxyport == 0)
1627
-				{
1628
-					$proxyport = 8080; // NB: even for HTTPS, local connection is on port 8080
1629
-				}
1630
-				curl_setopt($curl, CURLOPT_PROXY, $proxyhost.':'.$proxyport);
1631
-				//curl_setopt($curl, CURLOPT_PROXYPORT,$proxyport);
1632
-				if($proxyusername)
1633
-				{
1634
-					curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyusername.':'.$proxypassword);
1635
-					if (defined('CURLOPT_PROXYAUTH'))
1636
-					{
1637
-						curl_setopt($curl, CURLOPT_PROXYAUTH, $proxyauthtype);
1638
-					}
1639
-					else if ($proxyauthtype != 1)
1640
-					{
1641
-						error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported by the current PHP/curl install');
1642
-					}
1643
-				}
1644
-			}
1645
-
1646
-			// NB: should we build cookie http headers by hand rather than let CURL do it?
1647
-			// the following code does not honour 'expires', 'path' and 'domain' cookie attributes
1648
-			// set to client obj the the user...
1649
-			if (count($this->cookies))
1650
-			{
1651
-				$cookieheader = '';
1652
-				foreach ($this->cookies as $name => $cookie)
1653
-				{
1654
-					$cookieheader .= $name . '=' . $cookie['value'] . '; ';
1655
-				}
1656
-				curl_setopt($curl, CURLOPT_COOKIE, substr($cookieheader, 0, -2));
1657
-			}
1658
-
1659
-			foreach ($this->extracurlopts as $opt => $val)
1660
-			{
1661
-				curl_setopt($curl, $opt, $val);
1662
-			}
1663
-
1664
-			$result = curl_exec($curl);
1665
-
1666
-			if ($this->debug > 1)
1667
-			{
1668
-				print "<PRE>\n---CURL INFO---\n";
1669
-				foreach(curl_getinfo($curl) as $name => $val)
1670
-					 print $name . ': ' . htmlentities($val). "\n";
1671
-				print "---END---\n</PRE>";
1672
-			}
1673
-
1674
-			if(!$result) /// @todo we should use a better check here - what if we get back '' or '0'?
1675
-			{
1676
-				$this->errstr='no response';
1677
-				$resp=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['curl_fail'], $GLOBALS['xmlrpcstr']['curl_fail']. ': '. curl_error($curl));
1678
-				curl_close($curl);
1679
-				if($keepalive)
1680
-				{
1681
-					$this->xmlrpc_curl_handle = null;
1682
-				}
1683
-			}
1684
-			else
1685
-			{
1686
-				if(!$keepalive)
1687
-				{
1688
-					curl_close($curl);
1689
-				}
1690
-				$resp =& $msg->parseResponse($result, true, $this->return_type);
1691
-			}
1692
-			return $resp;
1693
-		}
1694
-
1695
-		/**
1696
-		* Send an array of request messages and return an array of responses.
1697
-		* Unless $this->no_multicall has been set to true, it will try first
1698
-		* to use one single xmlrpc call to server method system.multicall, and
1699
-		* revert to sending many successive calls in case of failure.
1700
-		* This failure is also stored in $this->no_multicall for subsequent calls.
1701
-		* Unfortunately, there is no server error code universally used to denote
1702
-		* the fact that multicall is unsupported, so there is no way to reliably
1703
-		* distinguish between that and a temporary failure.
1704
-		* If you are sure that server supports multicall and do not want to
1705
-		* fallback to using many single calls, set the fourth parameter to FALSE.
1706
-		*
1707
-		* NB: trying to shoehorn extra functionality into existing syntax has resulted
1708
-		* in pretty much convoluted code...
1709
-		*
1710
-		* @param array $msgs an array of xmlrpcmsg objects
1711
-		* @param integer $timeout connection timeout (in seconds)
1712
-		* @param string $method the http protocol variant to be used
1713
-		* @param boolean fallback When true, upon receiveing an error during multicall, multiple single calls will be attempted
1714
-		* @return array
1715
-		* @access public
1716
-		*/
1717
-		function multicall($msgs, $timeout=0, $method='', $fallback=true)
1718
-		{
1719
-			if ($method == '')
1720
-			{
1721
-				$method = $this->method;
1722
-			}
1723
-			if(!$this->no_multicall)
1724
-			{
1725
-				$results = $this->_try_multicall($msgs, $timeout, $method);
1726
-				if(is_array($results))
1727
-				{
1728
-					// System.multicall succeeded
1729
-					return $results;
1730
-				}
1731
-				else
1732
-				{
1733
-					// either system.multicall is unsupported by server,
1734
-					// or call failed for some other reason.
1735
-					if ($fallback)
1736
-					{
1737
-						// Don't try it next time...
1738
-						$this->no_multicall = true;
1739
-					}
1740
-					else
1741
-					{
1742
-						if (is_a($results, 'xmlrpcresp'))
1743
-						{
1744
-							$result = $results;
1745
-						}
1746
-						else
1747
-						{
1748
-							$result = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['multicall_error'], $GLOBALS['xmlrpcstr']['multicall_error']);
1749
-						}
1750
-					}
1751
-				}
1752
-			}
1753
-			else
1754
-			{
1755
-				// override fallback, in case careless user tries to do two
1756
-				// opposite things at the same time
1757
-				$fallback = true;
1758
-			}
1759
-
1760
-			$results = array();
1761
-			if ($fallback)
1762
-			{
1763
-				// system.multicall is (probably) unsupported by server:
1764
-				// emulate multicall via multiple requests
1765
-				foreach($msgs as $msg)
1766
-				{
1767
-					$results[] =& $this->send($msg, $timeout, $method);
1768
-				}
1769
-			}
1770
-			else
1771
-			{
1772
-				// user does NOT want to fallback on many single calls:
1773
-				// since we should always return an array of responses,
1774
-				// return an array with the same error repeated n times
1775
-				foreach($msgs as $msg)
1776
-				{
1777
-					$results[] = $result;
1778
-				}
1779
-			}
1780
-			return $results;
1781
-		}
1782
-
1783
-		/**
1784
-		* Attempt to boxcar $msgs via system.multicall.
1785
-		* Returns either an array of xmlrpcreponses, an xmlrpc error response
1786
-		* or false (when received response does not respect valid multicall syntax)
1787
-		* @access private
1788
-		*/
1789
-		function _try_multicall($msgs, $timeout, $method)
1790
-		{
1791
-			// Construct multicall message
1792
-			$calls = array();
1793
-			foreach($msgs as $msg)
1794
-			{
1795
-				$call['methodName'] = new xmlrpcval($msg->method(),'string');
1796
-				$numParams = $msg->getNumParams();
1797
-				$params = array();
1798
-				for($i = 0; $i < $numParams; $i++)
1799
-				{
1800
-					$params[$i] = $msg->getParam($i);
1801
-				}
1802
-				$call['params'] = new xmlrpcval($params, 'array');
1803
-				$calls[] = new xmlrpcval($call, 'struct');
1804
-			}
1805
-			$multicall = new xmlrpcmsg('system.multicall');
1806
-			$multicall->addParam(new xmlrpcval($calls, 'array'));
1807
-
1808
-			// Attempt RPC call
1809
-			$result =& $this->send($multicall, $timeout, $method);
1810
-
1811
-			if($result->faultCode() != 0)
1812
-			{
1813
-				// call to system.multicall failed
1814
-				return $result;
1815
-			}
1816
-
1817
-			// Unpack responses.
1818
-			$rets = $result->value();
1819
-
1820
-			if ($this->return_type == 'xml')
1821
-			{
1822
-					return $rets;
1823
-			}
1824
-			else if ($this->return_type == 'phpvals')
1825
-			{
1826
-				///@todo test this code branch...
1827
-				$rets = $result->value();
1828
-				if(!is_array($rets))
1829
-				{
1830
-					return false;		// bad return type from system.multicall
1831
-				}
1832
-				$numRets = count($rets);
1833
-				if($numRets != count($msgs))
1834
-				{
1835
-					return false;		// wrong number of return values.
1836
-				}
1837
-
1838
-				$response = array();
1839
-				for($i = 0; $i < $numRets; $i++)
1840
-				{
1841
-					$val = $rets[$i];
1842
-					if (!is_array($val)) {
1843
-						return false;
1844
-					}
1845
-					switch(count($val))
1846
-					{
1847
-						case 1:
1848
-							if(!isset($val[0]))
1849
-							{
1850
-								return false;		// Bad value
1851
-							}
1852
-							// Normal return value
1853
-							$response[$i] = new xmlrpcresp($val[0], 0, '', 'phpvals');
1854
-							break;
1855
-						case 2:
1856
-							///	@todo remove usage of @: it is apparently quite slow
1857
-							$code = @$val['faultCode'];
1858
-							if(!is_int($code))
1859
-							{
1860
-								return false;
1861
-							}
1862
-							$str = @$val['faultString'];
1863
-							if(!is_string($str))
1864
-							{
1865
-								return false;
1866
-							}
1867
-							$response[$i] = new xmlrpcresp(0, $code, $str);
1868
-							break;
1869
-						default:
1870
-							return false;
1871
-					}
1872
-				}
1873
-				return $response;
1874
-			}
1875
-			else // return type == 'xmlrpcvals'
1876
-			{
1877
-				$rets = $result->value();
1878
-				if($rets->kindOf() != 'array')
1879
-				{
1880
-					return false;		// bad return type from system.multicall
1881
-				}
1882
-				$numRets = $rets->arraysize();
1883
-				if($numRets != count($msgs))
1884
-				{
1885
-					return false;		// wrong number of return values.
1886
-				}
1887
-
1888
-				$response = array();
1889
-				for($i = 0; $i < $numRets; $i++)
1890
-				{
1891
-					$val = $rets->arraymem($i);
1892
-					switch($val->kindOf())
1893
-					{
1894
-						case 'array':
1895
-							if($val->arraysize() != 1)
1896
-							{
1897
-								return false;		// Bad value
1898
-							}
1899
-							// Normal return value
1900
-							$response[$i] = new xmlrpcresp($val->arraymem(0));
1901
-							break;
1902
-						case 'struct':
1903
-							$code = $val->structmem('faultCode');
1904
-							if($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
1905
-							{
1906
-								return false;
1907
-							}
1908
-							$str = $val->structmem('faultString');
1909
-							if($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
1910
-							{
1911
-								return false;
1912
-							}
1913
-							$response[$i] = new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
1914
-							break;
1915
-						default:
1916
-							return false;
1917
-					}
1918
-				}
1919
-				return $response;
1920
-			}
1921
-		}
1922
-	} // end class xmlrpc_client
1923
-
1924
-	class xmlrpcresp
1925
-	{
1926
-		var $val = 0;
1927
-		var $valtyp;
1928
-		var $errno = 0;
1929
-		var $errstr = '';
1930
-		var $payload;
1931
-		var $hdrs = array();
1932
-		var $_cookies = array();
1933
-		var $content_type = 'text/xml';
1934
-		var $raw_data = '';
1935
-
1936
-		/**
1937
-		* @param mixed $val either an xmlrpcval obj, a php value or the xml serialization of an xmlrpcval (a string)
1938
-		* @param integer $fcode set it to anything but 0 to create an error response
1939
-		* @param string $fstr the error string, in case of an error response
1940
-		* @param string $valtyp either 'xmlrpcvals', 'phpvals' or 'xml'
1941
-		*
1942
-		* @todo add check that $val / $fcode / $fstr is of correct type???
1943
-		* NB: as of now we do not do it, since it might be either an xmlrpcval or a plain
1944
-		* php val, or a complete xml chunk, depending on usage of xmlrpc_client::send() inside which creator is called...
1945
-		*/
1946
-		function xmlrpcresp($val, $fcode = 0, $fstr = '', $valtyp='')
1947
-		{
1948
-			if($fcode != 0)
1949
-			{
1950
-				// error response
1951
-				$this->errno = $fcode;
1952
-				$this->errstr = $fstr;
1953
-				//$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
1954
-			}
1955
-			else
1956
-			{
1957
-				// successful response
1958
-				$this->val = $val;
1959
-				if ($valtyp == '')
1960
-				{
1961
-					// user did not declare type of response value: try to guess it
1962
-					if (is_object($this->val) && is_a($this->val, 'xmlrpcval'))
1963
-					{
1964
-						$this->valtyp = 'xmlrpcvals';
1965
-					}
1966
-					else if (is_string($this->val))
1967
-					{
1968
-						$this->valtyp = 'xml';
1969
-
1970
-					}
1971
-					else
1972
-					{
1973
-						$this->valtyp = 'phpvals';
1974
-					}
1975
-				}
1976
-				else
1977
-				{
1978
-					// user declares type of resp value: believe him
1979
-					$this->valtyp = $valtyp;
1980
-				}
1981
-			}
1982
-		}
1983
-
1984
-		/**
1985
-		* Returns the error code of the response.
1986
-		* @return integer the error code of this response (0 for not-error responses)
1987
-		* @access public
1988
-		*/
1989
-		function faultCode()
1990
-		{
1991
-			return $this->errno;
1992
-		}
1993
-
1994
-		/**
1995
-		* Returns the error code of the response.
1996
-		* @return string the error string of this response ('' for not-error responses)
1997
-		* @access public
1998
-		*/
1999
-		function faultString()
2000
-		{
2001
-			return $this->errstr;
2002
-		}
2003
-
2004
-		/**
2005
-		* Returns the value received by the server.
2006
-		* @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
2007
-		* @access public
2008
-		*/
2009
-		function value()
2010
-		{
2011
-			return $this->val;
2012
-		}
2013
-
2014
-		/**
2015
-		* Returns an array with the cookies received from the server.
2016
-		* Array has the form: $cookiename => array ('value' => $val, $attr1 => $val1, $attr2 = $val2, ...)
2017
-		* with attributes being e.g. 'expires', 'path', domain'.
2018
-		* NB: cookies sent as 'expired' by the server (i.e. with an expiry date in the past)
2019
-		* are still present in the array. It is up to the user-defined code to decide
2020
-		* how to use the received cookies, and wheter they have to be sent back with the next
2021
-		* request to the server (using xmlrpc_client::setCookie) or not
2022
-		* @return array array of cookies received from the server
2023
-		* @access public
2024
-		*/
2025
-		function cookies()
2026
-		{
2027
-			return $this->_cookies;
2028
-		}
2029
-
2030
-		/**
2031
-		* Returns xml representation of the response. XML prologue not included
2032
-		* @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
2033
-		* @return string the xml representation of the response
2034
-		* @access public
2035
-		*/
2036
-		function serialize($charset_encoding='')
2037
-		{
2038
-			if ($charset_encoding != '')
2039
-				$this->content_type = 'text/xml; charset=' . $charset_encoding;
2040
-			else
2041
-				$this->content_type = 'text/xml';
2042
-			$result = "<methodResponse>\n";
2043
-			if($this->errno)
2044
-			{
2045
-				// G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
2046
-				// by xml-encoding non ascii chars
2047
-				$result .= "<fault>\n" .
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)
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)
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']);
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)
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)
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 $no_multicall=false;
825
+        var $proxy='';
826
+        var $proxyport=0;
827
+        var $proxy_user='';
828
+        var $proxy_pass='';
829
+        var $proxy_authtype=1;
830
+        var $cookies=array();
831
+        var $extracurlopts=array();
832
+
833
+        /**
834
+         * List of http compression methods accepted by the client for responses.
835
+         * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
836
+         *
837
+         * NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since
838
+         * in those cases it will be up to CURL to decide the compression methods
839
+         * it supports. You might check for the presence of 'zlib' in the output of
840
+         * curl_version() to determine wheter compression is supported or not
841
+         */
842
+        var $accepted_compression = array();
843
+        /**
844
+         * Name of compression scheme to be used for sending requests.
845
+         * Either null, gzip or deflate
846
+         */
847
+        var $request_compression = '';
848
+        /**
849
+         * CURL handle: used for keep-alive connections (PHP 4.3.8 up, see:
850
+         * http://curl.haxx.se/docs/faq.html#7.3)
851
+         */
852
+        var $xmlrpc_curl_handle = null;
853
+        /// Wheter to use persistent connections for http 1.1 and https
854
+        var $keepalive = false;
855
+        /// Charset encodings that can be decoded without problems by the client
856
+        var $accepted_charset_encodings = array();
857
+        /// Charset encoding to be used in serializing request. NULL = use ASCII
858
+        var $request_charset_encoding = '';
859
+        /**
860
+         * Decides the content of xmlrpcresp objects returned by calls to send()
861
+         * valid strings are 'xmlrpcvals', 'phpvals' or 'xml'
862
+         */
863
+        var $return_type = 'xmlrpcvals';
864
+        /**
865
+         * Sent to servers in http headers
866
+         */
867
+        var $user_agent;
868
+
869
+        /**
870
+         * @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php
871
+         * @param string $server the server name / ip address
872
+         * @param integer $port the port the server is listening on, defaults to 80 or 443 depending on protocol used
873
+         * @param string $method the http protocol variant: defaults to 'http', 'https' and 'http11' can be used if CURL is installed
874
+         */
875
+        function xmlrpc_client($path, $server='', $port='', $method='')
876
+        {
877
+            // allow user to specify all params in $path
878
+            if($server == '' and $port == '' and $method == '')
879
+            {
880
+                $parts = parse_url($path);
881
+                $server = $parts['host'];
882
+                $path = isset($parts['path']) ? $parts['path'] : '';
883
+                if(isset($parts['query']))
884
+                {
885
+                    $path .= '?'.$parts['query'];
886
+                }
887
+                if(isset($parts['fragment']))
888
+                {
889
+                    $path .= '#'.$parts['fragment'];
890
+                }
891
+                if(isset($parts['port']))
892
+                {
893
+                    $port = $parts['port'];
894
+                }
895
+                if(isset($parts['scheme']))
896
+                {
897
+                    $method = $parts['scheme'];
898
+                }
899
+                if(isset($parts['user']))
900
+                {
901
+                    $this->username = $parts['user'];
902
+                }
903
+                if(isset($parts['pass']))
904
+                {
905
+                    $this->password = $parts['pass'];
906
+                }
907
+            }
908
+            if($path == '' || $path[0] != '/')
909
+            {
910
+                $this->path='/'.$path;
911
+            }
912
+            else
913
+            {
914
+                $this->path=$path;
915
+            }
916
+            $this->server=$server;
917
+            if($port != '')
918
+            {
919
+                $this->port=$port;
920
+            }
921
+            if($method != '')
922
+            {
923
+                $this->method=$method;
924
+            }
925
+
926
+            // if ZLIB is enabled, let the client by default accept compressed responses
927
+            if(function_exists('gzinflate') || (
928
+                function_exists('curl_init') && (($info = curl_version()) &&
929
+                ((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version'])))
930
+            ))
931
+            {
932
+                $this->accepted_compression = array('gzip', 'deflate');
933
+            }
934
+
935
+            // keepalives: enabled by default
936
+            $this->keepalive = true;
937
+
938
+            // by default the xml parser can support these 3 charset encodings
939
+            $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
940
+
941
+            // initialize user_agent string
942
+            $this->user_agent = $GLOBALS['xmlrpcName'] . ' ' . $GLOBALS['xmlrpcVersion'];
943
+        }
944
+
945
+        /**
946
+         * Enables/disables the echoing to screen of the xmlrpc responses received
947
+         * @param integer $debug values 0, 1 and 2 are supported (2 = echo sent msg too, before received response)
948
+         * @access public
949
+         */
950
+        function setDebug($in)
951
+        {
952
+            $this->debug=$in;
953
+        }
954
+
955
+        /**
956
+         * Add some http BASIC AUTH credentials, used by the client to authenticate
957
+         * @param string $u username
958
+         * @param string $p password
959
+         * @param integer $t auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC (basic auth)
960
+         * @access public
961
+         */
962
+        function setCredentials($u, $p, $t=1)
963
+        {
964
+            $this->username=$u;
965
+            $this->password=$p;
966
+            $this->authtype=$t;
967
+        }
968
+
969
+        /**
970
+         * Add a client-side https certificate
971
+         * @param string $cert
972
+         * @param string $certpass
973
+         * @access public
974
+         */
975
+        function setCertificate($cert, $certpass)
976
+        {
977
+            $this->cert = $cert;
978
+            $this->certpass = $certpass;
979
+        }
980
+
981
+        /**
982
+         * Add a CA certificate to verify server with (see man page about
983
+         * CURLOPT_CAINFO for more details
984
+         * @param string $cacert certificate file name (or dir holding certificates)
985
+         * @param bool $is_dir set to true to indicate cacert is a dir. defaults to false
986
+         * @access public
987
+         */
988
+        function setCaCertificate($cacert, $is_dir=false)
989
+        {
990
+            if ($is_dir)
991
+            {
992
+                $this->cacertdir = $cacert;
993
+            }
994
+            else
995
+            {
996
+                $this->cacert = $cacert;
997
+            }
998
+        }
999
+
1000
+        /**
1001
+         * Set attributes for SSL communication: private SSL key
1002
+         * NB: does not work in older php/curl installs
1003
+         * Thanks to Daniel Convissor
1004
+         * @param string $key The name of a file containing a private SSL key
1005
+         * @param string $keypass The secret password needed to use the private SSL key
1006
+         * @access public
1007
+         */
1008
+        function setKey($key, $keypass)
1009
+        {
1010
+            $this->key = $key;
1011
+            $this->keypass = $keypass;
1012
+        }
1013
+
1014
+        /**
1015
+         * Set attributes for SSL communication: verify server certificate
1016
+         * @param bool $i enable/disable verification of peer certificate
1017
+         * @access public
1018
+         */
1019
+        function setSSLVerifyPeer($i)
1020
+        {
1021
+            $this->verifypeer = $i;
1022
+        }
1023
+
1024
+        /**
1025
+         * Set attributes for SSL communication: verify match of server cert w. hostname
1026
+         * @param int $i
1027
+         * @access public
1028
+         */
1029
+        function setSSLVerifyHost($i)
1030
+        {
1031
+            $this->verifyhost = $i;
1032
+        }
1033
+
1034
+        /**
1035
+         * Set proxy info
1036
+         * @param string $proxyhost
1037
+         * @param string $proxyport Defaults to 8080 for HTTP and 443 for HTTPS
1038
+         * @param string $proxyusername Leave blank if proxy has public access
1039
+         * @param string $proxypassword Leave blank if proxy has public access
1040
+         * @param int $proxyauthtype set to constant CURLAUTH_NTLM to use NTLM auth with proxy
1041
+         * @access public
1042
+         */
1043
+        function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1)
1044
+        {
1045
+            $this->proxy = $proxyhost;
1046
+            $this->proxyport = $proxyport;
1047
+            $this->proxy_user = $proxyusername;
1048
+            $this->proxy_pass = $proxypassword;
1049
+            $this->proxy_authtype = $proxyauthtype;
1050
+        }
1051
+
1052
+        /**
1053
+         * Enables/disables reception of compressed xmlrpc responses.
1054
+         * Note that enabling reception of compressed responses merely adds some standard
1055
+         * http headers to xmlrpc requests. It is up to the xmlrpc server to return
1056
+         * compressed responses when receiving such requests.
1057
+         * @param string $compmethod either 'gzip', 'deflate', 'any' or ''
1058
+         * @access public
1059
+         */
1060
+        function setAcceptedCompression($compmethod)
1061
+        {
1062
+            if ($compmethod == 'any')
1063
+                $this->accepted_compression = array('gzip', 'deflate');
1064
+            else
1065
+                $this->accepted_compression = array($compmethod);
1066
+        }
1067
+
1068
+        /**
1069
+         * Enables/disables http compression of xmlrpc request.
1070
+         * Take care when sending compressed requests: servers might not support them
1071
+         * (and automatic fallback to uncompressed requests is not yet implemented)
1072
+         * @param string $compmethod either 'gzip', 'deflate' or ''
1073
+         * @access public
1074
+         */
1075
+        function setRequestCompression($compmethod)
1076
+        {
1077
+            $this->request_compression = $compmethod;
1078
+        }
1079
+
1080
+        /**
1081
+         * Adds a cookie to list of cookies that will be sent to server.
1082
+         * NB: setting any param but name and value will turn the cookie into a 'version 1' cookie:
1083
+         * do not do it unless you know what you are doing
1084
+         * @param string $name
1085
+         * @param string $value
1086
+         * @param string $path
1087
+         * @param string $domain
1088
+         * @param int $port
1089
+         * @access public
1090
+         *
1091
+         * @todo check correctness of urlencoding cookie value (copied from php way of doing it...)
1092
+         */
1093
+        function setCookie($name, $value='', $path='', $domain='', $port=null)
1094
+        {
1095
+            $this->cookies[$name]['value'] = urlencode($value);
1096
+            if ($path || $domain || $port)
1097
+            {
1098
+                $this->cookies[$name]['path'] = $path;
1099
+                $this->cookies[$name]['domain'] = $domain;
1100
+                $this->cookies[$name]['port'] = $port;
1101
+                $this->cookies[$name]['version'] = 1;
1102
+            }
1103
+            else
1104
+            {
1105
+                $this->cookies[$name]['version'] = 0;
1106
+            }
1107
+        }
1108
+
1109
+        /**
1110
+         * Directly set cURL options, for extra flexibility
1111
+         * It allows eg. to bind client to a specific IP interface / address
1112
+         * @param $options array
1113
+         */
1114
+        function SetCurlOptions( $options )
1115
+        {
1116
+            $this->extracurlopts = $options;
1117
+        }
1118
+
1119
+        /**
1120
+         * Set user-agent string that will be used by this client instance
1121
+         * in http headers sent to the server
1122
+         */
1123
+        function SetUserAgent( $agentstring )
1124
+        {
1125
+            $this->user_agent = $agentstring;
1126
+        }
1127
+
1128
+        /**
1129
+         * Send an xmlrpc request
1130
+         * @param mixed $msg The message object, or an array of messages for using multicall, or the complete xml representation of a request
1131
+         * @param integer $timeout Connection timeout, in seconds, If unspecified, a platform specific timeout will apply
1132
+         * @param string $method if left unspecified, the http protocol chosen during creation of the object will be used
1133
+         * @return xmlrpcresp
1134
+         * @access public
1135
+         */
1136
+        function& send($msg, $timeout=0, $method='')
1137
+        {
1138
+            // if user deos not specify http protocol, use native method of this client
1139
+            // (i.e. method set during call to constructor)
1140
+            if($method == '')
1141
+            {
1142
+                $method = $this->method;
1143
+            }
1144
+
1145
+            if(is_array($msg))
1146
+            {
1147
+                // $msg is an array of xmlrpcmsg's
1148
+                $r = $this->multicall($msg, $timeout, $method);
1149
+                return $r;
1150
+            }
1151
+            elseif(is_string($msg))
1152
+            {
1153
+                $n = new xmlrpcmsg('');
1154
+                $n->payload = $msg;
1155
+                $msg = $n;
1156
+            }
1157
+
1158
+            // where msg is an xmlrpcmsg
1159
+            $msg->debug=$this->debug;
1160
+
1161
+            if($method == 'https')
1162
+            {
1163
+                $r =& $this->sendPayloadHTTPS(
1164
+                    $msg,
1165
+                    $this->server,
1166
+                    $this->port,
1167
+                    $timeout,
1168
+                    $this->username,
1169
+                    $this->password,
1170
+                    $this->authtype,
1171
+                    $this->cert,
1172
+                    $this->certpass,
1173
+                    $this->cacert,
1174
+                    $this->cacertdir,
1175
+                    $this->proxy,
1176
+                    $this->proxyport,
1177
+                    $this->proxy_user,
1178
+                    $this->proxy_pass,
1179
+                    $this->proxy_authtype,
1180
+                    $this->keepalive,
1181
+                    $this->key,
1182
+                    $this->keypass
1183
+                );
1184
+            }
1185
+            elseif($method == 'http11')
1186
+            {
1187
+                $r =& $this->sendPayloadCURL(
1188
+                    $msg,
1189
+                    $this->server,
1190
+                    $this->port,
1191
+                    $timeout,
1192
+                    $this->username,
1193
+                    $this->password,
1194
+                    $this->authtype,
1195
+                    null,
1196
+                    null,
1197
+                    null,
1198
+                    null,
1199
+                    $this->proxy,
1200
+                    $this->proxyport,
1201
+                    $this->proxy_user,
1202
+                    $this->proxy_pass,
1203
+                    $this->proxy_authtype,
1204
+                    'http',
1205
+                    $this->keepalive
1206
+                );
1207
+            }
1208
+            else
1209
+            {
1210
+                $r =& $this->sendPayloadHTTP10(
1211
+                    $msg,
1212
+                    $this->server,
1213
+                    $this->port,
1214
+                    $timeout,
1215
+                    $this->username,
1216
+                    $this->password,
1217
+                    $this->authtype,
1218
+                    $this->proxy,
1219
+                    $this->proxyport,
1220
+                    $this->proxy_user,
1221
+                    $this->proxy_pass,
1222
+                    $this->proxy_authtype
1223
+                );
1224
+            }
1225
+
1226
+            return $r;
1227
+        }
1228
+
1229
+        /**
1230
+         * @access private
1231
+         */
1232
+        function &sendPayloadHTTP10($msg, $server, $port, $timeout=0,
1233
+            $username='', $password='', $authtype=1, $proxyhost='',
1234
+            $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1)
1235
+        {
1236
+            if($port==0)
1237
+            {
1238
+                $port=80;
1239
+            }
1240
+
1241
+            // Only create the payload if it was not created previously
1242
+            if(empty($msg->payload))
1243
+            {
1244
+                $msg->createPayload($this->request_charset_encoding);
1245
+            }
1246
+
1247
+            $payload = $msg->payload;
1248
+            // Deflate request body and set appropriate request headers
1249
+            if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1250
+            {
1251
+                if($this->request_compression == 'gzip')
1252
+                {
1253
+                    $a = @gzencode($payload);
1254
+                    if($a)
1255
+                    {
1256
+                        $payload = $a;
1257
+                        $encoding_hdr = "Content-Encoding: gzip\r\n";
1258
+                    }
1259
+                }
1260
+                else
1261
+                {
1262
+                    $a = @gzcompress($payload);
1263
+                    if($a)
1264
+                    {
1265
+                        $payload = $a;
1266
+                        $encoding_hdr = "Content-Encoding: deflate\r\n";
1267
+                    }
1268
+                }
1269
+            }
1270
+            else
1271
+            {
1272
+                $encoding_hdr = '';
1273
+            }
1274
+
1275
+            // thanks to Grant Rauscher <[email protected]> for this
1276
+            $credentials='';
1277
+            if($username!='')
1278
+            {
1279
+                $credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
1280
+                if ($authtype != 1)
1281
+                {
1282
+                    error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported with HTTP 1.0');
1283
+                }
1284
+            }
1285
+
1286
+            $accepted_encoding = '';
1287
+            if(is_array($this->accepted_compression) && count($this->accepted_compression))
1288
+            {
1289
+                $accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n";
1290
+            }
1291
+
1292
+            $proxy_credentials = '';
1293
+            if($proxyhost)
1294
+            {
1295
+                if($proxyport == 0)
1296
+                {
1297
+                    $proxyport = 8080;
1298
+                }
1299
+                $connectserver = $proxyhost;
1300
+                $connectport = $proxyport;
1301
+                $uri = 'http://'.$server.':'.$port.$this->path;
1302
+                if($proxyusername != '')
1303
+                {
1304
+                    if ($proxyauthtype != 1)
1305
+                    {
1306
+                        error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported with HTTP 1.0');
1307
+                    }
1308
+                    $proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n";
1309
+                }
1310
+            }
1311
+            else
1312
+            {
1313
+                $connectserver = $server;
1314
+                $connectport = $port;
1315
+                $uri = $this->path;
1316
+            }
1317
+
1318
+            // Cookie generation, as per rfc2965 (version 1 cookies) or
1319
+            // netscape's rules (version 0 cookies)
1320
+            $cookieheader='';
1321
+            if (count($this->cookies))
1322
+            {
1323
+                $version = '';
1324
+                foreach ($this->cookies as $name => $cookie)
1325
+                {
1326
+                    if ($cookie['version'])
1327
+                    {
1328
+                        $version = ' $Version="' . $cookie['version'] . '";';
1329
+                        $cookieheader .= ' ' . $name . '="' . $cookie['value'] . '";';
1330
+                        if ($cookie['path'])
1331
+                            $cookieheader .= ' $Path="' . $cookie['path'] . '";';
1332
+                        if ($cookie['domain'])
1333
+                            $cookieheader .= ' $Domain="' . $cookie['domain'] . '";';
1334
+                        if ($cookie['port'])
1335
+                            $cookieheader .= ' $Port="' . $cookie['port'] . '";';
1336
+                    }
1337
+                    else
1338
+                    {
1339
+                        $cookieheader .= ' ' . $name . '=' . $cookie['value'] . ";";
1340
+                    }
1341
+                }
1342
+                $cookieheader = 'Cookie:' . $version . substr($cookieheader, 0, -1) . "\r\n";
1343
+            }
1344
+
1345
+            $op= 'POST ' . $uri. " HTTP/1.0\r\n" .
1346
+                'User-Agent: ' . $this->user_agent . "\r\n" .
1347
+                'Host: '. $server . ':' . $port . "\r\n" .
1348
+                $credentials .
1349
+                $proxy_credentials .
1350
+                $accepted_encoding .
1351
+                $encoding_hdr .
1352
+                'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings) . "\r\n" .
1353
+                $cookieheader .
1354
+                'Content-Type: ' . $msg->content_type . "\r\nContent-Length: " .
1355
+                strlen($payload) . "\r\n\r\n" .
1356
+                $payload;
1357
+
1358
+            if($this->debug > 1)
1359
+            {
1360
+                print "<PRE>\n---SENDING---\n" . htmlentities($op) . "\n---END---\n</PRE>";
1361
+                // let the client see this now in case http times out...
1362
+                flush();
1363
+            }
1364
+
1365
+            if($timeout>0)
1366
+            {
1367
+                $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout);
1368
+            }
1369
+            else
1370
+            {
1371
+                $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr);
1372
+            }
1373
+            if($fp)
1374
+            {
1375
+                if($timeout>0 && function_exists('stream_set_timeout'))
1376
+                {
1377
+                    stream_set_timeout($fp, $timeout);
1378
+                }
1379
+            }
1380
+            else
1381
+            {
1382
+                $this->errstr='Connect error: '.$this->errstr;
1383
+                $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')');
1384
+                return $r;
1385
+            }
1386
+
1387
+            if(!fputs($fp, $op, strlen($op)))
1388
+            {
1389
+                fclose($fp);
1390
+                $this->errstr='Write error';
1391
+                $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr);
1392
+                return $r;
1393
+            }
1394
+            else
1395
+            {
1396
+                // reset errno and errstr on succesful socket connection
1397
+                $this->errstr = '';
1398
+            }
1399
+            // G. Giunta 2005/10/24: close socket before parsing.
1400
+            // should yeld slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
1401
+            $ipd='';
1402
+            do
1403
+            {
1404
+                // shall we check for $data === FALSE?
1405
+                // as per the manual, it signals an error
1406
+                $ipd.=fread($fp, 32768);
1407
+            } while(!feof($fp));
1408
+            fclose($fp);
1409
+            $r =& $msg->parseResponse($ipd, false, $this->return_type);
1410
+            return $r;
1411
+
1412
+        }
1413
+
1414
+        /**
1415
+         * @access private
1416
+         */
1417
+        function &sendPayloadHTTPS($msg, $server, $port, $timeout=0, $username='',
1418
+            $password='', $authtype=1, $cert='',$certpass='', $cacert='', $cacertdir='',
1419
+            $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1,
1420
+            $keepalive=false, $key='', $keypass='')
1421
+        {
1422
+            $r =& $this->sendPayloadCURL($msg, $server, $port, $timeout, $username,
1423
+                $password, $authtype, $cert, $certpass, $cacert, $cacertdir, $proxyhost, $proxyport,
1424
+                $proxyusername, $proxypassword, $proxyauthtype, 'https', $keepalive, $key, $keypass);
1425
+            return $r;
1426
+        }
1427
+
1428
+        /**
1429
+         * Contributed by Justin Miller <[email protected]>
1430
+         * Requires curl to be built into PHP
1431
+         * NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
1432
+         * @access private
1433
+         */
1434
+        function &sendPayloadCURL($msg, $server, $port, $timeout=0, $username='',
1435
+            $password='', $authtype=1, $cert='', $certpass='', $cacert='', $cacertdir='',
1436
+            $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1, $method='https',
1437
+            $keepalive=false, $key='', $keypass='')
1438
+        {
1439
+            if(!function_exists('curl_init'))
1440
+            {
1441
+                $this->errstr='CURL unavailable on this install';
1442
+                $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']);
1443
+                return $r;
1444
+            }
1445
+            if($method == 'https')
1446
+            {
1447
+                if(($info = curl_version()) &&
1448
+                    ((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version']))))
1449
+                {
1450
+                    $this->errstr='SSL unavailable on this install';
1451
+                    $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']);
1452
+                    return $r;
1453
+                }
1454
+            }
1455
+
1456
+            if($port == 0)
1457
+            {
1458
+                if($method == 'http')
1459
+                {
1460
+                    $port = 80;
1461
+                }
1462
+                else
1463
+                {
1464
+                    $port = 443;
1465
+                }
1466
+            }
1467
+
1468
+            // Only create the payload if it was not created previously
1469
+            if(empty($msg->payload))
1470
+            {
1471
+                $msg->createPayload($this->request_charset_encoding);
1472
+            }
1473
+
1474
+            // Deflate request body and set appropriate request headers
1475
+            $payload = $msg->payload;
1476
+            if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1477
+            {
1478
+                if($this->request_compression == 'gzip')
1479
+                {
1480
+                    $a = @gzencode($payload);
1481
+                    if($a)
1482
+                    {
1483
+                        $payload = $a;
1484
+                        $encoding_hdr = 'Content-Encoding: gzip';
1485
+                    }
1486
+                }
1487
+                else
1488
+                {
1489
+                    $a = @gzcompress($payload);
1490
+                    if($a)
1491
+                    {
1492
+                        $payload = $a;
1493
+                        $encoding_hdr = 'Content-Encoding: deflate';
1494
+                    }
1495
+                }
1496
+            }
1497
+            else
1498
+            {
1499
+                $encoding_hdr = '';
1500
+            }
1501
+
1502
+            if($this->debug > 1)
1503
+            {
1504
+                print "<PRE>\n---SENDING---\n" . htmlentities($payload) . "\n---END---\n</PRE>";
1505
+                // let the client see this now in case http times out...
1506
+                flush();
1507
+            }
1508
+
1509
+            if(!$keepalive || !$this->xmlrpc_curl_handle)
1510
+            {
1511
+                $curl = curl_init($method . '://' . $server . ':' . $port . $this->path);
1512
+                if($keepalive)
1513
+                {
1514
+                    $this->xmlrpc_curl_handle = $curl;
1515
+                }
1516
+            }
1517
+            else
1518
+            {
1519
+                $curl = $this->xmlrpc_curl_handle;
1520
+            }
1521
+
1522
+            // results into variable
1523
+            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1524
+
1525
+            if($this->debug)
1526
+            {
1527
+                curl_setopt($curl, CURLOPT_VERBOSE, 1);
1528
+            }
1529
+            curl_setopt($curl, CURLOPT_USERAGENT, $this->user_agent);
1530
+            // required for XMLRPC: post the data
1531
+            curl_setopt($curl, CURLOPT_POST, 1);
1532
+            // the data
1533
+            curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
1534
+
1535
+            // return the header too
1536
+            curl_setopt($curl, CURLOPT_HEADER, 1);
1537
+
1538
+            // will only work with PHP >= 5.0
1539
+            // NB: if we set an empty string, CURL will add http header indicating
1540
+            // ALL methods it is supporting. This is possibly a better option than
1541
+            // letting the user tell what curl can / cannot do...
1542
+            if(is_array($this->accepted_compression) && count($this->accepted_compression))
1543
+            {
1544
+                //curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression));
1545
+                // empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1546
+                if (count($this->accepted_compression) == 1)
1547
+                {
1548
+                    curl_setopt($curl, CURLOPT_ENCODING, $this->accepted_compression[0]);
1549
+                }
1550
+                else
1551
+                    curl_setopt($curl, CURLOPT_ENCODING, '');
1552
+            }
1553
+            // extra headers
1554
+            $headers = array('Content-Type: ' . $msg->content_type , 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings));
1555
+            // if no keepalive is wanted, let the server know it in advance
1556
+            if(!$keepalive)
1557
+            {
1558
+                $headers[] = 'Connection: close';
1559
+            }
1560
+            // request compression header
1561
+            if($encoding_hdr)
1562
+            {
1563
+                $headers[] = $encoding_hdr;
1564
+            }
1565
+
1566
+            curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
1567
+            // timeout is borked
1568
+            if($timeout)
1569
+            {
1570
+                curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
1571
+            }
1572
+
1573
+            if($username && $password)
1574
+            {
1575
+                curl_setopt($curl, CURLOPT_USERPWD, $username.':'.$password);
1576
+                if (defined('CURLOPT_HTTPAUTH'))
1577
+                {
1578
+                    curl_setopt($curl, CURLOPT_HTTPAUTH, $authtype);
1579
+                }
1580
+                else if ($authtype != 1)
1581
+                {
1582
+                    error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported by the current PHP/curl install');
1583
+                }
1584
+            }
1585
+
1586
+            if($method == 'https')
1587
+            {
1588
+                // set cert file
1589
+                if($cert)
1590
+                {
1591
+                    curl_setopt($curl, CURLOPT_SSLCERT, $cert);
1592
+                }
1593
+                // set cert password
1594
+                if($certpass)
1595
+                {
1596
+                    curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass);
1597
+                }
1598
+                // whether to verify remote host's cert
1599
+                curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
1600
+                // set ca certificates file/dir
1601
+                if($cacert)
1602
+                {
1603
+                    curl_setopt($curl, CURLOPT_CAINFO, $cacert);
1604
+                }
1605
+                if($cacertdir)
1606
+                {
1607
+                    curl_setopt($curl, CURLOPT_CAPATH, $cacertdir);
1608
+                }
1609
+                // set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1610
+                if($key)
1611
+                {
1612
+                    curl_setopt($curl, CURLOPT_SSLKEY, $key);
1613
+                }
1614
+                // set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1615
+                if($keypass)
1616
+                {
1617
+                    curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $keypass);
1618
+                }
1619
+                // 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
1620
+                curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
1621
+            }
1622
+
1623
+            // proxy info
1624
+            if($proxyhost)
1625
+            {
1626
+                if($proxyport == 0)
1627
+                {
1628
+                    $proxyport = 8080; // NB: even for HTTPS, local connection is on port 8080
1629
+                }
1630
+                curl_setopt($curl, CURLOPT_PROXY, $proxyhost.':'.$proxyport);
1631
+                //curl_setopt($curl, CURLOPT_PROXYPORT,$proxyport);
1632
+                if($proxyusername)
1633
+                {
1634
+                    curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyusername.':'.$proxypassword);
1635
+                    if (defined('CURLOPT_PROXYAUTH'))
1636
+                    {
1637
+                        curl_setopt($curl, CURLOPT_PROXYAUTH, $proxyauthtype);
1638
+                    }
1639
+                    else if ($proxyauthtype != 1)
1640
+                    {
1641
+                        error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported by the current PHP/curl install');
1642
+                    }
1643
+                }
1644
+            }
1645
+
1646
+            // NB: should we build cookie http headers by hand rather than let CURL do it?
1647
+            // the following code does not honour 'expires', 'path' and 'domain' cookie attributes
1648
+            // set to client obj the the user...
1649
+            if (count($this->cookies))
1650
+            {
1651
+                $cookieheader = '';
1652
+                foreach ($this->cookies as $name => $cookie)
1653
+                {
1654
+                    $cookieheader .= $name . '=' . $cookie['value'] . '; ';
1655
+                }
1656
+                curl_setopt($curl, CURLOPT_COOKIE, substr($cookieheader, 0, -2));
1657
+            }
1658
+
1659
+            foreach ($this->extracurlopts as $opt => $val)
1660
+            {
1661
+                curl_setopt($curl, $opt, $val);
1662
+            }
1663
+
1664
+            $result = curl_exec($curl);
1665
+
1666
+            if ($this->debug > 1)
1667
+            {
1668
+                print "<PRE>\n---CURL INFO---\n";
1669
+                foreach(curl_getinfo($curl) as $name => $val)
1670
+                        print $name . ': ' . htmlentities($val). "\n";
1671
+                print "---END---\n</PRE>";
1672
+            }
1673
+
1674
+            if(!$result) /// @todo we should use a better check here - what if we get back '' or '0'?
1675
+            {
1676
+                $this->errstr='no response';
1677
+                $resp=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['curl_fail'], $GLOBALS['xmlrpcstr']['curl_fail']. ': '. curl_error($curl));
1678
+                curl_close($curl);
1679
+                if($keepalive)
1680
+                {
1681
+                    $this->xmlrpc_curl_handle = null;
1682
+                }
1683
+            }
1684
+            else
1685
+            {
1686
+                if(!$keepalive)
1687
+                {
1688
+                    curl_close($curl);
1689
+                }
1690
+                $resp =& $msg->parseResponse($result, true, $this->return_type);
1691
+            }
1692
+            return $resp;
1693
+        }
1694
+
1695
+        /**
1696
+         * Send an array of request messages and return an array of responses.
1697
+         * Unless $this->no_multicall has been set to true, it will try first
1698
+         * to use one single xmlrpc call to server method system.multicall, and
1699
+         * revert to sending many successive calls in case of failure.
1700
+         * This failure is also stored in $this->no_multicall for subsequent calls.
1701
+         * Unfortunately, there is no server error code universally used to denote
1702
+         * the fact that multicall is unsupported, so there is no way to reliably
1703
+         * distinguish between that and a temporary failure.
1704
+         * If you are sure that server supports multicall and do not want to
1705
+         * fallback to using many single calls, set the fourth parameter to FALSE.
1706
+         *
1707
+         * NB: trying to shoehorn extra functionality into existing syntax has resulted
1708
+         * in pretty much convoluted code...
1709
+         *
1710
+         * @param array $msgs an array of xmlrpcmsg objects
1711
+         * @param integer $timeout connection timeout (in seconds)
1712
+         * @param string $method the http protocol variant to be used
1713
+         * @param boolean fallback When true, upon receiveing an error during multicall, multiple single calls will be attempted
1714
+         * @return array
1715
+         * @access public
1716
+         */
1717
+        function multicall($msgs, $timeout=0, $method='', $fallback=true)
1718
+        {
1719
+            if ($method == '')
1720
+            {
1721
+                $method = $this->method;
1722
+            }
1723
+            if(!$this->no_multicall)
1724
+            {
1725
+                $results = $this->_try_multicall($msgs, $timeout, $method);
1726
+                if(is_array($results))
1727
+                {
1728
+                    // System.multicall succeeded
1729
+                    return $results;
1730
+                }
1731
+                else
1732
+                {
1733
+                    // either system.multicall is unsupported by server,
1734
+                    // or call failed for some other reason.
1735
+                    if ($fallback)
1736
+                    {
1737
+                        // Don't try it next time...
1738
+                        $this->no_multicall = true;
1739
+                    }
1740
+                    else
1741
+                    {
1742
+                        if (is_a($results, 'xmlrpcresp'))
1743
+                        {
1744
+                            $result = $results;
1745
+                        }
1746
+                        else
1747
+                        {
1748
+                            $result = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['multicall_error'], $GLOBALS['xmlrpcstr']['multicall_error']);
1749
+                        }
1750
+                    }
1751
+                }
1752
+            }
1753
+            else
1754
+            {
1755
+                // override fallback, in case careless user tries to do two
1756
+                // opposite things at the same time
1757
+                $fallback = true;
1758
+            }
1759
+
1760
+            $results = array();
1761
+            if ($fallback)
1762
+            {
1763
+                // system.multicall is (probably) unsupported by server:
1764
+                // emulate multicall via multiple requests
1765
+                foreach($msgs as $msg)
1766
+                {
1767
+                    $results[] =& $this->send($msg, $timeout, $method);
1768
+                }
1769
+            }
1770
+            else
1771
+            {
1772
+                // user does NOT want to fallback on many single calls:
1773
+                // since we should always return an array of responses,
1774
+                // return an array with the same error repeated n times
1775
+                foreach($msgs as $msg)
1776
+                {
1777
+                    $results[] = $result;
1778
+                }
1779
+            }
1780
+            return $results;
1781
+        }
1782
+
1783
+        /**
1784
+         * Attempt to boxcar $msgs via system.multicall.
1785
+         * Returns either an array of xmlrpcreponses, an xmlrpc error response
1786
+         * or false (when received response does not respect valid multicall syntax)
1787
+         * @access private
1788
+         */
1789
+        function _try_multicall($msgs, $timeout, $method)
1790
+        {
1791
+            // Construct multicall message
1792
+            $calls = array();
1793
+            foreach($msgs as $msg)
1794
+            {
1795
+                $call['methodName'] = new xmlrpcval($msg->method(),'string');
1796
+                $numParams = $msg->getNumParams();
1797
+                $params = array();
1798
+                for($i = 0; $i < $numParams; $i++)
1799
+                {
1800
+                    $params[$i] = $msg->getParam($i);
1801
+                }
1802
+                $call['params'] = new xmlrpcval($params, 'array');
1803
+                $calls[] = new xmlrpcval($call, 'struct');
1804
+            }
1805
+            $multicall = new xmlrpcmsg('system.multicall');
1806
+            $multicall->addParam(new xmlrpcval($calls, 'array'));
1807
+
1808
+            // Attempt RPC call
1809
+            $result =& $this->send($multicall, $timeout, $method);
1810
+
1811
+            if($result->faultCode() != 0)
1812
+            {
1813
+                // call to system.multicall failed
1814
+                return $result;
1815
+            }
1816
+
1817
+            // Unpack responses.
1818
+            $rets = $result->value();
1819
+
1820
+            if ($this->return_type == 'xml')
1821
+            {
1822
+                    return $rets;
1823
+            }
1824
+            else if ($this->return_type == 'phpvals')
1825
+            {
1826
+                ///@todo test this code branch...
1827
+                $rets = $result->value();
1828
+                if(!is_array($rets))
1829
+                {
1830
+                    return false;		// bad return type from system.multicall
1831
+                }
1832
+                $numRets = count($rets);
1833
+                if($numRets != count($msgs))
1834
+                {
1835
+                    return false;		// wrong number of return values.
1836
+                }
1837
+
1838
+                $response = array();
1839
+                for($i = 0; $i < $numRets; $i++)
1840
+                {
1841
+                    $val = $rets[$i];
1842
+                    if (!is_array($val)) {
1843
+                        return false;
1844
+                    }
1845
+                    switch(count($val))
1846
+                    {
1847
+                        case 1:
1848
+                            if(!isset($val[0]))
1849
+                            {
1850
+                                return false;		// Bad value
1851
+                            }
1852
+                            // Normal return value
1853
+                            $response[$i] = new xmlrpcresp($val[0], 0, '', 'phpvals');
1854
+                            break;
1855
+                        case 2:
1856
+                            ///	@todo remove usage of @: it is apparently quite slow
1857
+                            $code = @$val['faultCode'];
1858
+                            if(!is_int($code))
1859
+                            {
1860
+                                return false;
1861
+                            }
1862
+                            $str = @$val['faultString'];
1863
+                            if(!is_string($str))
1864
+                            {
1865
+                                return false;
1866
+                            }
1867
+                            $response[$i] = new xmlrpcresp(0, $code, $str);
1868
+                            break;
1869
+                        default:
1870
+                            return false;
1871
+                    }
1872
+                }
1873
+                return $response;
1874
+            }
1875
+            else // return type == 'xmlrpcvals'
1876
+            {
1877
+                $rets = $result->value();
1878
+                if($rets->kindOf() != 'array')
1879
+                {
1880
+                    return false;		// bad return type from system.multicall
1881
+                }
1882
+                $numRets = $rets->arraysize();
1883
+                if($numRets != count($msgs))
1884
+                {
1885
+                    return false;		// wrong number of return values.
1886
+                }
1887
+
1888
+                $response = array();
1889
+                for($i = 0; $i < $numRets; $i++)
1890
+                {
1891
+                    $val = $rets->arraymem($i);
1892
+                    switch($val->kindOf())
1893
+                    {
1894
+                        case 'array':
1895
+                            if($val->arraysize() != 1)
1896
+                            {
1897
+                                return false;		// Bad value
1898
+                            }
1899
+                            // Normal return value
1900
+                            $response[$i] = new xmlrpcresp($val->arraymem(0));
1901
+                            break;
1902
+                        case 'struct':
1903
+                            $code = $val->structmem('faultCode');
1904
+                            if($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
1905
+                            {
1906
+                                return false;
1907
+                            }
1908
+                            $str = $val->structmem('faultString');
1909
+                            if($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
1910
+                            {
1911
+                                return false;
1912
+                            }
1913
+                            $response[$i] = new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
1914
+                            break;
1915
+                        default:
1916
+                            return false;
1917
+                    }
1918
+                }
1919
+                return $response;
1920
+            }
1921
+        }
1922
+    } // end class xmlrpc_client
1923
+
1924
+    class xmlrpcresp
1925
+    {
1926
+        var $val = 0;
1927
+        var $valtyp;
1928
+        var $errno = 0;
1929
+        var $errstr = '';
1930
+        var $payload;
1931
+        var $hdrs = array();
1932
+        var $_cookies = array();
1933
+        var $content_type = 'text/xml';
1934
+        var $raw_data = '';
1935
+
1936
+        /**
1937
+         * @param mixed $val either an xmlrpcval obj, a php value or the xml serialization of an xmlrpcval (a string)
1938
+         * @param integer $fcode set it to anything but 0 to create an error response
1939
+         * @param string $fstr the error string, in case of an error response
1940
+         * @param string $valtyp either 'xmlrpcvals', 'phpvals' or 'xml'
1941
+         *
1942
+         * @todo add check that $val / $fcode / $fstr is of correct type???
1943
+         * NB: as of now we do not do it, since it might be either an xmlrpcval or a plain
1944
+         * php val, or a complete xml chunk, depending on usage of xmlrpc_client::send() inside which creator is called...
1945
+         */
1946
+        function xmlrpcresp($val, $fcode = 0, $fstr = '', $valtyp='')
1947
+        {
1948
+            if($fcode != 0)
1949
+            {
1950
+                // error response
1951
+                $this->errno = $fcode;
1952
+                $this->errstr = $fstr;
1953
+                //$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
1954
+            }
1955
+            else
1956
+            {
1957
+                // successful response
1958
+                $this->val = $val;
1959
+                if ($valtyp == '')
1960
+                {
1961
+                    // user did not declare type of response value: try to guess it
1962
+                    if (is_object($this->val) && is_a($this->val, 'xmlrpcval'))
1963
+                    {
1964
+                        $this->valtyp = 'xmlrpcvals';
1965
+                    }
1966
+                    else if (is_string($this->val))
1967
+                    {
1968
+                        $this->valtyp = 'xml';
1969
+
1970
+                    }
1971
+                    else
1972
+                    {
1973
+                        $this->valtyp = 'phpvals';
1974
+                    }
1975
+                }
1976
+                else
1977
+                {
1978
+                    // user declares type of resp value: believe him
1979
+                    $this->valtyp = $valtyp;
1980
+                }
1981
+            }
1982
+        }
1983
+
1984
+        /**
1985
+         * Returns the error code of the response.
1986
+         * @return integer the error code of this response (0 for not-error responses)
1987
+         * @access public
1988
+         */
1989
+        function faultCode()
1990
+        {
1991
+            return $this->errno;
1992
+        }
1993
+
1994
+        /**
1995
+         * Returns the error code of the response.
1996
+         * @return string the error string of this response ('' for not-error responses)
1997
+         * @access public
1998
+         */
1999
+        function faultString()
2000
+        {
2001
+            return $this->errstr;
2002
+        }
2003
+
2004
+        /**
2005
+         * Returns the value received by the server.
2006
+         * @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
2007
+         * @access public
2008
+         */
2009
+        function value()
2010
+        {
2011
+            return $this->val;
2012
+        }
2013
+
2014
+        /**
2015
+         * Returns an array with the cookies received from the server.
2016
+         * Array has the form: $cookiename => array ('value' => $val, $attr1 => $val1, $attr2 = $val2, ...)
2017
+         * with attributes being e.g. 'expires', 'path', domain'.
2018
+         * NB: cookies sent as 'expired' by the server (i.e. with an expiry date in the past)
2019
+         * are still present in the array. It is up to the user-defined code to decide
2020
+         * how to use the received cookies, and wheter they have to be sent back with the next
2021
+         * request to the server (using xmlrpc_client::setCookie) or not
2022
+         * @return array array of cookies received from the server
2023
+         * @access public
2024
+         */
2025
+        function cookies()
2026
+        {
2027
+            return $this->_cookies;
2028
+        }
2029
+
2030
+        /**
2031
+         * Returns xml representation of the response. XML prologue not included
2032
+         * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
2033
+         * @return string the xml representation of the response
2034
+         * @access public
2035
+         */
2036
+        function serialize($charset_encoding='')
2037
+        {
2038
+            if ($charset_encoding != '')
2039
+                $this->content_type = 'text/xml; charset=' . $charset_encoding;
2040
+            else
2041
+                $this->content_type = 'text/xml';
2042
+            $result = "<methodResponse>\n";
2043
+            if($this->errno)
2044
+            {
2045
+                // G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
2046
+                // by xml-encoding non ascii chars
2047
+                $result .= "<fault>\n" .
2048 2048
 "<value>\n<struct><member><name>faultCode</name>\n<value><int>" . $this->errno .
2049 2049
 "</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>" .
2050 2050
 xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "</string></value>\n</member>\n" .
2051 2051
 "</struct>\n</value>\n</fault>";
2052
-			}
2053
-			else
2054
-			{
2055
-				if(!is_object($this->val) || !is_a($this->val, 'xmlrpcval'))
2056
-				{
2057
-					if (is_string($this->val) && $this->valtyp == 'xml')
2058
-					{
2059
-						$result .= "<params>\n<param>\n" .
2060
-							$this->val .
2061
-							"</param>\n</params>";
2062
-					}
2063
-					else
2064
-					{
2065
-						/// @todo try to build something serializable?
2066
-						die('cannot serialize xmlrpcresp objects whose content is native php values');
2067
-					}
2068
-				}
2069
-				else
2070
-				{
2071
-					$result .= "<params>\n<param>\n" .
2072
-						$this->val->serialize($charset_encoding) .
2073
-						"</param>\n</params>";
2074
-				}
2075
-			}
2076
-			$result .= "\n</methodResponse>";
2077
-			$this->payload = $result;
2078
-			return $result;
2079
-		}
2080
-	}
2081
-
2082
-	class xmlrpcmsg
2083
-	{
2084
-		var $payload;
2085
-		var $methodname;
2086
-		var $params=array();
2087
-		var $debug=0;
2088
-		var $content_type = 'text/xml';
2089
-
2090
-		/**
2091
-		* @param string $meth the name of the method to invoke
2092
-		* @param array $pars array of parameters to be paased to the method (xmlrpcval objects)
2093
-		*/
2094
-		function xmlrpcmsg($meth, $pars=0)
2095
-		{
2096
-			$this->methodname=$meth;
2097
-			if(is_array($pars) && count($pars)>0)
2098
-			{
2099
-				for($i=0; $i<count($pars); $i++)
2100
-				{
2101
-					$this->addParam($pars[$i]);
2102
-				}
2103
-			}
2104
-		}
2105
-
2106
-		/**
2107
-		* @access private
2108
-		*/
2109
-		function xml_header($charset_encoding='')
2110
-		{
2111
-			if ($charset_encoding != '')
2112
-			{
2113
-				return "<?xml version=\"1.0\" encoding=\"$charset_encoding\" ?" . ">\n<methodCall>\n";
2114
-			}
2115
-			else
2116
-			{
2117
-				return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n";
2118
-			}
2119
-		}
2120
-
2121
-		/**
2122
-		* @access private
2123
-		*/
2124
-		function xml_footer()
2125
-		{
2126
-			return '</methodCall>';
2127
-		}
2128
-
2129
-		/**
2130
-		* @access private
2131
-		*/
2132
-		function kindOf()
2133
-		{
2134
-			return 'msg';
2135
-		}
2136
-
2137
-		/**
2138
-		* @access private
2139
-		*/
2140
-		function createPayload($charset_encoding='')
2141
-		{
2142
-			if ($charset_encoding != '')
2143
-				$this->content_type = 'text/xml; charset=' . $charset_encoding;
2144
-			else
2145
-				$this->content_type = 'text/xml';
2146
-			$this->payload=$this->xml_header($charset_encoding);
2147
-			$this->payload.='<methodName>' . $this->methodname . "</methodName>\n";
2148
-			$this->payload.="<params>\n";
2149
-			for($i=0; $i<count($this->params); $i++)
2150
-			{
2151
-				$p=$this->params[$i];
2152
-				$this->payload.="<param>\n" . $p->serialize($charset_encoding) .
2153
-				"</param>\n";
2154
-			}
2155
-			$this->payload.="</params>\n";
2156
-			$this->payload.=$this->xml_footer();
2157
-		}
2158
-
2159
-		/**
2160
-		* Gets/sets the xmlrpc method to be invoked
2161
-		* @param string $meth the method to be set (leave empty not to set it)
2162
-		* @return string the method that will be invoked
2163
-		* @access public
2164
-		*/
2165
-		function method($meth='')
2166
-		{
2167
-			if($meth!='')
2168
-			{
2169
-				$this->methodname=$meth;
2170
-			}
2171
-			return $this->methodname;
2172
-		}
2173
-
2174
-		/**
2175
-		* Returns xml representation of the message. XML prologue included
2176
-		* @return string the xml representation of the message, xml prologue included
2177
-		* @access public
2178
-		*/
2179
-		function serialize($charset_encoding='')
2180
-		{
2181
-			$this->createPayload($charset_encoding);
2182
-			return $this->payload;
2183
-		}
2184
-
2185
-		/**
2186
-		* Add a parameter to the list of parameters to be used upon method invocation
2187
-		* @param xmlrpcval $par
2188
-		* @return boolean false on failure
2189
-		* @access public
2190
-		*/
2191
-		function addParam($par)
2192
-		{
2193
-			// add check: do not add to self params which are not xmlrpcvals
2194
-			if(is_object($par) && is_a($par, 'xmlrpcval'))
2195
-			{
2196
-				$this->params[]=$par;
2197
-				return true;
2198
-			}
2199
-			else
2200
-			{
2201
-				return false;
2202
-			}
2203
-		}
2204
-
2205
-		/**
2206
-		* Returns the nth parameter in the message. The index zero-based.
2207
-		* @param integer $i the index of the parameter to fetch (zero based)
2208
-		* @return xmlrpcval the i-th parameter
2209
-		* @access public
2210
-		*/
2211
-		function getParam($i) { return $this->params[$i]; }
2212
-
2213
-		/**
2214
-		* Returns the number of parameters in the messge.
2215
-		* @return integer the number of parameters currently set
2216
-		* @access public
2217
-		*/
2218
-		function getNumParams() { return count($this->params); }
2219
-
2220
-		/**
2221
-		* Given an open file handle, read all data available and parse it as axmlrpc response.
2222
-		* NB: the file handle is not closed by this function.
2223
-		* NNB: might have trouble in rare cases to work on network streams, as we
2224
-		*      check for a read of 0 bytes instead of feof($fp).
2225
-		*      But since checking for feof(null) returns false, we would risk an
2226
-		*      infinite loop in that case, because we cannot trust the caller
2227
-		*      to give us a valid pointer to an open file...
2228
-		* @access public
2229
-		* @return xmlrpcresp
2230
-		* @todo add 2nd & 3rd param to be passed to ParseResponse() ???
2231
-		*/
2232
-		function &parseResponseFile($fp)
2233
-		{
2234
-			$ipd='';
2235
-			while($data=fread($fp, 32768))
2236
-			{
2237
-				$ipd.=$data;
2238
-			}
2239
-			//fclose($fp);
2240
-			$r =& $this->parseResponse($ipd);
2241
-			return $r;
2242
-		}
2243
-
2244
-		/**
2245
-		* Parses HTTP headers and separates them from data.
2246
-		* @access private
2247
-		*/
2248
-		function &parseResponseHeaders(&$data, $headers_processed=false)
2249
-		{
2250
-				// Support "web-proxy-tunelling" connections for https through proxies
2251
-				if(preg_match('/^HTTP\/1\.[0-1] 200 Connection established/', $data))
2252
-				{
2253
-					// Look for CR/LF or simple LF as line separator,
2254
-					// (even though it is not valid http)
2255
-					$pos = strpos($data,"\r\n\r\n");
2256
-					if($pos || is_int($pos))
2257
-					{
2258
-						$bd = $pos+4;
2259
-					}
2260
-					else
2261
-					{
2262
-						$pos = strpos($data,"\n\n");
2263
-						if($pos || is_int($pos))
2264
-						{
2265
-							$bd = $pos+2;
2266
-						}
2267
-						else
2268
-						{
2269
-							// No separation between response headers and body: fault?
2270
-							$bd = 0;
2271
-						}
2272
-					}
2273
-					if ($bd)
2274
-					{
2275
-						// this filters out all http headers from proxy.
2276
-						// maybe we could take them into account, too?
2277
-						$data = substr($data, $bd);
2278
-					}
2279
-					else
2280
-					{
2281
-						error_log('XML-RPC: '.__METHOD__.': HTTPS via proxy error, tunnel connection possibly failed');
2282
-						$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (HTTPS via proxy error, tunnel connection possibly failed)');
2283
-						return $r;
2284
-					}
2285
-				}
2286
-
2287
-				// Strip HTTP 1.1 100 Continue header if present
2288
-				while(preg_match('/^HTTP\/1\.1 1[0-9]{2} /', $data))
2289
-				{
2290
-					$pos = strpos($data, 'HTTP', 12);
2291
-					// server sent a Continue header without any (valid) content following...
2292
-					// give the client a chance to know it
2293
-					if(!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
2294
-					{
2295
-						break;
2296
-					}
2297
-					$data = substr($data, $pos);
2298
-				}
2299
-				if(!preg_match('/^HTTP\/[0-9.]+ 200 /', $data))
2300
-				{
2301
-					$errstr= substr($data, 0, strpos($data, "\n")-1);
2302
-					error_log('XML-RPC: '.__METHOD__.': HTTP error, got response: ' .$errstr);
2303
-					$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (' . $errstr . ')');
2304
-					return $r;
2305
-				}
2306
-
2307
-				$GLOBALS['_xh']['headers'] = array();
2308
-				$GLOBALS['_xh']['cookies'] = array();
2309
-
2310
-				// be tolerant to usage of \n instead of \r\n to separate headers and data
2311
-				// (even though it is not valid http)
2312
-				$pos = strpos($data,"\r\n\r\n");
2313
-				if($pos || is_int($pos))
2314
-				{
2315
-					$bd = $pos+4;
2316
-				}
2317
-				else
2318
-				{
2319
-					$pos = strpos($data,"\n\n");
2320
-					if($pos || is_int($pos))
2321
-					{
2322
-						$bd = $pos+2;
2323
-					}
2324
-					else
2325
-					{
2326
-						// No separation between response headers and body: fault?
2327
-						// we could take some action here instead of going on...
2328
-						$bd = 0;
2329
-					}
2330
-				}
2331
-				// be tolerant to line endings, and extra empty lines
2332
-				$ar = preg_split("/\r?\n/", trim(substr($data, 0, $pos)));
2333
-				while(list(,$line) = @each($ar))
2334
-				{
2335
-					// take care of multi-line headers and cookies
2336
-					$arr = explode(':',$line,2);
2337
-					if(count($arr) > 1)
2338
-					{
2339
-						$header_name = strtolower(trim($arr[0]));
2340
-						/// @todo some other headers (the ones that allow a CSV list of values)
2341
-						/// do allow many values to be passed using multiple header lines.
2342
-						/// We should add content to $GLOBALS['_xh']['headers'][$header_name]
2343
-						/// instead of replacing it for those...
2344
-						if ($header_name == 'set-cookie' || $header_name == 'set-cookie2')
2345
-						{
2346
-							if ($header_name == 'set-cookie2')
2347
-							{
2348
-								// version 2 cookies:
2349
-								// there could be many cookies on one line, comma separated
2350
-								$cookies = explode(',', $arr[1]);
2351
-							}
2352
-							else
2353
-							{
2354
-								$cookies = array($arr[1]);
2355
-							}
2356
-							foreach ($cookies as $cookie)
2357
-							{
2358
-								// glue together all received cookies, using a comma to separate them
2359
-								// (same as php does with getallheaders())
2360
-								if (isset($GLOBALS['_xh']['headers'][$header_name]))
2361
-									$GLOBALS['_xh']['headers'][$header_name] .= ', ' . trim($cookie);
2362
-								else
2363
-									$GLOBALS['_xh']['headers'][$header_name] = trim($cookie);
2364
-								// parse cookie attributes, in case user wants to correctly honour them
2365
-								// feature creep: only allow rfc-compliant cookie attributes?
2366
-								// @todo support for server sending multiple time cookie with same name, but using different PATHs
2367
-								$cookie = explode(';', $cookie);
2368
-								foreach ($cookie as $pos => $val)
2369
-								{
2370
-									$val = explode('=', $val, 2);
2371
-									$tag = trim($val[0]);
2372
-									$val = trim(@$val[1]);
2373
-									/// @todo with version 1 cookies, we should strip leading and trailing " chars
2374
-									if ($pos == 0)
2375
-									{
2376
-										$cookiename = $tag;
2377
-										$GLOBALS['_xh']['cookies'][$tag] = array();
2378
-										$GLOBALS['_xh']['cookies'][$cookiename]['value'] = urldecode($val);
2379
-									}
2380
-									else
2381
-									{
2382
-										if ($tag != 'value')
2383
-										{
2384
-										  $GLOBALS['_xh']['cookies'][$cookiename][$tag] = $val;
2385
-										}
2386
-									}
2387
-								}
2388
-							}
2389
-						}
2390
-						else
2391
-						{
2392
-							$GLOBALS['_xh']['headers'][$header_name] = trim($arr[1]);
2393
-						}
2394
-					}
2395
-					elseif(isset($header_name))
2396
-					{
2397
-						///	@todo version1 cookies might span multiple lines, thus breaking the parsing above
2398
-						$GLOBALS['_xh']['headers'][$header_name] .= ' ' . trim($line);
2399
-					}
2400
-				}
2401
-
2402
-				$data = substr($data, $bd);
2403
-
2404
-				if($this->debug && count($GLOBALS['_xh']['headers']))
2405
-				{
2406
-					print '<PRE>';
2407
-					foreach($GLOBALS['_xh']['headers'] as $header => $value)
2408
-					{
2409
-						print htmlentities("HEADER: $header: $value\n");
2410
-					}
2411
-					foreach($GLOBALS['_xh']['cookies'] as $header => $value)
2412
-					{
2413
-						print htmlentities("COOKIE: $header={$value['value']}\n");
2414
-					}
2415
-					print "</PRE>\n";
2416
-				}
2417
-
2418
-				// if CURL was used for the call, http headers have been processed,
2419
-				// and dechunking + reinflating have been carried out
2420
-				if(!$headers_processed)
2421
-				{
2422
-					// Decode chunked encoding sent by http 1.1 servers
2423
-					if(isset($GLOBALS['_xh']['headers']['transfer-encoding']) && $GLOBALS['_xh']['headers']['transfer-encoding'] == 'chunked')
2424
-					{
2425
-						if(!$data = decode_chunked($data))
2426
-						{
2427
-							error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to rebuild the chunked data received from server');
2428
-							$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['dechunk_fail'], $GLOBALS['xmlrpcstr']['dechunk_fail']);
2429
-							return $r;
2430
-						}
2431
-					}
2432
-
2433
-					// Decode gzip-compressed stuff
2434
-					// code shamelessly inspired from nusoap library by Dietrich Ayala
2435
-					if(isset($GLOBALS['_xh']['headers']['content-encoding']))
2436
-					{
2437
-						$GLOBALS['_xh']['headers']['content-encoding'] = str_replace('x-', '', $GLOBALS['_xh']['headers']['content-encoding']);
2438
-						if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' || $GLOBALS['_xh']['headers']['content-encoding'] == 'gzip')
2439
-						{
2440
-							// if decoding works, use it. else assume data wasn't gzencoded
2441
-							if(function_exists('gzinflate'))
2442
-							{
2443
-								if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data))
2444
-								{
2445
-									$data = $degzdata;
2446
-									if($this->debug)
2447
-									print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2448
-								}
2449
-								elseif($GLOBALS['_xh']['headers']['content-encoding'] == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
2450
-								{
2451
-									$data = $degzdata;
2452
-									if($this->debug)
2453
-									print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2454
-								}
2455
-								else
2456
-								{
2457
-									error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to decode the deflated data received from server');
2458
-									$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']);
2459
-									return $r;
2460
-								}
2461
-							}
2462
-							else
2463
-							{
2464
-								error_log('XML-RPC: '.__METHOD__.': the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
2465
-								$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']);
2466
-								return $r;
2467
-							}
2468
-						}
2469
-					}
2470
-				} // end of 'if needed, de-chunk, re-inflate response'
2471
-
2472
-				// real stupid hack to avoid PHP complaining about returning NULL by ref
2473
-				$r = null;
2474
-				$r =& $r;
2475
-				return $r;
2476
-		}
2477
-
2478
-		/**
2479
-		* Parse the xmlrpc response contained in the string $data and return an xmlrpcresp object.
2480
-		* @param string $data the xmlrpc response, eventually including http headers
2481
-		* @param bool $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and consequent decoding
2482
-		* @param string $return_type decides return type, i.e. content of response->value(). Either 'xmlrpcvals', 'xml' or 'phpvals'
2483
-		* @return xmlrpcresp
2484
-		* @access public
2485
-		*/
2486
-		function &parseResponse($data='', $headers_processed=false, $return_type='xmlrpcvals')
2487
-		{
2488
-			if($this->debug)
2489
-			{
2490
-				//by maHo, replaced htmlspecialchars with htmlentities
2491
-				print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
2492
-			}
2493
-
2494
-			if($data == '')
2495
-			{
2496
-				error_log('XML-RPC: '.__METHOD__.': no response received from server.');
2497
-				$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']);
2498
-				return $r;
2499
-			}
2500
-
2501
-			$GLOBALS['_xh']=array();
2502
-
2503
-			$raw_data = $data;
2504
-			// parse the HTTP headers of the response, if present, and separate them from data
2505
-			if(substr($data, 0, 4) == 'HTTP')
2506
-			{
2507
-				$r =& $this->parseResponseHeaders($data, $headers_processed);
2508
-				if ($r)
2509
-				{
2510
-					// failed processing of HTTP response headers
2511
-					// save into response obj the full payload received, for debugging
2512
-					$r->raw_data = $data;
2513
-					return $r;
2514
-				}
2515
-			}
2516
-			else
2517
-			{
2518
-				$GLOBALS['_xh']['headers'] = array();
2519
-				$GLOBALS['_xh']['cookies'] = array();
2520
-			}
2521
-
2522
-			if($this->debug)
2523
-			{
2524
-				$start = strpos($data, '<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2525
-				if ($start)
2526
-				{
2527
-					$start += strlen('<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2528
-					$end = strpos($data, '-->', $start);
2529
-					$comments = substr($data, $start, $end-$start);
2530
-					print "<PRE>---SERVER DEBUG INFO (DECODED) ---\n\t".htmlentities(str_replace("\n", "\n\t", base64_decode($comments)))."\n---END---\n</PRE>";
2531
-				}
2532
-			}
2533
-
2534
-			// be tolerant of extra whitespace in response body
2535
-			$data = trim($data);
2536
-
2537
-			/// @todo return an error msg if $data=='' ?
2538
-
2539
-			// be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts)
2540
-			// idea from Luca Mariano <[email protected]> originally in PEARified version of the lib
2541
-			$pos = strrpos($data, '</methodResponse>');
2542
-			if($pos !== false)
2543
-			{
2544
-				$data = substr($data, 0, $pos+17);
2545
-			}
2546
-
2547
-			// if user wants back raw xml, give it to him
2548
-			if ($return_type == 'xml')
2549
-			{
2550
-				$r = new xmlrpcresp($data, 0, '', 'xml');
2551
-				$r->hdrs = $GLOBALS['_xh']['headers'];
2552
-				$r->_cookies = $GLOBALS['_xh']['cookies'];
2553
-				$r->raw_data = $raw_data;
2554
-				return $r;
2555
-			}
2556
-
2557
-			// try to 'guestimate' the character encoding of the received response
2558
-			$resp_encoding = guess_encoding(@$GLOBALS['_xh']['headers']['content-type'], $data);
2559
-
2560
-			$GLOBALS['_xh']['ac']='';
2561
-			//$GLOBALS['_xh']['qt']=''; //unused...
2562
-			$GLOBALS['_xh']['stack'] = array();
2563
-			$GLOBALS['_xh']['valuestack'] = array();
2564
-			$GLOBALS['_xh']['isf']=0; // 0 = OK, 1 for xmlrpc fault responses, 2 = invalid xmlrpc
2565
-			$GLOBALS['_xh']['isf_reason']='';
2566
-			$GLOBALS['_xh']['rt']=''; // 'methodcall or 'methodresponse'
2567
-
2568
-			// if response charset encoding is not known / supported, try to use
2569
-			// the default encoding and parse the xml anyway, but log a warning...
2570
-			if (!in_array($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2571
-			// the following code might be better for mb_string enabled installs, but
2572
-			// makes the lib about 200% slower...
2573
-			//if (!is_valid_charset($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2574
-			{
2575
-				error_log('XML-RPC: '.__METHOD__.': invalid charset encoding of received response: '.$resp_encoding);
2576
-				$resp_encoding = $GLOBALS['xmlrpc_defencoding'];
2577
-			}
2578
-			$parser = xml_parser_create($resp_encoding);
2579
-			xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
2580
-			// G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
2581
-			// the xml parser to give us back data in the expected charset.
2582
-			// What if internal encoding is not in one of the 3 allowed?
2583
-			// we use the broadest one, ie. utf8
2584
-			// This allows to send data which is native in various charset,
2585
-			// by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
2586
-			if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2587
-			{
2588
-				xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
2589
-			}
2590
-			else
2591
-			{
2592
-				xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
2593
-			}
2594
-
2595
-			if ($return_type == 'phpvals')
2596
-			{
2597
-				xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
2598
-			}
2599
-			else
2600
-			{
2601
-				xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
2602
-			}
2603
-
2604
-			xml_set_character_data_handler($parser, 'xmlrpc_cd');
2605
-			xml_set_default_handler($parser, 'xmlrpc_dh');
2606
-
2607
-			// first error check: xml not well formed
2608
-			if(!xml_parse($parser, $data, count($data)))
2609
-			{
2610
-				// thanks to Peter Kocks <[email protected]>
2611
-				if((xml_get_current_line_number($parser)) == 1)
2612
-				{
2613
-					$errstr = 'XML error at line 1, check URL';
2614
-				}
2615
-				else
2616
-				{
2617
-					$errstr = sprintf('XML error: %s at line %d, column %d',
2618
-						xml_error_string(xml_get_error_code($parser)),
2619
-						xml_get_current_line_number($parser), xml_get_current_column_number($parser));
2620
-				}
2621
-				error_log($errstr);
2622
-				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return'].' ('.$errstr.')');
2623
-				xml_parser_free($parser);
2624
-				if($this->debug)
2625
-				{
2626
-					print $errstr;
2627
-				}
2628
-				$r->hdrs = $GLOBALS['_xh']['headers'];
2629
-				$r->_cookies = $GLOBALS['_xh']['cookies'];
2630
-				$r->raw_data = $raw_data;
2631
-				return $r;
2632
-			}
2633
-			xml_parser_free($parser);
2634
-			// second error check: xml well formed but not xml-rpc compliant
2635
-			if ($GLOBALS['_xh']['isf'] > 1)
2636
-			{
2637
-				if ($this->debug)
2638
-				{
2639
-					/// @todo echo something for user?
2640
-				}
2641
-
2642
-				$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2643
-				$GLOBALS['xmlrpcstr']['invalid_return'] . ' ' . $GLOBALS['_xh']['isf_reason']);
2644
-			}
2645
-			// third error check: parsing of the response has somehow gone boink.
2646
-			// NB: shall we omit this check, since we trust the parsing code?
2647
-			elseif ($return_type == 'xmlrpcvals' && !is_object($GLOBALS['_xh']['value']))
2648
-			{
2649
-				// something odd has happened
2650
-				// and it's time to generate a client side error
2651
-				// indicating something odd went on
2652
-				$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2653
-					$GLOBALS['xmlrpcstr']['invalid_return']);
2654
-			}
2655
-			else
2656
-			{
2657
-				if ($this->debug)
2658
-				{
2659
-					print "<PRE>---PARSED---\n";
2660
-					// somehow htmlentities chokes on var_export, and some full html string...
2661
-					//print htmlentitites(var_export($GLOBALS['_xh']['value'], true));
2662
-					print htmlspecialchars(var_export($GLOBALS['_xh']['value'], true));
2663
-					print "\n---END---</PRE>";
2664
-				}
2665
-
2666
-				// note that using =& will raise an error if $GLOBALS['_xh']['st'] does not generate an object.
2667
-				$v =& $GLOBALS['_xh']['value'];
2668
-
2669
-				if($GLOBALS['_xh']['isf'])
2670
-				{
2671
-					/// @todo we should test here if server sent an int and a string,
2672
-					/// and/or coerce them into such...
2673
-					if ($return_type == 'xmlrpcvals')
2674
-					{
2675
-						$errno_v = $v->structmem('faultCode');
2676
-						$errstr_v = $v->structmem('faultString');
2677
-						$errno = $errno_v->scalarval();
2678
-						$errstr = $errstr_v->scalarval();
2679
-					}
2680
-					else
2681
-					{
2682
-						$errno = $v['faultCode'];
2683
-						$errstr = $v['faultString'];
2684
-					}
2685
-
2686
-					if($errno == 0)
2687
-					{
2688
-						// FAULT returned, errno needs to reflect that
2689
-						$errno = -1;
2690
-					}
2691
-
2692
-					$r = new xmlrpcresp(0, $errno, $errstr);
2693
-				}
2694
-				else
2695
-				{
2696
-					$r=new xmlrpcresp($v, 0, '', $return_type);
2697
-				}
2698
-			}
2699
-
2700
-			$r->hdrs = $GLOBALS['_xh']['headers'];
2701
-			$r->_cookies = $GLOBALS['_xh']['cookies'];
2702
-			$r->raw_data = $raw_data;
2703
-			return $r;
2704
-		}
2705
-	}
2706
-
2707
-	class xmlrpcval
2708
-	{
2709
-		var $me=array();
2710
-		var $mytype=0;
2711
-		var $_php_class=null;
2712
-
2713
-		/**
2714
-		* @param mixed $val
2715
-		* @param string $type any valid xmlrpc type name (lowercase). If null, 'string' is assumed
2716
-		*/
2717
-		function xmlrpcval($val=-1, $type='')
2718
-		{
2719
-			/// @todo: optimization creep - do not call addXX, do it all inline.
2720
-			/// downside: booleans will not be coerced anymore
2721
-			if($val!==-1 || $type!='')
2722
-			{
2723
-				// optimization creep: inlined all work done by constructor
2724
-				switch($type)
2725
-				{
2726
-					case '':
2727
-						$this->mytype=1;
2728
-						$this->me['string']=$val;
2729
-						break;
2730
-					case 'i4':
2731
-					case 'int':
2732
-					case 'double':
2733
-					case 'string':
2734
-					case 'boolean':
2735
-					case 'dateTime.iso8601':
2736
-					case 'base64':
2737
-					case 'null':
2738
-						$this->mytype=1;
2739
-						$this->me[$type]=$val;
2740
-						break;
2741
-					case 'array':
2742
-						$this->mytype=2;
2743
-						$this->me['array']=$val;
2744
-						break;
2745
-					case 'struct':
2746
-						$this->mytype=3;
2747
-						$this->me['struct']=$val;
2748
-						break;
2749
-					default:
2750
-						error_log("XML-RPC: ".__METHOD__.": not a known type ($type)");
2751
-				}
2752
-				/*if($type=='')
2052
+            }
2053
+            else
2054
+            {
2055
+                if(!is_object($this->val) || !is_a($this->val, 'xmlrpcval'))
2056
+                {
2057
+                    if (is_string($this->val) && $this->valtyp == 'xml')
2058
+                    {
2059
+                        $result .= "<params>\n<param>\n" .
2060
+                            $this->val .
2061
+                            "</param>\n</params>";
2062
+                    }
2063
+                    else
2064
+                    {
2065
+                        /// @todo try to build something serializable?
2066
+                        die('cannot serialize xmlrpcresp objects whose content is native php values');
2067
+                    }
2068
+                }
2069
+                else
2070
+                {
2071
+                    $result .= "<params>\n<param>\n" .
2072
+                        $this->val->serialize($charset_encoding) .
2073
+                        "</param>\n</params>";
2074
+                }
2075
+            }
2076
+            $result .= "\n</methodResponse>";
2077
+            $this->payload = $result;
2078
+            return $result;
2079
+        }
2080
+    }
2081
+
2082
+    class xmlrpcmsg
2083
+    {
2084
+        var $payload;
2085
+        var $methodname;
2086
+        var $params=array();
2087
+        var $debug=0;
2088
+        var $content_type = 'text/xml';
2089
+
2090
+        /**
2091
+         * @param string $meth the name of the method to invoke
2092
+         * @param array $pars array of parameters to be paased to the method (xmlrpcval objects)
2093
+         */
2094
+        function xmlrpcmsg($meth, $pars=0)
2095
+        {
2096
+            $this->methodname=$meth;
2097
+            if(is_array($pars) && count($pars)>0)
2098
+            {
2099
+                for($i=0; $i<count($pars); $i++)
2100
+                {
2101
+                    $this->addParam($pars[$i]);
2102
+                }
2103
+            }
2104
+        }
2105
+
2106
+        /**
2107
+         * @access private
2108
+         */
2109
+        function xml_header($charset_encoding='')
2110
+        {
2111
+            if ($charset_encoding != '')
2112
+            {
2113
+                return "<?xml version=\"1.0\" encoding=\"$charset_encoding\" ?" . ">\n<methodCall>\n";
2114
+            }
2115
+            else
2116
+            {
2117
+                return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n";
2118
+            }
2119
+        }
2120
+
2121
+        /**
2122
+         * @access private
2123
+         */
2124
+        function xml_footer()
2125
+        {
2126
+            return '</methodCall>';
2127
+        }
2128
+
2129
+        /**
2130
+         * @access private
2131
+         */
2132
+        function kindOf()
2133
+        {
2134
+            return 'msg';
2135
+        }
2136
+
2137
+        /**
2138
+         * @access private
2139
+         */
2140
+        function createPayload($charset_encoding='')
2141
+        {
2142
+            if ($charset_encoding != '')
2143
+                $this->content_type = 'text/xml; charset=' . $charset_encoding;
2144
+            else
2145
+                $this->content_type = 'text/xml';
2146
+            $this->payload=$this->xml_header($charset_encoding);
2147
+            $this->payload.='<methodName>' . $this->methodname . "</methodName>\n";
2148
+            $this->payload.="<params>\n";
2149
+            for($i=0; $i<count($this->params); $i++)
2150
+            {
2151
+                $p=$this->params[$i];
2152
+                $this->payload.="<param>\n" . $p->serialize($charset_encoding) .
2153
+                "</param>\n";
2154
+            }
2155
+            $this->payload.="</params>\n";
2156
+            $this->payload.=$this->xml_footer();
2157
+        }
2158
+
2159
+        /**
2160
+         * Gets/sets the xmlrpc method to be invoked
2161
+         * @param string $meth the method to be set (leave empty not to set it)
2162
+         * @return string the method that will be invoked
2163
+         * @access public
2164
+         */
2165
+        function method($meth='')
2166
+        {
2167
+            if($meth!='')
2168
+            {
2169
+                $this->methodname=$meth;
2170
+            }
2171
+            return $this->methodname;
2172
+        }
2173
+
2174
+        /**
2175
+         * Returns xml representation of the message. XML prologue included
2176
+         * @return string the xml representation of the message, xml prologue included
2177
+         * @access public
2178
+         */
2179
+        function serialize($charset_encoding='')
2180
+        {
2181
+            $this->createPayload($charset_encoding);
2182
+            return $this->payload;
2183
+        }
2184
+
2185
+        /**
2186
+         * Add a parameter to the list of parameters to be used upon method invocation
2187
+         * @param xmlrpcval $par
2188
+         * @return boolean false on failure
2189
+         * @access public
2190
+         */
2191
+        function addParam($par)
2192
+        {
2193
+            // add check: do not add to self params which are not xmlrpcvals
2194
+            if(is_object($par) && is_a($par, 'xmlrpcval'))
2195
+            {
2196
+                $this->params[]=$par;
2197
+                return true;
2198
+            }
2199
+            else
2200
+            {
2201
+                return false;
2202
+            }
2203
+        }
2204
+
2205
+        /**
2206
+         * Returns the nth parameter in the message. The index zero-based.
2207
+         * @param integer $i the index of the parameter to fetch (zero based)
2208
+         * @return xmlrpcval the i-th parameter
2209
+         * @access public
2210
+         */
2211
+        function getParam($i) { return $this->params[$i]; }
2212
+
2213
+        /**
2214
+         * Returns the number of parameters in the messge.
2215
+         * @return integer the number of parameters currently set
2216
+         * @access public
2217
+         */
2218
+        function getNumParams() { return count($this->params); }
2219
+
2220
+        /**
2221
+         * Given an open file handle, read all data available and parse it as axmlrpc response.
2222
+         * NB: the file handle is not closed by this function.
2223
+         * NNB: might have trouble in rare cases to work on network streams, as we
2224
+         *      check for a read of 0 bytes instead of feof($fp).
2225
+         *      But since checking for feof(null) returns false, we would risk an
2226
+         *      infinite loop in that case, because we cannot trust the caller
2227
+         *      to give us a valid pointer to an open file...
2228
+         * @access public
2229
+         * @return xmlrpcresp
2230
+         * @todo add 2nd & 3rd param to be passed to ParseResponse() ???
2231
+         */
2232
+        function &parseResponseFile($fp)
2233
+        {
2234
+            $ipd='';
2235
+            while($data=fread($fp, 32768))
2236
+            {
2237
+                $ipd.=$data;
2238
+            }
2239
+            //fclose($fp);
2240
+            $r =& $this->parseResponse($ipd);
2241
+            return $r;
2242
+        }
2243
+
2244
+        /**
2245
+         * Parses HTTP headers and separates them from data.
2246
+         * @access private
2247
+         */
2248
+        function &parseResponseHeaders(&$data, $headers_processed=false)
2249
+        {
2250
+                // Support "web-proxy-tunelling" connections for https through proxies
2251
+                if(preg_match('/^HTTP\/1\.[0-1] 200 Connection established/', $data))
2252
+                {
2253
+                    // Look for CR/LF or simple LF as line separator,
2254
+                    // (even though it is not valid http)
2255
+                    $pos = strpos($data,"\r\n\r\n");
2256
+                    if($pos || is_int($pos))
2257
+                    {
2258
+                        $bd = $pos+4;
2259
+                    }
2260
+                    else
2261
+                    {
2262
+                        $pos = strpos($data,"\n\n");
2263
+                        if($pos || is_int($pos))
2264
+                        {
2265
+                            $bd = $pos+2;
2266
+                        }
2267
+                        else
2268
+                        {
2269
+                            // No separation between response headers and body: fault?
2270
+                            $bd = 0;
2271
+                        }
2272
+                    }
2273
+                    if ($bd)
2274
+                    {
2275
+                        // this filters out all http headers from proxy.
2276
+                        // maybe we could take them into account, too?
2277
+                        $data = substr($data, $bd);
2278
+                    }
2279
+                    else
2280
+                    {
2281
+                        error_log('XML-RPC: '.__METHOD__.': HTTPS via proxy error, tunnel connection possibly failed');
2282
+                        $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (HTTPS via proxy error, tunnel connection possibly failed)');
2283
+                        return $r;
2284
+                    }
2285
+                }
2286
+
2287
+                // Strip HTTP 1.1 100 Continue header if present
2288
+                while(preg_match('/^HTTP\/1\.1 1[0-9]{2} /', $data))
2289
+                {
2290
+                    $pos = strpos($data, 'HTTP', 12);
2291
+                    // server sent a Continue header without any (valid) content following...
2292
+                    // give the client a chance to know it
2293
+                    if(!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
2294
+                    {
2295
+                        break;
2296
+                    }
2297
+                    $data = substr($data, $pos);
2298
+                }
2299
+                if(!preg_match('/^HTTP\/[0-9.]+ 200 /', $data))
2300
+                {
2301
+                    $errstr= substr($data, 0, strpos($data, "\n")-1);
2302
+                    error_log('XML-RPC: '.__METHOD__.': HTTP error, got response: ' .$errstr);
2303
+                    $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (' . $errstr . ')');
2304
+                    return $r;
2305
+                }
2306
+
2307
+                $GLOBALS['_xh']['headers'] = array();
2308
+                $GLOBALS['_xh']['cookies'] = array();
2309
+
2310
+                // be tolerant to usage of \n instead of \r\n to separate headers and data
2311
+                // (even though it is not valid http)
2312
+                $pos = strpos($data,"\r\n\r\n");
2313
+                if($pos || is_int($pos))
2314
+                {
2315
+                    $bd = $pos+4;
2316
+                }
2317
+                else
2318
+                {
2319
+                    $pos = strpos($data,"\n\n");
2320
+                    if($pos || is_int($pos))
2321
+                    {
2322
+                        $bd = $pos+2;
2323
+                    }
2324
+                    else
2325
+                    {
2326
+                        // No separation between response headers and body: fault?
2327
+                        // we could take some action here instead of going on...
2328
+                        $bd = 0;
2329
+                    }
2330
+                }
2331
+                // be tolerant to line endings, and extra empty lines
2332
+                $ar = preg_split("/\r?\n/", trim(substr($data, 0, $pos)));
2333
+                while(list(,$line) = @each($ar))
2334
+                {
2335
+                    // take care of multi-line headers and cookies
2336
+                    $arr = explode(':',$line,2);
2337
+                    if(count($arr) > 1)
2338
+                    {
2339
+                        $header_name = strtolower(trim($arr[0]));
2340
+                        /// @todo some other headers (the ones that allow a CSV list of values)
2341
+                        /// do allow many values to be passed using multiple header lines.
2342
+                        /// We should add content to $GLOBALS['_xh']['headers'][$header_name]
2343
+                        /// instead of replacing it for those...
2344
+                        if ($header_name == 'set-cookie' || $header_name == 'set-cookie2')
2345
+                        {
2346
+                            if ($header_name == 'set-cookie2')
2347
+                            {
2348
+                                // version 2 cookies:
2349
+                                // there could be many cookies on one line, comma separated
2350
+                                $cookies = explode(',', $arr[1]);
2351
+                            }
2352
+                            else
2353
+                            {
2354
+                                $cookies = array($arr[1]);
2355
+                            }
2356
+                            foreach ($cookies as $cookie)
2357
+                            {
2358
+                                // glue together all received cookies, using a comma to separate them
2359
+                                // (same as php does with getallheaders())
2360
+                                if (isset($GLOBALS['_xh']['headers'][$header_name]))
2361
+                                    $GLOBALS['_xh']['headers'][$header_name] .= ', ' . trim($cookie);
2362
+                                else
2363
+                                    $GLOBALS['_xh']['headers'][$header_name] = trim($cookie);
2364
+                                // parse cookie attributes, in case user wants to correctly honour them
2365
+                                // feature creep: only allow rfc-compliant cookie attributes?
2366
+                                // @todo support for server sending multiple time cookie with same name, but using different PATHs
2367
+                                $cookie = explode(';', $cookie);
2368
+                                foreach ($cookie as $pos => $val)
2369
+                                {
2370
+                                    $val = explode('=', $val, 2);
2371
+                                    $tag = trim($val[0]);
2372
+                                    $val = trim(@$val[1]);
2373
+                                    /// @todo with version 1 cookies, we should strip leading and trailing " chars
2374
+                                    if ($pos == 0)
2375
+                                    {
2376
+                                        $cookiename = $tag;
2377
+                                        $GLOBALS['_xh']['cookies'][$tag] = array();
2378
+                                        $GLOBALS['_xh']['cookies'][$cookiename]['value'] = urldecode($val);
2379
+                                    }
2380
+                                    else
2381
+                                    {
2382
+                                        if ($tag != 'value')
2383
+                                        {
2384
+                                            $GLOBALS['_xh']['cookies'][$cookiename][$tag] = $val;
2385
+                                        }
2386
+                                    }
2387
+                                }
2388
+                            }
2389
+                        }
2390
+                        else
2391
+                        {
2392
+                            $GLOBALS['_xh']['headers'][$header_name] = trim($arr[1]);
2393
+                        }
2394
+                    }
2395
+                    elseif(isset($header_name))
2396
+                    {
2397
+                        ///	@todo version1 cookies might span multiple lines, thus breaking the parsing above
2398
+                        $GLOBALS['_xh']['headers'][$header_name] .= ' ' . trim($line);
2399
+                    }
2400
+                }
2401
+
2402
+                $data = substr($data, $bd);
2403
+
2404
+                if($this->debug && count($GLOBALS['_xh']['headers']))
2405
+                {
2406
+                    print '<PRE>';
2407
+                    foreach($GLOBALS['_xh']['headers'] as $header => $value)
2408
+                    {
2409
+                        print htmlentities("HEADER: $header: $value\n");
2410
+                    }
2411
+                    foreach($GLOBALS['_xh']['cookies'] as $header => $value)
2412
+                    {
2413
+                        print htmlentities("COOKIE: $header={$value['value']}\n");
2414
+                    }
2415
+                    print "</PRE>\n";
2416
+                }
2417
+
2418
+                // if CURL was used for the call, http headers have been processed,
2419
+                // and dechunking + reinflating have been carried out
2420
+                if(!$headers_processed)
2421
+                {
2422
+                    // Decode chunked encoding sent by http 1.1 servers
2423
+                    if(isset($GLOBALS['_xh']['headers']['transfer-encoding']) && $GLOBALS['_xh']['headers']['transfer-encoding'] == 'chunked')
2424
+                    {
2425
+                        if(!$data = decode_chunked($data))
2426
+                        {
2427
+                            error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to rebuild the chunked data received from server');
2428
+                            $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['dechunk_fail'], $GLOBALS['xmlrpcstr']['dechunk_fail']);
2429
+                            return $r;
2430
+                        }
2431
+                    }
2432
+
2433
+                    // Decode gzip-compressed stuff
2434
+                    // code shamelessly inspired from nusoap library by Dietrich Ayala
2435
+                    if(isset($GLOBALS['_xh']['headers']['content-encoding']))
2436
+                    {
2437
+                        $GLOBALS['_xh']['headers']['content-encoding'] = str_replace('x-', '', $GLOBALS['_xh']['headers']['content-encoding']);
2438
+                        if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' || $GLOBALS['_xh']['headers']['content-encoding'] == 'gzip')
2439
+                        {
2440
+                            // if decoding works, use it. else assume data wasn't gzencoded
2441
+                            if(function_exists('gzinflate'))
2442
+                            {
2443
+                                if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data))
2444
+                                {
2445
+                                    $data = $degzdata;
2446
+                                    if($this->debug)
2447
+                                    print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2448
+                                }
2449
+                                elseif($GLOBALS['_xh']['headers']['content-encoding'] == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
2450
+                                {
2451
+                                    $data = $degzdata;
2452
+                                    if($this->debug)
2453
+                                    print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2454
+                                }
2455
+                                else
2456
+                                {
2457
+                                    error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to decode the deflated data received from server');
2458
+                                    $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']);
2459
+                                    return $r;
2460
+                                }
2461
+                            }
2462
+                            else
2463
+                            {
2464
+                                error_log('XML-RPC: '.__METHOD__.': the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
2465
+                                $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']);
2466
+                                return $r;
2467
+                            }
2468
+                        }
2469
+                    }
2470
+                } // end of 'if needed, de-chunk, re-inflate response'
2471
+
2472
+                // real stupid hack to avoid PHP complaining about returning NULL by ref
2473
+                $r = null;
2474
+                $r =& $r;
2475
+                return $r;
2476
+        }
2477
+
2478
+        /**
2479
+         * Parse the xmlrpc response contained in the string $data and return an xmlrpcresp object.
2480
+         * @param string $data the xmlrpc response, eventually including http headers
2481
+         * @param bool $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and consequent decoding
2482
+         * @param string $return_type decides return type, i.e. content of response->value(). Either 'xmlrpcvals', 'xml' or 'phpvals'
2483
+         * @return xmlrpcresp
2484
+         * @access public
2485
+         */
2486
+        function &parseResponse($data='', $headers_processed=false, $return_type='xmlrpcvals')
2487
+        {
2488
+            if($this->debug)
2489
+            {
2490
+                //by maHo, replaced htmlspecialchars with htmlentities
2491
+                print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
2492
+            }
2493
+
2494
+            if($data == '')
2495
+            {
2496
+                error_log('XML-RPC: '.__METHOD__.': no response received from server.');
2497
+                $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']);
2498
+                return $r;
2499
+            }
2500
+
2501
+            $GLOBALS['_xh']=array();
2502
+
2503
+            $raw_data = $data;
2504
+            // parse the HTTP headers of the response, if present, and separate them from data
2505
+            if(substr($data, 0, 4) == 'HTTP')
2506
+            {
2507
+                $r =& $this->parseResponseHeaders($data, $headers_processed);
2508
+                if ($r)
2509
+                {
2510
+                    // failed processing of HTTP response headers
2511
+                    // save into response obj the full payload received, for debugging
2512
+                    $r->raw_data = $data;
2513
+                    return $r;
2514
+                }
2515
+            }
2516
+            else
2517
+            {
2518
+                $GLOBALS['_xh']['headers'] = array();
2519
+                $GLOBALS['_xh']['cookies'] = array();
2520
+            }
2521
+
2522
+            if($this->debug)
2523
+            {
2524
+                $start = strpos($data, '<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2525
+                if ($start)
2526
+                {
2527
+                    $start += strlen('<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2528
+                    $end = strpos($data, '-->', $start);
2529
+                    $comments = substr($data, $start, $end-$start);
2530
+                    print "<PRE>---SERVER DEBUG INFO (DECODED) ---\n\t".htmlentities(str_replace("\n", "\n\t", base64_decode($comments)))."\n---END---\n</PRE>";
2531
+                }
2532
+            }
2533
+
2534
+            // be tolerant of extra whitespace in response body
2535
+            $data = trim($data);
2536
+
2537
+            /// @todo return an error msg if $data=='' ?
2538
+
2539
+            // be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts)
2540
+            // idea from Luca Mariano <[email protected]> originally in PEARified version of the lib
2541
+            $pos = strrpos($data, '</methodResponse>');
2542
+            if($pos !== false)
2543
+            {
2544
+                $data = substr($data, 0, $pos+17);
2545
+            }
2546
+
2547
+            // if user wants back raw xml, give it to him
2548
+            if ($return_type == 'xml')
2549
+            {
2550
+                $r = new xmlrpcresp($data, 0, '', 'xml');
2551
+                $r->hdrs = $GLOBALS['_xh']['headers'];
2552
+                $r->_cookies = $GLOBALS['_xh']['cookies'];
2553
+                $r->raw_data = $raw_data;
2554
+                return $r;
2555
+            }
2556
+
2557
+            // try to 'guestimate' the character encoding of the received response
2558
+            $resp_encoding = guess_encoding(@$GLOBALS['_xh']['headers']['content-type'], $data);
2559
+
2560
+            $GLOBALS['_xh']['ac']='';
2561
+            //$GLOBALS['_xh']['qt']=''; //unused...
2562
+            $GLOBALS['_xh']['stack'] = array();
2563
+            $GLOBALS['_xh']['valuestack'] = array();
2564
+            $GLOBALS['_xh']['isf']=0; // 0 = OK, 1 for xmlrpc fault responses, 2 = invalid xmlrpc
2565
+            $GLOBALS['_xh']['isf_reason']='';
2566
+            $GLOBALS['_xh']['rt']=''; // 'methodcall or 'methodresponse'
2567
+
2568
+            // if response charset encoding is not known / supported, try to use
2569
+            // the default encoding and parse the xml anyway, but log a warning...
2570
+            if (!in_array($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2571
+            // the following code might be better for mb_string enabled installs, but
2572
+            // makes the lib about 200% slower...
2573
+            //if (!is_valid_charset($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2574
+            {
2575
+                error_log('XML-RPC: '.__METHOD__.': invalid charset encoding of received response: '.$resp_encoding);
2576
+                $resp_encoding = $GLOBALS['xmlrpc_defencoding'];
2577
+            }
2578
+            $parser = xml_parser_create($resp_encoding);
2579
+            xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
2580
+            // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
2581
+            // the xml parser to give us back data in the expected charset.
2582
+            // What if internal encoding is not in one of the 3 allowed?
2583
+            // we use the broadest one, ie. utf8
2584
+            // This allows to send data which is native in various charset,
2585
+            // by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
2586
+            if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2587
+            {
2588
+                xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
2589
+            }
2590
+            else
2591
+            {
2592
+                xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
2593
+            }
2594
+
2595
+            if ($return_type == 'phpvals')
2596
+            {
2597
+                xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
2598
+            }
2599
+            else
2600
+            {
2601
+                xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
2602
+            }
2603
+
2604
+            xml_set_character_data_handler($parser, 'xmlrpc_cd');
2605
+            xml_set_default_handler($parser, 'xmlrpc_dh');
2606
+
2607
+            // first error check: xml not well formed
2608
+            if(!xml_parse($parser, $data, count($data)))
2609
+            {
2610
+                // thanks to Peter Kocks <[email protected]>
2611
+                if((xml_get_current_line_number($parser)) == 1)
2612
+                {
2613
+                    $errstr = 'XML error at line 1, check URL';
2614
+                }
2615
+                else
2616
+                {
2617
+                    $errstr = sprintf('XML error: %s at line %d, column %d',
2618
+                        xml_error_string(xml_get_error_code($parser)),
2619
+                        xml_get_current_line_number($parser), xml_get_current_column_number($parser));
2620
+                }
2621
+                error_log($errstr);
2622
+                $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return'].' ('.$errstr.')');
2623
+                xml_parser_free($parser);
2624
+                if($this->debug)
2625
+                {
2626
+                    print $errstr;
2627
+                }
2628
+                $r->hdrs = $GLOBALS['_xh']['headers'];
2629
+                $r->_cookies = $GLOBALS['_xh']['cookies'];
2630
+                $r->raw_data = $raw_data;
2631
+                return $r;
2632
+            }
2633
+            xml_parser_free($parser);
2634
+            // second error check: xml well formed but not xml-rpc compliant
2635
+            if ($GLOBALS['_xh']['isf'] > 1)
2636
+            {
2637
+                if ($this->debug)
2638
+                {
2639
+                    /// @todo echo something for user?
2640
+                }
2641
+
2642
+                $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2643
+                $GLOBALS['xmlrpcstr']['invalid_return'] . ' ' . $GLOBALS['_xh']['isf_reason']);
2644
+            }
2645
+            // third error check: parsing of the response has somehow gone boink.
2646
+            // NB: shall we omit this check, since we trust the parsing code?
2647
+            elseif ($return_type == 'xmlrpcvals' && !is_object($GLOBALS['_xh']['value']))
2648
+            {
2649
+                // something odd has happened
2650
+                // and it's time to generate a client side error
2651
+                // indicating something odd went on
2652
+                $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2653
+                    $GLOBALS['xmlrpcstr']['invalid_return']);
2654
+            }
2655
+            else
2656
+            {
2657
+                if ($this->debug)
2658
+                {
2659
+                    print "<PRE>---PARSED---\n";
2660
+                    // somehow htmlentities chokes on var_export, and some full html string...
2661
+                    //print htmlentitites(var_export($GLOBALS['_xh']['value'], true));
2662
+                    print htmlspecialchars(var_export($GLOBALS['_xh']['value'], true));
2663
+                    print "\n---END---</PRE>";
2664
+                }
2665
+
2666
+                // note that using =& will raise an error if $GLOBALS['_xh']['st'] does not generate an object.
2667
+                $v =& $GLOBALS['_xh']['value'];
2668
+
2669
+                if($GLOBALS['_xh']['isf'])
2670
+                {
2671
+                    /// @todo we should test here if server sent an int and a string,
2672
+                    /// and/or coerce them into such...
2673
+                    if ($return_type == 'xmlrpcvals')
2674
+                    {
2675
+                        $errno_v = $v->structmem('faultCode');
2676
+                        $errstr_v = $v->structmem('faultString');
2677
+                        $errno = $errno_v->scalarval();
2678
+                        $errstr = $errstr_v->scalarval();
2679
+                    }
2680
+                    else
2681
+                    {
2682
+                        $errno = $v['faultCode'];
2683
+                        $errstr = $v['faultString'];
2684
+                    }
2685
+
2686
+                    if($errno == 0)
2687
+                    {
2688
+                        // FAULT returned, errno needs to reflect that
2689
+                        $errno = -1;
2690
+                    }
2691
+
2692
+                    $r = new xmlrpcresp(0, $errno, $errstr);
2693
+                }
2694
+                else
2695
+                {
2696
+                    $r=new xmlrpcresp($v, 0, '', $return_type);
2697
+                }
2698
+            }
2699
+
2700
+            $r->hdrs = $GLOBALS['_xh']['headers'];
2701
+            $r->_cookies = $GLOBALS['_xh']['cookies'];
2702
+            $r->raw_data = $raw_data;
2703
+            return $r;
2704
+        }
2705
+    }
2706
+
2707
+    class xmlrpcval
2708
+    {
2709
+        var $me=array();
2710
+        var $mytype=0;
2711
+        var $_php_class=null;
2712
+
2713
+        /**
2714
+         * @param mixed $val
2715
+         * @param string $type any valid xmlrpc type name (lowercase). If null, 'string' is assumed
2716
+         */
2717
+        function xmlrpcval($val=-1, $type='')
2718
+        {
2719
+            /// @todo: optimization creep - do not call addXX, do it all inline.
2720
+            /// downside: booleans will not be coerced anymore
2721
+            if($val!==-1 || $type!='')
2722
+            {
2723
+                // optimization creep: inlined all work done by constructor
2724
+                switch($type)
2725
+                {
2726
+                    case '':
2727
+                        $this->mytype=1;
2728
+                        $this->me['string']=$val;
2729
+                        break;
2730
+                    case 'i4':
2731
+                    case 'int':
2732
+                    case 'double':
2733
+                    case 'string':
2734
+                    case 'boolean':
2735
+                    case 'dateTime.iso8601':
2736
+                    case 'base64':
2737
+                    case 'null':
2738
+                        $this->mytype=1;
2739
+                        $this->me[$type]=$val;
2740
+                        break;
2741
+                    case 'array':
2742
+                        $this->mytype=2;
2743
+                        $this->me['array']=$val;
2744
+                        break;
2745
+                    case 'struct':
2746
+                        $this->mytype=3;
2747
+                        $this->me['struct']=$val;
2748
+                        break;
2749
+                    default:
2750
+                        error_log("XML-RPC: ".__METHOD__.": not a known type ($type)");
2751
+                }
2752
+                /*if($type=='')
2753 2753
 				{
2754 2754
 					$type='string';
2755 2755
 				}
@@ -2765,1012 +2765,1012 @@  discard block
 block discarded – undo
2765 2765
 				{
2766 2766
 					$this->addStruct($val);
2767 2767
 				}*/
2768
-			}
2769
-		}
2770
-
2771
-		/**
2772
-		* Add a single php value to an (unitialized) xmlrpcval
2773
-		* @param mixed $val
2774
-		* @param string $type
2775
-		* @return int 1 or 0 on failure
2776
-		*/
2777
-		function addScalar($val, $type='string')
2778
-		{
2779
-			$typeof=@$GLOBALS['xmlrpcTypes'][$type];
2780
-			if($typeof!=1)
2781
-			{
2782
-				error_log("XML-RPC: ".__METHOD__.": not a scalar type ($type)");
2783
-				return 0;
2784
-			}
2785
-
2786
-			// coerce booleans into correct values
2787
-			// NB: we should either do it for datetimes, integers and doubles, too,
2788
-			// or just plain remove this check, implemented on booleans only...
2789
-			if($type==$GLOBALS['xmlrpcBoolean'])
2790
-			{
2791
-				if(strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
2792
-				{
2793
-					$val=true;
2794
-				}
2795
-				else
2796
-				{
2797
-					$val=false;
2798
-				}
2799
-			}
2800
-
2801
-			switch($this->mytype)
2802
-			{
2803
-				case 1:
2804
-					error_log('XML-RPC: '.__METHOD__.': scalar xmlrpcval can have only one value');
2805
-					return 0;
2806
-				case 3:
2807
-					error_log('XML-RPC: '.__METHOD__.': cannot add anonymous scalar to struct xmlrpcval');
2808
-					return 0;
2809
-				case 2:
2810
-					// we're adding a scalar value to an array here
2811
-					//$ar=$this->me['array'];
2812
-					//$ar[]=new xmlrpcval($val, $type);
2813
-					//$this->me['array']=$ar;
2814
-					// Faster (?) avoid all the costly array-copy-by-val done here...
2815
-					$this->me['array'][]=new xmlrpcval($val, $type);
2816
-					return 1;
2817
-				default:
2818
-					// a scalar, so set the value and remember we're scalar
2819
-					$this->me[$type]=$val;
2820
-					$this->mytype=$typeof;
2821
-					return 1;
2822
-			}
2823
-		}
2824
-
2825
-		/**
2826
-		* Add an array of xmlrpcval objects to an xmlrpcval
2827
-		* @param array $vals
2828
-		* @return int 1 or 0 on failure
2829
-		* @access public
2830
-		*
2831
-		* @todo add some checking for $vals to be an array of xmlrpcvals?
2832
-		*/
2833
-		function addArray($vals)
2834
-		{
2835
-			if($this->mytype==0)
2836
-			{
2837
-				$this->mytype=$GLOBALS['xmlrpcTypes']['array'];
2838
-				$this->me['array']=$vals;
2839
-				return 1;
2840
-			}
2841
-			elseif($this->mytype==2)
2842
-			{
2843
-				// we're adding to an array here
2844
-				$this->me['array'] = array_merge($this->me['array'], $vals);
2845
-				return 1;
2846
-			}
2847
-			else
2848
-			{
2849
-				error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
2850
-				return 0;
2851
-			}
2852
-		}
2853
-
2854
-		/**
2855
-		* Add an array of named xmlrpcval objects to an xmlrpcval
2856
-		* @param array $vals
2857
-		* @return int 1 or 0 on failure
2858
-		* @access public
2859
-		*
2860
-		* @todo add some checking for $vals to be an array?
2861
-		*/
2862
-		function addStruct($vals)
2863
-		{
2864
-			if($this->mytype==0)
2865
-			{
2866
-				$this->mytype=$GLOBALS['xmlrpcTypes']['struct'];
2867
-				$this->me['struct']=$vals;
2868
-				return 1;
2869
-			}
2870
-			elseif($this->mytype==3)
2871
-			{
2872
-				// we're adding to a struct here
2873
-				$this->me['struct'] = array_merge($this->me['struct'], $vals);
2874
-				return 1;
2875
-			}
2876
-			else
2877
-			{
2878
-				error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
2879
-				return 0;
2880
-			}
2881
-		}
2882
-
2883
-		// poor man's version of print_r ???
2884
-		// DEPRECATED!
2885
-		function dump($ar)
2886
-		{
2887
-			foreach($ar as $key => $val)
2888
-			{
2889
-				echo "$key => $val<br />";
2890
-				if($key == 'array')
2891
-				{
2892
-					while(list($key2, $val2) = each($val))
2893
-					{
2894
-						echo "-- $key2 => $val2<br />";
2895
-					}
2896
-				}
2897
-			}
2898
-		}
2899
-
2900
-		/**
2901
-		* Returns a string containing "struct", "array" or "scalar" describing the base type of the value
2902
-		* @return string
2903
-		* @access public
2904
-		*/
2905
-		function kindOf()
2906
-		{
2907
-			switch($this->mytype)
2908
-			{
2909
-				case 3:
2910
-					return 'struct';
2911
-					break;
2912
-				case 2:
2913
-					return 'array';
2914
-					break;
2915
-				case 1:
2916
-					return 'scalar';
2917
-					break;
2918
-				default:
2919
-					return 'undef';
2920
-			}
2921
-		}
2922
-
2923
-		/**
2924
-		* @access private
2925
-		*/
2926
-		function serializedata($typ, $val, $charset_encoding='')
2927
-		{
2928
-			$rs='';
2929
-			switch(@$GLOBALS['xmlrpcTypes'][$typ])
2930
-			{
2931
-				case 1:
2932
-					switch($typ)
2933
-					{
2934
-						case $GLOBALS['xmlrpcBase64']:
2935
-							$rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
2936
-							break;
2937
-						case $GLOBALS['xmlrpcBoolean']:
2938
-							$rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
2939
-							break;
2940
-						case $GLOBALS['xmlrpcString']:
2941
-							// G. Giunta 2005/2/13: do NOT use htmlentities, since
2942
-							// it will produce named html entities, which are invalid xml
2943
-							$rs.="<${typ}>" . xmlrpc_encode_entitites($val, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding). "</${typ}>";
2944
-							break;
2945
-						case $GLOBALS['xmlrpcInt']:
2946
-						case $GLOBALS['xmlrpcI4']:
2947
-							$rs.="<${typ}>".(int)$val."</${typ}>";
2948
-							break;
2949
-						case $GLOBALS['xmlrpcDouble']:
2950
-							// avoid using standard conversion of float to string because it is locale-dependent,
2951
-							// and also because the xmlrpc spec forbids exponential notation.
2952
-							// sprintf('%F') could be most likely ok but it fails eg. on 2e-14.
2953
-							// The code below tries its best at keeping max precision while avoiding exp notation,
2954
-							// but there is of course no limit in the number of decimal places to be used...
2955
-							$rs.="<${typ}>".preg_replace('/\\.?0+$/','',number_format((double)$val, 128, '.', ''))."</${typ}>";
2956
-							break;
2957
-						case $GLOBALS['xmlrpcDateTime']:
2958
-							if (is_string($val))
2959
-							{
2960
-								$rs.="<${typ}>${val}</${typ}>";
2961
-							}
2962
-							else if(is_a($val, 'DateTime'))
2963
-							{
2964
-								$rs.="<${typ}>".$val->format('Ymd\TH:i:s')."</${typ}>";
2965
-							}
2966
-							else if(is_int($val))
2967
-							{
2968
-								$rs.="<${typ}>".strftime("%Y%m%dT%H:%M:%S", $val)."</${typ}>";
2969
-							}
2970
-							else
2971
-							{
2972
-								// not really a good idea here: but what shall we output anyway? left for backward compat...
2973
-								$rs.="<${typ}>${val}</${typ}>";
2974
-							}
2975
-							break;
2976
-						case $GLOBALS['xmlrpcNull']:
2977
-							if ($GLOBALS['xmlrpc_null_apache_encoding'])
2978
-							{
2979
-								$rs.="<ex:nil/>";
2980
-							}
2981
-							else
2982
-							{
2983
-								$rs.="<nil/>";
2984
-							}
2985
-							break;
2986
-						default:
2987
-							// no standard type value should arrive here, but provide a possibility
2988
-							// for xmlrpcvals of unknown type...
2989
-							$rs.="<${typ}>${val}</${typ}>";
2990
-					}
2991
-					break;
2992
-				case 3:
2993
-					// struct
2994
-					if ($this->_php_class)
2995
-					{
2996
-						$rs.='<struct php_class="' . $this->_php_class . "\">\n";
2997
-					}
2998
-					else
2999
-					{
3000
-						$rs.="<struct>\n";
3001
-					}
3002
-					foreach($val as $key2 => $val2)
3003
-					{
3004
-						$rs.='<member><name>'.xmlrpc_encode_entitites($key2, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding)."</name>\n";
3005
-						//$rs.=$this->serializeval($val2);
3006
-						$rs.=$val2->serialize($charset_encoding);
3007
-						$rs.="</member>\n";
3008
-					}
3009
-					$rs.='</struct>';
3010
-					break;
3011
-				case 2:
3012
-					// array
3013
-					$rs.="<array>\n<data>\n";
3014
-					for($i=0; $i<count($val); $i++)
3015
-					{
3016
-						//$rs.=$this->serializeval($val[$i]);
3017
-						$rs.=$val[$i]->serialize($charset_encoding);
3018
-					}
3019
-					$rs.="</data>\n</array>";
3020
-					break;
3021
-				default:
3022
-					break;
3023
-			}
3024
-			return $rs;
3025
-		}
3026
-
3027
-		/**
3028
-		* Returns xml representation of the value. XML prologue not included
3029
-		* @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
3030
-		* @return string
3031
-		* @access public
3032
-		*/
3033
-		function serialize($charset_encoding='')
3034
-		{
3035
-			// add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3036
-			//if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3037
-			//{
3038
-				reset($this->me);
3039
-				list($typ, $val) = each($this->me);
3040
-				return '<value>' . $this->serializedata($typ, $val, $charset_encoding) . "</value>\n";
3041
-			//}
3042
-		}
3043
-
3044
-		// DEPRECATED
3045
-		function serializeval($o)
3046
-		{
3047
-			// add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3048
-			//if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3049
-			//{
3050
-				$ar=$o->me;
3051
-				reset($ar);
3052
-				list($typ, $val) = each($ar);
3053
-				return '<value>' . $this->serializedata($typ, $val) . "</value>\n";
3054
-			//}
3055
-		}
3056
-
3057
-		/**
3058
-		* Checks wheter a struct member with a given name is present.
3059
-		* Works only on xmlrpcvals of type struct.
3060
-		* @param string $m the name of the struct member to be looked up
3061
-		* @return boolean
3062
-		* @access public
3063
-		*/
3064
-		function structmemexists($m)
3065
-		{
3066
-			return array_key_exists($m, $this->me['struct']);
3067
-		}
3068
-
3069
-		/**
3070
-		* Returns the value of a given struct member (an xmlrpcval object in itself).
3071
-		* Will raise a php warning if struct member of given name does not exist
3072
-		* @param string $m the name of the struct member to be looked up
3073
-		* @return xmlrpcval
3074
-		* @access public
3075
-		*/
3076
-		function structmem($m)
3077
-		{
3078
-			return $this->me['struct'][$m];
3079
-		}
3080
-
3081
-		/**
3082
-		* Reset internal pointer for xmlrpcvals of type struct.
3083
-		* @access public
3084
-		*/
3085
-		function structreset()
3086
-		{
3087
-			reset($this->me['struct']);
3088
-		}
3089
-
3090
-		/**
3091
-		* Return next member element for xmlrpcvals of type struct.
3092
-		* @return xmlrpcval
3093
-		* @access public
3094
-		*/
3095
-		function structeach()
3096
-		{
3097
-			return each($this->me['struct']);
3098
-		}
3099
-
3100
-		// DEPRECATED! this code looks like it is very fragile and has not been fixed
3101
-		// for a long long time. Shall we remove it for 2.0?
3102
-		function getval()
3103
-		{
3104
-			// UNSTABLE
3105
-			reset($this->me);
3106
-			list($a,$b)=each($this->me);
3107
-			// contributed by I Sofer, 2001-03-24
3108
-			// add support for nested arrays to scalarval
3109
-			// i've created a new method here, so as to
3110
-			// preserve back compatibility
3111
-
3112
-			if(is_array($b))
3113
-			{
3114
-				@reset($b);
3115
-				while(list($id,$cont) = @each($b))
3116
-				{
3117
-					$b[$id] = $cont->scalarval();
3118
-				}
3119
-			}
3120
-
3121
-			// add support for structures directly encoding php objects
3122
-			if(is_object($b))
3123
-			{
3124
-				$t = get_object_vars($b);
3125
-				@reset($t);
3126
-				while(list($id,$cont) = @each($t))
3127
-				{
3128
-					$t[$id] = $cont->scalarval();
3129
-				}
3130
-				@reset($t);
3131
-				while(list($id,$cont) = @each($t))
3132
-				{
3133
-					@$b->$id = $cont;
3134
-				}
3135
-			}
3136
-			// end contrib
3137
-			return $b;
3138
-		}
3139
-
3140
-		/**
3141
-		* Returns the value of a scalar xmlrpcval
3142
-		* @return mixed
3143
-		* @access public
3144
-		*/
3145
-		function scalarval()
3146
-		{
3147
-			reset($this->me);
3148
-			list(,$b)=each($this->me);
3149
-			return $b;
3150
-		}
3151
-
3152
-		/**
3153
-		* Returns the type of the xmlrpcval.
3154
-		* For integers, 'int' is always returned in place of 'i4'
3155
-		* @return string
3156
-		* @access public
3157
-		*/
3158
-		function scalartyp()
3159
-		{
3160
-			reset($this->me);
3161
-			list($a,)=each($this->me);
3162
-			if($a==$GLOBALS['xmlrpcI4'])
3163
-			{
3164
-				$a=$GLOBALS['xmlrpcInt'];
3165
-			}
3166
-			return $a;
3167
-		}
3168
-
3169
-		/**
3170
-		* Returns the m-th member of an xmlrpcval of struct type
3171
-		* @param integer $m the index of the value to be retrieved (zero based)
3172
-		* @return xmlrpcval
3173
-		* @access public
3174
-		*/
3175
-		function arraymem($m)
3176
-		{
3177
-			return $this->me['array'][$m];
3178
-		}
3179
-
3180
-		/**
3181
-		* Returns the number of members in an xmlrpcval of array type
3182
-		* @return integer
3183
-		* @access public
3184
-		*/
3185
-		function arraysize()
3186
-		{
3187
-			return count($this->me['array']);
3188
-		}
3189
-
3190
-		/**
3191
-		* Returns the number of members in an xmlrpcval of struct type
3192
-		* @return integer
3193
-		* @access public
3194
-		*/
3195
-		function structsize()
3196
-		{
3197
-			return count($this->me['struct']);
3198
-		}
3199
-	}
3200
-
3201
-
3202
-	// date helpers
3203
-
3204
-	/**
3205
-	* Given a timestamp, return the corresponding ISO8601 encoded string.
3206
-	*
3207
-	* Really, timezones ought to be supported
3208
-	* but the XML-RPC spec says:
3209
-	*
3210
-	* "Don't assume a timezone. It should be specified by the server in its
3211
-	* documentation what assumptions it makes about timezones."
3212
-	*
3213
-	* These routines always assume localtime unless
3214
-	* $utc is set to 1, in which case UTC is assumed
3215
-	* and an adjustment for locale is made when encoding
3216
-	*
3217
-	* @param int $timet (timestamp)
3218
-	* @param int $utc (0 or 1)
3219
-	* @return string
3220
-	*/
3221
-	function iso8601_encode($timet, $utc=0)
3222
-	{
3223
-		if(!$utc)
3224
-		{
3225
-			$t=strftime("%Y%m%dT%H:%M:%S", $timet);
3226
-		}
3227
-		else
3228
-		{
3229
-			if(function_exists('gmstrftime'))
3230
-			{
3231
-				// gmstrftime doesn't exist in some versions
3232
-				// of PHP
3233
-				$t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
3234
-			}
3235
-			else
3236
-			{
3237
-				$t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z'));
3238
-			}
3239
-		}
3240
-		return $t;
3241
-	}
3242
-
3243
-	/**
3244
-	* Given an ISO8601 date string, return a timet in the localtime, or UTC
3245
-	* @param string $idate
3246
-	* @param int $utc either 0 or 1
3247
-	* @return int (datetime)
3248
-	*/
3249
-	function iso8601_decode($idate, $utc=0)
3250
-	{
3251
-		$t=0;
3252
-		if(preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $idate, $regs))
3253
-		{
3254
-			if($utc)
3255
-			{
3256
-				$t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3257
-			}
3258
-			else
3259
-			{
3260
-				$t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3261
-			}
3262
-		}
3263
-		return $t;
3264
-	}
3265
-
3266
-	/**
3267
-	* Takes an xmlrpc value in PHP xmlrpcval object format and translates it into native PHP types.
3268
-	*
3269
-	* Works with xmlrpc message objects as input, too.
3270
-	*
3271
-	* Given proper options parameter, can rebuild generic php object instances
3272
-	* (provided those have been encoded to xmlrpc format using a corresponding
3273
-	* option in php_xmlrpc_encode())
3274
-	* PLEASE NOTE that rebuilding php objects involves calling their constructor function.
3275
-	* This means that the remote communication end can decide which php code will
3276
-	* get executed on your server, leaving the door possibly open to 'php-injection'
3277
-	* style of attacks (provided you have some classes defined on your server that
3278
-	* might wreak havoc if instances are built outside an appropriate context).
3279
-	* Make sure you trust the remote server/client before eanbling this!
3280
-	*
3281
-	* @author Dan Libby ([email protected])
3282
-	*
3283
-	* @param xmlrpcval $xmlrpc_val
3284
-	* @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
3285
-	* @return mixed
3286
-	*/
3287
-	function php_xmlrpc_decode($xmlrpc_val, $options=array())
3288
-	{
3289
-		switch($xmlrpc_val->kindOf())
3290
-		{
3291
-			case 'scalar':
3292
-				if (in_array('extension_api', $options))
3293
-				{
3294
-					reset($xmlrpc_val->me);
3295
-					list($typ,$val) = each($xmlrpc_val->me);
3296
-					switch ($typ)
3297
-					{
3298
-						case 'dateTime.iso8601':
3299
-							$xmlrpc_val->scalar = $val;
3300
-							$xmlrpc_val->xmlrpc_type = 'datetime';
3301
-							$xmlrpc_val->timestamp = iso8601_decode($val);
3302
-							return $xmlrpc_val;
3303
-						case 'base64':
3304
-							$xmlrpc_val->scalar = $val;
3305
-							$xmlrpc_val->type = $typ;
3306
-							return $xmlrpc_val;
3307
-						default:
3308
-							return $xmlrpc_val->scalarval();
3309
-					}
3310
-				}
3311
-				if (in_array('dates_as_objects', $options) && $xmlrpc_val->scalartyp() == 'dateTime.iso8601')
3312
-				{
3313
-					// we return a Datetime object instead of a string
3314
-					// since now the constructor of xmlrpcval accepts safely strings, ints and datetimes,
3315
-					// we cater to all 3 cases here
3316
-					$out = $xmlrpc_val->scalarval();
3317
-					if (is_string($out))
3318
-					{
3319
-						$out = strtotime($out);
3320
-					}
3321
-					if (is_int($out))
3322
-					{
3323
-						$result = new Datetime();
3324
-						$result->setTimestamp($out);
3325
-						return $result;
3326
-					}
3327
-					elseif (is_a($out, 'Datetime'))
3328
-					{
3329
-						return $out;
3330
-					}
3331
-				}
3332
-				return $xmlrpc_val->scalarval();
3333
-			case 'array':
3334
-				$size = $xmlrpc_val->arraysize();
3335
-				$arr = array();
3336
-				for($i = 0; $i < $size; $i++)
3337
-				{
3338
-					$arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i), $options);
3339
-				}
3340
-				return $arr;
3341
-			case 'struct':
3342
-				$xmlrpc_val->structreset();
3343
-				// If user said so, try to rebuild php objects for specific struct vals.
3344
-				/// @todo should we raise a warning for class not found?
3345
-				// shall we check for proper subclass of xmlrpcval instead of
3346
-				// presence of _php_class to detect what we can do?
3347
-				if (in_array('decode_php_objs', $options) && $xmlrpc_val->_php_class != ''
3348
-					&& class_exists($xmlrpc_val->_php_class))
3349
-				{
3350
-					$obj = @new $xmlrpc_val->_php_class;
3351
-					while(list($key,$value)=$xmlrpc_val->structeach())
3352
-					{
3353
-						$obj->$key = php_xmlrpc_decode($value, $options);
3354
-					}
3355
-					return $obj;
3356
-				}
3357
-				else
3358
-				{
3359
-					$arr = array();
3360
-					while(list($key,$value)=$xmlrpc_val->structeach())
3361
-					{
3362
-						$arr[$key] = php_xmlrpc_decode($value, $options);
3363
-					}
3364
-					return $arr;
3365
-				}
3366
-			case 'msg':
3367
-				$paramcount = $xmlrpc_val->getNumParams();
3368
-				$arr = array();
3369
-				for($i = 0; $i < $paramcount; $i++)
3370
-				{
3371
-					$arr[] = php_xmlrpc_decode($xmlrpc_val->getParam($i));
3372
-				}
3373
-				return $arr;
3374
-			}
3375
-	}
3376
-
3377
-	// This constant left here only for historical reasons...
3378
-	// it was used to decide if we have to define xmlrpc_encode on our own, but
3379
-	// we do not do it anymore
3380
-	if(function_exists('xmlrpc_decode'))
3381
-	{
3382
-		define('XMLRPC_EPI_ENABLED','1');
3383
-	}
3384
-	else
3385
-	{
3386
-		define('XMLRPC_EPI_ENABLED','0');
3387
-	}
3388
-
3389
-	/**
3390
-	* Takes native php types and encodes them into xmlrpc PHP object format.
3391
-	* It will not re-encode xmlrpcval objects.
3392
-	*
3393
-	* Feature creep -- could support more types via optional type argument
3394
-	* (string => datetime support has been added, ??? => base64 not yet)
3395
-	*
3396
-	* If given a proper options parameter, php object instances will be encoded
3397
-	* into 'special' xmlrpc values, that can later be decoded into php objects
3398
-	* by calling php_xmlrpc_decode() with a corresponding option
3399
-	*
3400
-	* @author Dan Libby ([email protected])
3401
-	*
3402
-	* @param mixed $php_val the value to be converted into an xmlrpcval object
3403
-	* @param array $options	can include 'encode_php_objs', 'auto_dates', 'null_extension' or 'extension_api'
3404
-	* @return xmlrpcval
3405
-	*/
3406
-	function php_xmlrpc_encode($php_val, $options=array())
3407
-	{
3408
-		$type = gettype($php_val);
3409
-		switch($type)
3410
-		{
3411
-			case 'string':
3412
-				if (in_array('auto_dates', $options) && preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $php_val))
3413
-					$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDateTime']);
3414
-				else
3415
-					$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcString']);
3416
-				break;
3417
-			case 'integer':
3418
-				$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcInt']);
3419
-				break;
3420
-			case 'double':
3421
-				$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDouble']);
3422
-				break;
3423
-				// <G_Giunta_2001-02-29>
3424
-				// Add support for encoding/decoding of booleans, since they are supported in PHP
3425
-			case 'boolean':
3426
-				$xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcBoolean']);
3427
-				break;
3428
-				// </G_Giunta_2001-02-29>
3429
-			case 'array':
3430
-				// PHP arrays can be encoded to either xmlrpc structs or arrays,
3431
-				// depending on wheter they are hashes or plain 0..n integer indexed
3432
-				// A shorter one-liner would be
3433
-				// $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
3434
-				// but execution time skyrockets!
3435
-				$j = 0;
3436
-				$arr = array();
3437
-				$ko = false;
3438
-				foreach($php_val as $key => $val)
3439
-				{
3440
-					$arr[$key] = php_xmlrpc_encode($val, $options);
3441
-					if(!$ko && $key !== $j)
3442
-					{
3443
-						$ko = true;
3444
-					}
3445
-					$j++;
3446
-				}
3447
-				if($ko)
3448
-				{
3449
-					$xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
3450
-				}
3451
-				else
3452
-				{
3453
-					$xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcArray']);
3454
-				}
3455
-				break;
3456
-			case 'object':
3457
-				if(is_a($php_val, 'xmlrpcval'))
3458
-				{
3459
-					$xmlrpc_val = $php_val;
3460
-				}
3461
-				else if(is_a($php_val, 'DateTime'))
3462
-				{
3463
-					$xmlrpc_val = new xmlrpcval($php_val->format('Ymd\TH:i:s'), $GLOBALS['xmlrpcStruct']);
3464
-				}
3465
-				else
3466
-				{
3467
-					$arr = array();
3468
-					reset($php_val);
3469
-					while(list($k,$v) = each($php_val))
3470
-					{
3471
-						$arr[$k] = php_xmlrpc_encode($v, $options);
3472
-					}
3473
-					$xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
3474
-					if (in_array('encode_php_objs', $options))
3475
-					{
3476
-						// let's save original class name into xmlrpcval:
3477
-						// might be useful later on...
3478
-						$xmlrpc_val->_php_class = get_class($php_val);
3479
-					}
3480
-				}
3481
-				break;
3482
-			case 'NULL':
3483
-				if (in_array('extension_api', $options))
3484
-				{
3485
-					$xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcString']);
3486
-				}
3487
-				else if (in_array('null_extension', $options))
3488
-				{
3489
-					$xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcNull']);
3490
-				}
3491
-				else
3492
-				{
3493
-					$xmlrpc_val = new xmlrpcval();
3494
-				}
3495
-				break;
3496
-			case 'resource':
3497
-				if (in_array('extension_api', $options))
3498
-				{
3499
-					$xmlrpc_val = new xmlrpcval((int)$php_val, $GLOBALS['xmlrpcInt']);
3500
-				}
3501
-				else
3502
-				{
3503
-					$xmlrpc_val = new xmlrpcval();
3504
-				}
3505
-			// catch "user function", "unknown type"
3506
-			default:
3507
-				// giancarlo pinerolo <[email protected]>
3508
-				// it has to return
3509
-				// an empty object in case, not a boolean.
3510
-				$xmlrpc_val = new xmlrpcval();
3511
-				break;
3512
-			}
3513
-			return $xmlrpc_val;
3514
-	}
3515
-
3516
-	/**
3517
-	* Convert the xml representation of a method response, method request or single
3518
-	* xmlrpc value into the appropriate object (a.k.a. deserialize)
3519
-	* @param string $xml_val
3520
-	* @param array $options
3521
-	* @return mixed false on error, or an instance of either xmlrpcval, xmlrpcmsg or xmlrpcresp
3522
-	*/
3523
-	function php_xmlrpc_decode_xml($xml_val, $options=array())
3524
-	{
3525
-		$GLOBALS['_xh'] = array();
3526
-		$GLOBALS['_xh']['ac'] = '';
3527
-		$GLOBALS['_xh']['stack'] = array();
3528
-		$GLOBALS['_xh']['valuestack'] = array();
3529
-		$GLOBALS['_xh']['params'] = array();
3530
-		$GLOBALS['_xh']['pt'] = array();
3531
-		$GLOBALS['_xh']['isf'] = 0;
3532
-		$GLOBALS['_xh']['isf_reason'] = '';
3533
-		$GLOBALS['_xh']['method'] = false;
3534
-		$GLOBALS['_xh']['rt'] = '';
3535
-		/// @todo 'guestimate' encoding
3536
-		$parser = xml_parser_create();
3537
-		xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
3538
-		// What if internal encoding is not in one of the 3 allowed?
3539
-		// we use the broadest one, ie. utf8!
3540
-		if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
3541
-		{
3542
-			xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
3543
-		}
3544
-		else
3545
-		{
3546
-			xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
3547
-		}
3548
-		xml_set_element_handler($parser, 'xmlrpc_se_any', 'xmlrpc_ee');
3549
-		xml_set_character_data_handler($parser, 'xmlrpc_cd');
3550
-		xml_set_default_handler($parser, 'xmlrpc_dh');
3551
-		if(!xml_parse($parser, $xml_val, 1))
3552
-		{
3553
-			$errstr = sprintf('XML error: %s at line %d, column %d',
3554
-						xml_error_string(xml_get_error_code($parser)),
3555
-						xml_get_current_line_number($parser), xml_get_current_column_number($parser));
3556
-			error_log($errstr);
3557
-			xml_parser_free($parser);
3558
-			return false;
3559
-		}
3560
-		xml_parser_free($parser);
3561
-		if ($GLOBALS['_xh']['isf'] > 1) // test that $GLOBALS['_xh']['value'] is an obj, too???
3562
-		{
3563
-			error_log($GLOBALS['_xh']['isf_reason']);
3564
-			return false;
3565
-		}
3566
-		switch ($GLOBALS['_xh']['rt'])
3567
-		{
3568
-			case 'methodresponse':
3569
-				$v =& $GLOBALS['_xh']['value'];
3570
-				if ($GLOBALS['_xh']['isf'] == 1)
3571
-				{
3572
-					$vc = $v->structmem('faultCode');
3573
-					$vs = $v->structmem('faultString');
3574
-					$r = new xmlrpcresp(0, $vc->scalarval(), $vs->scalarval());
3575
-				}
3576
-				else
3577
-				{
3578
-					$r = new xmlrpcresp($v);
3579
-				}
3580
-				return $r;
3581
-			case 'methodcall':
3582
-				$m = new xmlrpcmsg($GLOBALS['_xh']['method']);
3583
-				for($i=0; $i < count($GLOBALS['_xh']['params']); $i++)
3584
-				{
3585
-					$m->addParam($GLOBALS['_xh']['params'][$i]);
3586
-				}
3587
-				return $m;
3588
-			case 'value':
3589
-				return $GLOBALS['_xh']['value'];
3590
-			default:
3591
-				return false;
3592
-		}
3593
-	}
3594
-
3595
-	/**
3596
-	* decode a string that is encoded w/ "chunked" transfer encoding
3597
-	* as defined in rfc2068 par. 19.4.6
3598
-	* code shamelessly stolen from nusoap library by Dietrich Ayala
3599
-	*
3600
-	* @param string $buffer the string to be decoded
3601
-	* @return string
3602
-	*/
3603
-	function decode_chunked($buffer)
3604
-	{
3605
-		// length := 0
3606
-		$length = 0;
3607
-		$new = '';
3608
-
3609
-		// read chunk-size, chunk-extension (if any) and crlf
3610
-		// get the position of the linebreak
3611
-		$chunkend = strpos($buffer,"\r\n") + 2;
3612
-		$temp = substr($buffer,0,$chunkend);
3613
-		$chunk_size = hexdec( trim($temp) );
3614
-		$chunkstart = $chunkend;
3615
-		while($chunk_size > 0)
3616
-		{
3617
-			$chunkend = strpos($buffer, "\r\n", $chunkstart + $chunk_size);
3618
-
3619
-			// just in case we got a broken connection
3620
-			if($chunkend == false)
3621
-			{
3622
-				$chunk = substr($buffer,$chunkstart);
3623
-				// append chunk-data to entity-body
3624
-				$new .= $chunk;
3625
-				$length += strlen($chunk);
3626
-				break;
3627
-			}
3628
-
3629
-			// read chunk-data and crlf
3630
-			$chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3631
-			// append chunk-data to entity-body
3632
-			$new .= $chunk;
3633
-			// length := length + chunk-size
3634
-			$length += strlen($chunk);
3635
-			// read chunk-size and crlf
3636
-			$chunkstart = $chunkend + 2;
3637
-
3638
-			$chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
3639
-			if($chunkend == false)
3640
-			{
3641
-				break; //just in case we got a broken connection
3642
-			}
3643
-			$temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3644
-			$chunk_size = hexdec( trim($temp) );
3645
-			$chunkstart = $chunkend;
3646
-		}
3647
-		return $new;
3648
-	}
3649
-
3650
-	/**
3651
-	* xml charset encoding guessing helper function.
3652
-	* Tries to determine the charset encoding of an XML chunk received over HTTP.
3653
-	* NB: according to the spec (RFC 3023), if text/xml content-type is received over HTTP without a content-type,
3654
-	* we SHOULD assume it is strictly US-ASCII. But we try to be more tolerant of unconforming (legacy?) clients/servers,
3655
-	* which will be most probably using UTF-8 anyway...
3656
-	*
3657
-	* @param string $httpheaders the http Content-type header
3658
-	* @param string $xmlchunk xml content buffer
3659
-	* @param string $encoding_prefs comma separated list of character encodings to be used as default (when mb extension is enabled)
3660
-	*
3661
-	* @todo explore usage of mb_http_input(): does it detect http headers + post data? if so, use it instead of hand-detection!!!
3662
-	*/
3663
-	function guess_encoding($httpheader='', $xmlchunk='', $encoding_prefs=null)
3664
-	{
3665
-		// discussion: see http://www.yale.edu/pclt/encoding/
3666
-		// 1 - test if encoding is specified in HTTP HEADERS
3667
-
3668
-		//Details:
3669
-		// LWS:           (\13\10)?( |\t)+
3670
-		// token:         (any char but excluded stuff)+
3671
-		// quoted string: " (any char but double quotes and cointrol chars)* "
3672
-		// header:        Content-type = ...; charset=value(; ...)*
3673
-		//   where value is of type token, no LWS allowed between 'charset' and value
3674
-		// Note: we do not check for invalid chars in VALUE:
3675
-		//   this had better be done using pure ereg as below
3676
-		// Note 2: we might be removing whitespace/tabs that ought to be left in if
3677
-		//   the received charset is a quoted string. But nobody uses such charset names...
3678
-
3679
-		/// @todo this test will pass if ANY header has charset specification, not only Content-Type. Fix it?
3680
-		$matches = array();
3681
-		if(preg_match('/;\s*charset\s*=([^;]+)/i', $httpheader, $matches))
3682
-		{
3683
-			return strtoupper(trim($matches[1], " \t\""));
3684
-		}
3685
-
3686
-		// 2 - scan the first bytes of the data for a UTF-16 (or other) BOM pattern
3687
-		//     (source: http://www.w3.org/TR/2000/REC-xml-20001006)
3688
-		//     NOTE: actually, according to the spec, even if we find the BOM and determine
3689
-		//     an encoding, we should check if there is an encoding specified
3690
-		//     in the xml declaration, and verify if they match.
3691
-		/// @todo implement check as described above?
3692
-		/// @todo implement check for first bytes of string even without a BOM? (It sure looks harder than for cases WITH a BOM)
3693
-		if(preg_match('/^(\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\x00\x00\xFF\xFE|\xFE\xFF\x00\x00)/', $xmlchunk))
3694
-		{
3695
-			return 'UCS-4';
3696
-		}
3697
-		elseif(preg_match('/^(\xFE\xFF|\xFF\xFE)/', $xmlchunk))
3698
-		{
3699
-			return 'UTF-16';
3700
-		}
3701
-		elseif(preg_match('/^(\xEF\xBB\xBF)/', $xmlchunk))
3702
-		{
3703
-			return 'UTF-8';
3704
-		}
3705
-
3706
-		// 3 - test if encoding is specified in the xml declaration
3707
-		// Details:
3708
-		// SPACE:         (#x20 | #x9 | #xD | #xA)+ === [ \x9\xD\xA]+
3709
-		// EQ:            SPACE?=SPACE? === [ \x9\xD\xA]*=[ \x9\xD\xA]*
3710
-		if (preg_match('/^<\?xml\s+version\s*=\s*'. "((?:\"[a-zA-Z0-9_.:-]+\")|(?:'[a-zA-Z0-9_.:-]+'))".
3711
-			'\s+encoding\s*=\s*' . "((?:\"[A-Za-z][A-Za-z0-9._-]*\")|(?:'[A-Za-z][A-Za-z0-9._-]*'))/",
3712
-			$xmlchunk, $matches))
3713
-		{
3714
-			return strtoupper(substr($matches[2], 1, -1));
3715
-		}
3716
-
3717
-		// 4 - if mbstring is available, let it do the guesswork
3718
-		// NB: we favour finding an encoding that is compatible with what we can process
3719
-		if(extension_loaded('mbstring'))
3720
-		{
3721
-			if($encoding_prefs)
3722
-			{
3723
-				$enc = mb_detect_encoding($xmlchunk, $encoding_prefs);
3724
-			}
3725
-			else
3726
-			{
3727
-				$enc = mb_detect_encoding($xmlchunk);
3728
-			}
3729
-			// NB: mb_detect likes to call it ascii, xml parser likes to call it US_ASCII...
3730
-			// IANA also likes better US-ASCII, so go with it
3731
-			if($enc == 'ASCII')
3732
-			{
3733
-				$enc = 'US-'.$enc;
3734
-			}
3735
-			return $enc;
3736
-		}
3737
-		else
3738
-		{
3739
-			// no encoding specified: as per HTTP1.1 assume it is iso-8859-1?
3740
-			// Both RFC 2616 (HTTP 1.1) and 1945 (HTTP 1.0) clearly state that for text/xxx content types
3741
-			// this should be the standard. And we should be getting text/xml as request and response.
3742
-			// BUT we have to be backward compatible with the lib, which always used UTF-8 as default...
3743
-			return $GLOBALS['xmlrpc_defencoding'];
3744
-		}
3745
-	}
3746
-
3747
-	/**
3748
-	* Checks if a given charset encoding is present in a list of encodings or
3749
-	* if it is a valid subset of any encoding in the list
3750
-	* @param string $encoding charset to be tested
3751
-	* @param mixed $validlist comma separated list of valid charsets (or array of charsets)
3752
-	*/
3753
-	function is_valid_charset($encoding, $validlist)
3754
-	{
3755
-		$charset_supersets = array(
3756
-			'US-ASCII' => array ('ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
3757
-				'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8',
3758
-				'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-11', 'ISO-8859-12',
3759
-				'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'UTF-8',
3760
-				'EUC-JP', 'EUC-', 'EUC-KR', 'EUC-CN')
3761
-		);
3762
-		if (is_string($validlist))
3763
-			$validlist = explode(',', $validlist);
3764
-		if (@in_array(strtoupper($encoding), $validlist))
3765
-			return true;
3766
-		else
3767
-		{
3768
-			if (array_key_exists($encoding, $charset_supersets))
3769
-				foreach ($validlist as $allowed)
3770
-					if (in_array($allowed, $charset_supersets[$encoding]))
3771
-						return true;
3772
-				return false;
3773
-		}
3774
-	}
2768
+            }
2769
+        }
2770
+
2771
+        /**
2772
+         * Add a single php value to an (unitialized) xmlrpcval
2773
+         * @param mixed $val
2774
+         * @param string $type
2775
+         * @return int 1 or 0 on failure
2776
+         */
2777
+        function addScalar($val, $type='string')
2778
+        {
2779
+            $typeof=@$GLOBALS['xmlrpcTypes'][$type];
2780
+            if($typeof!=1)
2781
+            {
2782
+                error_log("XML-RPC: ".__METHOD__.": not a scalar type ($type)");
2783
+                return 0;
2784
+            }
2785
+
2786
+            // coerce booleans into correct values
2787
+            // NB: we should either do it for datetimes, integers and doubles, too,
2788
+            // or just plain remove this check, implemented on booleans only...
2789
+            if($type==$GLOBALS['xmlrpcBoolean'])
2790
+            {
2791
+                if(strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
2792
+                {
2793
+                    $val=true;
2794
+                }
2795
+                else
2796
+                {
2797
+                    $val=false;
2798
+                }
2799
+            }
2800
+
2801
+            switch($this->mytype)
2802
+            {
2803
+                case 1:
2804
+                    error_log('XML-RPC: '.__METHOD__.': scalar xmlrpcval can have only one value');
2805
+                    return 0;
2806
+                case 3:
2807
+                    error_log('XML-RPC: '.__METHOD__.': cannot add anonymous scalar to struct xmlrpcval');
2808
+                    return 0;
2809
+                case 2:
2810
+                    // we're adding a scalar value to an array here
2811
+                    //$ar=$this->me['array'];
2812
+                    //$ar[]=new xmlrpcval($val, $type);
2813
+                    //$this->me['array']=$ar;
2814
+                    // Faster (?) avoid all the costly array-copy-by-val done here...
2815
+                    $this->me['array'][]=new xmlrpcval($val, $type);
2816
+                    return 1;
2817
+                default:
2818
+                    // a scalar, so set the value and remember we're scalar
2819
+                    $this->me[$type]=$val;
2820
+                    $this->mytype=$typeof;
2821
+                    return 1;
2822
+            }
2823
+        }
2824
+
2825
+        /**
2826
+         * Add an array of xmlrpcval objects to an xmlrpcval
2827
+         * @param array $vals
2828
+         * @return int 1 or 0 on failure
2829
+         * @access public
2830
+         *
2831
+         * @todo add some checking for $vals to be an array of xmlrpcvals?
2832
+         */
2833
+        function addArray($vals)
2834
+        {
2835
+            if($this->mytype==0)
2836
+            {
2837
+                $this->mytype=$GLOBALS['xmlrpcTypes']['array'];
2838
+                $this->me['array']=$vals;
2839
+                return 1;
2840
+            }
2841
+            elseif($this->mytype==2)
2842
+            {
2843
+                // we're adding to an array here
2844
+                $this->me['array'] = array_merge($this->me['array'], $vals);
2845
+                return 1;
2846
+            }
2847
+            else
2848
+            {
2849
+                error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
2850
+                return 0;
2851
+            }
2852
+        }
2853
+
2854
+        /**
2855
+         * Add an array of named xmlrpcval objects to an xmlrpcval
2856
+         * @param array $vals
2857
+         * @return int 1 or 0 on failure
2858
+         * @access public
2859
+         *
2860
+         * @todo add some checking for $vals to be an array?
2861
+         */
2862
+        function addStruct($vals)
2863
+        {
2864
+            if($this->mytype==0)
2865
+            {
2866
+                $this->mytype=$GLOBALS['xmlrpcTypes']['struct'];
2867
+                $this->me['struct']=$vals;
2868
+                return 1;
2869
+            }
2870
+            elseif($this->mytype==3)
2871
+            {
2872
+                // we're adding to a struct here
2873
+                $this->me['struct'] = array_merge($this->me['struct'], $vals);
2874
+                return 1;
2875
+            }
2876
+            else
2877
+            {
2878
+                error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
2879
+                return 0;
2880
+            }
2881
+        }
2882
+
2883
+        // poor man's version of print_r ???
2884
+        // DEPRECATED!
2885
+        function dump($ar)
2886
+        {
2887
+            foreach($ar as $key => $val)
2888
+            {
2889
+                echo "$key => $val<br />";
2890
+                if($key == 'array')
2891
+                {
2892
+                    while(list($key2, $val2) = each($val))
2893
+                    {
2894
+                        echo "-- $key2 => $val2<br />";
2895
+                    }
2896
+                }
2897
+            }
2898
+        }
2899
+
2900
+        /**
2901
+         * Returns a string containing "struct", "array" or "scalar" describing the base type of the value
2902
+         * @return string
2903
+         * @access public
2904
+         */
2905
+        function kindOf()
2906
+        {
2907
+            switch($this->mytype)
2908
+            {
2909
+                case 3:
2910
+                    return 'struct';
2911
+                    break;
2912
+                case 2:
2913
+                    return 'array';
2914
+                    break;
2915
+                case 1:
2916
+                    return 'scalar';
2917
+                    break;
2918
+                default:
2919
+                    return 'undef';
2920
+            }
2921
+        }
2922
+
2923
+        /**
2924
+         * @access private
2925
+         */
2926
+        function serializedata($typ, $val, $charset_encoding='')
2927
+        {
2928
+            $rs='';
2929
+            switch(@$GLOBALS['xmlrpcTypes'][$typ])
2930
+            {
2931
+                case 1:
2932
+                    switch($typ)
2933
+                    {
2934
+                        case $GLOBALS['xmlrpcBase64']:
2935
+                            $rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
2936
+                            break;
2937
+                        case $GLOBALS['xmlrpcBoolean']:
2938
+                            $rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
2939
+                            break;
2940
+                        case $GLOBALS['xmlrpcString']:
2941
+                            // G. Giunta 2005/2/13: do NOT use htmlentities, since
2942
+                            // it will produce named html entities, which are invalid xml
2943
+                            $rs.="<${typ}>" . xmlrpc_encode_entitites($val, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding). "</${typ}>";
2944
+                            break;
2945
+                        case $GLOBALS['xmlrpcInt']:
2946
+                        case $GLOBALS['xmlrpcI4']:
2947
+                            $rs.="<${typ}>".(int)$val."</${typ}>";
2948
+                            break;
2949
+                        case $GLOBALS['xmlrpcDouble']:
2950
+                            // avoid using standard conversion of float to string because it is locale-dependent,
2951
+                            // and also because the xmlrpc spec forbids exponential notation.
2952
+                            // sprintf('%F') could be most likely ok but it fails eg. on 2e-14.
2953
+                            // The code below tries its best at keeping max precision while avoiding exp notation,
2954
+                            // but there is of course no limit in the number of decimal places to be used...
2955
+                            $rs.="<${typ}>".preg_replace('/\\.?0+$/','',number_format((double)$val, 128, '.', ''))."</${typ}>";
2956
+                            break;
2957
+                        case $GLOBALS['xmlrpcDateTime']:
2958
+                            if (is_string($val))
2959
+                            {
2960
+                                $rs.="<${typ}>${val}</${typ}>";
2961
+                            }
2962
+                            else if(is_a($val, 'DateTime'))
2963
+                            {
2964
+                                $rs.="<${typ}>".$val->format('Ymd\TH:i:s')."</${typ}>";
2965
+                            }
2966
+                            else if(is_int($val))
2967
+                            {
2968
+                                $rs.="<${typ}>".strftime("%Y%m%dT%H:%M:%S", $val)."</${typ}>";
2969
+                            }
2970
+                            else
2971
+                            {
2972
+                                // not really a good idea here: but what shall we output anyway? left for backward compat...
2973
+                                $rs.="<${typ}>${val}</${typ}>";
2974
+                            }
2975
+                            break;
2976
+                        case $GLOBALS['xmlrpcNull']:
2977
+                            if ($GLOBALS['xmlrpc_null_apache_encoding'])
2978
+                            {
2979
+                                $rs.="<ex:nil/>";
2980
+                            }
2981
+                            else
2982
+                            {
2983
+                                $rs.="<nil/>";
2984
+                            }
2985
+                            break;
2986
+                        default:
2987
+                            // no standard type value should arrive here, but provide a possibility
2988
+                            // for xmlrpcvals of unknown type...
2989
+                            $rs.="<${typ}>${val}</${typ}>";
2990
+                    }
2991
+                    break;
2992
+                case 3:
2993
+                    // struct
2994
+                    if ($this->_php_class)
2995
+                    {
2996
+                        $rs.='<struct php_class="' . $this->_php_class . "\">\n";
2997
+                    }
2998
+                    else
2999
+                    {
3000
+                        $rs.="<struct>\n";
3001
+                    }
3002
+                    foreach($val as $key2 => $val2)
3003
+                    {
3004
+                        $rs.='<member><name>'.xmlrpc_encode_entitites($key2, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding)."</name>\n";
3005
+                        //$rs.=$this->serializeval($val2);
3006
+                        $rs.=$val2->serialize($charset_encoding);
3007
+                        $rs.="</member>\n";
3008
+                    }
3009
+                    $rs.='</struct>';
3010
+                    break;
3011
+                case 2:
3012
+                    // array
3013
+                    $rs.="<array>\n<data>\n";
3014
+                    for($i=0; $i<count($val); $i++)
3015
+                    {
3016
+                        //$rs.=$this->serializeval($val[$i]);
3017
+                        $rs.=$val[$i]->serialize($charset_encoding);
3018
+                    }
3019
+                    $rs.="</data>\n</array>";
3020
+                    break;
3021
+                default:
3022
+                    break;
3023
+            }
3024
+            return $rs;
3025
+        }
3026
+
3027
+        /**
3028
+         * Returns xml representation of the value. XML prologue not included
3029
+         * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
3030
+         * @return string
3031
+         * @access public
3032
+         */
3033
+        function serialize($charset_encoding='')
3034
+        {
3035
+            // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3036
+            //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3037
+            //{
3038
+                reset($this->me);
3039
+                list($typ, $val) = each($this->me);
3040
+                return '<value>' . $this->serializedata($typ, $val, $charset_encoding) . "</value>\n";
3041
+            //}
3042
+        }
3043
+
3044
+        // DEPRECATED
3045
+        function serializeval($o)
3046
+        {
3047
+            // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3048
+            //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3049
+            //{
3050
+                $ar=$o->me;
3051
+                reset($ar);
3052
+                list($typ, $val) = each($ar);
3053
+                return '<value>' . $this->serializedata($typ, $val) . "</value>\n";
3054
+            //}
3055
+        }
3056
+
3057
+        /**
3058
+         * Checks wheter a struct member with a given name is present.
3059
+         * Works only on xmlrpcvals of type struct.
3060
+         * @param string $m the name of the struct member to be looked up
3061
+         * @return boolean
3062
+         * @access public
3063
+         */
3064
+        function structmemexists($m)
3065
+        {
3066
+            return array_key_exists($m, $this->me['struct']);
3067
+        }
3068
+
3069
+        /**
3070
+         * Returns the value of a given struct member (an xmlrpcval object in itself).
3071
+         * Will raise a php warning if struct member of given name does not exist
3072
+         * @param string $m the name of the struct member to be looked up
3073
+         * @return xmlrpcval
3074
+         * @access public
3075
+         */
3076
+        function structmem($m)
3077
+        {
3078
+            return $this->me['struct'][$m];
3079
+        }
3080
+
3081
+        /**
3082
+         * Reset internal pointer for xmlrpcvals of type struct.
3083
+         * @access public
3084
+         */
3085
+        function structreset()
3086
+        {
3087
+            reset($this->me['struct']);
3088
+        }
3089
+
3090
+        /**
3091
+         * Return next member element for xmlrpcvals of type struct.
3092
+         * @return xmlrpcval
3093
+         * @access public
3094
+         */
3095
+        function structeach()
3096
+        {
3097
+            return each($this->me['struct']);
3098
+        }
3099
+
3100
+        // DEPRECATED! this code looks like it is very fragile and has not been fixed
3101
+        // for a long long time. Shall we remove it for 2.0?
3102
+        function getval()
3103
+        {
3104
+            // UNSTABLE
3105
+            reset($this->me);
3106
+            list($a,$b)=each($this->me);
3107
+            // contributed by I Sofer, 2001-03-24
3108
+            // add support for nested arrays to scalarval
3109
+            // i've created a new method here, so as to
3110
+            // preserve back compatibility
3111
+
3112
+            if(is_array($b))
3113
+            {
3114
+                @reset($b);
3115
+                while(list($id,$cont) = @each($b))
3116
+                {
3117
+                    $b[$id] = $cont->scalarval();
3118
+                }
3119
+            }
3120
+
3121
+            // add support for structures directly encoding php objects
3122
+            if(is_object($b))
3123
+            {
3124
+                $t = get_object_vars($b);
3125
+                @reset($t);
3126
+                while(list($id,$cont) = @each($t))
3127
+                {
3128
+                    $t[$id] = $cont->scalarval();
3129
+                }
3130
+                @reset($t);
3131
+                while(list($id,$cont) = @each($t))
3132
+                {
3133
+                    @$b->$id = $cont;
3134
+                }
3135
+            }
3136
+            // end contrib
3137
+            return $b;
3138
+        }
3139
+
3140
+        /**
3141
+         * Returns the value of a scalar xmlrpcval
3142
+         * @return mixed
3143
+         * @access public
3144
+         */
3145
+        function scalarval()
3146
+        {
3147
+            reset($this->me);
3148
+            list(,$b)=each($this->me);
3149
+            return $b;
3150
+        }
3151
+
3152
+        /**
3153
+         * Returns the type of the xmlrpcval.
3154
+         * For integers, 'int' is always returned in place of 'i4'
3155
+         * @return string
3156
+         * @access public
3157
+         */
3158
+        function scalartyp()
3159
+        {
3160
+            reset($this->me);
3161
+            list($a,)=each($this->me);
3162
+            if($a==$GLOBALS['xmlrpcI4'])
3163
+            {
3164
+                $a=$GLOBALS['xmlrpcInt'];
3165
+            }
3166
+            return $a;
3167
+        }
3168
+
3169
+        /**
3170
+         * Returns the m-th member of an xmlrpcval of struct type
3171
+         * @param integer $m the index of the value to be retrieved (zero based)
3172
+         * @return xmlrpcval
3173
+         * @access public
3174
+         */
3175
+        function arraymem($m)
3176
+        {
3177
+            return $this->me['array'][$m];
3178
+        }
3179
+
3180
+        /**
3181
+         * Returns the number of members in an xmlrpcval of array type
3182
+         * @return integer
3183
+         * @access public
3184
+         */
3185
+        function arraysize()
3186
+        {
3187
+            return count($this->me['array']);
3188
+        }
3189
+
3190
+        /**
3191
+         * Returns the number of members in an xmlrpcval of struct type
3192
+         * @return integer
3193
+         * @access public
3194
+         */
3195
+        function structsize()
3196
+        {
3197
+            return count($this->me['struct']);
3198
+        }
3199
+    }
3200
+
3201
+
3202
+    // date helpers
3203
+
3204
+    /**
3205
+     * Given a timestamp, return the corresponding ISO8601 encoded string.
3206
+     *
3207
+     * Really, timezones ought to be supported
3208
+     * but the XML-RPC spec says:
3209
+     *
3210
+     * "Don't assume a timezone. It should be specified by the server in its
3211
+     * documentation what assumptions it makes about timezones."
3212
+     *
3213
+     * These routines always assume localtime unless
3214
+     * $utc is set to 1, in which case UTC is assumed
3215
+     * and an adjustment for locale is made when encoding
3216
+     *
3217
+     * @param int $timet (timestamp)
3218
+     * @param int $utc (0 or 1)
3219
+     * @return string
3220
+     */
3221
+    function iso8601_encode($timet, $utc=0)
3222
+    {
3223
+        if(!$utc)
3224
+        {
3225
+            $t=strftime("%Y%m%dT%H:%M:%S", $timet);
3226
+        }
3227
+        else
3228
+        {
3229
+            if(function_exists('gmstrftime'))
3230
+            {
3231
+                // gmstrftime doesn't exist in some versions
3232
+                // of PHP
3233
+                $t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
3234
+            }
3235
+            else
3236
+            {
3237
+                $t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z'));
3238
+            }
3239
+        }
3240
+        return $t;
3241
+    }
3242
+
3243
+    /**
3244
+     * Given an ISO8601 date string, return a timet in the localtime, or UTC
3245
+     * @param string $idate
3246
+     * @param int $utc either 0 or 1
3247
+     * @return int (datetime)
3248
+     */
3249
+    function iso8601_decode($idate, $utc=0)
3250
+    {
3251
+        $t=0;
3252
+        if(preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $idate, $regs))
3253
+        {
3254
+            if($utc)
3255
+            {
3256
+                $t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3257
+            }
3258
+            else
3259
+            {
3260
+                $t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3261
+            }
3262
+        }
3263
+        return $t;
3264
+    }
3265
+
3266
+    /**
3267
+     * Takes an xmlrpc value in PHP xmlrpcval object format and translates it into native PHP types.
3268
+     *
3269
+     * Works with xmlrpc message objects as input, too.
3270
+     *
3271
+     * Given proper options parameter, can rebuild generic php object instances
3272
+     * (provided those have been encoded to xmlrpc format using a corresponding
3273
+     * option in php_xmlrpc_encode())
3274
+     * PLEASE NOTE that rebuilding php objects involves calling their constructor function.
3275
+     * This means that the remote communication end can decide which php code will
3276
+     * get executed on your server, leaving the door possibly open to 'php-injection'
3277
+     * style of attacks (provided you have some classes defined on your server that
3278
+     * might wreak havoc if instances are built outside an appropriate context).
3279
+     * Make sure you trust the remote server/client before eanbling this!
3280
+     *
3281
+     * @author Dan Libby ([email protected])
3282
+     *
3283
+     * @param xmlrpcval $xmlrpc_val
3284
+     * @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
3285
+     * @return mixed
3286
+     */
3287
+    function php_xmlrpc_decode($xmlrpc_val, $options=array())
3288
+    {
3289
+        switch($xmlrpc_val->kindOf())
3290
+        {
3291
+            case 'scalar':
3292
+                if (in_array('extension_api', $options))
3293
+                {
3294
+                    reset($xmlrpc_val->me);
3295
+                    list($typ,$val) = each($xmlrpc_val->me);
3296
+                    switch ($typ)
3297
+                    {
3298
+                        case 'dateTime.iso8601':
3299
+                            $xmlrpc_val->scalar = $val;
3300
+                            $xmlrpc_val->xmlrpc_type = 'datetime';
3301
+                            $xmlrpc_val->timestamp = iso8601_decode($val);
3302
+                            return $xmlrpc_val;
3303
+                        case 'base64':
3304
+                            $xmlrpc_val->scalar = $val;
3305
+                            $xmlrpc_val->type = $typ;
3306
+                            return $xmlrpc_val;
3307
+                        default:
3308
+                            return $xmlrpc_val->scalarval();
3309
+                    }
3310
+                }
3311
+                if (in_array('dates_as_objects', $options) && $xmlrpc_val->scalartyp() == 'dateTime.iso8601')
3312
+                {
3313
+                    // we return a Datetime object instead of a string
3314
+                    // since now the constructor of xmlrpcval accepts safely strings, ints and datetimes,
3315
+                    // we cater to all 3 cases here
3316
+                    $out = $xmlrpc_val->scalarval();
3317
+                    if (is_string($out))
3318
+                    {
3319
+                        $out = strtotime($out);
3320
+                    }
3321
+                    if (is_int($out))
3322
+                    {
3323
+                        $result = new Datetime();
3324
+                        $result->setTimestamp($out);
3325
+                        return $result;
3326
+                    }
3327
+                    elseif (is_a($out, 'Datetime'))
3328
+                    {
3329
+                        return $out;
3330
+                    }
3331
+                }
3332
+                return $xmlrpc_val->scalarval();
3333
+            case 'array':
3334
+                $size = $xmlrpc_val->arraysize();
3335
+                $arr = array();
3336
+                for($i = 0; $i < $size; $i++)
3337
+                {
3338
+                    $arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i), $options);
3339
+                }
3340
+                return $arr;
3341
+            case 'struct':
3342
+                $xmlrpc_val->structreset();
3343
+                // If user said so, try to rebuild php objects for specific struct vals.
3344
+                /// @todo should we raise a warning for class not found?
3345
+                // shall we check for proper subclass of xmlrpcval instead of
3346
+                // presence of _php_class to detect what we can do?
3347
+                if (in_array('decode_php_objs', $options) && $xmlrpc_val->_php_class != ''
3348
+                    && class_exists($xmlrpc_val->_php_class))
3349
+                {
3350
+                    $obj = @new $xmlrpc_val->_php_class;
3351
+                    while(list($key,$value)=$xmlrpc_val->structeach())
3352
+                    {
3353
+                        $obj->$key = php_xmlrpc_decode($value, $options);
3354
+                    }
3355
+                    return $obj;
3356
+                }
3357
+                else
3358
+                {
3359
+                    $arr = array();
3360
+                    while(list($key,$value)=$xmlrpc_val->structeach())
3361
+                    {
3362
+                        $arr[$key] = php_xmlrpc_decode($value, $options);
3363
+                    }
3364
+                    return $arr;
3365
+                }
3366
+            case 'msg':
3367
+                $paramcount = $xmlrpc_val->getNumParams();
3368
+                $arr = array();
3369
+                for($i = 0; $i < $paramcount; $i++)
3370
+                {
3371
+                    $arr[] = php_xmlrpc_decode($xmlrpc_val->getParam($i));
3372
+                }
3373
+                return $arr;
3374
+            }
3375
+    }
3376
+
3377
+    // This constant left here only for historical reasons...
3378
+    // it was used to decide if we have to define xmlrpc_encode on our own, but
3379
+    // we do not do it anymore
3380
+    if(function_exists('xmlrpc_decode'))
3381
+    {
3382
+        define('XMLRPC_EPI_ENABLED','1');
3383
+    }
3384
+    else
3385
+    {
3386
+        define('XMLRPC_EPI_ENABLED','0');
3387
+    }
3388
+
3389
+    /**
3390
+     * Takes native php types and encodes them into xmlrpc PHP object format.
3391
+     * It will not re-encode xmlrpcval objects.
3392
+     *
3393
+     * Feature creep -- could support more types via optional type argument
3394
+     * (string => datetime support has been added, ??? => base64 not yet)
3395
+     *
3396
+     * If given a proper options parameter, php object instances will be encoded
3397
+     * into 'special' xmlrpc values, that can later be decoded into php objects
3398
+     * by calling php_xmlrpc_decode() with a corresponding option
3399
+     *
3400
+     * @author Dan Libby ([email protected])
3401
+     *
3402
+     * @param mixed $php_val the value to be converted into an xmlrpcval object
3403
+     * @param array $options	can include 'encode_php_objs', 'auto_dates', 'null_extension' or 'extension_api'
3404
+     * @return xmlrpcval
3405
+     */
3406
+    function php_xmlrpc_encode($php_val, $options=array())
3407
+    {
3408
+        $type = gettype($php_val);
3409
+        switch($type)
3410
+        {
3411
+            case 'string':
3412
+                if (in_array('auto_dates', $options) && preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $php_val))
3413
+                    $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDateTime']);
3414
+                else
3415
+                    $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcString']);
3416
+                break;
3417
+            case 'integer':
3418
+                $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcInt']);
3419
+                break;
3420
+            case 'double':
3421
+                $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDouble']);
3422
+                break;
3423
+                // <G_Giunta_2001-02-29>
3424
+                // Add support for encoding/decoding of booleans, since they are supported in PHP
3425
+            case 'boolean':
3426
+                $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcBoolean']);
3427
+                break;
3428
+                // </G_Giunta_2001-02-29>
3429
+            case 'array':
3430
+                // PHP arrays can be encoded to either xmlrpc structs or arrays,
3431
+                // depending on wheter they are hashes or plain 0..n integer indexed
3432
+                // A shorter one-liner would be
3433
+                // $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
3434
+                // but execution time skyrockets!
3435
+                $j = 0;
3436
+                $arr = array();
3437
+                $ko = false;
3438
+                foreach($php_val as $key => $val)
3439
+                {
3440
+                    $arr[$key] = php_xmlrpc_encode($val, $options);
3441
+                    if(!$ko && $key !== $j)
3442
+                    {
3443
+                        $ko = true;
3444
+                    }
3445
+                    $j++;
3446
+                }
3447
+                if($ko)
3448
+                {
3449
+                    $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
3450
+                }
3451
+                else
3452
+                {
3453
+                    $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcArray']);
3454
+                }
3455
+                break;
3456
+            case 'object':
3457
+                if(is_a($php_val, 'xmlrpcval'))
3458
+                {
3459
+                    $xmlrpc_val = $php_val;
3460
+                }
3461
+                else if(is_a($php_val, 'DateTime'))
3462
+                {
3463
+                    $xmlrpc_val = new xmlrpcval($php_val->format('Ymd\TH:i:s'), $GLOBALS['xmlrpcStruct']);
3464
+                }
3465
+                else
3466
+                {
3467
+                    $arr = array();
3468
+                    reset($php_val);
3469
+                    while(list($k,$v) = each($php_val))
3470
+                    {
3471
+                        $arr[$k] = php_xmlrpc_encode($v, $options);
3472
+                    }
3473
+                    $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
3474
+                    if (in_array('encode_php_objs', $options))
3475
+                    {
3476
+                        // let's save original class name into xmlrpcval:
3477
+                        // might be useful later on...
3478
+                        $xmlrpc_val->_php_class = get_class($php_val);
3479
+                    }
3480
+                }
3481
+                break;
3482
+            case 'NULL':
3483
+                if (in_array('extension_api', $options))
3484
+                {
3485
+                    $xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcString']);
3486
+                }
3487
+                else if (in_array('null_extension', $options))
3488
+                {
3489
+                    $xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcNull']);
3490
+                }
3491
+                else
3492
+                {
3493
+                    $xmlrpc_val = new xmlrpcval();
3494
+                }
3495
+                break;
3496
+            case 'resource':
3497
+                if (in_array('extension_api', $options))
3498
+                {
3499
+                    $xmlrpc_val = new xmlrpcval((int)$php_val, $GLOBALS['xmlrpcInt']);
3500
+                }
3501
+                else
3502
+                {
3503
+                    $xmlrpc_val = new xmlrpcval();
3504
+                }
3505
+            // catch "user function", "unknown type"
3506
+            default:
3507
+                // giancarlo pinerolo <[email protected]>
3508
+                // it has to return
3509
+                // an empty object in case, not a boolean.
3510
+                $xmlrpc_val = new xmlrpcval();
3511
+                break;
3512
+            }
3513
+            return $xmlrpc_val;
3514
+    }
3515
+
3516
+    /**
3517
+     * Convert the xml representation of a method response, method request or single
3518
+     * xmlrpc value into the appropriate object (a.k.a. deserialize)
3519
+     * @param string $xml_val
3520
+     * @param array $options
3521
+     * @return mixed false on error, or an instance of either xmlrpcval, xmlrpcmsg or xmlrpcresp
3522
+     */
3523
+    function php_xmlrpc_decode_xml($xml_val, $options=array())
3524
+    {
3525
+        $GLOBALS['_xh'] = array();
3526
+        $GLOBALS['_xh']['ac'] = '';
3527
+        $GLOBALS['_xh']['stack'] = array();
3528
+        $GLOBALS['_xh']['valuestack'] = array();
3529
+        $GLOBALS['_xh']['params'] = array();
3530
+        $GLOBALS['_xh']['pt'] = array();
3531
+        $GLOBALS['_xh']['isf'] = 0;
3532
+        $GLOBALS['_xh']['isf_reason'] = '';
3533
+        $GLOBALS['_xh']['method'] = false;
3534
+        $GLOBALS['_xh']['rt'] = '';
3535
+        /// @todo 'guestimate' encoding
3536
+        $parser = xml_parser_create();
3537
+        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
3538
+        // What if internal encoding is not in one of the 3 allowed?
3539
+        // we use the broadest one, ie. utf8!
3540
+        if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
3541
+        {
3542
+            xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
3543
+        }
3544
+        else
3545
+        {
3546
+            xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
3547
+        }
3548
+        xml_set_element_handler($parser, 'xmlrpc_se_any', 'xmlrpc_ee');
3549
+        xml_set_character_data_handler($parser, 'xmlrpc_cd');
3550
+        xml_set_default_handler($parser, 'xmlrpc_dh');
3551
+        if(!xml_parse($parser, $xml_val, 1))
3552
+        {
3553
+            $errstr = sprintf('XML error: %s at line %d, column %d',
3554
+                        xml_error_string(xml_get_error_code($parser)),
3555
+                        xml_get_current_line_number($parser), xml_get_current_column_number($parser));
3556
+            error_log($errstr);
3557
+            xml_parser_free($parser);
3558
+            return false;
3559
+        }
3560
+        xml_parser_free($parser);
3561
+        if ($GLOBALS['_xh']['isf'] > 1) // test that $GLOBALS['_xh']['value'] is an obj, too???
3562
+        {
3563
+            error_log($GLOBALS['_xh']['isf_reason']);
3564
+            return false;
3565
+        }
3566
+        switch ($GLOBALS['_xh']['rt'])
3567
+        {
3568
+            case 'methodresponse':
3569
+                $v =& $GLOBALS['_xh']['value'];
3570
+                if ($GLOBALS['_xh']['isf'] == 1)
3571
+                {
3572
+                    $vc = $v->structmem('faultCode');
3573
+                    $vs = $v->structmem('faultString');
3574
+                    $r = new xmlrpcresp(0, $vc->scalarval(), $vs->scalarval());
3575
+                }
3576
+                else
3577
+                {
3578
+                    $r = new xmlrpcresp($v);
3579
+                }
3580
+                return $r;
3581
+            case 'methodcall':
3582
+                $m = new xmlrpcmsg($GLOBALS['_xh']['method']);
3583
+                for($i=0; $i < count($GLOBALS['_xh']['params']); $i++)
3584
+                {
3585
+                    $m->addParam($GLOBALS['_xh']['params'][$i]);
3586
+                }
3587
+                return $m;
3588
+            case 'value':
3589
+                return $GLOBALS['_xh']['value'];
3590
+            default:
3591
+                return false;
3592
+        }
3593
+    }
3594
+
3595
+    /**
3596
+     * decode a string that is encoded w/ "chunked" transfer encoding
3597
+     * as defined in rfc2068 par. 19.4.6
3598
+     * code shamelessly stolen from nusoap library by Dietrich Ayala
3599
+     *
3600
+     * @param string $buffer the string to be decoded
3601
+     * @return string
3602
+     */
3603
+    function decode_chunked($buffer)
3604
+    {
3605
+        // length := 0
3606
+        $length = 0;
3607
+        $new = '';
3608
+
3609
+        // read chunk-size, chunk-extension (if any) and crlf
3610
+        // get the position of the linebreak
3611
+        $chunkend = strpos($buffer,"\r\n") + 2;
3612
+        $temp = substr($buffer,0,$chunkend);
3613
+        $chunk_size = hexdec( trim($temp) );
3614
+        $chunkstart = $chunkend;
3615
+        while($chunk_size > 0)
3616
+        {
3617
+            $chunkend = strpos($buffer, "\r\n", $chunkstart + $chunk_size);
3618
+
3619
+            // just in case we got a broken connection
3620
+            if($chunkend == false)
3621
+            {
3622
+                $chunk = substr($buffer,$chunkstart);
3623
+                // append chunk-data to entity-body
3624
+                $new .= $chunk;
3625
+                $length += strlen($chunk);
3626
+                break;
3627
+            }
3628
+
3629
+            // read chunk-data and crlf
3630
+            $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3631
+            // append chunk-data to entity-body
3632
+            $new .= $chunk;
3633
+            // length := length + chunk-size
3634
+            $length += strlen($chunk);
3635
+            // read chunk-size and crlf
3636
+            $chunkstart = $chunkend + 2;
3637
+
3638
+            $chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
3639
+            if($chunkend == false)
3640
+            {
3641
+                break; //just in case we got a broken connection
3642
+            }
3643
+            $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3644
+            $chunk_size = hexdec( trim($temp) );
3645
+            $chunkstart = $chunkend;
3646
+        }
3647
+        return $new;
3648
+    }
3649
+
3650
+    /**
3651
+     * xml charset encoding guessing helper function.
3652
+     * Tries to determine the charset encoding of an XML chunk received over HTTP.
3653
+     * NB: according to the spec (RFC 3023), if text/xml content-type is received over HTTP without a content-type,
3654
+     * we SHOULD assume it is strictly US-ASCII. But we try to be more tolerant of unconforming (legacy?) clients/servers,
3655
+     * which will be most probably using UTF-8 anyway...
3656
+     *
3657
+     * @param string $httpheaders the http Content-type header
3658
+     * @param string $xmlchunk xml content buffer
3659
+     * @param string $encoding_prefs comma separated list of character encodings to be used as default (when mb extension is enabled)
3660
+     *
3661
+     * @todo explore usage of mb_http_input(): does it detect http headers + post data? if so, use it instead of hand-detection!!!
3662
+     */
3663
+    function guess_encoding($httpheader='', $xmlchunk='', $encoding_prefs=null)
3664
+    {
3665
+        // discussion: see http://www.yale.edu/pclt/encoding/
3666
+        // 1 - test if encoding is specified in HTTP HEADERS
3667
+
3668
+        //Details:
3669
+        // LWS:           (\13\10)?( |\t)+
3670
+        // token:         (any char but excluded stuff)+
3671
+        // quoted string: " (any char but double quotes and cointrol chars)* "
3672
+        // header:        Content-type = ...; charset=value(; ...)*
3673
+        //   where value is of type token, no LWS allowed between 'charset' and value
3674
+        // Note: we do not check for invalid chars in VALUE:
3675
+        //   this had better be done using pure ereg as below
3676
+        // Note 2: we might be removing whitespace/tabs that ought to be left in if
3677
+        //   the received charset is a quoted string. But nobody uses such charset names...
3678
+
3679
+        /// @todo this test will pass if ANY header has charset specification, not only Content-Type. Fix it?
3680
+        $matches = array();
3681
+        if(preg_match('/;\s*charset\s*=([^;]+)/i', $httpheader, $matches))
3682
+        {
3683
+            return strtoupper(trim($matches[1], " \t\""));
3684
+        }
3685
+
3686
+        // 2 - scan the first bytes of the data for a UTF-16 (or other) BOM pattern
3687
+        //     (source: http://www.w3.org/TR/2000/REC-xml-20001006)
3688
+        //     NOTE: actually, according to the spec, even if we find the BOM and determine
3689
+        //     an encoding, we should check if there is an encoding specified
3690
+        //     in the xml declaration, and verify if they match.
3691
+        /// @todo implement check as described above?
3692
+        /// @todo implement check for first bytes of string even without a BOM? (It sure looks harder than for cases WITH a BOM)
3693
+        if(preg_match('/^(\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\x00\x00\xFF\xFE|\xFE\xFF\x00\x00)/', $xmlchunk))
3694
+        {
3695
+            return 'UCS-4';
3696
+        }
3697
+        elseif(preg_match('/^(\xFE\xFF|\xFF\xFE)/', $xmlchunk))
3698
+        {
3699
+            return 'UTF-16';
3700
+        }
3701
+        elseif(preg_match('/^(\xEF\xBB\xBF)/', $xmlchunk))
3702
+        {
3703
+            return 'UTF-8';
3704
+        }
3705
+
3706
+        // 3 - test if encoding is specified in the xml declaration
3707
+        // Details:
3708
+        // SPACE:         (#x20 | #x9 | #xD | #xA)+ === [ \x9\xD\xA]+
3709
+        // EQ:            SPACE?=SPACE? === [ \x9\xD\xA]*=[ \x9\xD\xA]*
3710
+        if (preg_match('/^<\?xml\s+version\s*=\s*'. "((?:\"[a-zA-Z0-9_.:-]+\")|(?:'[a-zA-Z0-9_.:-]+'))".
3711
+            '\s+encoding\s*=\s*' . "((?:\"[A-Za-z][A-Za-z0-9._-]*\")|(?:'[A-Za-z][A-Za-z0-9._-]*'))/",
3712
+            $xmlchunk, $matches))
3713
+        {
3714
+            return strtoupper(substr($matches[2], 1, -1));
3715
+        }
3716
+
3717
+        // 4 - if mbstring is available, let it do the guesswork
3718
+        // NB: we favour finding an encoding that is compatible with what we can process
3719
+        if(extension_loaded('mbstring'))
3720
+        {
3721
+            if($encoding_prefs)
3722
+            {
3723
+                $enc = mb_detect_encoding($xmlchunk, $encoding_prefs);
3724
+            }
3725
+            else
3726
+            {
3727
+                $enc = mb_detect_encoding($xmlchunk);
3728
+            }
3729
+            // NB: mb_detect likes to call it ascii, xml parser likes to call it US_ASCII...
3730
+            // IANA also likes better US-ASCII, so go with it
3731
+            if($enc == 'ASCII')
3732
+            {
3733
+                $enc = 'US-'.$enc;
3734
+            }
3735
+            return $enc;
3736
+        }
3737
+        else
3738
+        {
3739
+            // no encoding specified: as per HTTP1.1 assume it is iso-8859-1?
3740
+            // Both RFC 2616 (HTTP 1.1) and 1945 (HTTP 1.0) clearly state that for text/xxx content types
3741
+            // this should be the standard. And we should be getting text/xml as request and response.
3742
+            // BUT we have to be backward compatible with the lib, which always used UTF-8 as default...
3743
+            return $GLOBALS['xmlrpc_defencoding'];
3744
+        }
3745
+    }
3746
+
3747
+    /**
3748
+     * Checks if a given charset encoding is present in a list of encodings or
3749
+     * if it is a valid subset of any encoding in the list
3750
+     * @param string $encoding charset to be tested
3751
+     * @param mixed $validlist comma separated list of valid charsets (or array of charsets)
3752
+     */
3753
+    function is_valid_charset($encoding, $validlist)
3754
+    {
3755
+        $charset_supersets = array(
3756
+            'US-ASCII' => array ('ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
3757
+                'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8',
3758
+                'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-11', 'ISO-8859-12',
3759
+                'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'UTF-8',
3760
+                'EUC-JP', 'EUC-', 'EUC-KR', 'EUC-CN')
3761
+        );
3762
+        if (is_string($validlist))
3763
+            $validlist = explode(',', $validlist);
3764
+        if (@in_array(strtoupper($encoding), $validlist))
3765
+            return true;
3766
+        else
3767
+        {
3768
+            if (array_key_exists($encoding, $charset_supersets))
3769
+                foreach ($validlist as $allowed)
3770
+                    if (in_array($allowed, $charset_supersets[$encoding]))
3771
+                        return true;
3772
+                return false;
3773
+        }
3774
+    }
3775 3775
 
3776 3776
 ?>
3777 3777
\ No newline at end of file
Please login to merge, or discard this patch.
Components/Klarna/transport/xmlrpc-3.0.0.beta/lib/xmlrpc_wrappers.inc 1 patch
Indentation   +890 added lines, -890 removed lines patch added patch discarded remove patch
@@ -15,159 +15,159 @@  discard block
 block discarded – undo
15 15
  * @todo implement self-parsing of php code for PHP <= 4
16 16
  */
17 17
 
18
-	// requires: xmlrpc.inc
19
-
20
-	/**
21
-	* Given a string defining a php type or phpxmlrpc type (loosely defined: strings
22
-	* accepted come from javadoc blocks), return corresponding phpxmlrpc type.
23
-	* NB: for php 'resource' types returns empty string, since resources cannot be serialized;
24
-	* for php class names returns 'struct', since php objects can be serialized as xmlrpc structs
25
-	* for php arrays always return array, even though arrays sometiles serialize as json structs
26
-	* @param string $phptype
27
-	* @return string
28
-	*/
29
-	function php_2_xmlrpc_type($phptype)
30
-	{
31
-		switch(strtolower($phptype))
32
-		{
33
-			case 'string':
34
-				return $GLOBALS['xmlrpcString'];
35
-			case 'integer':
36
-			case $GLOBALS['xmlrpcInt']: // 'int'
37
-			case $GLOBALS['xmlrpcI4']:
38
-				return $GLOBALS['xmlrpcInt'];
39
-			case 'double':
40
-				return $GLOBALS['xmlrpcDouble'];
41
-			case 'boolean':
42
-				return $GLOBALS['xmlrpcBoolean'];
43
-			case 'array':
44
-				return $GLOBALS['xmlrpcArray'];
45
-			case 'object':
46
-				return $GLOBALS['xmlrpcStruct'];
47
-			case $GLOBALS['xmlrpcBase64']:
48
-			case $GLOBALS['xmlrpcStruct']:
49
-				return strtolower($phptype);
50
-			case 'resource':
51
-				return '';
52
-			default:
53
-				if(class_exists($phptype))
54
-				{
55
-					return $GLOBALS['xmlrpcStruct'];
56
-				}
57
-				else
58
-				{
59
-					// unknown: might be any 'extended' xmlrpc type
60
-					return $GLOBALS['xmlrpcValue'];
61
-				}
62
-		}
63
-	}
64
-
65
-	/**
66
-	* Given a string defining a phpxmlrpc type return corresponding php type.
67
-	* @param string $xmlrpctype
68
-	* @return string
69
-	*/
70
-	function xmlrpc_2_php_type($xmlrpctype)
71
-	{
72
-		switch(strtolower($xmlrpctype))
73
-		{
74
-			case 'base64':
75
-			case 'datetime.iso8601':
76
-			case 'string':
77
-				return $GLOBALS['xmlrpcString'];
78
-			case 'int':
79
-			case 'i4':
80
-				return 'integer';
81
-			case 'struct':
82
-			case 'array':
83
-				return 'array';
84
-			case 'double':
85
-				return 'float';
86
-			case 'undefined':
87
-				return 'mixed';
88
-			case 'boolean':
89
-			case 'null':
90
-			default:
91
-				// unknown: might be any xmlrpc type
92
-				return strtolower($xmlrpctype);
93
-		}
94
-	}
95
-
96
-	/**
97
-	* Given a user-defined PHP function, create a PHP 'wrapper' function that can
98
-	* be exposed as xmlrpc method from an xmlrpc_server object and called from remote
99
-	* clients (as well as its corresponding signature info).
100
-	*
101
-	* Since php is a typeless language, to infer types of input and output parameters,
102
-	* it relies on parsing the javadoc-style comment block associated with the given
103
-	* function. Usage of xmlrpc native types (such as datetime.dateTime.iso8601 and base64)
104
-	* in the @param tag is also allowed, if you need the php function to receive/send
105
-	* data in that particular format (note that base64 encoding/decoding is transparently
106
-	* carried out by the lib, while datetime vals are passed around as strings)
107
-	*
108
-	* Known limitations:
109
-	* - requires PHP 5.0.3 +
110
-	* - only works for user-defined functions, not for PHP internal functions
111
-	*   (reflection does not support retrieving number/type of params for those)
112
-	* - functions returning php objects will generate special xmlrpc responses:
113
-	*   when the xmlrpc decoding of those responses is carried out by this same lib, using
114
-	*   the appropriate param in php_xmlrpc_decode, the php objects will be rebuilt.
115
-	*   In short: php objects can be serialized, too (except for their resource members),
116
-	*   using this function.
117
-	*   Other libs might choke on the very same xml that will be generated in this case
118
-	*   (i.e. it has a nonstandard attribute on struct element tags)
119
-	* - usage of javadoc @param tags using param names in a different order from the
120
-	*   function prototype is not considered valid (to be fixed?)
121
-	*
122
-	* Note that since rel. 2.0RC3 the preferred method to have the server call 'standard'
123
-	* php functions (ie. functions not expecting a single xmlrpcmsg obj as parameter)
124
-	* is by making use of the functions_parameters_type class member.
125
-	*
126
-	* @param string $funcname the name of the PHP user function to be exposed as xmlrpc method; array($obj, 'methodname') and array('class', 'methodname') are ok too
127
-	* @param string $newfuncname (optional) name for function to be created
128
-	* @param array $extra_options (optional) array of options for conversion. valid values include:
129
-	*        bool  return_source when true, php code w. function definition will be returned, not evaluated
130
-	*        bool  encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
131
-	*        bool  decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
132
-	*        bool  suppress_warnings  remove from produced xml any runtime warnings due to the php function being invoked
133
-	* @return false on error, or an array containing the name of the new php function,
134
-	*         its signature and docs, to be used in the server dispatch map
135
-	*
136
-	* @todo decide how to deal with params passed by ref: bomb out or allow?
137
-	* @todo finish using javadoc info to build method sig if all params are named but out of order
138
-	* @todo add a check for params of 'resource' type
139
-	* @todo add some trigger_errors / error_log when returning false?
140
-	* @todo what to do when the PHP function returns NULL? we are currently returning an empty string value...
141
-	* @todo add an option to suppress php warnings in invocation of user function, similar to server debug level 3?
142
-	* @todo if $newfuncname is empty, we could use create_user_func instead of eval, as it is possibly faster
143
-	* @todo add a verbatim_object_copy parameter to allow avoiding the same obj instance?
144
-	*/
145
-	function wrap_php_function($funcname, $newfuncname='', $extra_options=array())
146
-	{
147
-		$buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
148
-		$prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
149
-		$encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
150
-		$decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
151
-		$catch_warnings = isset($extra_options['suppress_warnings']) && $extra_options['suppress_warnings'] ? '@' : '';
152
-
153
-		if(version_compare(phpversion(), '5.0.3') == -1)
154
-		{
155
-			// up to php 5.0.3 some useful reflection methods were missing
156
-			error_log('XML-RPC: cannot not wrap php functions unless running php version bigger than 5.0.3');
157
-			return false;
158
-		}
18
+    // requires: xmlrpc.inc
19
+
20
+    /**
21
+     * Given a string defining a php type or phpxmlrpc type (loosely defined: strings
22
+     * accepted come from javadoc blocks), return corresponding phpxmlrpc type.
23
+     * NB: for php 'resource' types returns empty string, since resources cannot be serialized;
24
+     * for php class names returns 'struct', since php objects can be serialized as xmlrpc structs
25
+     * for php arrays always return array, even though arrays sometiles serialize as json structs
26
+     * @param string $phptype
27
+     * @return string
28
+     */
29
+    function php_2_xmlrpc_type($phptype)
30
+    {
31
+        switch(strtolower($phptype))
32
+        {
33
+            case 'string':
34
+                return $GLOBALS['xmlrpcString'];
35
+            case 'integer':
36
+            case $GLOBALS['xmlrpcInt']: // 'int'
37
+            case $GLOBALS['xmlrpcI4']:
38
+                return $GLOBALS['xmlrpcInt'];
39
+            case 'double':
40
+                return $GLOBALS['xmlrpcDouble'];
41
+            case 'boolean':
42
+                return $GLOBALS['xmlrpcBoolean'];
43
+            case 'array':
44
+                return $GLOBALS['xmlrpcArray'];
45
+            case 'object':
46
+                return $GLOBALS['xmlrpcStruct'];
47
+            case $GLOBALS['xmlrpcBase64']:
48
+            case $GLOBALS['xmlrpcStruct']:
49
+                return strtolower($phptype);
50
+            case 'resource':
51
+                return '';
52
+            default:
53
+                if(class_exists($phptype))
54
+                {
55
+                    return $GLOBALS['xmlrpcStruct'];
56
+                }
57
+                else
58
+                {
59
+                    // unknown: might be any 'extended' xmlrpc type
60
+                    return $GLOBALS['xmlrpcValue'];
61
+                }
62
+        }
63
+    }
64
+
65
+    /**
66
+     * Given a string defining a phpxmlrpc type return corresponding php type.
67
+     * @param string $xmlrpctype
68
+     * @return string
69
+     */
70
+    function xmlrpc_2_php_type($xmlrpctype)
71
+    {
72
+        switch(strtolower($xmlrpctype))
73
+        {
74
+            case 'base64':
75
+            case 'datetime.iso8601':
76
+            case 'string':
77
+                return $GLOBALS['xmlrpcString'];
78
+            case 'int':
79
+            case 'i4':
80
+                return 'integer';
81
+            case 'struct':
82
+            case 'array':
83
+                return 'array';
84
+            case 'double':
85
+                return 'float';
86
+            case 'undefined':
87
+                return 'mixed';
88
+            case 'boolean':
89
+            case 'null':
90
+            default:
91
+                // unknown: might be any xmlrpc type
92
+                return strtolower($xmlrpctype);
93
+        }
94
+    }
95
+
96
+    /**
97
+     * Given a user-defined PHP function, create a PHP 'wrapper' function that can
98
+     * be exposed as xmlrpc method from an xmlrpc_server object and called from remote
99
+     * clients (as well as its corresponding signature info).
100
+     *
101
+     * Since php is a typeless language, to infer types of input and output parameters,
102
+     * it relies on parsing the javadoc-style comment block associated with the given
103
+     * function. Usage of xmlrpc native types (such as datetime.dateTime.iso8601 and base64)
104
+     * in the @param tag is also allowed, if you need the php function to receive/send
105
+     * data in that particular format (note that base64 encoding/decoding is transparently
106
+     * carried out by the lib, while datetime vals are passed around as strings)
107
+     *
108
+     * Known limitations:
109
+     * - requires PHP 5.0.3 +
110
+     * - only works for user-defined functions, not for PHP internal functions
111
+     *   (reflection does not support retrieving number/type of params for those)
112
+     * - functions returning php objects will generate special xmlrpc responses:
113
+     *   when the xmlrpc decoding of those responses is carried out by this same lib, using
114
+     *   the appropriate param in php_xmlrpc_decode, the php objects will be rebuilt.
115
+     *   In short: php objects can be serialized, too (except for their resource members),
116
+     *   using this function.
117
+     *   Other libs might choke on the very same xml that will be generated in this case
118
+     *   (i.e. it has a nonstandard attribute on struct element tags)
119
+     * - usage of javadoc @param tags using param names in a different order from the
120
+     *   function prototype is not considered valid (to be fixed?)
121
+     *
122
+     * Note that since rel. 2.0RC3 the preferred method to have the server call 'standard'
123
+     * php functions (ie. functions not expecting a single xmlrpcmsg obj as parameter)
124
+     * is by making use of the functions_parameters_type class member.
125
+     *
126
+     * @param string $funcname the name of the PHP user function to be exposed as xmlrpc method; array($obj, 'methodname') and array('class', 'methodname') are ok too
127
+     * @param string $newfuncname (optional) name for function to be created
128
+     * @param array $extra_options (optional) array of options for conversion. valid values include:
129
+     *        bool  return_source when true, php code w. function definition will be returned, not evaluated
130
+     *        bool  encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
131
+     *        bool  decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
132
+     *        bool  suppress_warnings  remove from produced xml any runtime warnings due to the php function being invoked
133
+     * @return false on error, or an array containing the name of the new php function,
134
+     *         its signature and docs, to be used in the server dispatch map
135
+     *
136
+     * @todo decide how to deal with params passed by ref: bomb out or allow?
137
+     * @todo finish using javadoc info to build method sig if all params are named but out of order
138
+     * @todo add a check for params of 'resource' type
139
+     * @todo add some trigger_errors / error_log when returning false?
140
+     * @todo what to do when the PHP function returns NULL? we are currently returning an empty string value...
141
+     * @todo add an option to suppress php warnings in invocation of user function, similar to server debug level 3?
142
+     * @todo if $newfuncname is empty, we could use create_user_func instead of eval, as it is possibly faster
143
+     * @todo add a verbatim_object_copy parameter to allow avoiding the same obj instance?
144
+     */
145
+    function wrap_php_function($funcname, $newfuncname='', $extra_options=array())
146
+    {
147
+        $buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
148
+        $prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
149
+        $encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
150
+        $decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
151
+        $catch_warnings = isset($extra_options['suppress_warnings']) && $extra_options['suppress_warnings'] ? '@' : '';
152
+
153
+        if(version_compare(phpversion(), '5.0.3') == -1)
154
+        {
155
+            // up to php 5.0.3 some useful reflection methods were missing
156
+            error_log('XML-RPC: cannot not wrap php functions unless running php version bigger than 5.0.3');
157
+            return false;
158
+        }
159 159
 
160 160
         $exists = false;
161
-	    if (is_string($funcname) && strpos($funcname, '::') !== false)
162
-	    {
163
-	        $funcname = explode('::', $funcname);
164
-	    }
161
+        if (is_string($funcname) && strpos($funcname, '::') !== false)
162
+        {
163
+            $funcname = explode('::', $funcname);
164
+        }
165 165
         if(is_array($funcname))
166 166
         {
167 167
             if(count($funcname) < 2 || (!is_string($funcname[0]) && !is_object($funcname[0])))
168 168
             {
169
-    			error_log('XML-RPC: syntax for function to be wrapped is wrong');
170
-    			return false;
169
+                error_log('XML-RPC: syntax for function to be wrapped is wrong');
170
+                return false;
171 171
             }
172 172
             if(is_string($funcname[0]))
173 173
             {
@@ -180,8 +180,8 @@  discard block
 block discarded – undo
180 180
             $exists = method_exists($funcname[0], $funcname[1]);
181 181
             if (!$exists && version_compare(phpversion(), '5.1') < 0)
182 182
             {
183
-               // workaround for php 5.0: static class methods are not seen by method_exists
184
-               $exists = is_callable( $funcname );
183
+                // workaround for php 5.0: static class methods are not seen by method_exists
184
+                $exists = is_callable( $funcname );
185 185
             }
186 186
         }
187 187
         else
@@ -190,766 +190,766 @@  discard block
 block discarded – undo
190 190
             $exists = function_exists($funcname);
191 191
         }
192 192
 
193
-		if(!$exists)
194
-		{
195
-			error_log('XML-RPC: function to be wrapped is not defined: '.$plainfuncname);
196
-			return false;
197
-		}
198
-		else
199
-		{
200
-			// determine name of new php function
201
-			if($newfuncname == '')
202
-			{
203
-				if(is_array($funcname))
204
-				{
205
-    				if(is_string($funcname[0]))
206
-        				$xmlrpcfuncname = "{$prefix}_".implode('_', $funcname);
207
-    				else
208
-    					$xmlrpcfuncname = "{$prefix}_".get_class($funcname[0]) . '_' . $funcname[1];
209
-				}
210
-				else
211
-				{
212
-					$xmlrpcfuncname = "{$prefix}_$funcname";
213
-				}
214
-			}
215
-			else
216
-			{
217
-				$xmlrpcfuncname = $newfuncname;
218
-			}
219
-			while($buildit && function_exists($xmlrpcfuncname))
220
-			{
221
-				$xmlrpcfuncname .= 'x';
222
-			}
223
-
224
-			// start to introspect PHP code
225
-			if(is_array($funcname))
226
-			{
227
-    			$func = new ReflectionMethod($funcname[0], $funcname[1]);
228
-    			if($func->isPrivate())
229
-    			{
230
-    				error_log('XML-RPC: method to be wrapped is private: '.$plainfuncname);
231
-    				return false;
232
-    			}
233
-    			if($func->isProtected())
234
-    			{
235
-    				error_log('XML-RPC: method to be wrapped is protected: '.$plainfuncname);
236
-    				return false;
237
-    			}
238
-     			if($func->isConstructor())
239
-    			{
240
-    				error_log('XML-RPC: method to be wrapped is the constructor: '.$plainfuncname);
241
-    				return false;
242
-    			}
243
-			    // php 503 always says isdestructor = true...
193
+        if(!$exists)
194
+        {
195
+            error_log('XML-RPC: function to be wrapped is not defined: '.$plainfuncname);
196
+            return false;
197
+        }
198
+        else
199
+        {
200
+            // determine name of new php function
201
+            if($newfuncname == '')
202
+            {
203
+                if(is_array($funcname))
204
+                {
205
+                    if(is_string($funcname[0]))
206
+                        $xmlrpcfuncname = "{$prefix}_".implode('_', $funcname);
207
+                    else
208
+                        $xmlrpcfuncname = "{$prefix}_".get_class($funcname[0]) . '_' . $funcname[1];
209
+                }
210
+                else
211
+                {
212
+                    $xmlrpcfuncname = "{$prefix}_$funcname";
213
+                }
214
+            }
215
+            else
216
+            {
217
+                $xmlrpcfuncname = $newfuncname;
218
+            }
219
+            while($buildit && function_exists($xmlrpcfuncname))
220
+            {
221
+                $xmlrpcfuncname .= 'x';
222
+            }
223
+
224
+            // start to introspect PHP code
225
+            if(is_array($funcname))
226
+            {
227
+                $func = new ReflectionMethod($funcname[0], $funcname[1]);
228
+                if($func->isPrivate())
229
+                {
230
+                    error_log('XML-RPC: method to be wrapped is private: '.$plainfuncname);
231
+                    return false;
232
+                }
233
+                if($func->isProtected())
234
+                {
235
+                    error_log('XML-RPC: method to be wrapped is protected: '.$plainfuncname);
236
+                    return false;
237
+                }
238
+                    if($func->isConstructor())
239
+                {
240
+                    error_log('XML-RPC: method to be wrapped is the constructor: '.$plainfuncname);
241
+                    return false;
242
+                }
243
+                // php 503 always says isdestructor = true...
244 244
                 if( version_compare(phpversion(), '5.0.3') != 0 && $func->isDestructor())
245
-    			{
246
-    				error_log('XML-RPC: method to be wrapped is the destructor: '.$plainfuncname);
247
-    				return false;
248
-    			}
249
-    			if($func->isAbstract())
250
-    			{
251
-    				error_log('XML-RPC: method to be wrapped is abstract: '.$plainfuncname);
252
-    				return false;
253
-    			}
245
+                {
246
+                    error_log('XML-RPC: method to be wrapped is the destructor: '.$plainfuncname);
247
+                    return false;
248
+                }
249
+                if($func->isAbstract())
250
+                {
251
+                    error_log('XML-RPC: method to be wrapped is abstract: '.$plainfuncname);
252
+                    return false;
253
+                }
254 254
                 /// @todo add more checks for static vs. nonstatic?
255 255
             }
256
-			else
257
-			{
258
-    			$func = new ReflectionFunction($funcname);
259
-            }
260
-			if($func->isInternal())
261
-			{
262
-				// Note: from PHP 5.1.0 onward, we will possibly be able to use invokeargs
263
-				// instead of getparameters to fully reflect internal php functions ?
264
-				error_log('XML-RPC: function to be wrapped is internal: '.$plainfuncname);
265
-				return false;
266
-			}
267
-
268
-			// retrieve parameter names, types and description from javadoc comments
269
-
270
-			// function description
271
-			$desc = '';
272
-			// type of return val: by default 'any'
273
-			$returns = $GLOBALS['xmlrpcValue'];
274
-			// desc of return val
275
-			$returnsDocs = '';
276
-			// type + name of function parameters
277
-			$paramDocs = array();
278
-
279
-			$docs = $func->getDocComment();
280
-			if($docs != '')
281
-			{
282
-				$docs = explode("\n", $docs);
283
-				$i = 0;
284
-				foreach($docs as $doc)
285
-				{
286
-					$doc = trim($doc, " \r\t/*");
287
-					if(strlen($doc) && strpos($doc, '@') !== 0 && !$i)
288
-					{
289
-						if($desc)
290
-						{
291
-							$desc .= "\n";
292
-						}
293
-						$desc .= $doc;
294
-					}
295
-					elseif(strpos($doc, '@param') === 0)
296
-					{
297
-						// syntax: @param type [$name] desc
298
-						if(preg_match('/@param\s+(\S+)(\s+\$\S+)?\s+(.+)/', $doc, $matches))
299
-						{
300
-							if(strpos($matches[1], '|'))
301
-							{
302
-								//$paramDocs[$i]['type'] = explode('|', $matches[1]);
303
-								$paramDocs[$i]['type'] = 'mixed';
304
-							}
305
-							else
306
-							{
307
-								$paramDocs[$i]['type'] = $matches[1];
308
-							}
309
-							$paramDocs[$i]['name'] = trim($matches[2]);
310
-							$paramDocs[$i]['doc'] = $matches[3];
311
-						}
312
-						$i++;
313
-					}
314
-					elseif(strpos($doc, '@return') === 0)
315
-					{
316
-						// syntax: @return type desc
317
-						//$returns = preg_split('/\s+/', $doc);
318
-						if(preg_match('/@return\s+(\S+)\s+(.+)/', $doc, $matches))
319
-						{
320
-							$returns = php_2_xmlrpc_type($matches[1]);
321
-							if(isset($matches[2]))
322
-							{
323
-								$returnsDocs = $matches[2];
324
-							}
325
-						}
326
-					}
327
-				}
328
-			}
329
-
330
-			// execute introspection of actual function prototype
331
-			$params = array();
332
-			$i = 0;
333
-			foreach($func->getParameters() as $paramobj)
334
-			{
335
-				$params[$i] = array();
336
-				$params[$i]['name'] = '$'.$paramobj->getName();
337
-				$params[$i]['isoptional'] = $paramobj->isOptional();
338
-				$i++;
339
-			}
340
-
341
-
342
-			// start  building of PHP code to be eval'd
343
-			$innercode = '';
344
-			$i = 0;
345
-			$parsvariations = array();
346
-			$pars = array();
347
-			$pnum = count($params);
348
-			foreach($params as $param)
349
-			{
350
-				if (isset($paramDocs[$i]['name']) && $paramDocs[$i]['name'] && strtolower($paramDocs[$i]['name']) != strtolower($param['name']))
351
-				{
352
-					// param name from phpdoc info does not match param definition!
353
-					$paramDocs[$i]['type'] = 'mixed';
354
-				}
355
-
356
-				if($param['isoptional'])
357
-				{
358
-					// this particular parameter is optional. save as valid previous list of parameters
359
-					$innercode .= "if (\$paramcount > $i) {\n";
360
-					$parsvariations[] = $pars;
361
-				}
362
-				$innercode .= "\$p$i = \$msg->getParam($i);\n";
363
-				if ($decode_php_objects)
364
-				{
365
-					$innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i, array('decode_php_objs'));\n";
366
-				}
367
-				else
368
-				{
369
-					$innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i);\n";
370
-				}
371
-
372
-				$pars[] = "\$p$i";
373
-				$i++;
374
-				if($param['isoptional'])
375
-				{
376
-					$innercode .= "}\n";
377
-				}
378
-				if($i == $pnum)
379
-				{
380
-					// last allowed parameters combination
381
-					$parsvariations[] = $pars;
382
-				}
383
-			}
384
-
385
-			$sigs = array();
386
-			$psigs = array();
387
-			if(count($parsvariations) == 0)
388
-			{
389
-				// only known good synopsis = no parameters
390
-				$parsvariations[] = array();
391
-				$minpars = 0;
392
-			}
393
-			else
394
-			{
395
-				$minpars = count($parsvariations[0]);
396
-			}
397
-
398
-			if($minpars)
399
-			{
400
-				// add to code the check for min params number
401
-				// NB: this check needs to be done BEFORE decoding param values
402
-				$innercode = "\$paramcount = \$msg->getNumParams();\n" .
403
-				"if (\$paramcount < $minpars) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}');\n" . $innercode;
404
-			}
405
-			else
406
-			{
407
-				$innercode = "\$paramcount = \$msg->getNumParams();\n" . $innercode;
408
-			}
409
-
410
-			$innercode .= "\$np = false;\n";
411
-			// since there are no closures in php, if we are given an object instance,
256
+            else
257
+            {
258
+                $func = new ReflectionFunction($funcname);
259
+            }
260
+            if($func->isInternal())
261
+            {
262
+                // Note: from PHP 5.1.0 onward, we will possibly be able to use invokeargs
263
+                // instead of getparameters to fully reflect internal php functions ?
264
+                error_log('XML-RPC: function to be wrapped is internal: '.$plainfuncname);
265
+                return false;
266
+            }
267
+
268
+            // retrieve parameter names, types and description from javadoc comments
269
+
270
+            // function description
271
+            $desc = '';
272
+            // type of return val: by default 'any'
273
+            $returns = $GLOBALS['xmlrpcValue'];
274
+            // desc of return val
275
+            $returnsDocs = '';
276
+            // type + name of function parameters
277
+            $paramDocs = array();
278
+
279
+            $docs = $func->getDocComment();
280
+            if($docs != '')
281
+            {
282
+                $docs = explode("\n", $docs);
283
+                $i = 0;
284
+                foreach($docs as $doc)
285
+                {
286
+                    $doc = trim($doc, " \r\t/*");
287
+                    if(strlen($doc) && strpos($doc, '@') !== 0 && !$i)
288
+                    {
289
+                        if($desc)
290
+                        {
291
+                            $desc .= "\n";
292
+                        }
293
+                        $desc .= $doc;
294
+                    }
295
+                    elseif(strpos($doc, '@param') === 0)
296
+                    {
297
+                        // syntax: @param type [$name] desc
298
+                        if(preg_match('/@param\s+(\S+)(\s+\$\S+)?\s+(.+)/', $doc, $matches))
299
+                        {
300
+                            if(strpos($matches[1], '|'))
301
+                            {
302
+                                //$paramDocs[$i]['type'] = explode('|', $matches[1]);
303
+                                $paramDocs[$i]['type'] = 'mixed';
304
+                            }
305
+                            else
306
+                            {
307
+                                $paramDocs[$i]['type'] = $matches[1];
308
+                            }
309
+                            $paramDocs[$i]['name'] = trim($matches[2]);
310
+                            $paramDocs[$i]['doc'] = $matches[3];
311
+                        }
312
+                        $i++;
313
+                    }
314
+                    elseif(strpos($doc, '@return') === 0)
315
+                    {
316
+                        // syntax: @return type desc
317
+                        //$returns = preg_split('/\s+/', $doc);
318
+                        if(preg_match('/@return\s+(\S+)\s+(.+)/', $doc, $matches))
319
+                        {
320
+                            $returns = php_2_xmlrpc_type($matches[1]);
321
+                            if(isset($matches[2]))
322
+                            {
323
+                                $returnsDocs = $matches[2];
324
+                            }
325
+                        }
326
+                    }
327
+                }
328
+            }
329
+
330
+            // execute introspection of actual function prototype
331
+            $params = array();
332
+            $i = 0;
333
+            foreach($func->getParameters() as $paramobj)
334
+            {
335
+                $params[$i] = array();
336
+                $params[$i]['name'] = '$'.$paramobj->getName();
337
+                $params[$i]['isoptional'] = $paramobj->isOptional();
338
+                $i++;
339
+            }
340
+
341
+
342
+            // start  building of PHP code to be eval'd
343
+            $innercode = '';
344
+            $i = 0;
345
+            $parsvariations = array();
346
+            $pars = array();
347
+            $pnum = count($params);
348
+            foreach($params as $param)
349
+            {
350
+                if (isset($paramDocs[$i]['name']) && $paramDocs[$i]['name'] && strtolower($paramDocs[$i]['name']) != strtolower($param['name']))
351
+                {
352
+                    // param name from phpdoc info does not match param definition!
353
+                    $paramDocs[$i]['type'] = 'mixed';
354
+                }
355
+
356
+                if($param['isoptional'])
357
+                {
358
+                    // this particular parameter is optional. save as valid previous list of parameters
359
+                    $innercode .= "if (\$paramcount > $i) {\n";
360
+                    $parsvariations[] = $pars;
361
+                }
362
+                $innercode .= "\$p$i = \$msg->getParam($i);\n";
363
+                if ($decode_php_objects)
364
+                {
365
+                    $innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i, array('decode_php_objs'));\n";
366
+                }
367
+                else
368
+                {
369
+                    $innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i);\n";
370
+                }
371
+
372
+                $pars[] = "\$p$i";
373
+                $i++;
374
+                if($param['isoptional'])
375
+                {
376
+                    $innercode .= "}\n";
377
+                }
378
+                if($i == $pnum)
379
+                {
380
+                    // last allowed parameters combination
381
+                    $parsvariations[] = $pars;
382
+                }
383
+            }
384
+
385
+            $sigs = array();
386
+            $psigs = array();
387
+            if(count($parsvariations) == 0)
388
+            {
389
+                // only known good synopsis = no parameters
390
+                $parsvariations[] = array();
391
+                $minpars = 0;
392
+            }
393
+            else
394
+            {
395
+                $minpars = count($parsvariations[0]);
396
+            }
397
+
398
+            if($minpars)
399
+            {
400
+                // add to code the check for min params number
401
+                // NB: this check needs to be done BEFORE decoding param values
402
+                $innercode = "\$paramcount = \$msg->getNumParams();\n" .
403
+                "if (\$paramcount < $minpars) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}');\n" . $innercode;
404
+            }
405
+            else
406
+            {
407
+                $innercode = "\$paramcount = \$msg->getNumParams();\n" . $innercode;
408
+            }
409
+
410
+            $innercode .= "\$np = false;\n";
411
+            // since there are no closures in php, if we are given an object instance,
412 412
             // we store a pointer to it in a global var...
413
-			if ( is_array($funcname) && is_object($funcname[0]) )
414
-			{
415
-			    $GLOBALS['xmlrpcWPFObjHolder'][$xmlrpcfuncname] =& $funcname[0];
416
-			    $innercode .= "\$obj =& \$GLOBALS['xmlrpcWPFObjHolder']['$xmlrpcfuncname'];\n";
417
-			    $realfuncname = '$obj->'.$funcname[1];
418
-			}
419
-			else
420
-			{
421
-    			$realfuncname = $plainfuncname;
422
-            }
423
-			foreach($parsvariations as $pars)
424
-			{
425
-				$innercode .= "if (\$paramcount == " . count($pars) . ") \$retval = {$catch_warnings}$realfuncname(" . implode(',', $pars) . "); else\n";
426
-				// build a 'generic' signature (only use an appropriate return type)
427
-				$sig = array($returns);
428
-				$psig = array($returnsDocs);
429
-				for($i=0; $i < count($pars); $i++)
430
-				{
431
-					if (isset($paramDocs[$i]['type']))
432
-					{
433
-						$sig[] = php_2_xmlrpc_type($paramDocs[$i]['type']);
434
-					}
435
-					else
436
-					{
437
-						$sig[] = $GLOBALS['xmlrpcValue'];
438
-					}
439
-					$psig[] = isset($paramDocs[$i]['doc']) ? $paramDocs[$i]['doc'] : '';
440
-				}
441
-				$sigs[] = $sig;
442
-				$psigs[] = $psig;
443
-			}
444
-			$innercode .= "\$np = true;\n";
445
-			$innercode .= "if (\$np) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}'); else {\n";
446
-			//$innercode .= "if (\$_xmlrpcs_error_occurred) return new xmlrpcresp(0, $GLOBALS['xmlrpcerr']user, \$_xmlrpcs_error_occurred); else\n";
447
-			$innercode .= "if (is_a(\$retval, '{$prefix}resp')) return \$retval; else\n";
448
-			if($returns == $GLOBALS['xmlrpcDateTime'] || $returns == $GLOBALS['xmlrpcBase64'])
449
-			{
450
-				$innercode .= "return new {$prefix}resp(new {$prefix}val(\$retval, '$returns'));";
451
-			}
452
-			else
453
-			{
454
-				if ($encode_php_objects)
455
-					$innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval, array('encode_php_objs')));\n";
456
-				else
457
-					$innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval));\n";
458
-			}
459
-			// shall we exclude functions returning by ref?
460
-			// if($func->returnsReference())
461
-			// 	return false;
462
-			$code = "function $xmlrpcfuncname(\$msg) {\n" . $innercode . "}\n}";
463
-			//print_r($code);
464
-			if ($buildit)
465
-			{
466
-				$allOK = 0;
467
-				eval($code.'$allOK=1;');
468
-				// alternative
469
-				//$xmlrpcfuncname = create_function('$m', $innercode);
470
-
471
-				if(!$allOK)
472
-				{
473
-					error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap php function '.$plainfuncname);
474
-					return false;
475
-				}
476
-			}
477
-
478
-			/// @todo examine if $paramDocs matches $parsvariations and build array for
479
-			/// usage as method signature, plus put together a nice string for docs
480
-
481
-			$ret = array('function' => $xmlrpcfuncname, 'signature' => $sigs, 'docstring' => $desc, 'signature_docs' => $psigs, 'source' => $code);
482
-			return $ret;
483
-		}
484
-	}
413
+            if ( is_array($funcname) && is_object($funcname[0]) )
414
+            {
415
+                $GLOBALS['xmlrpcWPFObjHolder'][$xmlrpcfuncname] =& $funcname[0];
416
+                $innercode .= "\$obj =& \$GLOBALS['xmlrpcWPFObjHolder']['$xmlrpcfuncname'];\n";
417
+                $realfuncname = '$obj->'.$funcname[1];
418
+            }
419
+            else
420
+            {
421
+                $realfuncname = $plainfuncname;
422
+            }
423
+            foreach($parsvariations as $pars)
424
+            {
425
+                $innercode .= "if (\$paramcount == " . count($pars) . ") \$retval = {$catch_warnings}$realfuncname(" . implode(',', $pars) . "); else\n";
426
+                // build a 'generic' signature (only use an appropriate return type)
427
+                $sig = array($returns);
428
+                $psig = array($returnsDocs);
429
+                for($i=0; $i < count($pars); $i++)
430
+                {
431
+                    if (isset($paramDocs[$i]['type']))
432
+                    {
433
+                        $sig[] = php_2_xmlrpc_type($paramDocs[$i]['type']);
434
+                    }
435
+                    else
436
+                    {
437
+                        $sig[] = $GLOBALS['xmlrpcValue'];
438
+                    }
439
+                    $psig[] = isset($paramDocs[$i]['doc']) ? $paramDocs[$i]['doc'] : '';
440
+                }
441
+                $sigs[] = $sig;
442
+                $psigs[] = $psig;
443
+            }
444
+            $innercode .= "\$np = true;\n";
445
+            $innercode .= "if (\$np) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}'); else {\n";
446
+            //$innercode .= "if (\$_xmlrpcs_error_occurred) return new xmlrpcresp(0, $GLOBALS['xmlrpcerr']user, \$_xmlrpcs_error_occurred); else\n";
447
+            $innercode .= "if (is_a(\$retval, '{$prefix}resp')) return \$retval; else\n";
448
+            if($returns == $GLOBALS['xmlrpcDateTime'] || $returns == $GLOBALS['xmlrpcBase64'])
449
+            {
450
+                $innercode .= "return new {$prefix}resp(new {$prefix}val(\$retval, '$returns'));";
451
+            }
452
+            else
453
+            {
454
+                if ($encode_php_objects)
455
+                    $innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval, array('encode_php_objs')));\n";
456
+                else
457
+                    $innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval));\n";
458
+            }
459
+            // shall we exclude functions returning by ref?
460
+            // if($func->returnsReference())
461
+            // 	return false;
462
+            $code = "function $xmlrpcfuncname(\$msg) {\n" . $innercode . "}\n}";
463
+            //print_r($code);
464
+            if ($buildit)
465
+            {
466
+                $allOK = 0;
467
+                eval($code.'$allOK=1;');
468
+                // alternative
469
+                //$xmlrpcfuncname = create_function('$m', $innercode);
470
+
471
+                if(!$allOK)
472
+                {
473
+                    error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap php function '.$plainfuncname);
474
+                    return false;
475
+                }
476
+            }
477
+
478
+            /// @todo examine if $paramDocs matches $parsvariations and build array for
479
+            /// usage as method signature, plus put together a nice string for docs
480
+
481
+            $ret = array('function' => $xmlrpcfuncname, 'signature' => $sigs, 'docstring' => $desc, 'signature_docs' => $psigs, 'source' => $code);
482
+            return $ret;
483
+        }
484
+    }
485 485
 
486 486
     /**
487
-    * Given a user-defined PHP class or php object, map its methods onto a list of
488
-	* PHP 'wrapper' functions that can be exposed as xmlrpc methods from an xmlrpc_server
489
-	* object and called from remote clients (as well as their corresponding signature info).
490
-	*
491
-    * @param mixed $classname the name of the class whose methods are to be exposed as xmlrpc methods, or an object instance of that class
492
-    * @param array $extra_options see the docs for wrap_php_method for more options
493
-    *        string method_type 'static', 'nonstatic', 'all' and 'auto' (default); the latter will switch between static and non-static depending on wheter $classname is a class name or object instance
494
-    * @return array or false on failure
495
-    *
496
-    * @todo get_class_methods will return both static and non-static methods.
497
-    *       we have to differentiate the action, depending on wheter we recived a class name or object
498
-    */
487
+     * Given a user-defined PHP class or php object, map its methods onto a list of
488
+     * PHP 'wrapper' functions that can be exposed as xmlrpc methods from an xmlrpc_server
489
+     * object and called from remote clients (as well as their corresponding signature info).
490
+     *
491
+     * @param mixed $classname the name of the class whose methods are to be exposed as xmlrpc methods, or an object instance of that class
492
+     * @param array $extra_options see the docs for wrap_php_method for more options
493
+     *        string method_type 'static', 'nonstatic', 'all' and 'auto' (default); the latter will switch between static and non-static depending on wheter $classname is a class name or object instance
494
+     * @return array or false on failure
495
+     *
496
+     * @todo get_class_methods will return both static and non-static methods.
497
+     *       we have to differentiate the action, depending on wheter we recived a class name or object
498
+     */
499 499
     function wrap_php_class($classname, $extra_options=array())
500 500
     {
501
-		$methodfilter = isset($extra_options['method_filter']) ? $extra_options['method_filter'] : '';
502
-		$methodtype = isset($extra_options['method_type']) ? $extra_options['method_type'] : 'auto';
501
+        $methodfilter = isset($extra_options['method_filter']) ? $extra_options['method_filter'] : '';
502
+        $methodtype = isset($extra_options['method_type']) ? $extra_options['method_type'] : 'auto';
503 503
 
504 504
         if(version_compare(phpversion(), '5.0.3') == -1)
505
-		{
506
-			// up to php 5.0.3 some useful reflection methods were missing
507
-			error_log('XML-RPC: cannot not wrap php functions unless running php version bigger than 5.0.3');
508
-			return false;
509
-		}
505
+        {
506
+            // up to php 5.0.3 some useful reflection methods were missing
507
+            error_log('XML-RPC: cannot not wrap php functions unless running php version bigger than 5.0.3');
508
+            return false;
509
+        }
510 510
 
511 511
         $result = array();
512
-		$mlist = get_class_methods($classname);
513
-		foreach($mlist as $mname)
514
-		{
515
-    		if ($methodfilter == '' || preg_match($methodfilter, $mname))
516
-			{
517
-    			// echo $mlist."\n";
518
-    			$func = new ReflectionMethod($classname, $mname);
519
-    			if(!$func->isPrivate() && !$func->isProtected() && !$func->isConstructor() && !$func->isDestructor() && !$func->isAbstract())
520
-    			{
521
-        			if(($func->isStatic && ($methodtype == 'all' || $methodtype == 'static' || ($methodtype == 'auto' && is_string($classname)))) ||
522
-            			(!$func->isStatic && ($methodtype == 'all' || $methodtype == 'nonstatic' || ($methodtype == 'auto' && is_object($classname)))))
523
-            		{
512
+        $mlist = get_class_methods($classname);
513
+        foreach($mlist as $mname)
514
+        {
515
+            if ($methodfilter == '' || preg_match($methodfilter, $mname))
516
+            {
517
+                // echo $mlist."\n";
518
+                $func = new ReflectionMethod($classname, $mname);
519
+                if(!$func->isPrivate() && !$func->isProtected() && !$func->isConstructor() && !$func->isDestructor() && !$func->isAbstract())
520
+                {
521
+                    if(($func->isStatic && ($methodtype == 'all' || $methodtype == 'static' || ($methodtype == 'auto' && is_string($classname)))) ||
522
+                        (!$func->isStatic && ($methodtype == 'all' || $methodtype == 'nonstatic' || ($methodtype == 'auto' && is_object($classname)))))
523
+                    {
524 524
                         $methodwrap = wrap_php_function(array($classname, $mname), '', $extra_options);
525 525
                         if ( $methodwrap )
526 526
                         {
527 527
                             $result[$methodwrap['function']] = $methodwrap['function'];
528 528
                         }
529 529
                     }
530
-    			}
531
-			}
532
-		}
530
+                }
531
+            }
532
+        }
533 533
         return $result;
534 534
     }
535 535
 
536
-	/**
537
-	* Given an xmlrpc client and a method name, register a php wrapper function
538
-	* that will call it and return results using native php types for both
539
-	* params and results. The generated php function will return an xmlrpcresp
540
-	* oject for failed xmlrpc calls
541
-	*
542
-	* Known limitations:
543
-	* - server must support system.methodsignature for the wanted xmlrpc method
544
-	* - for methods that expose many signatures, only one can be picked (we
545
-	*   could in priciple check if signatures differ only by number of params
546
-	*   and not by type, but it would be more complication than we can spare time)
547
-	* - nested xmlrpc params: the caller of the generated php function has to
548
-	*   encode on its own the params passed to the php function if these are structs
549
-	*   or arrays whose (sub)members include values of type datetime or base64
550
-	*
551
-	* Notes: the connection properties of the given client will be copied
552
-	* and reused for the connection used during the call to the generated
553
-	* php function.
554
-	* Calling the generated php function 'might' be slow: a new xmlrpc client
555
-	* is created on every invocation and an xmlrpc-connection opened+closed.
556
-	* An extra 'debug' param is appended to param list of xmlrpc method, useful
557
-	* for debugging purposes.
558
-	*
559
-	* @param xmlrpc_client $client     an xmlrpc client set up correctly to communicate with target server
560
-	* @param string        $methodname the xmlrpc method to be mapped to a php function
561
-	* @param array         $extra_options array of options that specify conversion details. valid ptions include
562
-	*        integer       signum      the index of the method signature to use in mapping (if method exposes many sigs)
563
-	*        integer       timeout     timeout (in secs) to be used when executing function/calling remote method
564
-	*        string        protocol    'http' (default), 'http11' or 'https'
565
-	*        string        new_function_name the name of php function to create. If unsepcified, lib will pick an appropriate name
566
-	*        string        return_source if true return php code w. function definition instead fo function name
567
-	*        bool          encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
568
-	*        bool          decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
569
-	*        mixed         return_on_fault a php value to be returned when the xmlrpc call fails/returns a fault response (by default the xmlrpcresp object is returned in this case). If a string is used, '%faultCode%' and '%faultString%' tokens will be substituted with actual error values
570
-	*        bool          debug        set it to 1 or 2 to see debug results of querying server for method synopsis
571
-	* @return string                   the name of the generated php function (or false) - OR AN ARRAY...
572
-	*/
573
-	function wrap_xmlrpc_method($client, $methodname, $extra_options=0, $timeout=0, $protocol='', $newfuncname='')
574
-	{
575
-		// mind numbing: let caller use sane calling convention (as per javadoc, 3 params),
576
-		// OR the 2.0 calling convention (no options) - we really love backward compat, don't we?
577
-		if (!is_array($extra_options))
578
-		{
579
-			$signum = $extra_options;
580
-			$extra_options = array();
581
-		}
582
-		else
583
-		{
584
-			$signum = isset($extra_options['signum']) ? (int)$extra_options['signum'] : 0;
585
-			$timeout = isset($extra_options['timeout']) ? (int)$extra_options['timeout'] : 0;
586
-			$protocol = isset($extra_options['protocol']) ? $extra_options['protocol'] : '';
587
-			$newfuncname = isset($extra_options['new_function_name']) ? $extra_options['new_function_name'] : '';
588
-		}
589
-		//$encode_php_objects = in_array('encode_php_objects', $extra_options);
590
-		//$verbatim_client_copy = in_array('simple_client_copy', $extra_options) ? 1 :
591
-		//	in_array('build_class_code', $extra_options) ? 2 : 0;
592
-
593
-		$encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
594
-		$decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
595
-		$simple_client_copy = isset($extra_options['simple_client_copy']) ? (int)($extra_options['simple_client_copy']) : 0;
596
-		$buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
597
-		$prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
598
-		if (isset($extra_options['return_on_fault']))
599
-		{
600
-			$decode_fault = true;
601
-			$fault_response = $extra_options['return_on_fault'];
602
-		}
603
-		else
604
-		{
605
-			$decode_fault = false;
606
-			$fault_response = '';
607
-		}
608
-		$debug = isset($extra_options['debug']) ? ($extra_options['debug']) : 0;
609
-
610
-		$msgclass = $prefix.'msg';
611
-		$valclass = $prefix.'val';
612
-		$decodefunc = 'php_'.$prefix.'_decode';
613
-
614
-		$msg = new $msgclass('system.methodSignature');
615
-		$msg->addparam(new $valclass($methodname));
616
-		$client->setDebug($debug);
617
-		$response =& $client->send($msg, $timeout, $protocol);
618
-		if($response->faultCode())
619
-		{
620
-			error_log('XML-RPC: could not retrieve method signature from remote server for method '.$methodname);
621
-			return false;
622
-		}
623
-		else
624
-		{
625
-			$msig = $response->value();
626
-			if ($client->return_type != 'phpvals')
627
-			{
628
-				$msig = $decodefunc($msig);
629
-			}
630
-			if(!is_array($msig) || count($msig) <= $signum)
631
-			{
632
-				error_log('XML-RPC: could not retrieve method signature nr.'.$signum.' from remote server for method '.$methodname);
633
-				return false;
634
-			}
635
-			else
636
-			{
637
-				// pick a suitable name for the new function, avoiding collisions
638
-				if($newfuncname != '')
639
-				{
640
-					$xmlrpcfuncname = $newfuncname;
641
-				}
642
-				else
643
-				{
644
-					// take care to insure that methodname is translated to valid
645
-					// php function name
646
-					$xmlrpcfuncname = $prefix.'_'.preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
647
-						array('_', ''), $methodname);
648
-				}
649
-				while($buildit && function_exists($xmlrpcfuncname))
650
-				{
651
-					$xmlrpcfuncname .= 'x';
652
-				}
653
-
654
-				$msig = $msig[$signum];
655
-				$mdesc = '';
656
-				// if in 'offline' mode, get method description too.
657
-				// in online mode, favour speed of operation
658
-				if(!$buildit)
659
-				{
660
-					$msg = new $msgclass('system.methodHelp');
661
-					$msg->addparam(new $valclass($methodname));
662
-					$response =& $client->send($msg, $timeout, $protocol);
663
-					if (!$response->faultCode())
664
-					{
665
-						$mdesc = $response->value();
666
-						if ($client->return_type != 'phpvals')
667
-						{
668
-							$mdesc = $mdesc->scalarval();
669
-						}
670
-					}
671
-				}
672
-
673
-				$results = build_remote_method_wrapper_code($client, $methodname,
674
-					$xmlrpcfuncname, $msig, $mdesc, $timeout, $protocol, $simple_client_copy,
675
-					$prefix, $decode_php_objects, $encode_php_objects, $decode_fault,
676
-					$fault_response);
677
-
678
-				//print_r($code);
679
-				if ($buildit)
680
-				{
681
-					$allOK = 0;
682
-					eval($results['source'].'$allOK=1;');
683
-					// alternative
684
-					//$xmlrpcfuncname = create_function('$m', $innercode);
685
-					if($allOK)
686
-					{
687
-						return $xmlrpcfuncname;
688
-					}
689
-					else
690
-					{
691
-						error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap remote method '.$methodname);
692
-						return false;
693
-					}
694
-				}
695
-				else
696
-				{
697
-					$results['function'] = $xmlrpcfuncname;
698
-					return $results;
699
-				}
700
-			}
701
-		}
702
-	}
703
-
704
-	/**
705
-	* Similar to wrap_xmlrpc_method, but will generate a php class that wraps
706
-	* all xmlrpc methods exposed by the remote server as own methods.
707
-	* For more details see wrap_xmlrpc_method.
708
-	* @param xmlrpc_client $client the client obj all set to query the desired server
709
-	* @param array $extra_options list of options for wrapped code
710
-	* @return mixed false on error, the name of the created class if all ok or an array with code, class name and comments (if the appropriatevoption is set in extra_options)
711
-	*/
712
-	function wrap_xmlrpc_server($client, $extra_options=array())
713
-	{
714
-		$methodfilter = isset($extra_options['method_filter']) ? $extra_options['method_filter'] : '';
715
-		//$signum = isset($extra_options['signum']) ? (int)$extra_options['signum'] : 0;
716
-		$timeout = isset($extra_options['timeout']) ? (int)$extra_options['timeout'] : 0;
717
-		$protocol = isset($extra_options['protocol']) ? $extra_options['protocol'] : '';
718
-		$newclassname = isset($extra_options['new_class_name']) ? $extra_options['new_class_name'] : '';
719
-		$encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
720
-		$decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
721
-		$verbatim_client_copy = isset($extra_options['simple_client_copy']) ? !($extra_options['simple_client_copy']) : true;
722
-		$buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
723
-		$prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
724
-
725
-		$msgclass = $prefix.'msg';
726
-		//$valclass = $prefix.'val';
727
-		$decodefunc = 'php_'.$prefix.'_decode';
728
-
729
-		$msg = new $msgclass('system.listMethods');
730
-		$response =& $client->send($msg, $timeout, $protocol);
731
-		if($response->faultCode())
732
-		{
733
-			error_log('XML-RPC: could not retrieve method list from remote server');
734
-			return false;
735
-		}
736
-		else
737
-		{
738
-			$mlist = $response->value();
739
-			if ($client->return_type != 'phpvals')
740
-			{
741
-				$mlist = $decodefunc($mlist);
742
-			}
743
-			if(!is_array($mlist) || !count($mlist))
744
-			{
745
-				error_log('XML-RPC: could not retrieve meaningful method list from remote server');
746
-				return false;
747
-			}
748
-			else
749
-			{
750
-				// pick a suitable name for the new function, avoiding collisions
751
-				if($newclassname != '')
752
-				{
753
-					$xmlrpcclassname = $newclassname;
754
-				}
755
-				else
756
-				{
757
-					$xmlrpcclassname = $prefix.'_'.preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
758
-						array('_', ''), $client->server).'_client';
759
-				}
760
-				while($buildit && class_exists($xmlrpcclassname))
761
-				{
762
-					$xmlrpcclassname .= 'x';
763
-				}
764
-
765
-				/// @todo add function setdebug() to new class, to enable/disable debugging
766
-				$source = "class $xmlrpcclassname\n{\nvar \$client;\n\n";
767
-				$source .= "function $xmlrpcclassname()\n{\n";
768
-				$source .= build_client_wrapper_code($client, $verbatim_client_copy, $prefix);
769
-				$source .= "\$this->client =& \$client;\n}\n\n";
770
-				$opts = array('simple_client_copy' => 2, 'return_source' => true,
771
-					'timeout' => $timeout, 'protocol' => $protocol,
772
-					'encode_php_objs' => $encode_php_objects, 'prefix' => $prefix,
773
-					'decode_php_objs' => $decode_php_objects
774
-					);
775
-				/// @todo build javadoc for class definition, too
776
-				foreach($mlist as $mname)
777
-				{
778
-					if ($methodfilter == '' || preg_match($methodfilter, $mname))
779
-					{
780
-						$opts['new_function_name'] = preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
781
-							array('_', ''), $mname);
782
-						$methodwrap = wrap_xmlrpc_method($client, $mname, $opts);
783
-						if ($methodwrap)
784
-						{
785
-							if (!$buildit)
786
-							{
787
-								$source .= $methodwrap['docstring'];
788
-							}
789
-							$source .= $methodwrap['source']."\n";
790
-						}
791
-						else
792
-						{
793
-							error_log('XML-RPC: will not create class method to wrap remote method '.$mname);
794
-						}
795
-					}
796
-				}
797
-				$source .= "}\n";
798
-				if ($buildit)
799
-				{
800
-					$allOK = 0;
801
-					eval($source.'$allOK=1;');
802
-					// alternative
803
-					//$xmlrpcfuncname = create_function('$m', $innercode);
804
-					if($allOK)
805
-					{
806
-						return $xmlrpcclassname;
807
-					}
808
-					else
809
-					{
810
-						error_log('XML-RPC: could not create class '.$xmlrpcclassname.' to wrap remote server '.$client->server);
811
-						return false;
812
-					}
813
-				}
814
-				else
815
-				{
816
-					return array('class' => $xmlrpcclassname, 'code' => $source, 'docstring' => '');
817
-				}
818
-			}
819
-		}
820
-	}
821
-
822
-	/**
823
-	* Given the necessary info, build php code that creates a new function to
824
-	* invoke a remote xmlrpc method.
825
-	* Take care that no full checking of input parameters is done to ensure that
826
-	* valid php code is emitted.
827
-	* Note: real spaghetti code follows...
828
-	* @access private
829
-	*/
830
-	function build_remote_method_wrapper_code($client, $methodname, $xmlrpcfuncname,
831
-		$msig, $mdesc='', $timeout=0, $protocol='', $client_copy_mode=0, $prefix='xmlrpc',
832
-		$decode_php_objects=false, $encode_php_objects=false, $decode_fault=false,
833
-		$fault_response='')
834
-	{
835
-		$code = "function $xmlrpcfuncname (";
836
-		if ($client_copy_mode < 2)
837
-		{
838
-			// client copy mode 0 or 1 == partial / full client copy in emitted code
839
-			$innercode = build_client_wrapper_code($client, $client_copy_mode, $prefix);
840
-			$innercode .= "\$client->setDebug(\$debug);\n";
841
-			$this_ = '';
842
-		}
843
-		else
844
-		{
845
-			// client copy mode 2 == no client copy in emitted code
846
-			$innercode = '';
847
-			$this_ = 'this->';
848
-		}
849
-		$innercode .= "\$msg = new {$prefix}msg('$methodname');\n";
850
-
851
-		if ($mdesc != '')
852
-		{
853
-			// take care that PHP comment is not terminated unwillingly by method description
854
-			$mdesc = "/**\n* ".str_replace('*/', '* /', $mdesc)."\n";
855
-		}
856
-		else
857
-		{
858
-			$mdesc = "/**\nFunction $xmlrpcfuncname\n";
859
-		}
860
-
861
-		// param parsing
862
-		$plist = array();
863
-		$pcount = count($msig);
864
-		for($i = 1; $i < $pcount; $i++)
865
-		{
866
-			$plist[] = "\$p$i";
867
-			$ptype = $msig[$i];
868
-			if($ptype == 'i4' || $ptype == 'int' || $ptype == 'boolean' || $ptype == 'double' ||
869
-				$ptype == 'string' || $ptype == 'dateTime.iso8601' || $ptype == 'base64' || $ptype == 'null')
870
-			{
871
-				// only build directly xmlrpcvals when type is known and scalar
872
-				$innercode .= "\$p$i = new {$prefix}val(\$p$i, '$ptype');\n";
873
-			}
874
-			else
875
-			{
876
-				if ($encode_php_objects)
877
-				{
878
-					$innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i, array('encode_php_objs'));\n";
879
-				}
880
-				else
881
-				{
882
-					$innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i);\n";
883
-				}
884
-			}
885
-			$innercode .= "\$msg->addparam(\$p$i);\n";
886
-			$mdesc .= '* @param '.xmlrpc_2_php_type($ptype)." \$p$i\n";
887
-		}
888
-		if ($client_copy_mode < 2)
889
-		{
890
-			$plist[] = '$debug=0';
891
-			$mdesc .= "* @param int \$debug when 1 (or 2) will enable debugging of the underlying {$prefix} call (defaults to 0)\n";
892
-		}
893
-		$plist = implode(', ', $plist);
894
-		$mdesc .= '* @return '.xmlrpc_2_php_type($msig[0])." (or an {$prefix}resp obj instance if call fails)\n*/\n";
895
-
896
-		$innercode .= "\$res =& \${$this_}client->send(\$msg, $timeout, '$protocol');\n";
897
-		if ($decode_fault)
898
-		{
899
-			if (is_string($fault_response) && ((strpos($fault_response, '%faultCode%') !== false) || (strpos($fault_response, '%faultString%') !== false)))
900
-			{
901
-				$respcode = "str_replace(array('%faultCode%', '%faultString%'), array(\$res->faultCode(), \$res->faultString()), '".str_replace("'", "''", $fault_response)."')";
902
-			}
903
-			else
904
-			{
905
-				$respcode = var_export($fault_response, true);
906
-			}
907
-		}
908
-		else
909
-		{
910
-			$respcode = '$res';
911
-		}
912
-		if ($decode_php_objects)
913
-		{
914
-			$innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value(), array('decode_php_objs'));";
915
-		}
916
-		else
917
-		{
918
-			$innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value());";
919
-		}
920
-
921
-		$code = $code . $plist. ") {\n" . $innercode . "\n}\n";
922
-
923
-		return array('source' => $code, 'docstring' => $mdesc);
924
-	}
925
-
926
-	/**
927
-	* Given necessary info, generate php code that will rebuild a client object
928
-	* Take care that no full checking of input parameters is done to ensure that
929
-	* valid php code is emitted.
930
-	* @access private
931
-	*/
932
-	function build_client_wrapper_code($client, $verbatim_client_copy, $prefix='xmlrpc')
933
-	{
934
-		$code = "\$client = new {$prefix}_client('".str_replace("'", "\'", $client->path).
935
-			"', '" . str_replace("'", "\'", $client->server) . "', $client->port);\n";
936
-
937
-		// copy all client fields to the client that will be generated runtime
938
-		// (this provides for future expansion or subclassing of client obj)
939
-		if ($verbatim_client_copy)
940
-		{
941
-			foreach($client as $fld => $val)
942
-			{
943
-				if($fld != 'debug' && $fld != 'return_type')
944
-				{
945
-					$val = var_export($val, true);
946
-					$code .= "\$client->$fld = $val;\n";
947
-				}
948
-			}
949
-		}
950
-		// only make sure that client always returns the correct data type
951
-		$code .= "\$client->return_type = '{$prefix}vals';\n";
952
-		//$code .= "\$client->setDebug(\$debug);\n";
953
-		return $code;
954
-	}
536
+    /**
537
+     * Given an xmlrpc client and a method name, register a php wrapper function
538
+     * that will call it and return results using native php types for both
539
+     * params and results. The generated php function will return an xmlrpcresp
540
+     * oject for failed xmlrpc calls
541
+     *
542
+     * Known limitations:
543
+     * - server must support system.methodsignature for the wanted xmlrpc method
544
+     * - for methods that expose many signatures, only one can be picked (we
545
+     *   could in priciple check if signatures differ only by number of params
546
+     *   and not by type, but it would be more complication than we can spare time)
547
+     * - nested xmlrpc params: the caller of the generated php function has to
548
+     *   encode on its own the params passed to the php function if these are structs
549
+     *   or arrays whose (sub)members include values of type datetime or base64
550
+     *
551
+     * Notes: the connection properties of the given client will be copied
552
+     * and reused for the connection used during the call to the generated
553
+     * php function.
554
+     * Calling the generated php function 'might' be slow: a new xmlrpc client
555
+     * is created on every invocation and an xmlrpc-connection opened+closed.
556
+     * An extra 'debug' param is appended to param list of xmlrpc method, useful
557
+     * for debugging purposes.
558
+     *
559
+     * @param xmlrpc_client $client     an xmlrpc client set up correctly to communicate with target server
560
+     * @param string        $methodname the xmlrpc method to be mapped to a php function
561
+     * @param array         $extra_options array of options that specify conversion details. valid ptions include
562
+     *        integer       signum      the index of the method signature to use in mapping (if method exposes many sigs)
563
+     *        integer       timeout     timeout (in secs) to be used when executing function/calling remote method
564
+     *        string        protocol    'http' (default), 'http11' or 'https'
565
+     *        string        new_function_name the name of php function to create. If unsepcified, lib will pick an appropriate name
566
+     *        string        return_source if true return php code w. function definition instead fo function name
567
+     *        bool          encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
568
+     *        bool          decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
569
+     *        mixed         return_on_fault a php value to be returned when the xmlrpc call fails/returns a fault response (by default the xmlrpcresp object is returned in this case). If a string is used, '%faultCode%' and '%faultString%' tokens will be substituted with actual error values
570
+     *        bool          debug        set it to 1 or 2 to see debug results of querying server for method synopsis
571
+     * @return string                   the name of the generated php function (or false) - OR AN ARRAY...
572
+     */
573
+    function wrap_xmlrpc_method($client, $methodname, $extra_options=0, $timeout=0, $protocol='', $newfuncname='')
574
+    {
575
+        // mind numbing: let caller use sane calling convention (as per javadoc, 3 params),
576
+        // OR the 2.0 calling convention (no options) - we really love backward compat, don't we?
577
+        if (!is_array($extra_options))
578
+        {
579
+            $signum = $extra_options;
580
+            $extra_options = array();
581
+        }
582
+        else
583
+        {
584
+            $signum = isset($extra_options['signum']) ? (int)$extra_options['signum'] : 0;
585
+            $timeout = isset($extra_options['timeout']) ? (int)$extra_options['timeout'] : 0;
586
+            $protocol = isset($extra_options['protocol']) ? $extra_options['protocol'] : '';
587
+            $newfuncname = isset($extra_options['new_function_name']) ? $extra_options['new_function_name'] : '';
588
+        }
589
+        //$encode_php_objects = in_array('encode_php_objects', $extra_options);
590
+        //$verbatim_client_copy = in_array('simple_client_copy', $extra_options) ? 1 :
591
+        //	in_array('build_class_code', $extra_options) ? 2 : 0;
592
+
593
+        $encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
594
+        $decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
595
+        $simple_client_copy = isset($extra_options['simple_client_copy']) ? (int)($extra_options['simple_client_copy']) : 0;
596
+        $buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
597
+        $prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
598
+        if (isset($extra_options['return_on_fault']))
599
+        {
600
+            $decode_fault = true;
601
+            $fault_response = $extra_options['return_on_fault'];
602
+        }
603
+        else
604
+        {
605
+            $decode_fault = false;
606
+            $fault_response = '';
607
+        }
608
+        $debug = isset($extra_options['debug']) ? ($extra_options['debug']) : 0;
609
+
610
+        $msgclass = $prefix.'msg';
611
+        $valclass = $prefix.'val';
612
+        $decodefunc = 'php_'.$prefix.'_decode';
613
+
614
+        $msg = new $msgclass('system.methodSignature');
615
+        $msg->addparam(new $valclass($methodname));
616
+        $client->setDebug($debug);
617
+        $response =& $client->send($msg, $timeout, $protocol);
618
+        if($response->faultCode())
619
+        {
620
+            error_log('XML-RPC: could not retrieve method signature from remote server for method '.$methodname);
621
+            return false;
622
+        }
623
+        else
624
+        {
625
+            $msig = $response->value();
626
+            if ($client->return_type != 'phpvals')
627
+            {
628
+                $msig = $decodefunc($msig);
629
+            }
630
+            if(!is_array($msig) || count($msig) <= $signum)
631
+            {
632
+                error_log('XML-RPC: could not retrieve method signature nr.'.$signum.' from remote server for method '.$methodname);
633
+                return false;
634
+            }
635
+            else
636
+            {
637
+                // pick a suitable name for the new function, avoiding collisions
638
+                if($newfuncname != '')
639
+                {
640
+                    $xmlrpcfuncname = $newfuncname;
641
+                }
642
+                else
643
+                {
644
+                    // take care to insure that methodname is translated to valid
645
+                    // php function name
646
+                    $xmlrpcfuncname = $prefix.'_'.preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
647
+                        array('_', ''), $methodname);
648
+                }
649
+                while($buildit && function_exists($xmlrpcfuncname))
650
+                {
651
+                    $xmlrpcfuncname .= 'x';
652
+                }
653
+
654
+                $msig = $msig[$signum];
655
+                $mdesc = '';
656
+                // if in 'offline' mode, get method description too.
657
+                // in online mode, favour speed of operation
658
+                if(!$buildit)
659
+                {
660
+                    $msg = new $msgclass('system.methodHelp');
661
+                    $msg->addparam(new $valclass($methodname));
662
+                    $response =& $client->send($msg, $timeout, $protocol);
663
+                    if (!$response->faultCode())
664
+                    {
665
+                        $mdesc = $response->value();
666
+                        if ($client->return_type != 'phpvals')
667
+                        {
668
+                            $mdesc = $mdesc->scalarval();
669
+                        }
670
+                    }
671
+                }
672
+
673
+                $results = build_remote_method_wrapper_code($client, $methodname,
674
+                    $xmlrpcfuncname, $msig, $mdesc, $timeout, $protocol, $simple_client_copy,
675
+                    $prefix, $decode_php_objects, $encode_php_objects, $decode_fault,
676
+                    $fault_response);
677
+
678
+                //print_r($code);
679
+                if ($buildit)
680
+                {
681
+                    $allOK = 0;
682
+                    eval($results['source'].'$allOK=1;');
683
+                    // alternative
684
+                    //$xmlrpcfuncname = create_function('$m', $innercode);
685
+                    if($allOK)
686
+                    {
687
+                        return $xmlrpcfuncname;
688
+                    }
689
+                    else
690
+                    {
691
+                        error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap remote method '.$methodname);
692
+                        return false;
693
+                    }
694
+                }
695
+                else
696
+                {
697
+                    $results['function'] = $xmlrpcfuncname;
698
+                    return $results;
699
+                }
700
+            }
701
+        }
702
+    }
703
+
704
+    /**
705
+     * Similar to wrap_xmlrpc_method, but will generate a php class that wraps
706
+     * all xmlrpc methods exposed by the remote server as own methods.
707
+     * For more details see wrap_xmlrpc_method.
708
+     * @param xmlrpc_client $client the client obj all set to query the desired server
709
+     * @param array $extra_options list of options for wrapped code
710
+     * @return mixed false on error, the name of the created class if all ok or an array with code, class name and comments (if the appropriatevoption is set in extra_options)
711
+     */
712
+    function wrap_xmlrpc_server($client, $extra_options=array())
713
+    {
714
+        $methodfilter = isset($extra_options['method_filter']) ? $extra_options['method_filter'] : '';
715
+        //$signum = isset($extra_options['signum']) ? (int)$extra_options['signum'] : 0;
716
+        $timeout = isset($extra_options['timeout']) ? (int)$extra_options['timeout'] : 0;
717
+        $protocol = isset($extra_options['protocol']) ? $extra_options['protocol'] : '';
718
+        $newclassname = isset($extra_options['new_class_name']) ? $extra_options['new_class_name'] : '';
719
+        $encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
720
+        $decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
721
+        $verbatim_client_copy = isset($extra_options['simple_client_copy']) ? !($extra_options['simple_client_copy']) : true;
722
+        $buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
723
+        $prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
724
+
725
+        $msgclass = $prefix.'msg';
726
+        //$valclass = $prefix.'val';
727
+        $decodefunc = 'php_'.$prefix.'_decode';
728
+
729
+        $msg = new $msgclass('system.listMethods');
730
+        $response =& $client->send($msg, $timeout, $protocol);
731
+        if($response->faultCode())
732
+        {
733
+            error_log('XML-RPC: could not retrieve method list from remote server');
734
+            return false;
735
+        }
736
+        else
737
+        {
738
+            $mlist = $response->value();
739
+            if ($client->return_type != 'phpvals')
740
+            {
741
+                $mlist = $decodefunc($mlist);
742
+            }
743
+            if(!is_array($mlist) || !count($mlist))
744
+            {
745
+                error_log('XML-RPC: could not retrieve meaningful method list from remote server');
746
+                return false;
747
+            }
748
+            else
749
+            {
750
+                // pick a suitable name for the new function, avoiding collisions
751
+                if($newclassname != '')
752
+                {
753
+                    $xmlrpcclassname = $newclassname;
754
+                }
755
+                else
756
+                {
757
+                    $xmlrpcclassname = $prefix.'_'.preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
758
+                        array('_', ''), $client->server).'_client';
759
+                }
760
+                while($buildit && class_exists($xmlrpcclassname))
761
+                {
762
+                    $xmlrpcclassname .= 'x';
763
+                }
764
+
765
+                /// @todo add function setdebug() to new class, to enable/disable debugging
766
+                $source = "class $xmlrpcclassname\n{\nvar \$client;\n\n";
767
+                $source .= "function $xmlrpcclassname()\n{\n";
768
+                $source .= build_client_wrapper_code($client, $verbatim_client_copy, $prefix);
769
+                $source .= "\$this->client =& \$client;\n}\n\n";
770
+                $opts = array('simple_client_copy' => 2, 'return_source' => true,
771
+                    'timeout' => $timeout, 'protocol' => $protocol,
772
+                    'encode_php_objs' => $encode_php_objects, 'prefix' => $prefix,
773
+                    'decode_php_objs' => $decode_php_objects
774
+                    );
775
+                /// @todo build javadoc for class definition, too
776
+                foreach($mlist as $mname)
777
+                {
778
+                    if ($methodfilter == '' || preg_match($methodfilter, $mname))
779
+                    {
780
+                        $opts['new_function_name'] = preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
781
+                            array('_', ''), $mname);
782
+                        $methodwrap = wrap_xmlrpc_method($client, $mname, $opts);
783
+                        if ($methodwrap)
784
+                        {
785
+                            if (!$buildit)
786
+                            {
787
+                                $source .= $methodwrap['docstring'];
788
+                            }
789
+                            $source .= $methodwrap['source']."\n";
790
+                        }
791
+                        else
792
+                        {
793
+                            error_log('XML-RPC: will not create class method to wrap remote method '.$mname);
794
+                        }
795
+                    }
796
+                }
797
+                $source .= "}\n";
798
+                if ($buildit)
799
+                {
800
+                    $allOK = 0;
801
+                    eval($source.'$allOK=1;');
802
+                    // alternative
803
+                    //$xmlrpcfuncname = create_function('$m', $innercode);
804
+                    if($allOK)
805
+                    {
806
+                        return $xmlrpcclassname;
807
+                    }
808
+                    else
809
+                    {
810
+                        error_log('XML-RPC: could not create class '.$xmlrpcclassname.' to wrap remote server '.$client->server);
811
+                        return false;
812
+                    }
813
+                }
814
+                else
815
+                {
816
+                    return array('class' => $xmlrpcclassname, 'code' => $source, 'docstring' => '');
817
+                }
818
+            }
819
+        }
820
+    }
821
+
822
+    /**
823
+     * Given the necessary info, build php code that creates a new function to
824
+     * invoke a remote xmlrpc method.
825
+     * Take care that no full checking of input parameters is done to ensure that
826
+     * valid php code is emitted.
827
+     * Note: real spaghetti code follows...
828
+     * @access private
829
+     */
830
+    function build_remote_method_wrapper_code($client, $methodname, $xmlrpcfuncname,
831
+        $msig, $mdesc='', $timeout=0, $protocol='', $client_copy_mode=0, $prefix='xmlrpc',
832
+        $decode_php_objects=false, $encode_php_objects=false, $decode_fault=false,
833
+        $fault_response='')
834
+    {
835
+        $code = "function $xmlrpcfuncname (";
836
+        if ($client_copy_mode < 2)
837
+        {
838
+            // client copy mode 0 or 1 == partial / full client copy in emitted code
839
+            $innercode = build_client_wrapper_code($client, $client_copy_mode, $prefix);
840
+            $innercode .= "\$client->setDebug(\$debug);\n";
841
+            $this_ = '';
842
+        }
843
+        else
844
+        {
845
+            // client copy mode 2 == no client copy in emitted code
846
+            $innercode = '';
847
+            $this_ = 'this->';
848
+        }
849
+        $innercode .= "\$msg = new {$prefix}msg('$methodname');\n";
850
+
851
+        if ($mdesc != '')
852
+        {
853
+            // take care that PHP comment is not terminated unwillingly by method description
854
+            $mdesc = "/**\n* ".str_replace('*/', '* /', $mdesc)."\n";
855
+        }
856
+        else
857
+        {
858
+            $mdesc = "/**\nFunction $xmlrpcfuncname\n";
859
+        }
860
+
861
+        // param parsing
862
+        $plist = array();
863
+        $pcount = count($msig);
864
+        for($i = 1; $i < $pcount; $i++)
865
+        {
866
+            $plist[] = "\$p$i";
867
+            $ptype = $msig[$i];
868
+            if($ptype == 'i4' || $ptype == 'int' || $ptype == 'boolean' || $ptype == 'double' ||
869
+                $ptype == 'string' || $ptype == 'dateTime.iso8601' || $ptype == 'base64' || $ptype == 'null')
870
+            {
871
+                // only build directly xmlrpcvals when type is known and scalar
872
+                $innercode .= "\$p$i = new {$prefix}val(\$p$i, '$ptype');\n";
873
+            }
874
+            else
875
+            {
876
+                if ($encode_php_objects)
877
+                {
878
+                    $innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i, array('encode_php_objs'));\n";
879
+                }
880
+                else
881
+                {
882
+                    $innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i);\n";
883
+                }
884
+            }
885
+            $innercode .= "\$msg->addparam(\$p$i);\n";
886
+            $mdesc .= '* @param '.xmlrpc_2_php_type($ptype)." \$p$i\n";
887
+        }
888
+        if ($client_copy_mode < 2)
889
+        {
890
+            $plist[] = '$debug=0';
891
+            $mdesc .= "* @param int \$debug when 1 (or 2) will enable debugging of the underlying {$prefix} call (defaults to 0)\n";
892
+        }
893
+        $plist = implode(', ', $plist);
894
+        $mdesc .= '* @return '.xmlrpc_2_php_type($msig[0])." (or an {$prefix}resp obj instance if call fails)\n*/\n";
895
+
896
+        $innercode .= "\$res =& \${$this_}client->send(\$msg, $timeout, '$protocol');\n";
897
+        if ($decode_fault)
898
+        {
899
+            if (is_string($fault_response) && ((strpos($fault_response, '%faultCode%') !== false) || (strpos($fault_response, '%faultString%') !== false)))
900
+            {
901
+                $respcode = "str_replace(array('%faultCode%', '%faultString%'), array(\$res->faultCode(), \$res->faultString()), '".str_replace("'", "''", $fault_response)."')";
902
+            }
903
+            else
904
+            {
905
+                $respcode = var_export($fault_response, true);
906
+            }
907
+        }
908
+        else
909
+        {
910
+            $respcode = '$res';
911
+        }
912
+        if ($decode_php_objects)
913
+        {
914
+            $innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value(), array('decode_php_objs'));";
915
+        }
916
+        else
917
+        {
918
+            $innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value());";
919
+        }
920
+
921
+        $code = $code . $plist. ") {\n" . $innercode . "\n}\n";
922
+
923
+        return array('source' => $code, 'docstring' => $mdesc);
924
+    }
925
+
926
+    /**
927
+     * Given necessary info, generate php code that will rebuild a client object
928
+     * Take care that no full checking of input parameters is done to ensure that
929
+     * valid php code is emitted.
930
+     * @access private
931
+     */
932
+    function build_client_wrapper_code($client, $verbatim_client_copy, $prefix='xmlrpc')
933
+    {
934
+        $code = "\$client = new {$prefix}_client('".str_replace("'", "\'", $client->path).
935
+            "', '" . str_replace("'", "\'", $client->server) . "', $client->port);\n";
936
+
937
+        // copy all client fields to the client that will be generated runtime
938
+        // (this provides for future expansion or subclassing of client obj)
939
+        if ($verbatim_client_copy)
940
+        {
941
+            foreach($client as $fld => $val)
942
+            {
943
+                if($fld != 'debug' && $fld != 'return_type')
944
+                {
945
+                    $val = var_export($val, true);
946
+                    $code .= "\$client->$fld = $val;\n";
947
+                }
948
+            }
949
+        }
950
+        // only make sure that client always returns the correct data type
951
+        $code .= "\$client->return_type = '{$prefix}vals';\n";
952
+        //$code .= "\$client->setDebug(\$debug);\n";
953
+        return $code;
954
+    }
955 955
 ?>
956 956
\ No newline at end of file
Please login to merge, or discard this patch.
Components/Klarna/transport/xmlrpc-3.0.0.beta/lib/xmlrpcs.inc 1 patch
Indentation   +1205 added lines, -1205 removed lines patch added patch discarded remove patch
@@ -35,1212 +35,1212 @@
 block discarded – undo
35 35
 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36 36
 // OF THE POSSIBILITY OF SUCH DAMAGE.
37 37
 
38
-	// XML RPC Server class
39
-	// requires: xmlrpc.inc
40
-
41
-	$GLOBALS['xmlrpcs_capabilities'] = array(
42
-		// xmlrpc spec: always supported
43
-		'xmlrpc' => new xmlrpcval(array(
44
-			'specUrl' => new xmlrpcval('http://www.xmlrpc.com/spec', 'string'),
45
-			'specVersion' => new xmlrpcval(1, 'int')
46
-		), 'struct'),
47
-		// if we support system.xxx functions, we always support multicall, too...
48
-		// Note that, as of 2006/09/17, the following URL does not respond anymore
49
-		'system.multicall' => new xmlrpcval(array(
50
-			'specUrl' => new xmlrpcval('http://www.xmlrpc.com/discuss/msgReader$1208', 'string'),
51
-			'specVersion' => new xmlrpcval(1, 'int')
52
-		), 'struct'),
53
-		// introspection: version 2! we support 'mixed', too
54
-		'introspection' => new xmlrpcval(array(
55
-			'specUrl' => new xmlrpcval('http://phpxmlrpc.sourceforge.net/doc-2/ch10.html', 'string'),
56
-			'specVersion' => new xmlrpcval(2, 'int')
57
-		), 'struct')
58
-	);
59
-
60
-	/* Functions that implement system.XXX methods of xmlrpc servers */
61
-	$_xmlrpcs_getCapabilities_sig=array(array($GLOBALS['xmlrpcStruct']));
62
-	$_xmlrpcs_getCapabilities_doc='This method lists all the capabilites that the XML-RPC server has: the (more or less standard) extensions to the xmlrpc spec that it adheres to';
63
-	$_xmlrpcs_getCapabilities_sdoc=array(array('list of capabilities, described as structs with a version number and url for the spec'));
64
-	function _xmlrpcs_getCapabilities($server, $m=null)
65
-	{
66
-		$outAr = $GLOBALS['xmlrpcs_capabilities'];
67
-		// NIL extension
68
-		if ($GLOBALS['xmlrpc_null_extension']) {
69
-			$outAr['nil'] = new xmlrpcval(array(
70
-				'specUrl' => new xmlrpcval('http://www.ontosys.com/xml-rpc/extensions.php', 'string'),
71
-				'specVersion' => new xmlrpcval(1, 'int')
72
-			), 'struct');
73
-		}
74
-		return new xmlrpcresp(new xmlrpcval($outAr, 'struct'));
75
-	}
76
-
77
-	// listMethods: signature was either a string, or nothing.
78
-	// The useless string variant has been removed
79
-	$_xmlrpcs_listMethods_sig=array(array($GLOBALS['xmlrpcArray']));
80
-	$_xmlrpcs_listMethods_doc='This method lists all the methods that the XML-RPC server knows how to dispatch';
81
-	$_xmlrpcs_listMethods_sdoc=array(array('list of method names'));
82
-	function _xmlrpcs_listMethods($server, $m=null) // if called in plain php values mode, second param is missing
83
-	{
84
-
85
-		$outAr=array();
86
-		foreach($server->dmap as $key => $val)
87
-		{
88
-			$outAr[]=new xmlrpcval($key, 'string');
89
-		}
90
-		if($server->allow_system_funcs)
91
-		{
92
-			foreach($GLOBALS['_xmlrpcs_dmap'] as $key => $val)
93
-			{
94
-				$outAr[]=new xmlrpcval($key, 'string');
95
-			}
96
-		}
97
-		return new xmlrpcresp(new xmlrpcval($outAr, 'array'));
98
-	}
99
-
100
-	$_xmlrpcs_methodSignature_sig=array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcString']));
101
-	$_xmlrpcs_methodSignature_doc='Returns an array of known signatures (an array of arrays) for the method name passed. If no signatures are known, returns a none-array (test for type != array to detect missing signature)';
102
-	$_xmlrpcs_methodSignature_sdoc=array(array('list of known signatures, each sig being an array of xmlrpc type names', 'name of method to be described'));
103
-	function _xmlrpcs_methodSignature($server, $m)
104
-	{
105
-		// let accept as parameter both an xmlrpcval or string
106
-		if (is_object($m))
107
-		{
108
-			$methName=$m->getParam(0);
109
-			$methName=$methName->scalarval();
110
-		}
111
-		else
112
-		{
113
-			$methName=$m;
114
-		}
115
-		if(strpos($methName, "system.") === 0)
116
-		{
117
-			$dmap=$GLOBALS['_xmlrpcs_dmap']; $sysCall=1;
118
-		}
119
-		else
120
-		{
121
-			$dmap=$server->dmap; $sysCall=0;
122
-		}
123
-		if(isset($dmap[$methName]))
124
-		{
125
-			if(isset($dmap[$methName]['signature']))
126
-			{
127
-				$sigs=array();
128
-				foreach($dmap[$methName]['signature'] as $inSig)
129
-				{
130
-					$cursig=array();
131
-					foreach($inSig as $sig)
132
-					{
133
-						$cursig[]=new xmlrpcval($sig, 'string');
134
-					}
135
-					$sigs[]=new xmlrpcval($cursig, 'array');
136
-				}
137
-				$r=new xmlrpcresp(new xmlrpcval($sigs, 'array'));
138
-			}
139
-			else
140
-			{
141
-				// NB: according to the official docs, we should be returning a
142
-				// "none-array" here, which means not-an-array
143
-				$r=new xmlrpcresp(new xmlrpcval('undef', 'string'));
144
-			}
145
-		}
146
-		else
147
-		{
148
-			$r=new xmlrpcresp(0,$GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
149
-		}
150
-		return $r;
151
-	}
152
-
153
-	$_xmlrpcs_methodHelp_sig=array(array($GLOBALS['xmlrpcString'], $GLOBALS['xmlrpcString']));
154
-	$_xmlrpcs_methodHelp_doc='Returns help text if defined for the method passed, otherwise returns an empty string';
155
-	$_xmlrpcs_methodHelp_sdoc=array(array('method description', 'name of the method to be described'));
156
-	function _xmlrpcs_methodHelp($server, $m)
157
-	{
158
-		// let accept as parameter both an xmlrpcval or string
159
-		if (is_object($m))
160
-		{
161
-			$methName=$m->getParam(0);
162
-			$methName=$methName->scalarval();
163
-		}
164
-		else
165
-		{
166
-			$methName=$m;
167
-		}
168
-		if(strpos($methName, "system.") === 0)
169
-		{
170
-			$dmap=$GLOBALS['_xmlrpcs_dmap']; $sysCall=1;
171
-		}
172
-		else
173
-		{
174
-			$dmap=$server->dmap; $sysCall=0;
175
-		}
176
-		if(isset($dmap[$methName]))
177
-		{
178
-			if(isset($dmap[$methName]['docstring']))
179
-			{
180
-				$r=new xmlrpcresp(new xmlrpcval($dmap[$methName]['docstring']), 'string');
181
-			}
182
-			else
183
-			{
184
-				$r=new xmlrpcresp(new xmlrpcval('', 'string'));
185
-			}
186
-		}
187
-		else
188
-		{
189
-			$r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
190
-		}
191
-		return $r;
192
-	}
193
-
194
-	$_xmlrpcs_multicall_sig = array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcArray']));
195
-	$_xmlrpcs_multicall_doc = 'Boxcar multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details';
196
-	$_xmlrpcs_multicall_sdoc = array(array('list of response structs, where each struct has the usual members', 'list of calls, with each call being represented as a struct, with members "methodname" and "params"'));
197
-	function _xmlrpcs_multicall_error($err)
198
-	{
199
-		if(is_string($err))
200
-		{
201
-			$str = $GLOBALS['xmlrpcstr']["multicall_${err}"];
202
-			$code = $GLOBALS['xmlrpcerr']["multicall_${err}"];
203
-		}
204
-		else
205
-		{
206
-			$code = $err->faultCode();
207
-			$str = $err->faultString();
208
-		}
209
-		$struct = array();
210
-		$struct['faultCode'] = new xmlrpcval($code, 'int');
211
-		$struct['faultString'] = new xmlrpcval($str, 'string');
212
-		return new xmlrpcval($struct, 'struct');
213
-	}
214
-
215
-	function _xmlrpcs_multicall_do_call($server, $call)
216
-	{
217
-		if($call->kindOf() != 'struct')
218
-		{
219
-			return _xmlrpcs_multicall_error('notstruct');
220
-		}
221
-		$methName = @$call->structmem('methodName');
222
-		if(!$methName)
223
-		{
224
-			return _xmlrpcs_multicall_error('nomethod');
225
-		}
226
-		if($methName->kindOf() != 'scalar' || $methName->scalartyp() != 'string')
227
-		{
228
-			return _xmlrpcs_multicall_error('notstring');
229
-		}
230
-		if($methName->scalarval() == 'system.multicall')
231
-		{
232
-			return _xmlrpcs_multicall_error('recursion');
233
-		}
234
-
235
-		$params = @$call->structmem('params');
236
-		if(!$params)
237
-		{
238
-			return _xmlrpcs_multicall_error('noparams');
239
-		}
240
-		if($params->kindOf() != 'array')
241
-		{
242
-			return _xmlrpcs_multicall_error('notarray');
243
-		}
244
-		$numParams = $params->arraysize();
245
-
246
-		$msg = new xmlrpcmsg($methName->scalarval());
247
-		for($i = 0; $i < $numParams; $i++)
248
-		{
249
-			if(!$msg->addParam($params->arraymem($i)))
250
-			{
251
-				$i++;
252
-				return _xmlrpcs_multicall_error(new xmlrpcresp(0,
253
-					$GLOBALS['xmlrpcerr']['incorrect_params'],
254
-					$GLOBALS['xmlrpcstr']['incorrect_params'] . ": probable xml error in param " . $i));
255
-			}
256
-		}
257
-
258
-		$result = $server->execute($msg);
259
-
260
-		if($result->faultCode() != 0)
261
-		{
262
-			return _xmlrpcs_multicall_error($result);		// Method returned fault.
263
-		}
264
-
265
-		return new xmlrpcval(array($result->value()), 'array');
266
-	}
267
-
268
-	function _xmlrpcs_multicall_do_call_phpvals($server, $call)
269
-	{
270
-		if(!is_array($call))
271
-		{
272
-			return _xmlrpcs_multicall_error('notstruct');
273
-		}
274
-		if(!array_key_exists('methodName', $call))
275
-		{
276
-			return _xmlrpcs_multicall_error('nomethod');
277
-		}
278
-		if (!is_string($call['methodName']))
279
-		{
280
-			return _xmlrpcs_multicall_error('notstring');
281
-		}
282
-		if($call['methodName'] == 'system.multicall')
283
-		{
284
-			return _xmlrpcs_multicall_error('recursion');
285
-		}
286
-		if(!array_key_exists('params', $call))
287
-		{
288
-			return _xmlrpcs_multicall_error('noparams');
289
-		}
290
-		if(!is_array($call['params']))
291
-		{
292
-			return _xmlrpcs_multicall_error('notarray');
293
-		}
294
-
295
-		// this is a real dirty and simplistic hack, since we might have received a
296
-		// base64 or datetime values, but they will be listed as strings here...
297
-		$numParams = count($call['params']);
298
-		$pt = array();
299
-		foreach($call['params'] as $val)
300
-			$pt[] = php_2_xmlrpc_type(gettype($val));
301
-
302
-		$result = $server->execute($call['methodName'], $call['params'], $pt);
303
-
304
-		if($result->faultCode() != 0)
305
-		{
306
-			return _xmlrpcs_multicall_error($result);		// Method returned fault.
307
-		}
308
-
309
-		return new xmlrpcval(array($result->value()), 'array');
310
-	}
311
-
312
-	function _xmlrpcs_multicall($server, $m)
313
-	{
314
-		$result = array();
315
-		// let accept a plain list of php parameters, beside a single xmlrpc msg object
316
-		if (is_object($m))
317
-		{
318
-			$calls = $m->getParam(0);
319
-			$numCalls = $calls->arraysize();
320
-			for($i = 0; $i < $numCalls; $i++)
321
-			{
322
-				$call = $calls->arraymem($i);
323
-				$result[$i] = _xmlrpcs_multicall_do_call($server, $call);
324
-			}
325
-		}
326
-		else
327
-		{
328
-			$numCalls=count($m);
329
-			for($i = 0; $i < $numCalls; $i++)
330
-			{
331
-				$result[$i] = _xmlrpcs_multicall_do_call_phpvals($server, $m[$i]);
332
-			}
333
-		}
334
-
335
-		return new xmlrpcresp(new xmlrpcval($result, 'array'));
336
-	}
337
-
338
-	$GLOBALS['_xmlrpcs_dmap']=array(
339
-		'system.listMethods' => array(
340
-			'function' => '_xmlrpcs_listMethods',
341
-			'signature' => $_xmlrpcs_listMethods_sig,
342
-			'docstring' => $_xmlrpcs_listMethods_doc,
343
-			'signature_docs' => $_xmlrpcs_listMethods_sdoc),
344
-		'system.methodHelp' => array(
345
-			'function' => '_xmlrpcs_methodHelp',
346
-			'signature' => $_xmlrpcs_methodHelp_sig,
347
-			'docstring' => $_xmlrpcs_methodHelp_doc,
348
-			'signature_docs' => $_xmlrpcs_methodHelp_sdoc),
349
-		'system.methodSignature' => array(
350
-			'function' => '_xmlrpcs_methodSignature',
351
-			'signature' => $_xmlrpcs_methodSignature_sig,
352
-			'docstring' => $_xmlrpcs_methodSignature_doc,
353
-			'signature_docs' => $_xmlrpcs_methodSignature_sdoc),
354
-		'system.multicall' => array(
355
-			'function' => '_xmlrpcs_multicall',
356
-			'signature' => $_xmlrpcs_multicall_sig,
357
-			'docstring' => $_xmlrpcs_multicall_doc,
358
-			'signature_docs' => $_xmlrpcs_multicall_sdoc),
359
-		'system.getCapabilities' => array(
360
-			'function' => '_xmlrpcs_getCapabilities',
361
-			'signature' => $_xmlrpcs_getCapabilities_sig,
362
-			'docstring' => $_xmlrpcs_getCapabilities_doc,
363
-			'signature_docs' => $_xmlrpcs_getCapabilities_sdoc)
364
-	);
365
-
366
-	$GLOBALS['_xmlrpcs_occurred_errors'] = '';
367
-	$GLOBALS['_xmlrpcs_prev_ehandler'] = '';
368
-
369
-	/**
370
-	* Error handler used to track errors that occur during server-side execution of PHP code.
371
-	* This allows to report back to the client whether an internal error has occurred or not
372
-	* using an xmlrpc response object, instead of letting the client deal with the html junk
373
-	* that a PHP execution error on the server generally entails.
374
-	*
375
-	* NB: in fact a user defined error handler can only handle WARNING, NOTICE and USER_* errors.
376
-	*
377
-	*/
378
-	function _xmlrpcs_errorHandler($errcode, $errstring, $filename=null, $lineno=null, $context=null)
379
-	{
380
-		// obey the @ protocol
381
-		if (error_reporting() == 0)
382
-			return;
383
-
384
-		//if($errcode != E_NOTICE && $errcode != E_WARNING && $errcode != E_USER_NOTICE && $errcode != E_USER_WARNING)
385
-		if($errcode != E_STRICT)
386
-		{
387
-			$GLOBALS['_xmlrpcs_occurred_errors'] = $GLOBALS['_xmlrpcs_occurred_errors'] . $errstring . "\n";
388
-		}
389
-		// Try to avoid as much as possible disruption to the previous error handling
390
-		// mechanism in place
391
-		if($GLOBALS['_xmlrpcs_prev_ehandler'] == '')
392
-		{
393
-			// The previous error handler was the default: all we should do is log error
394
-			// to the default error log (if level high enough)
395
-			if(ini_get('log_errors') && (intval(ini_get('error_reporting')) & $errcode))
396
-			{
397
-				error_log($errstring);
398
-			}
399
-		}
400
-		else
401
-		{
402
-			// Pass control on to previous error handler, trying to avoid loops...
403
-			if($GLOBALS['_xmlrpcs_prev_ehandler'] != '_xmlrpcs_errorHandler')
404
-			{
405
-				// NB: this code will NOT work on php < 4.0.2: only 2 params were used for error handlers
406
-				if(is_array($GLOBALS['_xmlrpcs_prev_ehandler']))
407
-				{
408
-					// the following works both with static class methods and plain object methods as error handler
409
-					call_user_func_array($GLOBALS['_xmlrpcs_prev_ehandler'], array($errcode, $errstring, $filename, $lineno, $context));
410
-				}
411
-				else
412
-				{
413
-					$GLOBALS['_xmlrpcs_prev_ehandler']($errcode, $errstring, $filename, $lineno, $context);
414
-				}
415
-			}
416
-		}
417
-	}
418
-
419
-	$GLOBALS['_xmlrpc_debuginfo']='';
420
-
421
-	/**
422
-	* Add a string to the debug info that can be later seralized by the server
423
-	* as part of the response message.
424
-	* Note that for best compatbility, the debug string should be encoded using
425
-	* the $GLOBALS['xmlrpc_internalencoding'] character set.
426
-	* @param string $m
427
-	* @access public
428
-	*/
429
-	function xmlrpc_debugmsg($m)
430
-	{
431
-		$GLOBALS['_xmlrpc_debuginfo'] .= $m . "\n";
432
-	}
433
-
434
-	class xmlrpc_server
435
-	{
436
-		/**
437
-		* Array defining php functions exposed as xmlrpc methods by this server
438
-		* @access private
439
-		*/
440
-		var $dmap=array();
441
-		/**
442
-		* Defines how functions in dmap will be invoked: either using an xmlrpc msg object
443
-		* or plain php values.
444
-		* valid strings are 'xmlrpcvals', 'phpvals' or 'epivals'
445
-		*/
446
-		var $functions_parameters_type='xmlrpcvals';
447
-		/**
448
-		* Option used for fine-tuning the encoding the php values returned from
449
-		* functions registered in the dispatch map when the functions_parameters_types
450
-		* member is set to 'phpvals'
451
-		* @see php_xmlrpc_encode for a list of values
452
-		*/
453
-		var $phpvals_encoding_options = array( 'auto_dates' );
454
-		/// controls wether the server is going to echo debugging messages back to the client as comments in response body. valid values: 0,1,2,3
455
-		var $debug = 1;
456
-		/**
457
-		* Controls behaviour of server when invoked user function throws an exception:
458
-		* 0 = catch it and return an 'internal error' xmlrpc response (default)
459
-		* 1 = catch it and return an xmlrpc response with the error corresponding to the exception
460
-		* 2 = allow the exception to float to the upper layers
461
-		*/
462
-		var $exception_handling = 0;
463
-		/**
464
-		* When set to true, it will enable HTTP compression of the response, in case
465
-		* the client has declared its support for compression in the request.
466
-		*/
467
-		var $compress_response = false;
468
-		/**
469
-		* List of http compression methods accepted by the server for requests.
470
-		* NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
471
-		*/
472
-		var $accepted_compression = array();
473
-		/// shall we serve calls to system.* methods?
474
-		var $allow_system_funcs = true;
475
-		/// list of charset encodings natively accepted for requests
476
-		var $accepted_charset_encodings = array();
477
-		/**
478
-		* charset encoding to be used for response.
479
-		* NB: if we can, we will convert the generated response from internal_encoding to the intended one.
480
-		* can be: a supported xml encoding (only UTF-8 and ISO-8859-1 at present, unless mbstring is enabled),
481
-		* null (leave unspecified in response, convert output stream to US_ASCII),
482
-		* 'default' (use xmlrpc library default as specified in xmlrpc.inc, convert output stream if needed),
483
-		* or 'auto' (use client-specified charset encoding or same as request if request headers do not specify it (unless request is US-ASCII: then use library default anyway).
484
-		* NB: pretty dangerous if you accept every charset and do not have mbstring enabled)
485
-		*/
486
-		var $response_charset_encoding = '';
487
-		/**
488
-		* Storage for internal debug info
489
-		* @access private
490
-		*/
491
-		var $debug_info = '';
492
-		/**
493
-		* Extra data passed at runtime to method handling functions. Used only by EPI layer
494
-		*/
495
-		var $user_data = null;
496
-
497
-		/**
498
-		* @param array $dispmap the dispatch map withd efinition of exposed services
499
-		* @param boolean $servicenow set to false to prevent the server from runnung upon construction
500
-		*/
501
-		function xmlrpc_server($dispMap=null, $serviceNow=true)
502
-		{
503
-			// if ZLIB is enabled, let the server by default accept compressed requests,
504
-			// and compress responses sent to clients that support them
505
-			if(function_exists('gzinflate'))
506
-			{
507
-				$this->accepted_compression = array('gzip', 'deflate');
508
-				$this->compress_response = true;
509
-			}
510
-
511
-			// by default the xml parser can support these 3 charset encodings
512
-			$this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
513
-
514
-			// dispMap is a dispatch array of methods
515
-			// mapped to function names and signatures
516
-			// if a method
517
-			// doesn't appear in the map then an unknown
518
-			// method error is generated
519
-			/* milosch - changed to make passing dispMap optional.
38
+    // XML RPC Server class
39
+    // requires: xmlrpc.inc
40
+
41
+    $GLOBALS['xmlrpcs_capabilities'] = array(
42
+        // xmlrpc spec: always supported
43
+        'xmlrpc' => new xmlrpcval(array(
44
+            'specUrl' => new xmlrpcval('http://www.xmlrpc.com/spec', 'string'),
45
+            'specVersion' => new xmlrpcval(1, 'int')
46
+        ), 'struct'),
47
+        // if we support system.xxx functions, we always support multicall, too...
48
+        // Note that, as of 2006/09/17, the following URL does not respond anymore
49
+        'system.multicall' => new xmlrpcval(array(
50
+            'specUrl' => new xmlrpcval('http://www.xmlrpc.com/discuss/msgReader$1208', 'string'),
51
+            'specVersion' => new xmlrpcval(1, 'int')
52
+        ), 'struct'),
53
+        // introspection: version 2! we support 'mixed', too
54
+        'introspection' => new xmlrpcval(array(
55
+            'specUrl' => new xmlrpcval('http://phpxmlrpc.sourceforge.net/doc-2/ch10.html', 'string'),
56
+            'specVersion' => new xmlrpcval(2, 'int')
57
+        ), 'struct')
58
+    );
59
+
60
+    /* Functions that implement system.XXX methods of xmlrpc servers */
61
+    $_xmlrpcs_getCapabilities_sig=array(array($GLOBALS['xmlrpcStruct']));
62
+    $_xmlrpcs_getCapabilities_doc='This method lists all the capabilites that the XML-RPC server has: the (more or less standard) extensions to the xmlrpc spec that it adheres to';
63
+    $_xmlrpcs_getCapabilities_sdoc=array(array('list of capabilities, described as structs with a version number and url for the spec'));
64
+    function _xmlrpcs_getCapabilities($server, $m=null)
65
+    {
66
+        $outAr = $GLOBALS['xmlrpcs_capabilities'];
67
+        // NIL extension
68
+        if ($GLOBALS['xmlrpc_null_extension']) {
69
+            $outAr['nil'] = new xmlrpcval(array(
70
+                'specUrl' => new xmlrpcval('http://www.ontosys.com/xml-rpc/extensions.php', 'string'),
71
+                'specVersion' => new xmlrpcval(1, 'int')
72
+            ), 'struct');
73
+        }
74
+        return new xmlrpcresp(new xmlrpcval($outAr, 'struct'));
75
+    }
76
+
77
+    // listMethods: signature was either a string, or nothing.
78
+    // The useless string variant has been removed
79
+    $_xmlrpcs_listMethods_sig=array(array($GLOBALS['xmlrpcArray']));
80
+    $_xmlrpcs_listMethods_doc='This method lists all the methods that the XML-RPC server knows how to dispatch';
81
+    $_xmlrpcs_listMethods_sdoc=array(array('list of method names'));
82
+    function _xmlrpcs_listMethods($server, $m=null) // if called in plain php values mode, second param is missing
83
+    {
84
+
85
+        $outAr=array();
86
+        foreach($server->dmap as $key => $val)
87
+        {
88
+            $outAr[]=new xmlrpcval($key, 'string');
89
+        }
90
+        if($server->allow_system_funcs)
91
+        {
92
+            foreach($GLOBALS['_xmlrpcs_dmap'] as $key => $val)
93
+            {
94
+                $outAr[]=new xmlrpcval($key, 'string');
95
+            }
96
+        }
97
+        return new xmlrpcresp(new xmlrpcval($outAr, 'array'));
98
+    }
99
+
100
+    $_xmlrpcs_methodSignature_sig=array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcString']));
101
+    $_xmlrpcs_methodSignature_doc='Returns an array of known signatures (an array of arrays) for the method name passed. If no signatures are known, returns a none-array (test for type != array to detect missing signature)';
102
+    $_xmlrpcs_methodSignature_sdoc=array(array('list of known signatures, each sig being an array of xmlrpc type names', 'name of method to be described'));
103
+    function _xmlrpcs_methodSignature($server, $m)
104
+    {
105
+        // let accept as parameter both an xmlrpcval or string
106
+        if (is_object($m))
107
+        {
108
+            $methName=$m->getParam(0);
109
+            $methName=$methName->scalarval();
110
+        }
111
+        else
112
+        {
113
+            $methName=$m;
114
+        }
115
+        if(strpos($methName, "system.") === 0)
116
+        {
117
+            $dmap=$GLOBALS['_xmlrpcs_dmap']; $sysCall=1;
118
+        }
119
+        else
120
+        {
121
+            $dmap=$server->dmap; $sysCall=0;
122
+        }
123
+        if(isset($dmap[$methName]))
124
+        {
125
+            if(isset($dmap[$methName]['signature']))
126
+            {
127
+                $sigs=array();
128
+                foreach($dmap[$methName]['signature'] as $inSig)
129
+                {
130
+                    $cursig=array();
131
+                    foreach($inSig as $sig)
132
+                    {
133
+                        $cursig[]=new xmlrpcval($sig, 'string');
134
+                    }
135
+                    $sigs[]=new xmlrpcval($cursig, 'array');
136
+                }
137
+                $r=new xmlrpcresp(new xmlrpcval($sigs, 'array'));
138
+            }
139
+            else
140
+            {
141
+                // NB: according to the official docs, we should be returning a
142
+                // "none-array" here, which means not-an-array
143
+                $r=new xmlrpcresp(new xmlrpcval('undef', 'string'));
144
+            }
145
+        }
146
+        else
147
+        {
148
+            $r=new xmlrpcresp(0,$GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
149
+        }
150
+        return $r;
151
+    }
152
+
153
+    $_xmlrpcs_methodHelp_sig=array(array($GLOBALS['xmlrpcString'], $GLOBALS['xmlrpcString']));
154
+    $_xmlrpcs_methodHelp_doc='Returns help text if defined for the method passed, otherwise returns an empty string';
155
+    $_xmlrpcs_methodHelp_sdoc=array(array('method description', 'name of the method to be described'));
156
+    function _xmlrpcs_methodHelp($server, $m)
157
+    {
158
+        // let accept as parameter both an xmlrpcval or string
159
+        if (is_object($m))
160
+        {
161
+            $methName=$m->getParam(0);
162
+            $methName=$methName->scalarval();
163
+        }
164
+        else
165
+        {
166
+            $methName=$m;
167
+        }
168
+        if(strpos($methName, "system.") === 0)
169
+        {
170
+            $dmap=$GLOBALS['_xmlrpcs_dmap']; $sysCall=1;
171
+        }
172
+        else
173
+        {
174
+            $dmap=$server->dmap; $sysCall=0;
175
+        }
176
+        if(isset($dmap[$methName]))
177
+        {
178
+            if(isset($dmap[$methName]['docstring']))
179
+            {
180
+                $r=new xmlrpcresp(new xmlrpcval($dmap[$methName]['docstring']), 'string');
181
+            }
182
+            else
183
+            {
184
+                $r=new xmlrpcresp(new xmlrpcval('', 'string'));
185
+            }
186
+        }
187
+        else
188
+        {
189
+            $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
190
+        }
191
+        return $r;
192
+    }
193
+
194
+    $_xmlrpcs_multicall_sig = array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcArray']));
195
+    $_xmlrpcs_multicall_doc = 'Boxcar multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details';
196
+    $_xmlrpcs_multicall_sdoc = array(array('list of response structs, where each struct has the usual members', 'list of calls, with each call being represented as a struct, with members "methodname" and "params"'));
197
+    function _xmlrpcs_multicall_error($err)
198
+    {
199
+        if(is_string($err))
200
+        {
201
+            $str = $GLOBALS['xmlrpcstr']["multicall_${err}"];
202
+            $code = $GLOBALS['xmlrpcerr']["multicall_${err}"];
203
+        }
204
+        else
205
+        {
206
+            $code = $err->faultCode();
207
+            $str = $err->faultString();
208
+        }
209
+        $struct = array();
210
+        $struct['faultCode'] = new xmlrpcval($code, 'int');
211
+        $struct['faultString'] = new xmlrpcval($str, 'string');
212
+        return new xmlrpcval($struct, 'struct');
213
+    }
214
+
215
+    function _xmlrpcs_multicall_do_call($server, $call)
216
+    {
217
+        if($call->kindOf() != 'struct')
218
+        {
219
+            return _xmlrpcs_multicall_error('notstruct');
220
+        }
221
+        $methName = @$call->structmem('methodName');
222
+        if(!$methName)
223
+        {
224
+            return _xmlrpcs_multicall_error('nomethod');
225
+        }
226
+        if($methName->kindOf() != 'scalar' || $methName->scalartyp() != 'string')
227
+        {
228
+            return _xmlrpcs_multicall_error('notstring');
229
+        }
230
+        if($methName->scalarval() == 'system.multicall')
231
+        {
232
+            return _xmlrpcs_multicall_error('recursion');
233
+        }
234
+
235
+        $params = @$call->structmem('params');
236
+        if(!$params)
237
+        {
238
+            return _xmlrpcs_multicall_error('noparams');
239
+        }
240
+        if($params->kindOf() != 'array')
241
+        {
242
+            return _xmlrpcs_multicall_error('notarray');
243
+        }
244
+        $numParams = $params->arraysize();
245
+
246
+        $msg = new xmlrpcmsg($methName->scalarval());
247
+        for($i = 0; $i < $numParams; $i++)
248
+        {
249
+            if(!$msg->addParam($params->arraymem($i)))
250
+            {
251
+                $i++;
252
+                return _xmlrpcs_multicall_error(new xmlrpcresp(0,
253
+                    $GLOBALS['xmlrpcerr']['incorrect_params'],
254
+                    $GLOBALS['xmlrpcstr']['incorrect_params'] . ": probable xml error in param " . $i));
255
+            }
256
+        }
257
+
258
+        $result = $server->execute($msg);
259
+
260
+        if($result->faultCode() != 0)
261
+        {
262
+            return _xmlrpcs_multicall_error($result);		// Method returned fault.
263
+        }
264
+
265
+        return new xmlrpcval(array($result->value()), 'array');
266
+    }
267
+
268
+    function _xmlrpcs_multicall_do_call_phpvals($server, $call)
269
+    {
270
+        if(!is_array($call))
271
+        {
272
+            return _xmlrpcs_multicall_error('notstruct');
273
+        }
274
+        if(!array_key_exists('methodName', $call))
275
+        {
276
+            return _xmlrpcs_multicall_error('nomethod');
277
+        }
278
+        if (!is_string($call['methodName']))
279
+        {
280
+            return _xmlrpcs_multicall_error('notstring');
281
+        }
282
+        if($call['methodName'] == 'system.multicall')
283
+        {
284
+            return _xmlrpcs_multicall_error('recursion');
285
+        }
286
+        if(!array_key_exists('params', $call))
287
+        {
288
+            return _xmlrpcs_multicall_error('noparams');
289
+        }
290
+        if(!is_array($call['params']))
291
+        {
292
+            return _xmlrpcs_multicall_error('notarray');
293
+        }
294
+
295
+        // this is a real dirty and simplistic hack, since we might have received a
296
+        // base64 or datetime values, but they will be listed as strings here...
297
+        $numParams = count($call['params']);
298
+        $pt = array();
299
+        foreach($call['params'] as $val)
300
+            $pt[] = php_2_xmlrpc_type(gettype($val));
301
+
302
+        $result = $server->execute($call['methodName'], $call['params'], $pt);
303
+
304
+        if($result->faultCode() != 0)
305
+        {
306
+            return _xmlrpcs_multicall_error($result);		// Method returned fault.
307
+        }
308
+
309
+        return new xmlrpcval(array($result->value()), 'array');
310
+    }
311
+
312
+    function _xmlrpcs_multicall($server, $m)
313
+    {
314
+        $result = array();
315
+        // let accept a plain list of php parameters, beside a single xmlrpc msg object
316
+        if (is_object($m))
317
+        {
318
+            $calls = $m->getParam(0);
319
+            $numCalls = $calls->arraysize();
320
+            for($i = 0; $i < $numCalls; $i++)
321
+            {
322
+                $call = $calls->arraymem($i);
323
+                $result[$i] = _xmlrpcs_multicall_do_call($server, $call);
324
+            }
325
+        }
326
+        else
327
+        {
328
+            $numCalls=count($m);
329
+            for($i = 0; $i < $numCalls; $i++)
330
+            {
331
+                $result[$i] = _xmlrpcs_multicall_do_call_phpvals($server, $m[$i]);
332
+            }
333
+        }
334
+
335
+        return new xmlrpcresp(new xmlrpcval($result, 'array'));
336
+    }
337
+
338
+    $GLOBALS['_xmlrpcs_dmap']=array(
339
+        'system.listMethods' => array(
340
+            'function' => '_xmlrpcs_listMethods',
341
+            'signature' => $_xmlrpcs_listMethods_sig,
342
+            'docstring' => $_xmlrpcs_listMethods_doc,
343
+            'signature_docs' => $_xmlrpcs_listMethods_sdoc),
344
+        'system.methodHelp' => array(
345
+            'function' => '_xmlrpcs_methodHelp',
346
+            'signature' => $_xmlrpcs_methodHelp_sig,
347
+            'docstring' => $_xmlrpcs_methodHelp_doc,
348
+            'signature_docs' => $_xmlrpcs_methodHelp_sdoc),
349
+        'system.methodSignature' => array(
350
+            'function' => '_xmlrpcs_methodSignature',
351
+            'signature' => $_xmlrpcs_methodSignature_sig,
352
+            'docstring' => $_xmlrpcs_methodSignature_doc,
353
+            'signature_docs' => $_xmlrpcs_methodSignature_sdoc),
354
+        'system.multicall' => array(
355
+            'function' => '_xmlrpcs_multicall',
356
+            'signature' => $_xmlrpcs_multicall_sig,
357
+            'docstring' => $_xmlrpcs_multicall_doc,
358
+            'signature_docs' => $_xmlrpcs_multicall_sdoc),
359
+        'system.getCapabilities' => array(
360
+            'function' => '_xmlrpcs_getCapabilities',
361
+            'signature' => $_xmlrpcs_getCapabilities_sig,
362
+            'docstring' => $_xmlrpcs_getCapabilities_doc,
363
+            'signature_docs' => $_xmlrpcs_getCapabilities_sdoc)
364
+    );
365
+
366
+    $GLOBALS['_xmlrpcs_occurred_errors'] = '';
367
+    $GLOBALS['_xmlrpcs_prev_ehandler'] = '';
368
+
369
+    /**
370
+     * Error handler used to track errors that occur during server-side execution of PHP code.
371
+     * This allows to report back to the client whether an internal error has occurred or not
372
+     * using an xmlrpc response object, instead of letting the client deal with the html junk
373
+     * that a PHP execution error on the server generally entails.
374
+     *
375
+     * NB: in fact a user defined error handler can only handle WARNING, NOTICE and USER_* errors.
376
+     *
377
+     */
378
+    function _xmlrpcs_errorHandler($errcode, $errstring, $filename=null, $lineno=null, $context=null)
379
+    {
380
+        // obey the @ protocol
381
+        if (error_reporting() == 0)
382
+            return;
383
+
384
+        //if($errcode != E_NOTICE && $errcode != E_WARNING && $errcode != E_USER_NOTICE && $errcode != E_USER_WARNING)
385
+        if($errcode != E_STRICT)
386
+        {
387
+            $GLOBALS['_xmlrpcs_occurred_errors'] = $GLOBALS['_xmlrpcs_occurred_errors'] . $errstring . "\n";
388
+        }
389
+        // Try to avoid as much as possible disruption to the previous error handling
390
+        // mechanism in place
391
+        if($GLOBALS['_xmlrpcs_prev_ehandler'] == '')
392
+        {
393
+            // The previous error handler was the default: all we should do is log error
394
+            // to the default error log (if level high enough)
395
+            if(ini_get('log_errors') && (intval(ini_get('error_reporting')) & $errcode))
396
+            {
397
+                error_log($errstring);
398
+            }
399
+        }
400
+        else
401
+        {
402
+            // Pass control on to previous error handler, trying to avoid loops...
403
+            if($GLOBALS['_xmlrpcs_prev_ehandler'] != '_xmlrpcs_errorHandler')
404
+            {
405
+                // NB: this code will NOT work on php < 4.0.2: only 2 params were used for error handlers
406
+                if(is_array($GLOBALS['_xmlrpcs_prev_ehandler']))
407
+                {
408
+                    // the following works both with static class methods and plain object methods as error handler
409
+                    call_user_func_array($GLOBALS['_xmlrpcs_prev_ehandler'], array($errcode, $errstring, $filename, $lineno, $context));
410
+                }
411
+                else
412
+                {
413
+                    $GLOBALS['_xmlrpcs_prev_ehandler']($errcode, $errstring, $filename, $lineno, $context);
414
+                }
415
+            }
416
+        }
417
+    }
418
+
419
+    $GLOBALS['_xmlrpc_debuginfo']='';
420
+
421
+    /**
422
+     * Add a string to the debug info that can be later seralized by the server
423
+     * as part of the response message.
424
+     * Note that for best compatbility, the debug string should be encoded using
425
+     * the $GLOBALS['xmlrpc_internalencoding'] character set.
426
+     * @param string $m
427
+     * @access public
428
+     */
429
+    function xmlrpc_debugmsg($m)
430
+    {
431
+        $GLOBALS['_xmlrpc_debuginfo'] .= $m . "\n";
432
+    }
433
+
434
+    class xmlrpc_server
435
+    {
436
+        /**
437
+         * Array defining php functions exposed as xmlrpc methods by this server
438
+         * @access private
439
+         */
440
+        var $dmap=array();
441
+        /**
442
+         * Defines how functions in dmap will be invoked: either using an xmlrpc msg object
443
+         * or plain php values.
444
+         * valid strings are 'xmlrpcvals', 'phpvals' or 'epivals'
445
+         */
446
+        var $functions_parameters_type='xmlrpcvals';
447
+        /**
448
+         * Option used for fine-tuning the encoding the php values returned from
449
+         * functions registered in the dispatch map when the functions_parameters_types
450
+         * member is set to 'phpvals'
451
+         * @see php_xmlrpc_encode for a list of values
452
+         */
453
+        var $phpvals_encoding_options = array( 'auto_dates' );
454
+        /// controls wether the server is going to echo debugging messages back to the client as comments in response body. valid values: 0,1,2,3
455
+        var $debug = 1;
456
+        /**
457
+         * Controls behaviour of server when invoked user function throws an exception:
458
+         * 0 = catch it and return an 'internal error' xmlrpc response (default)
459
+         * 1 = catch it and return an xmlrpc response with the error corresponding to the exception
460
+         * 2 = allow the exception to float to the upper layers
461
+         */
462
+        var $exception_handling = 0;
463
+        /**
464
+         * When set to true, it will enable HTTP compression of the response, in case
465
+         * the client has declared its support for compression in the request.
466
+         */
467
+        var $compress_response = false;
468
+        /**
469
+         * List of http compression methods accepted by the server for requests.
470
+         * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
471
+         */
472
+        var $accepted_compression = array();
473
+        /// shall we serve calls to system.* methods?
474
+        var $allow_system_funcs = true;
475
+        /// list of charset encodings natively accepted for requests
476
+        var $accepted_charset_encodings = array();
477
+        /**
478
+         * charset encoding to be used for response.
479
+         * NB: if we can, we will convert the generated response from internal_encoding to the intended one.
480
+         * can be: a supported xml encoding (only UTF-8 and ISO-8859-1 at present, unless mbstring is enabled),
481
+         * null (leave unspecified in response, convert output stream to US_ASCII),
482
+         * 'default' (use xmlrpc library default as specified in xmlrpc.inc, convert output stream if needed),
483
+         * or 'auto' (use client-specified charset encoding or same as request if request headers do not specify it (unless request is US-ASCII: then use library default anyway).
484
+         * NB: pretty dangerous if you accept every charset and do not have mbstring enabled)
485
+         */
486
+        var $response_charset_encoding = '';
487
+        /**
488
+         * Storage for internal debug info
489
+         * @access private
490
+         */
491
+        var $debug_info = '';
492
+        /**
493
+         * Extra data passed at runtime to method handling functions. Used only by EPI layer
494
+         */
495
+        var $user_data = null;
496
+
497
+        /**
498
+         * @param array $dispmap the dispatch map withd efinition of exposed services
499
+         * @param boolean $servicenow set to false to prevent the server from runnung upon construction
500
+         */
501
+        function xmlrpc_server($dispMap=null, $serviceNow=true)
502
+        {
503
+            // if ZLIB is enabled, let the server by default accept compressed requests,
504
+            // and compress responses sent to clients that support them
505
+            if(function_exists('gzinflate'))
506
+            {
507
+                $this->accepted_compression = array('gzip', 'deflate');
508
+                $this->compress_response = true;
509
+            }
510
+
511
+            // by default the xml parser can support these 3 charset encodings
512
+            $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
513
+
514
+            // dispMap is a dispatch array of methods
515
+            // mapped to function names and signatures
516
+            // if a method
517
+            // doesn't appear in the map then an unknown
518
+            // method error is generated
519
+            /* milosch - changed to make passing dispMap optional.
520 520
 			 * instead, you can use the class add_to_map() function
521 521
 			 * to add functions manually (borrowed from SOAPX4)
522 522
 			 */
523
-			if($dispMap)
524
-			{
525
-				$this->dmap = $dispMap;
526
-				if($serviceNow)
527
-				{
528
-					$this->service();
529
-				}
530
-			}
531
-		}
532
-
533
-		/**
534
-		* Set debug level of server.
535
-		* @param integer $in debug lvl: determines info added to xmlrpc responses (as xml comments)
536
-		* 0 = no debug info,
537
-		* 1 = msgs set from user with debugmsg(),
538
-		* 2 = add complete xmlrpc request (headers and body),
539
-		* 3 = add also all processing warnings happened during method processing
540
-		* (NB: this involves setting a custom error handler, and might interfere
541
-		* with the standard processing of the php function exposed as method. In
542
-		* particular, triggering an USER_ERROR level error will not halt script
543
-		* execution anymore, but just end up logged in the xmlrpc response)
544
-		* Note that info added at elevel 2 and 3 will be base64 encoded
545
-		* @access public
546
-		*/
547
-		function setDebug($in)
548
-		{
549
-			$this->debug=$in;
550
-		}
551
-
552
-		/**
553
-		* Return a string with the serialized representation of all debug info
554
-		* @param string $charset_encoding the target charset encoding for the serialization
555
-		* @return string an XML comment (or two)
556
-		*/
557
-		function serializeDebug($charset_encoding='')
558
-		{
559
-			// Tough encoding problem: which internal charset should we assume for debug info?
560
-			// It might contain a copy of raw data received from client, ie with unknown encoding,
561
-			// intermixed with php generated data and user generated data...
562
-			// so we split it: system debug is base 64 encoded,
563
-			// user debug info should be encoded by the end user using the INTERNAL_ENCODING
564
-			$out = '';
565
-			if ($this->debug_info != '')
566
-			{
567
-				$out .= "<!-- SERVER DEBUG INFO (BASE64 ENCODED):\n".base64_encode($this->debug_info)."\n-->\n";
568
-			}
569
-			if($GLOBALS['_xmlrpc_debuginfo']!='')
570
-			{
571
-
572
-				$out .= "<!-- DEBUG INFO:\n" . xmlrpc_encode_entitites(str_replace('--', '_-', $GLOBALS['_xmlrpc_debuginfo']), $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "\n-->\n";
573
-				// NB: a better solution MIGHT be to use CDATA, but we need to insert it
574
-				// into return payload AFTER the beginning tag
575
-				//$out .= "<![CDATA[ DEBUG INFO:\n\n" . str_replace(']]>', ']_]_>', $GLOBALS['_xmlrpc_debuginfo']) . "\n]]>\n";
576
-			}
577
-			return $out;
578
-		}
579
-
580
-		/**
581
-		* Execute the xmlrpc request, printing the response
582
-		* @param string $data the request body. If null, the http POST request will be examined
583
-		* @return xmlrpcresp the response object (usually not used by caller...)
584
-		* @access public
585
-		*/
586
-		function service($data=null, $return_payload=false)
587
-		{
588
-			if ($data === null)
589
-			{
590
-				// workaround for a known bug in php ver. 5.2.2 that broke $HTTP_RAW_POST_DATA
591
-				$ver = phpversion();
592
-				if ($ver[0] >= 5)
593
-				{
594
-					$data = file_get_contents('php://input');
595
-				}
596
-				else
597
-				{
598
-					$data = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
599
-				}
600
-			}
601
-			$raw_data = $data;
602
-
603
-			// reset internal debug info
604
-			$this->debug_info = '';
605
-
606
-			// Echo back what we received, before parsing it
607
-			if($this->debug > 1)
608
-			{
609
-				$this->debugmsg("+++GOT+++\n" . $data . "\n+++END+++");
610
-			}
611
-
612
-			$r = $this->parseRequestHeaders($data, $req_charset, $resp_charset, $resp_encoding);
613
-			if (!$r)
614
-			{
615
-				$r=$this->parseRequest($data, $req_charset);
616
-			}
617
-
618
-			// save full body of request into response, for more debugging usages
619
-			$r->raw_data = $raw_data;
620
-
621
-			if($this->debug > 2 && $GLOBALS['_xmlrpcs_occurred_errors'])
622
-			{
623
-				$this->debugmsg("+++PROCESSING ERRORS AND WARNINGS+++\n" .
624
-					$GLOBALS['_xmlrpcs_occurred_errors'] . "+++END+++");
625
-			}
626
-
627
-			$payload=$this->xml_header($resp_charset);
628
-			if($this->debug > 0)
629
-			{
630
-				$payload = $payload . $this->serializeDebug($resp_charset);
631
-			}
632
-
633
-			// G. Giunta 2006-01-27: do not create response serialization if it has
634
-			// already happened. Helps building json magic
635
-			if (empty($r->payload))
636
-			{
637
-				$r->serialize($resp_charset);
638
-			}
639
-			$payload = $payload . $r->payload;
640
-
641
-			if ($return_payload)
642
-			{
643
-				return $payload;
644
-			}
645
-
646
-			// if we get a warning/error that has output some text before here, then we cannot
647
-			// add a new header. We cannot say we are sending xml, either...
648
-			if(!headers_sent())
649
-			{
650
-				header('Content-Type: '.$r->content_type);
651
-				// we do not know if client actually told us an accepted charset, but if he did
652
-				// we have to tell him what we did
653
-				header("Vary: Accept-Charset");
654
-
655
-				// http compression of output: only
656
-				// if we can do it, and we want to do it, and client asked us to,
657
-				// and php ini settings do not force it already
658
-				$php_no_self_compress = !ini_get('zlib.output_compression') && (ini_get('output_handler') != 'ob_gzhandler');
659
-				if($this->compress_response && function_exists('gzencode') && $resp_encoding != ''
660
-					&& $php_no_self_compress)
661
-				{
662
-					if(strpos($resp_encoding, 'gzip') !== false)
663
-					{
664
-						$payload = gzencode($payload);
665
-						header("Content-Encoding: gzip");
666
-						header("Vary: Accept-Encoding");
667
-					}
668
-					elseif (strpos($resp_encoding, 'deflate') !== false)
669
-					{
670
-						$payload = gzcompress($payload);
671
-						header("Content-Encoding: deflate");
672
-						header("Vary: Accept-Encoding");
673
-					}
674
-				}
675
-
676
-				// do not ouput content-length header if php is compressing output for us:
677
-				// it will mess up measurements
678
-				if($php_no_self_compress)
679
-				{
680
-					header('Content-Length: ' . (int)strlen($payload));
681
-				}
682
-			}
683
-			else
684
-			{
685
-				error_log('XML-RPC: '.__METHOD__.': http headers already sent before response is fully generated. Check for php warning or error messages');
686
-			}
687
-
688
-			print $payload;
689
-
690
-			// return request, in case subclasses want it
691
-			return $r;
692
-		}
693
-
694
-		/**
695
-		* Add a method to the dispatch map
696
-		* @param string $methodname the name with which the method will be made available
697
-		* @param string $function the php function that will get invoked
698
-		* @param array $sig the array of valid method signatures
699
-		* @param string $doc method documentation
700
-		* @param array $sigdoc the array of valid method signatures docs (one string per param, one for return type)
701
-		* @access public
702
-		*/
703
-		function add_to_map($methodname,$function,$sig=null,$doc=false,$sigdoc=false)
704
-		{
705
-			$this->dmap[$methodname] = array(
706
-				'function'	=> $function,
707
-				'docstring' => $doc
708
-			);
709
-			if ($sig)
710
-			{
711
-				$this->dmap[$methodname]['signature'] = $sig;
712
-			}
713
-			if ($sigdoc)
714
-			{
715
-				$this->dmap[$methodname]['signature_docs'] = $sigdoc;
716
-			}
717
-		}
718
-
719
-		/**
720
-		* Verify type and number of parameters received against a list of known signatures
721
-		* @param array $in array of either xmlrpcval objects or xmlrpc type definitions
722
-		* @param array $sig array of known signatures to match against
723
-		* @access private
724
-		*/
725
-		function verifySignature($in, $sig)
726
-		{
727
-			// check each possible signature in turn
728
-			if (is_object($in))
729
-			{
730
-				$numParams = $in->getNumParams();
731
-			}
732
-			else
733
-			{
734
-				$numParams = count($in);
735
-			}
736
-			foreach($sig as $cursig)
737
-			{
738
-				if(count($cursig)==$numParams+1)
739
-				{
740
-					$itsOK=1;
741
-					for($n=0; $n<$numParams; $n++)
742
-					{
743
-						if (is_object($in))
744
-						{
745
-							$p=$in->getParam($n);
746
-							if($p->kindOf() == 'scalar')
747
-							{
748
-								$pt=$p->scalartyp();
749
-							}
750
-							else
751
-							{
752
-								$pt=$p->kindOf();
753
-							}
754
-						}
755
-						else
756
-						{
757
-							$pt= $in[$n] == 'i4' ? 'int' : strtolower($in[$n]); // dispatch maps never use i4...
758
-						}
759
-
760
-						// param index is $n+1, as first member of sig is return type
761
-						if($pt != $cursig[$n+1] && $cursig[$n+1] != $GLOBALS['xmlrpcValue'])
762
-						{
763
-							$itsOK=0;
764
-							$pno=$n+1;
765
-							$wanted=$cursig[$n+1];
766
-							$got=$pt;
767
-							break;
768
-						}
769
-					}
770
-					if($itsOK)
771
-					{
772
-						return array(1,'');
773
-					}
774
-				}
775
-			}
776
-			if(isset($wanted))
777
-			{
778
-				return array(0, "Wanted ${wanted}, got ${got} at param ${pno}");
779
-			}
780
-			else
781
-			{
782
-				return array(0, "No method signature matches number of parameters");
783
-			}
784
-		}
785
-
786
-		/**
787
-		* Parse http headers received along with xmlrpc request. If needed, inflate request
788
-		* @return null on success or an xmlrpcresp
789
-		* @access private
790
-		*/
791
-		function parseRequestHeaders(&$data, &$req_encoding, &$resp_encoding, &$resp_compression)
792
-		{
793
-			// check if $_SERVER is populated: it might have been disabled via ini file
794
-			// (this is true even when in CLI mode)
795
-			if (count($_SERVER) == 0)
796
-			{
797
-				error_log('XML-RPC: '.__METHOD__.': cannot parse request headers as $_SERVER is not populated');
798
-			}
799
-
800
-			if($this->debug > 1)
801
-			{
802
-				if(function_exists('getallheaders'))
803
-				{
804
-					$this->debugmsg(''); // empty line
805
-					foreach(getallheaders() as $name => $val)
806
-					{
807
-						$this->debugmsg("HEADER: $name: $val");
808
-					}
809
-				}
810
-
811
-			}
812
-
813
-			if(isset($_SERVER['HTTP_CONTENT_ENCODING']))
814
-			{
815
-				$content_encoding = str_replace('x-', '', $_SERVER['HTTP_CONTENT_ENCODING']);
816
-			}
817
-			else
818
-			{
819
-				$content_encoding = '';
820
-			}
821
-
822
-			// check if request body has been compressed and decompress it
823
-			if($content_encoding != '' && strlen($data))
824
-			{
825
-				if($content_encoding == 'deflate' || $content_encoding == 'gzip')
826
-				{
827
-					// if decoding works, use it. else assume data wasn't gzencoded
828
-					if(function_exists('gzinflate') && in_array($content_encoding, $this->accepted_compression))
829
-					{
830
-						if($content_encoding == 'deflate' && $degzdata = @gzuncompress($data))
831
-						{
832
-							$data = $degzdata;
833
-							if($this->debug > 1)
834
-							{
835
-								$this->debugmsg("\n+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
836
-							}
837
-						}
838
-						elseif($content_encoding == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
839
-						{
840
-							$data = $degzdata;
841
-							if($this->debug > 1)
842
-								$this->debugmsg("+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
843
-						}
844
-						else
845
-						{
846
-							$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_decompress_fail'], $GLOBALS['xmlrpcstr']['server_decompress_fail']);
847
-							return $r;
848
-						}
849
-					}
850
-					else
851
-					{
852
-						//error_log('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
853
-						$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_cannot_decompress'], $GLOBALS['xmlrpcstr']['server_cannot_decompress']);
854
-						return $r;
855
-					}
856
-				}
857
-			}
858
-
859
-			// check if client specified accepted charsets, and if we know how to fulfill
860
-			// the request
861
-			if ($this->response_charset_encoding == 'auto')
862
-			{
863
-				$resp_encoding = '';
864
-				if (isset($_SERVER['HTTP_ACCEPT_CHARSET']))
865
-				{
866
-					// here we should check if we can match the client-requested encoding
867
-					// with the encodings we know we can generate.
868
-					/// @todo we should parse q=0.x preferences instead of getting first charset specified...
869
-					$client_accepted_charsets = explode(',', strtoupper($_SERVER['HTTP_ACCEPT_CHARSET']));
870
-					// Give preference to internal encoding
871
-					$known_charsets = array($GLOBALS['xmlrpc_internalencoding'], 'UTF-8', 'ISO-8859-1', 'US-ASCII');
872
-					foreach ($known_charsets as $charset)
873
-					{
874
-						foreach ($client_accepted_charsets as $accepted)
875
-							if (strpos($accepted, $charset) === 0)
876
-							{
877
-								$resp_encoding = $charset;
878
-								break;
879
-							}
880
-						if ($resp_encoding)
881
-							break;
882
-					}
883
-				}
884
-			}
885
-			else
886
-			{
887
-				$resp_encoding = $this->response_charset_encoding;
888
-			}
889
-
890
-			if (isset($_SERVER['HTTP_ACCEPT_ENCODING']))
891
-			{
892
-				$resp_compression = $_SERVER['HTTP_ACCEPT_ENCODING'];
893
-			}
894
-			else
895
-			{
896
-				$resp_compression = '';
897
-			}
898
-
899
-			// 'guestimate' request encoding
900
-			/// @todo check if mbstring is enabled and automagic input conversion is on: it might mingle with this check???
901
-			$req_encoding = guess_encoding(isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '',
902
-				$data);
903
-
904
-			return null;
905
-		}
906
-
907
-		/**
908
-		* Parse an xml chunk containing an xmlrpc request and execute the corresponding
909
-		* php function registered with the server
910
-		* @param string $data the xml request
911
-		* @param string $req_encoding (optional) the charset encoding of the xml request
912
-		* @return xmlrpcresp
913
-		* @access private
914
-		*/
915
-		function parseRequest($data, $req_encoding='')
916
-		{
917
-			// 2005/05/07 commented and moved into caller function code
918
-			//if($data=='')
919
-			//{
920
-			//	$data=$GLOBALS['HTTP_RAW_POST_DATA'];
921
-			//}
922
-
923
-			// G. Giunta 2005/02/13: we do NOT expect to receive html entities
924
-			// so we do not try to convert them into xml character entities
925
-			//$data = xmlrpc_html_entity_xlate($data);
926
-
927
-			$GLOBALS['_xh']=array();
928
-			$GLOBALS['_xh']['ac']='';
929
-			$GLOBALS['_xh']['stack']=array();
930
-			$GLOBALS['_xh']['valuestack'] = array();
931
-			$GLOBALS['_xh']['params']=array();
932
-			$GLOBALS['_xh']['pt']=array();
933
-			$GLOBALS['_xh']['isf']=0;
934
-			$GLOBALS['_xh']['isf_reason']='';
935
-			$GLOBALS['_xh']['method']=false; // so we can check later if we got a methodname or not
936
-			$GLOBALS['_xh']['rt']='';
937
-
938
-			// decompose incoming XML into request structure
939
-			if ($req_encoding != '')
940
-			{
941
-				if (!in_array($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
942
-				// the following code might be better for mb_string enabled installs, but
943
-				// makes the lib about 200% slower...
944
-				//if (!is_valid_charset($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
945
-				{
946
-					error_log('XML-RPC: '.__METHOD__.': invalid charset encoding of received request: '.$req_encoding);
947
-					$req_encoding = $GLOBALS['xmlrpc_defencoding'];
948
-				}
949
-				/// @BUG this will fail on PHP 5 if charset is not specified in the xml prologue,
950
-				// the encoding is not UTF8 and there are non-ascii chars in the text...
951
-				/// @todo use an ampty string for php 5 ???
952
-				$parser = xml_parser_create($req_encoding);
953
-			}
954
-			else
955
-			{
956
-				$parser = xml_parser_create();
957
-			}
958
-
959
-			xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
960
-			// G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
961
-			// the xml parser to give us back data in the expected charset
962
-			// What if internal encoding is not in one of the 3 allowed?
963
-			// we use the broadest one, ie. utf8
964
-			// This allows to send data which is native in various charset,
965
-			// by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
966
-			if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
967
-			{
968
-				xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
969
-			}
970
-			else
971
-			{
972
-				xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
973
-			}
974
-
975
-			if ($this->functions_parameters_type != 'xmlrpcvals')
976
-				xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
977
-			else
978
-				xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
979
-			xml_set_character_data_handler($parser, 'xmlrpc_cd');
980
-			xml_set_default_handler($parser, 'xmlrpc_dh');
981
-			if(!xml_parse($parser, $data, 1))
982
-			{
983
-				// return XML error as a faultCode
984
-				$r=new xmlrpcresp(0,
985
-				$GLOBALS['xmlrpcerrxml']+xml_get_error_code($parser),
986
-				sprintf('XML error: %s at line %d, column %d',
987
-					xml_error_string(xml_get_error_code($parser)),
988
-					xml_get_current_line_number($parser), xml_get_current_column_number($parser)));
989
-				xml_parser_free($parser);
990
-			}
991
-			elseif ($GLOBALS['_xh']['isf'])
992
-			{
993
-				xml_parser_free($parser);
994
-				$r=new xmlrpcresp(0,
995
-					$GLOBALS['xmlrpcerr']['invalid_request'],
996
-					$GLOBALS['xmlrpcstr']['invalid_request'] . ' ' . $GLOBALS['_xh']['isf_reason']);
997
-			}
998
-			else
999
-			{
1000
-				xml_parser_free($parser);
1001
-				// small layering violation in favor of speed and memory usage:
1002
-				// we should allow the 'execute' method handle this, but in the
1003
-				// most common scenario (xmlrpcvals type server with some methods
1004
-				// registered as phpvals) that would mean a useless encode+decode pass
1005
-				if ($this->functions_parameters_type != 'xmlrpcvals' || (isset($this->dmap[$GLOBALS['_xh']['method']]['parameters_type']) && ($this->dmap[$GLOBALS['_xh']['method']]['parameters_type'] == 'phpvals')))
1006
-				{
1007
-					if($this->debug > 1)
1008
-					{
1009
-						$this->debugmsg("\n+++PARSED+++\n".var_export($GLOBALS['_xh']['params'], true)."\n+++END+++");
1010
-					}
1011
-					$r = $this->execute($GLOBALS['_xh']['method'], $GLOBALS['_xh']['params'], $GLOBALS['_xh']['pt']);
1012
-				}
1013
-				else
1014
-				{
1015
-					// build an xmlrpcmsg object with data parsed from xml
1016
-					$m=new xmlrpcmsg($GLOBALS['_xh']['method']);
1017
-					// now add parameters in
1018
-					for($i=0; $i<count($GLOBALS['_xh']['params']); $i++)
1019
-					{
1020
-						$m->addParam($GLOBALS['_xh']['params'][$i]);
1021
-					}
1022
-
1023
-					if($this->debug > 1)
1024
-					{
1025
-						$this->debugmsg("\n+++PARSED+++\n".var_export($m, true)."\n+++END+++");
1026
-					}
1027
-					$r = $this->execute($m);
1028
-				}
1029
-			}
1030
-			return $r;
1031
-		}
1032
-
1033
-		/**
1034
-		* Execute a method invoked by the client, checking parameters used
1035
-		* @param mixed $m either an xmlrpcmsg obj or a method name
1036
-		* @param array $params array with method parameters as php types (if m is method name only)
1037
-		* @param array $paramtypes array with xmlrpc types of method parameters (if m is method name only)
1038
-		* @return xmlrpcresp
1039
-		* @access private
1040
-		*/
1041
-		function execute($m, $params=null, $paramtypes=null)
1042
-		{
1043
-			if (is_object($m))
1044
-			{
1045
-				$methName = $m->method();
1046
-			}
1047
-			else
1048
-			{
1049
-				$methName = $m;
1050
-			}
1051
-			$sysCall = $this->allow_system_funcs && (strpos($methName, "system.") === 0);
1052
-			$dmap = $sysCall ? $GLOBALS['_xmlrpcs_dmap'] : $this->dmap;
1053
-
1054
-			if(!isset($dmap[$methName]['function']))
1055
-			{
1056
-				// No such method
1057
-				return new xmlrpcresp(0,
1058
-					$GLOBALS['xmlrpcerr']['unknown_method'],
1059
-					$GLOBALS['xmlrpcstr']['unknown_method']);
1060
-			}
1061
-
1062
-			// Check signature
1063
-			if(isset($dmap[$methName]['signature']))
1064
-			{
1065
-				$sig = $dmap[$methName]['signature'];
1066
-				if (is_object($m))
1067
-				{
1068
-					list($ok, $errstr) = $this->verifySignature($m, $sig);
1069
-				}
1070
-				else
1071
-				{
1072
-					list($ok, $errstr) = $this->verifySignature($paramtypes, $sig);
1073
-				}
1074
-				if(!$ok)
1075
-				{
1076
-					// Didn't match.
1077
-					return new xmlrpcresp(
1078
-						0,
1079
-						$GLOBALS['xmlrpcerr']['incorrect_params'],
1080
-						$GLOBALS['xmlrpcstr']['incorrect_params'] . ": ${errstr}"
1081
-					);
1082
-				}
1083
-			}
1084
-
1085
-			$func = $dmap[$methName]['function'];
1086
-			// let the 'class::function' syntax be accepted in dispatch maps
1087
-			if(is_string($func) && strpos($func, '::'))
1088
-			{
1089
-				$func = explode('::', $func);
1090
-			}
1091
-			// verify that function to be invoked is in fact callable
1092
-			if(!is_callable($func))
1093
-			{
1094
-				error_log("XML-RPC: ".__METHOD__.": function $func registered as method handler is not callable");
1095
-				return new xmlrpcresp(
1096
-					0,
1097
-					$GLOBALS['xmlrpcerr']['server_error'],
1098
-					$GLOBALS['xmlrpcstr']['server_error'] . ": no function matches method"
1099
-				);
1100
-			}
1101
-
1102
-			// If debug level is 3, we should catch all errors generated during
1103
-			// processing of user function, and log them as part of response
1104
-			if($this->debug > 2)
1105
-			{
1106
-				$GLOBALS['_xmlrpcs_prev_ehandler'] = set_error_handler('_xmlrpcs_errorHandler');
1107
-			}
1108
-			try
1109
-			{
1110
-				// Allow mixed-convention servers
1111
-				if (is_object($m))
1112
-				{
1113
-					if($sysCall)
1114
-					{
1115
-						$r = call_user_func($func, $this, $m);
1116
-					}
1117
-					else
1118
-					{
1119
-						$r = call_user_func($func, $m);
1120
-					}
1121
-					if (!is_a($r, 'xmlrpcresp'))
1122
-					{
1123
-						error_log("XML-RPC: ".__METHOD__.": function $func registered as method handler does not return an xmlrpcresp object");
1124
-						if (is_a($r, 'xmlrpcval'))
1125
-						{
1126
-							$r = new xmlrpcresp($r);
1127
-						}
1128
-						else
1129
-						{
1130
-							$r = new xmlrpcresp(
1131
-								0,
1132
-								$GLOBALS['xmlrpcerr']['server_error'],
1133
-								$GLOBALS['xmlrpcstr']['server_error'] . ": function does not return xmlrpcresp object"
1134
-							);
1135
-						}
1136
-					}
1137
-				}
1138
-				else
1139
-				{
1140
-					// call a 'plain php' function
1141
-					if($sysCall)
1142
-					{
1143
-						array_unshift($params, $this);
1144
-						$r = call_user_func_array($func, $params);
1145
-					}
1146
-					else
1147
-					{
1148
-						// 3rd API convention for method-handling functions: EPI-style
1149
-						if ($this->functions_parameters_type == 'epivals')
1150
-						{
1151
-							$r = call_user_func_array($func, array($methName, $params, $this->user_data));
1152
-							// mimic EPI behaviour: if we get an array that looks like an error, make it
1153
-							// an eror response
1154
-							if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r))
1155
-							{
1156
-								$r = new xmlrpcresp(0, (integer)$r['faultCode'], (string)$r['faultString']);
1157
-							}
1158
-							else
1159
-							{
1160
-								// functions using EPI api should NOT return resp objects,
1161
-								// so make sure we encode the return type correctly
1162
-								$r = new xmlrpcresp(php_xmlrpc_encode($r, array('extension_api')));
1163
-							}
1164
-						}
1165
-						else
1166
-						{
1167
-							$r = call_user_func_array($func, $params);
1168
-						}
1169
-					}
1170
-					// the return type can be either an xmlrpcresp object or a plain php value...
1171
-					if (!is_a($r, 'xmlrpcresp'))
1172
-					{
1173
-						// what should we assume here about automatic encoding of datetimes
1174
-						// and php classes instances???
1175
-						$r = new xmlrpcresp(php_xmlrpc_encode($r, $this->phpvals_encoding_options));
1176
-					}
1177
-				}
1178
-			}
1179
-			catch(Exception $e)
1180
-			{
1181
-				// (barring errors in the lib) an uncatched exception happened
1182
-				// in the called function, we wrap it in a proper error-response
1183
-				switch($this->exception_handling)
1184
-				{
1185
-					case 2:
1186
-						throw $e;
1187
-						break;
1188
-					case 1:
1189
-						$r = new xmlrpcresp(0, $e->getCode(), $e->getMessage());
1190
-						break;
1191
-					default:
1192
-						$r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_error'], $GLOBALS['xmlrpcstr']['server_error']);
1193
-				}
1194
-			}
1195
-			if($this->debug > 2)
1196
-			{
1197
-				// note: restore the error handler we found before calling the
1198
-				// user func, even if it has been changed inside the func itself
1199
-				if($GLOBALS['_xmlrpcs_prev_ehandler'])
1200
-				{
1201
-					set_error_handler($GLOBALS['_xmlrpcs_prev_ehandler']);
1202
-				}
1203
-				else
1204
-				{
1205
-					restore_error_handler();
1206
-				}
1207
-			}
1208
-			return $r;
1209
-		}
1210
-
1211
-		/**
1212
-		* add a string to the 'internal debug message' (separate from 'user debug message')
1213
-		* @param string $strings
1214
-		* @access private
1215
-		*/
1216
-		function debugmsg($string)
1217
-		{
1218
-			$this->debug_info .= $string."\n";
1219
-		}
1220
-
1221
-		/**
1222
-		* @access private
1223
-		*/
1224
-		function xml_header($charset_encoding='')
1225
-		{
1226
-			if ($charset_encoding != '')
1227
-			{
1228
-				return "<?xml version=\"1.0\" encoding=\"$charset_encoding\"?" . ">\n";
1229
-			}
1230
-			else
1231
-			{
1232
-				return "<?xml version=\"1.0\"?" . ">\n";
1233
-			}
1234
-		}
1235
-
1236
-		/**
1237
-		* A debugging routine: just echoes back the input packet as a string value
1238
-		* DEPRECATED!
1239
-		*/
1240
-		function echoInput()
1241
-		{
1242
-			$r=new xmlrpcresp(new xmlrpcval( "'Aha said I: '" . $GLOBALS['HTTP_RAW_POST_DATA'], 'string'));
1243
-			print $r->serialize();
1244
-		}
1245
-	}
523
+            if($dispMap)
524
+            {
525
+                $this->dmap = $dispMap;
526
+                if($serviceNow)
527
+                {
528
+                    $this->service();
529
+                }
530
+            }
531
+        }
532
+
533
+        /**
534
+         * Set debug level of server.
535
+         * @param integer $in debug lvl: determines info added to xmlrpc responses (as xml comments)
536
+         * 0 = no debug info,
537
+         * 1 = msgs set from user with debugmsg(),
538
+         * 2 = add complete xmlrpc request (headers and body),
539
+         * 3 = add also all processing warnings happened during method processing
540
+         * (NB: this involves setting a custom error handler, and might interfere
541
+         * with the standard processing of the php function exposed as method. In
542
+         * particular, triggering an USER_ERROR level error will not halt script
543
+         * execution anymore, but just end up logged in the xmlrpc response)
544
+         * Note that info added at elevel 2 and 3 will be base64 encoded
545
+         * @access public
546
+         */
547
+        function setDebug($in)
548
+        {
549
+            $this->debug=$in;
550
+        }
551
+
552
+        /**
553
+         * Return a string with the serialized representation of all debug info
554
+         * @param string $charset_encoding the target charset encoding for the serialization
555
+         * @return string an XML comment (or two)
556
+         */
557
+        function serializeDebug($charset_encoding='')
558
+        {
559
+            // Tough encoding problem: which internal charset should we assume for debug info?
560
+            // It might contain a copy of raw data received from client, ie with unknown encoding,
561
+            // intermixed with php generated data and user generated data...
562
+            // so we split it: system debug is base 64 encoded,
563
+            // user debug info should be encoded by the end user using the INTERNAL_ENCODING
564
+            $out = '';
565
+            if ($this->debug_info != '')
566
+            {
567
+                $out .= "<!-- SERVER DEBUG INFO (BASE64 ENCODED):\n".base64_encode($this->debug_info)."\n-->\n";
568
+            }
569
+            if($GLOBALS['_xmlrpc_debuginfo']!='')
570
+            {
571
+
572
+                $out .= "<!-- DEBUG INFO:\n" . xmlrpc_encode_entitites(str_replace('--', '_-', $GLOBALS['_xmlrpc_debuginfo']), $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "\n-->\n";
573
+                // NB: a better solution MIGHT be to use CDATA, but we need to insert it
574
+                // into return payload AFTER the beginning tag
575
+                //$out .= "<![CDATA[ DEBUG INFO:\n\n" . str_replace(']]>', ']_]_>', $GLOBALS['_xmlrpc_debuginfo']) . "\n]]>\n";
576
+            }
577
+            return $out;
578
+        }
579
+
580
+        /**
581
+         * Execute the xmlrpc request, printing the response
582
+         * @param string $data the request body. If null, the http POST request will be examined
583
+         * @return xmlrpcresp the response object (usually not used by caller...)
584
+         * @access public
585
+         */
586
+        function service($data=null, $return_payload=false)
587
+        {
588
+            if ($data === null)
589
+            {
590
+                // workaround for a known bug in php ver. 5.2.2 that broke $HTTP_RAW_POST_DATA
591
+                $ver = phpversion();
592
+                if ($ver[0] >= 5)
593
+                {
594
+                    $data = file_get_contents('php://input');
595
+                }
596
+                else
597
+                {
598
+                    $data = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
599
+                }
600
+            }
601
+            $raw_data = $data;
602
+
603
+            // reset internal debug info
604
+            $this->debug_info = '';
605
+
606
+            // Echo back what we received, before parsing it
607
+            if($this->debug > 1)
608
+            {
609
+                $this->debugmsg("+++GOT+++\n" . $data . "\n+++END+++");
610
+            }
611
+
612
+            $r = $this->parseRequestHeaders($data, $req_charset, $resp_charset, $resp_encoding);
613
+            if (!$r)
614
+            {
615
+                $r=$this->parseRequest($data, $req_charset);
616
+            }
617
+
618
+            // save full body of request into response, for more debugging usages
619
+            $r->raw_data = $raw_data;
620
+
621
+            if($this->debug > 2 && $GLOBALS['_xmlrpcs_occurred_errors'])
622
+            {
623
+                $this->debugmsg("+++PROCESSING ERRORS AND WARNINGS+++\n" .
624
+                    $GLOBALS['_xmlrpcs_occurred_errors'] . "+++END+++");
625
+            }
626
+
627
+            $payload=$this->xml_header($resp_charset);
628
+            if($this->debug > 0)
629
+            {
630
+                $payload = $payload . $this->serializeDebug($resp_charset);
631
+            }
632
+
633
+            // G. Giunta 2006-01-27: do not create response serialization if it has
634
+            // already happened. Helps building json magic
635
+            if (empty($r->payload))
636
+            {
637
+                $r->serialize($resp_charset);
638
+            }
639
+            $payload = $payload . $r->payload;
640
+
641
+            if ($return_payload)
642
+            {
643
+                return $payload;
644
+            }
645
+
646
+            // if we get a warning/error that has output some text before here, then we cannot
647
+            // add a new header. We cannot say we are sending xml, either...
648
+            if(!headers_sent())
649
+            {
650
+                header('Content-Type: '.$r->content_type);
651
+                // we do not know if client actually told us an accepted charset, but if he did
652
+                // we have to tell him what we did
653
+                header("Vary: Accept-Charset");
654
+
655
+                // http compression of output: only
656
+                // if we can do it, and we want to do it, and client asked us to,
657
+                // and php ini settings do not force it already
658
+                $php_no_self_compress = !ini_get('zlib.output_compression') && (ini_get('output_handler') != 'ob_gzhandler');
659
+                if($this->compress_response && function_exists('gzencode') && $resp_encoding != ''
660
+                    && $php_no_self_compress)
661
+                {
662
+                    if(strpos($resp_encoding, 'gzip') !== false)
663
+                    {
664
+                        $payload = gzencode($payload);
665
+                        header("Content-Encoding: gzip");
666
+                        header("Vary: Accept-Encoding");
667
+                    }
668
+                    elseif (strpos($resp_encoding, 'deflate') !== false)
669
+                    {
670
+                        $payload = gzcompress($payload);
671
+                        header("Content-Encoding: deflate");
672
+                        header("Vary: Accept-Encoding");
673
+                    }
674
+                }
675
+
676
+                // do not ouput content-length header if php is compressing output for us:
677
+                // it will mess up measurements
678
+                if($php_no_self_compress)
679
+                {
680
+                    header('Content-Length: ' . (int)strlen($payload));
681
+                }
682
+            }
683
+            else
684
+            {
685
+                error_log('XML-RPC: '.__METHOD__.': http headers already sent before response is fully generated. Check for php warning or error messages');
686
+            }
687
+
688
+            print $payload;
689
+
690
+            // return request, in case subclasses want it
691
+            return $r;
692
+        }
693
+
694
+        /**
695
+         * Add a method to the dispatch map
696
+         * @param string $methodname the name with which the method will be made available
697
+         * @param string $function the php function that will get invoked
698
+         * @param array $sig the array of valid method signatures
699
+         * @param string $doc method documentation
700
+         * @param array $sigdoc the array of valid method signatures docs (one string per param, one for return type)
701
+         * @access public
702
+         */
703
+        function add_to_map($methodname,$function,$sig=null,$doc=false,$sigdoc=false)
704
+        {
705
+            $this->dmap[$methodname] = array(
706
+                'function'	=> $function,
707
+                'docstring' => $doc
708
+            );
709
+            if ($sig)
710
+            {
711
+                $this->dmap[$methodname]['signature'] = $sig;
712
+            }
713
+            if ($sigdoc)
714
+            {
715
+                $this->dmap[$methodname]['signature_docs'] = $sigdoc;
716
+            }
717
+        }
718
+
719
+        /**
720
+         * Verify type and number of parameters received against a list of known signatures
721
+         * @param array $in array of either xmlrpcval objects or xmlrpc type definitions
722
+         * @param array $sig array of known signatures to match against
723
+         * @access private
724
+         */
725
+        function verifySignature($in, $sig)
726
+        {
727
+            // check each possible signature in turn
728
+            if (is_object($in))
729
+            {
730
+                $numParams = $in->getNumParams();
731
+            }
732
+            else
733
+            {
734
+                $numParams = count($in);
735
+            }
736
+            foreach($sig as $cursig)
737
+            {
738
+                if(count($cursig)==$numParams+1)
739
+                {
740
+                    $itsOK=1;
741
+                    for($n=0; $n<$numParams; $n++)
742
+                    {
743
+                        if (is_object($in))
744
+                        {
745
+                            $p=$in->getParam($n);
746
+                            if($p->kindOf() == 'scalar')
747
+                            {
748
+                                $pt=$p->scalartyp();
749
+                            }
750
+                            else
751
+                            {
752
+                                $pt=$p->kindOf();
753
+                            }
754
+                        }
755
+                        else
756
+                        {
757
+                            $pt= $in[$n] == 'i4' ? 'int' : strtolower($in[$n]); // dispatch maps never use i4...
758
+                        }
759
+
760
+                        // param index is $n+1, as first member of sig is return type
761
+                        if($pt != $cursig[$n+1] && $cursig[$n+1] != $GLOBALS['xmlrpcValue'])
762
+                        {
763
+                            $itsOK=0;
764
+                            $pno=$n+1;
765
+                            $wanted=$cursig[$n+1];
766
+                            $got=$pt;
767
+                            break;
768
+                        }
769
+                    }
770
+                    if($itsOK)
771
+                    {
772
+                        return array(1,'');
773
+                    }
774
+                }
775
+            }
776
+            if(isset($wanted))
777
+            {
778
+                return array(0, "Wanted ${wanted}, got ${got} at param ${pno}");
779
+            }
780
+            else
781
+            {
782
+                return array(0, "No method signature matches number of parameters");
783
+            }
784
+        }
785
+
786
+        /**
787
+         * Parse http headers received along with xmlrpc request. If needed, inflate request
788
+         * @return null on success or an xmlrpcresp
789
+         * @access private
790
+         */
791
+        function parseRequestHeaders(&$data, &$req_encoding, &$resp_encoding, &$resp_compression)
792
+        {
793
+            // check if $_SERVER is populated: it might have been disabled via ini file
794
+            // (this is true even when in CLI mode)
795
+            if (count($_SERVER) == 0)
796
+            {
797
+                error_log('XML-RPC: '.__METHOD__.': cannot parse request headers as $_SERVER is not populated');
798
+            }
799
+
800
+            if($this->debug > 1)
801
+            {
802
+                if(function_exists('getallheaders'))
803
+                {
804
+                    $this->debugmsg(''); // empty line
805
+                    foreach(getallheaders() as $name => $val)
806
+                    {
807
+                        $this->debugmsg("HEADER: $name: $val");
808
+                    }
809
+                }
810
+
811
+            }
812
+
813
+            if(isset($_SERVER['HTTP_CONTENT_ENCODING']))
814
+            {
815
+                $content_encoding = str_replace('x-', '', $_SERVER['HTTP_CONTENT_ENCODING']);
816
+            }
817
+            else
818
+            {
819
+                $content_encoding = '';
820
+            }
821
+
822
+            // check if request body has been compressed and decompress it
823
+            if($content_encoding != '' && strlen($data))
824
+            {
825
+                if($content_encoding == 'deflate' || $content_encoding == 'gzip')
826
+                {
827
+                    // if decoding works, use it. else assume data wasn't gzencoded
828
+                    if(function_exists('gzinflate') && in_array($content_encoding, $this->accepted_compression))
829
+                    {
830
+                        if($content_encoding == 'deflate' && $degzdata = @gzuncompress($data))
831
+                        {
832
+                            $data = $degzdata;
833
+                            if($this->debug > 1)
834
+                            {
835
+                                $this->debugmsg("\n+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
836
+                            }
837
+                        }
838
+                        elseif($content_encoding == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
839
+                        {
840
+                            $data = $degzdata;
841
+                            if($this->debug > 1)
842
+                                $this->debugmsg("+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
843
+                        }
844
+                        else
845
+                        {
846
+                            $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_decompress_fail'], $GLOBALS['xmlrpcstr']['server_decompress_fail']);
847
+                            return $r;
848
+                        }
849
+                    }
850
+                    else
851
+                    {
852
+                        //error_log('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
853
+                        $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_cannot_decompress'], $GLOBALS['xmlrpcstr']['server_cannot_decompress']);
854
+                        return $r;
855
+                    }
856
+                }
857
+            }
858
+
859
+            // check if client specified accepted charsets, and if we know how to fulfill
860
+            // the request
861
+            if ($this->response_charset_encoding == 'auto')
862
+            {
863
+                $resp_encoding = '';
864
+                if (isset($_SERVER['HTTP_ACCEPT_CHARSET']))
865
+                {
866
+                    // here we should check if we can match the client-requested encoding
867
+                    // with the encodings we know we can generate.
868
+                    /// @todo we should parse q=0.x preferences instead of getting first charset specified...
869
+                    $client_accepted_charsets = explode(',', strtoupper($_SERVER['HTTP_ACCEPT_CHARSET']));
870
+                    // Give preference to internal encoding
871
+                    $known_charsets = array($GLOBALS['xmlrpc_internalencoding'], 'UTF-8', 'ISO-8859-1', 'US-ASCII');
872
+                    foreach ($known_charsets as $charset)
873
+                    {
874
+                        foreach ($client_accepted_charsets as $accepted)
875
+                            if (strpos($accepted, $charset) === 0)
876
+                            {
877
+                                $resp_encoding = $charset;
878
+                                break;
879
+                            }
880
+                        if ($resp_encoding)
881
+                            break;
882
+                    }
883
+                }
884
+            }
885
+            else
886
+            {
887
+                $resp_encoding = $this->response_charset_encoding;
888
+            }
889
+
890
+            if (isset($_SERVER['HTTP_ACCEPT_ENCODING']))
891
+            {
892
+                $resp_compression = $_SERVER['HTTP_ACCEPT_ENCODING'];
893
+            }
894
+            else
895
+            {
896
+                $resp_compression = '';
897
+            }
898
+
899
+            // 'guestimate' request encoding
900
+            /// @todo check if mbstring is enabled and automagic input conversion is on: it might mingle with this check???
901
+            $req_encoding = guess_encoding(isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '',
902
+                $data);
903
+
904
+            return null;
905
+        }
906
+
907
+        /**
908
+         * Parse an xml chunk containing an xmlrpc request and execute the corresponding
909
+         * php function registered with the server
910
+         * @param string $data the xml request
911
+         * @param string $req_encoding (optional) the charset encoding of the xml request
912
+         * @return xmlrpcresp
913
+         * @access private
914
+         */
915
+        function parseRequest($data, $req_encoding='')
916
+        {
917
+            // 2005/05/07 commented and moved into caller function code
918
+            //if($data=='')
919
+            //{
920
+            //	$data=$GLOBALS['HTTP_RAW_POST_DATA'];
921
+            //}
922
+
923
+            // G. Giunta 2005/02/13: we do NOT expect to receive html entities
924
+            // so we do not try to convert them into xml character entities
925
+            //$data = xmlrpc_html_entity_xlate($data);
926
+
927
+            $GLOBALS['_xh']=array();
928
+            $GLOBALS['_xh']['ac']='';
929
+            $GLOBALS['_xh']['stack']=array();
930
+            $GLOBALS['_xh']['valuestack'] = array();
931
+            $GLOBALS['_xh']['params']=array();
932
+            $GLOBALS['_xh']['pt']=array();
933
+            $GLOBALS['_xh']['isf']=0;
934
+            $GLOBALS['_xh']['isf_reason']='';
935
+            $GLOBALS['_xh']['method']=false; // so we can check later if we got a methodname or not
936
+            $GLOBALS['_xh']['rt']='';
937
+
938
+            // decompose incoming XML into request structure
939
+            if ($req_encoding != '')
940
+            {
941
+                if (!in_array($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
942
+                // the following code might be better for mb_string enabled installs, but
943
+                // makes the lib about 200% slower...
944
+                //if (!is_valid_charset($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
945
+                {
946
+                    error_log('XML-RPC: '.__METHOD__.': invalid charset encoding of received request: '.$req_encoding);
947
+                    $req_encoding = $GLOBALS['xmlrpc_defencoding'];
948
+                }
949
+                /// @BUG this will fail on PHP 5 if charset is not specified in the xml prologue,
950
+                // the encoding is not UTF8 and there are non-ascii chars in the text...
951
+                /// @todo use an ampty string for php 5 ???
952
+                $parser = xml_parser_create($req_encoding);
953
+            }
954
+            else
955
+            {
956
+                $parser = xml_parser_create();
957
+            }
958
+
959
+            xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
960
+            // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
961
+            // the xml parser to give us back data in the expected charset
962
+            // What if internal encoding is not in one of the 3 allowed?
963
+            // we use the broadest one, ie. utf8
964
+            // This allows to send data which is native in various charset,
965
+            // by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
966
+            if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
967
+            {
968
+                xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
969
+            }
970
+            else
971
+            {
972
+                xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
973
+            }
974
+
975
+            if ($this->functions_parameters_type != 'xmlrpcvals')
976
+                xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
977
+            else
978
+                xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
979
+            xml_set_character_data_handler($parser, 'xmlrpc_cd');
980
+            xml_set_default_handler($parser, 'xmlrpc_dh');
981
+            if(!xml_parse($parser, $data, 1))
982
+            {
983
+                // return XML error as a faultCode
984
+                $r=new xmlrpcresp(0,
985
+                $GLOBALS['xmlrpcerrxml']+xml_get_error_code($parser),
986
+                sprintf('XML error: %s at line %d, column %d',
987
+                    xml_error_string(xml_get_error_code($parser)),
988
+                    xml_get_current_line_number($parser), xml_get_current_column_number($parser)));
989
+                xml_parser_free($parser);
990
+            }
991
+            elseif ($GLOBALS['_xh']['isf'])
992
+            {
993
+                xml_parser_free($parser);
994
+                $r=new xmlrpcresp(0,
995
+                    $GLOBALS['xmlrpcerr']['invalid_request'],
996
+                    $GLOBALS['xmlrpcstr']['invalid_request'] . ' ' . $GLOBALS['_xh']['isf_reason']);
997
+            }
998
+            else
999
+            {
1000
+                xml_parser_free($parser);
1001
+                // small layering violation in favor of speed and memory usage:
1002
+                // we should allow the 'execute' method handle this, but in the
1003
+                // most common scenario (xmlrpcvals type server with some methods
1004
+                // registered as phpvals) that would mean a useless encode+decode pass
1005
+                if ($this->functions_parameters_type != 'xmlrpcvals' || (isset($this->dmap[$GLOBALS['_xh']['method']]['parameters_type']) && ($this->dmap[$GLOBALS['_xh']['method']]['parameters_type'] == 'phpvals')))
1006
+                {
1007
+                    if($this->debug > 1)
1008
+                    {
1009
+                        $this->debugmsg("\n+++PARSED+++\n".var_export($GLOBALS['_xh']['params'], true)."\n+++END+++");
1010
+                    }
1011
+                    $r = $this->execute($GLOBALS['_xh']['method'], $GLOBALS['_xh']['params'], $GLOBALS['_xh']['pt']);
1012
+                }
1013
+                else
1014
+                {
1015
+                    // build an xmlrpcmsg object with data parsed from xml
1016
+                    $m=new xmlrpcmsg($GLOBALS['_xh']['method']);
1017
+                    // now add parameters in
1018
+                    for($i=0; $i<count($GLOBALS['_xh']['params']); $i++)
1019
+                    {
1020
+                        $m->addParam($GLOBALS['_xh']['params'][$i]);
1021
+                    }
1022
+
1023
+                    if($this->debug > 1)
1024
+                    {
1025
+                        $this->debugmsg("\n+++PARSED+++\n".var_export($m, true)."\n+++END+++");
1026
+                    }
1027
+                    $r = $this->execute($m);
1028
+                }
1029
+            }
1030
+            return $r;
1031
+        }
1032
+
1033
+        /**
1034
+         * Execute a method invoked by the client, checking parameters used
1035
+         * @param mixed $m either an xmlrpcmsg obj or a method name
1036
+         * @param array $params array with method parameters as php types (if m is method name only)
1037
+         * @param array $paramtypes array with xmlrpc types of method parameters (if m is method name only)
1038
+         * @return xmlrpcresp
1039
+         * @access private
1040
+         */
1041
+        function execute($m, $params=null, $paramtypes=null)
1042
+        {
1043
+            if (is_object($m))
1044
+            {
1045
+                $methName = $m->method();
1046
+            }
1047
+            else
1048
+            {
1049
+                $methName = $m;
1050
+            }
1051
+            $sysCall = $this->allow_system_funcs && (strpos($methName, "system.") === 0);
1052
+            $dmap = $sysCall ? $GLOBALS['_xmlrpcs_dmap'] : $this->dmap;
1053
+
1054
+            if(!isset($dmap[$methName]['function']))
1055
+            {
1056
+                // No such method
1057
+                return new xmlrpcresp(0,
1058
+                    $GLOBALS['xmlrpcerr']['unknown_method'],
1059
+                    $GLOBALS['xmlrpcstr']['unknown_method']);
1060
+            }
1061
+
1062
+            // Check signature
1063
+            if(isset($dmap[$methName]['signature']))
1064
+            {
1065
+                $sig = $dmap[$methName]['signature'];
1066
+                if (is_object($m))
1067
+                {
1068
+                    list($ok, $errstr) = $this->verifySignature($m, $sig);
1069
+                }
1070
+                else
1071
+                {
1072
+                    list($ok, $errstr) = $this->verifySignature($paramtypes, $sig);
1073
+                }
1074
+                if(!$ok)
1075
+                {
1076
+                    // Didn't match.
1077
+                    return new xmlrpcresp(
1078
+                        0,
1079
+                        $GLOBALS['xmlrpcerr']['incorrect_params'],
1080
+                        $GLOBALS['xmlrpcstr']['incorrect_params'] . ": ${errstr}"
1081
+                    );
1082
+                }
1083
+            }
1084
+
1085
+            $func = $dmap[$methName]['function'];
1086
+            // let the 'class::function' syntax be accepted in dispatch maps
1087
+            if(is_string($func) && strpos($func, '::'))
1088
+            {
1089
+                $func = explode('::', $func);
1090
+            }
1091
+            // verify that function to be invoked is in fact callable
1092
+            if(!is_callable($func))
1093
+            {
1094
+                error_log("XML-RPC: ".__METHOD__.": function $func registered as method handler is not callable");
1095
+                return new xmlrpcresp(
1096
+                    0,
1097
+                    $GLOBALS['xmlrpcerr']['server_error'],
1098
+                    $GLOBALS['xmlrpcstr']['server_error'] . ": no function matches method"
1099
+                );
1100
+            }
1101
+
1102
+            // If debug level is 3, we should catch all errors generated during
1103
+            // processing of user function, and log them as part of response
1104
+            if($this->debug > 2)
1105
+            {
1106
+                $GLOBALS['_xmlrpcs_prev_ehandler'] = set_error_handler('_xmlrpcs_errorHandler');
1107
+            }
1108
+            try
1109
+            {
1110
+                // Allow mixed-convention servers
1111
+                if (is_object($m))
1112
+                {
1113
+                    if($sysCall)
1114
+                    {
1115
+                        $r = call_user_func($func, $this, $m);
1116
+                    }
1117
+                    else
1118
+                    {
1119
+                        $r = call_user_func($func, $m);
1120
+                    }
1121
+                    if (!is_a($r, 'xmlrpcresp'))
1122
+                    {
1123
+                        error_log("XML-RPC: ".__METHOD__.": function $func registered as method handler does not return an xmlrpcresp object");
1124
+                        if (is_a($r, 'xmlrpcval'))
1125
+                        {
1126
+                            $r = new xmlrpcresp($r);
1127
+                        }
1128
+                        else
1129
+                        {
1130
+                            $r = new xmlrpcresp(
1131
+                                0,
1132
+                                $GLOBALS['xmlrpcerr']['server_error'],
1133
+                                $GLOBALS['xmlrpcstr']['server_error'] . ": function does not return xmlrpcresp object"
1134
+                            );
1135
+                        }
1136
+                    }
1137
+                }
1138
+                else
1139
+                {
1140
+                    // call a 'plain php' function
1141
+                    if($sysCall)
1142
+                    {
1143
+                        array_unshift($params, $this);
1144
+                        $r = call_user_func_array($func, $params);
1145
+                    }
1146
+                    else
1147
+                    {
1148
+                        // 3rd API convention for method-handling functions: EPI-style
1149
+                        if ($this->functions_parameters_type == 'epivals')
1150
+                        {
1151
+                            $r = call_user_func_array($func, array($methName, $params, $this->user_data));
1152
+                            // mimic EPI behaviour: if we get an array that looks like an error, make it
1153
+                            // an eror response
1154
+                            if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r))
1155
+                            {
1156
+                                $r = new xmlrpcresp(0, (integer)$r['faultCode'], (string)$r['faultString']);
1157
+                            }
1158
+                            else
1159
+                            {
1160
+                                // functions using EPI api should NOT return resp objects,
1161
+                                // so make sure we encode the return type correctly
1162
+                                $r = new xmlrpcresp(php_xmlrpc_encode($r, array('extension_api')));
1163
+                            }
1164
+                        }
1165
+                        else
1166
+                        {
1167
+                            $r = call_user_func_array($func, $params);
1168
+                        }
1169
+                    }
1170
+                    // the return type can be either an xmlrpcresp object or a plain php value...
1171
+                    if (!is_a($r, 'xmlrpcresp'))
1172
+                    {
1173
+                        // what should we assume here about automatic encoding of datetimes
1174
+                        // and php classes instances???
1175
+                        $r = new xmlrpcresp(php_xmlrpc_encode($r, $this->phpvals_encoding_options));
1176
+                    }
1177
+                }
1178
+            }
1179
+            catch(Exception $e)
1180
+            {
1181
+                // (barring errors in the lib) an uncatched exception happened
1182
+                // in the called function, we wrap it in a proper error-response
1183
+                switch($this->exception_handling)
1184
+                {
1185
+                    case 2:
1186
+                        throw $e;
1187
+                        break;
1188
+                    case 1:
1189
+                        $r = new xmlrpcresp(0, $e->getCode(), $e->getMessage());
1190
+                        break;
1191
+                    default:
1192
+                        $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_error'], $GLOBALS['xmlrpcstr']['server_error']);
1193
+                }
1194
+            }
1195
+            if($this->debug > 2)
1196
+            {
1197
+                // note: restore the error handler we found before calling the
1198
+                // user func, even if it has been changed inside the func itself
1199
+                if($GLOBALS['_xmlrpcs_prev_ehandler'])
1200
+                {
1201
+                    set_error_handler($GLOBALS['_xmlrpcs_prev_ehandler']);
1202
+                }
1203
+                else
1204
+                {
1205
+                    restore_error_handler();
1206
+                }
1207
+            }
1208
+            return $r;
1209
+        }
1210
+
1211
+        /**
1212
+         * add a string to the 'internal debug message' (separate from 'user debug message')
1213
+         * @param string $strings
1214
+         * @access private
1215
+         */
1216
+        function debugmsg($string)
1217
+        {
1218
+            $this->debug_info .= $string."\n";
1219
+        }
1220
+
1221
+        /**
1222
+         * @access private
1223
+         */
1224
+        function xml_header($charset_encoding='')
1225
+        {
1226
+            if ($charset_encoding != '')
1227
+            {
1228
+                return "<?xml version=\"1.0\" encoding=\"$charset_encoding\"?" . ">\n";
1229
+            }
1230
+            else
1231
+            {
1232
+                return "<?xml version=\"1.0\"?" . ">\n";
1233
+            }
1234
+        }
1235
+
1236
+        /**
1237
+         * A debugging routine: just echoes back the input packet as a string value
1238
+         * DEPRECATED!
1239
+         */
1240
+        function echoInput()
1241
+        {
1242
+            $r=new xmlrpcresp(new xmlrpcval( "'Aha said I: '" . $GLOBALS['HTTP_RAW_POST_DATA'], 'string'));
1243
+            print $r->serialize();
1244
+        }
1245
+    }
1246 1246
 ?>
1247 1247
\ No newline at end of file
Please login to merge, or discard this patch.
Components/Klarna/Klarna.php 1 patch
Indentation   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -4425,15 +4425,15 @@
 block discarded – undo
4425 4425
         }
4426 4426
     }
4427 4427
 
4428
-     /**
4429
-     * Check so required argument is supplied.
4430
-     *
4431
-     * @param string $argument argument to check
4432
-     * @param string $name     name of argument
4433
-     *
4434
-     * @throws Klarna_ArgumentNotSetException
4435
-     * @return void
4436
-     */
4428
+        /**
4429
+         * Check so required argument is supplied.
4430
+         *
4431
+         * @param string $argument argument to check
4432
+         * @param string $name     name of argument
4433
+         *
4434
+         * @throws Klarna_ArgumentNotSetException
4435
+         * @return void
4436
+         */
4437 4437
     private function _checkArgument($argument, $name)
4438 4438
     {
4439 4439
         if (!is_string($argument)) {
Please login to merge, or discard this patch.
Components/Klarna/Country.php 1 patch
Indentation   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -248,11 +248,11 @@
 block discarded – undo
248 248
 
249 249
     private static $_tlcFlip = array();
250 250
 
251
-      /**
252
-     * Cache for the flipped country array
253
-     *
254
-     * @var array
255
-     */
251
+        /**
252
+         * Cache for the flipped country array
253
+         *
254
+         * @var array
255
+         */
256 256
     private static $_countryFlip = array();
257 257
 
258 258
     /**
Please login to merge, or discard this patch.
Components/Klarna/Exceptions.php 1 patch
Indentation   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -724,10 +724,10 @@
 block discarded – undo
724 724
 class Klarna_XMLParseException extends KlarnaException
725 725
 {
726 726
         /**
727
-     * Constructor
728
-     *
729
-     * @param string $file filename
730
-     */
727
+         * Constructor
728
+         *
729
+         * @param string $file filename
730
+         */
731 731
     public function __construct($file)
732 732
     {
733 733
         parent::__construct("Unable to parse XML file: {$file}!");
Please login to merge, or discard this patch.
Components/KlarnaCheckout/Checkout/ConnectionErrorException.php 1 patch
Indentation   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -26,17 +26,17 @@
 block discarded – undo
26 26
  * @link      http://integration.klarna.com/
27 27
  */
28 28
 
29
- /**
30
-  * Connection exception
31
-  *
32
-  * @category  Payment
33
-  * @package   Klarna_Checkout
34
-  * @author    Rickard D. <[email protected]>
35
-  * @author    Christer G. <[email protected]>
36
-  * @copyright 2012 Klarna AB
37
-  * @license   http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
38
-  * @link      http://integration.klarna.com/
39
-  */
29
+    /**
30
+     * Connection exception
31
+     *
32
+     * @category  Payment
33
+     * @package   Klarna_Checkout
34
+     * @author    Rickard D. <[email protected]>
35
+     * @author    Christer G. <[email protected]>
36
+     * @copyright 2012 Klarna AB
37
+     * @license   http://www.apache.org/licenses/LICENSE-2.0 Apache license v2.0
38
+     * @link      http://integration.klarna.com/
39
+     */
40 40
 class Klarna_Checkout_ConnectionErrorException extends Klarna_Checkout_Exception
41 41
 {
42 42
 
Please login to merge, or discard this patch.
Bootstrap.php 1 patch
Indentation   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -491,7 +491,7 @@  discard block
 block discarded – undo
491 491
         $args->setReturn($builder);
492 492
     }
493 493
 
494
-     public function onGetStreetSplitService()
494
+        public function onGetStreetSplitService()
495 495
     {
496 496
         $this->Application()->Loader()->registerNamespace(
497 497
             'Shopware\Components',
@@ -1364,12 +1364,12 @@  discard block
 block discarded – undo
1364 1364
         /** @var Enlight_Components_Session_Namespace $session */
1365 1365
         $session = $this->Application()->Session();
1366 1366
 
1367
-         // Switch User to external PaymentId after registration
1368
-         if (isset($session['KlarnaExternalPaymentId'])) {
1367
+            // Switch User to external PaymentId after registration
1368
+            if (isset($session['KlarnaExternalPaymentId'])) {
1369 1369
                 $this->savePayment($session['KlarnaExternalPaymentId']);
1370 1370
         }
1371 1371
 
1372
-       $view->assign('klarnaRedirect', $request->has('klarnaRedirect') && $request->getParam('klarnaRedirect') == 1);
1372
+        $view->assign('klarnaRedirect', $request->has('klarnaRedirect') && $request->getParam('klarnaRedirect') == 1);
1373 1373
     }
1374 1374
     
1375 1375
 
@@ -1460,7 +1460,7 @@  discard block
 block discarded – undo
1460 1460
             $userData['billingaddress']['country'] =$country;   
1461 1461
             $userData['shippingaddress']['countryID'] = $session['sChangedCountry'];
1462 1462
             $userData['billingaddress']['countryID'] = $session['sChangedCountry'];
1463
-         }
1463
+            }
1464 1464
         
1465 1465
         $orderVariables['sUserData'] = $userData;
1466 1466
         $testbreakpoint = 0;
@@ -2385,14 +2385,14 @@  discard block
 block discarded – undo
2385 2385
                 } else {
2386 2386
                     $url = $front->Router()->assemble(array('controller' => 'index', 'module' => 'frontend'));
2387 2387
                 }
2388
-		if (version_compare(Shopware::VERSION, '5.2.0', '>=')) {
2388
+        if (version_compare(Shopware::VERSION, '5.2.0', '>=')) {
2389 2389
             # make sure me get only the image name, path is set in case of upgarde from SW.5.1 to 5.2
2390 2390
             $parts = explode('/', $media);
2391 2391
             $end = end($parts);
2392
-			$media = $url . 'media/image/' . $end;
2393
-		} else {
2394
-	                $media = $url . $media;
2395
-		}
2392
+            $media = $url . 'media/image/' . $end;
2393
+        } else {
2394
+                    $media = $url . $media;
2395
+        }
2396 2396
             }
2397 2397
 
2398 2398
             return $media;
Please login to merge, or discard this patch.
Controllers/Frontend/PaymentKlarna.php 1 patch
Indentation   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -132,7 +132,7 @@  discard block
 block discarded – undo
132 132
     public function showIframeAction() {
133 133
 
134 134
         $this->basket = $this->get('modules')->Basket()->sGetBasket();
135
-         $preFill = $this->config['preFillCheckout'];
135
+            $preFill = $this->config['preFillCheckout'];
136 136
         if ($this->isUserLoggedIn()){
137 137
             $user = Shopware()->Modules()->Admin()->sGetUserData();
138 138
         }
@@ -283,7 +283,7 @@  discard block
 block discarded – undo
283 283
 
284 284
         // Klarna Prefill checkBox
285 285
         if ($this->isUserLoggedIn()) {
286
-               $this->View()->assign('KlarnaPreFillSelect', !$this->session['klarnaPreFill']);
286
+                $this->View()->assign('KlarnaPreFillSelect', !$this->session['klarnaPreFill']);
287 287
         }
288 288
 
289 289
         // Iframe Backend Config
@@ -403,7 +403,7 @@  discard block
 block discarded – undo
403 403
         if ($this->Request()->getParam('sArticle') && $this->Request()->getParam('sQuantity')) {
404 404
             $this->View()->sBasketInfo = $basketObj->sUpdateArticle($this->Request()->getParam('sArticle'), $this->Request()->getParam('sQuantity'));
405 405
         }
406
-       $this->redirect(['action' => $this->Request()->getParam('sTargetAction', 'showIframe')]);
406
+        $this->redirect(['action' => $this->Request()->getParam('sTargetAction', 'showIframe')]);
407 407
     }
408 408
 
409 409
     /**
@@ -576,7 +576,7 @@  discard block
 block discarded – undo
576 576
         return array("id" => $this->session['sState']);
577 577
     }
578 578
 
579
-   /**
579
+    /**
580 580
      * @param Klarna_Checkout_Order $order
581 581
      */
582 582
     public function createAccount($order = null, $checkLoginState=true)
@@ -704,8 +704,8 @@  discard block
 block discarded – undo
704 704
                     }
705 705
                     $module->sSYSTEM->_POST = $data['billing'];
706 706
                     if (Shopware::VERSION === '___VERSION___' || version_compare(Shopware::VERSION, '5.2.0', '>=')) {                
707
-                         $userId = $session->offsetGet('sUserId');
708
-                         $this->updateBilling($userId, $data['billing']);
707
+                            $userId = $session->offsetGet('sUserId');
708
+                            $this->updateBilling($userId, $data['billing']);
709 709
                     } else{
710 710
                         $module->sUpdateBilling();
711 711
                     }
@@ -817,13 +817,13 @@  discard block
 block discarded – undo
817 817
 
818 818
         // get updated password; it is md5 randomized after register
819 819
         $getUser = Shopware()->Models()->getRepository('Shopware\Models\Customer\Customer')->findOneBy(
820
-		array('email' =>  $data['auth']['email'])
821
-		);
820
+        array('email' =>  $data['auth']['email'])
821
+        );
822 822
 
823
-       $data['auth']['password']= $getUser->getPassword();
824
-       $data['auth']['passwordMD5']= $getUser->getPassword();
825
-       $data['auth']['encoderName'] = 'md5';
826
-       return $data;
823
+        $data['auth']['password']= $getUser->getPassword();
824
+        $data['auth']['passwordMD5']= $getUser->getPassword();
825
+        $data['auth']['encoderName'] = 'md5';
826
+        return $data;
827 827
     }
828 828
 
829 829
 
@@ -845,19 +845,19 @@  discard block
 block discarded – undo
845 845
         unset ($data['billing']);        
846 846
 
847 847
         $customer = Shopware()->Models()->getRepository('Shopware\Models\Customer\Customer')->findOneBy(
848
-		array('id' =>  $userId)
849
-		);
848
+        array('id' =>  $userId)
849
+        );
850 850
         $customer->fromArray($data);
851 851
         Shopware()->Container()->get('shopware_account.customer_service')->update($customer);
852 852
     }
853 853
 
854 854
 
855
-     /**
856
-     * Updates the shipping address
857
-     *
858
-     * @param int $userId
859
-     * @param array $shippingData
860
-     */
855
+        /**
856
+         * Updates the shipping address
857
+         *
858
+         * @param int $userId
859
+         * @param array $shippingData
860
+         */
861 861
     private function updateShipping($userId, $shippingData)
862 862
     {
863 863
         /** @var \Shopware\Components\Model\ModelManager $em */
@@ -870,7 +870,7 @@  discard block
 block discarded – undo
870 870
         $addressold = $customer->getDefaultShippingAddress();
871 871
         $address = new \Shopware\Models\Customer\Address();
872 872
         
873
-         /** @var \Shopware\Models\Country\Country $country */
873
+            /** @var \Shopware\Models\Country\Country $country */
874 874
         $country = $addressold->getCountry();
875 875
         $shippingData['country'] = $country;
876 876
         if ($shippingData['phone'] === null) {
@@ -904,7 +904,7 @@  discard block
 block discarded – undo
904 904
         /** @var \Shopware\Models\Customer\Address $address */
905 905
         $address = $customer->getDefaultBillingAddress();
906 906
         
907
-         /** @var \Shopware\Models\Country\Country $country */
907
+            /** @var \Shopware\Models\Country\Country $country */
908 908
         $country = $address->getCountry();
909 909
         $billingData['country'] = $country;
910 910
         $address->fromArray($billingData);
@@ -963,14 +963,14 @@  discard block
 block discarded – undo
963 963
 
964 964
             if ($order['status'] == 'checkout_complete') {
965 965
                 $this->plugin->klarnaLog("Entering Shopware_Controllers_Frontend_PaymentKlarna::returnAction: checkout_complete. Save oder if session values match.",3);
966
-             if (Shopware()->Session()->offsetGet('KlarnaTransactionId') == null){
966
+                if (Shopware()->Session()->offsetGet('KlarnaTransactionId') == null){
967 967
                     $this->plugin->klarnaLog("Entering Shopware_Controllers_Frontend_PaymentKlarna::returnAction: Session matches. Order will be saved",3);
968 968
                     Shopware()->Session()->offsetSet('KlarnaTransactionId', $transactionId );
969 969
                     $orderNumber = $this->saveOrder(
970 970
                         $order['reservation'],
971 971
                         $order['reference']
972 972
                     );
973
-                     Shopware()->Session()->offsetSet('KlarnaTransactionId', null );
973
+                        Shopware()->Session()->offsetSet('KlarnaTransactionId', null );
974 974
                 }
975 975
             }
976 976
 
@@ -1400,7 +1400,7 @@  discard block
 block discarded – undo
1400 1400
 
1401 1401
     protected function isUserLoggedIn()
1402 1402
     {        
1403
-         return (isset($this->session->sUserId) && !empty($this->session->sUserId));
1403
+            return (isset($this->session->sUserId) && !empty($this->session->sUserId));
1404 1404
     }
1405 1405
 
1406 1406
     /**
Please login to merge, or discard this patch.