~technofluid-team/openobject-addons/technofluid_multiple_installations

« back to all changes in this revision

Viewing changes to esale_joomla/connector/xmlrpc.inc

  • Committer: pinky
  • Date: 2006-12-07 13:41:40 UTC
  • Revision ID: pinky-dedd7f8a42bd4557112a0513082691b8590ad6cc
New trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php                                   // -*-c++-*-
 
2
// by Edd Dumbill (C) 1999-2002
 
3
// <edd@usefulinc.com>
 
4
// $Id: xmlrpc.inc,v 1.113 2006/01/22 23:55:57 ggiunta Exp $
 
5
 
 
6
 
 
7
// Copyright (c) 1999,2000,2002 Edd Dumbill.
 
8
// All rights reserved.
 
9
//
 
10
// Redistribution and use in source and binary forms, with or without
 
11
// modification, are permitted provided that the following conditions
 
12
// are met:
 
13
//
 
14
//    * Redistributions of source code must retain the above copyright
 
15
//      notice, this list of conditions and the following disclaimer.
 
16
//
 
17
//    * Redistributions in binary form must reproduce the above
 
18
//      copyright notice, this list of conditions and the following
 
19
//      disclaimer in the documentation and/or other materials provided
 
20
//      with the distribution.
 
21
//
 
22
//    * Neither the name of the "XML-RPC for PHP" nor the names of its
 
23
//      contributors may be used to endorse or promote products derived
 
24
//      from this software without specific prior written permission.
 
25
//
 
26
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
27
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
28
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 
29
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 
30
// REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 
31
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 
32
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 
33
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 
34
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 
35
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
36
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 
37
// OF THE POSSIBILITY OF SUCH DAMAGE.
 
38
 
 
39
        if(!function_exists('xml_parser_create'))
 
40
        {
 
41
                // For PHP 4 onward, XML functionality is always compiled-in on windows:
 
42
                // no more need to dl-open it. It might have been compiled out on *nix...
 
43
                if(strtoupper(substr(PHP_OS, 0, 3) != 'WIN'))
 
44
                {
 
45
                        dl('xml.so');
 
46
                }
 
47
        }
 
48
 
 
49
        // Try to be backward compat with php < 4.2 (are we not being nice ?)
 
50
        if(substr(phpversion(), 0, 3) == '4.0' || @version_compare(substr(phpversion(), 0, 3) == '4.1'))
 
51
        {
 
52
                // give an opportunity to user to specify where to include other files from
 
53
                if(!defined('PHP_XMLRPC_COMPAT_DIR'))
 
54
                {
 
55
                        define('PHP_XMLRPC_COMPAT_DIR',dirname(__FILE__).'/compat/');
 
56
                }
 
57
                if(substr(phpversion(), 0, 3) == '4.0')
 
58
                {
 
59
                        include(PHP_XMLRPC_COMPAT_DIR."is_scalar.php");
 
60
                        include(PHP_XMLRPC_COMPAT_DIR."array_key_exists.php");
 
61
                        include(PHP_XMLRPC_COMPAT_DIR."version_compare.php");
 
62
                }
 
63
                include(PHP_XMLRPC_COMPAT_DIR."var_export.php");
 
64
                include(PHP_XMLRPC_COMPAT_DIR."is_a.php");
 
65
        }
 
66
 
 
67
        // G. Giunta 2005/01/29: declare global these variables,
 
68
        // so that xmlrpc.inc will work even if included from within a function
 
69
        // Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used.
 
70
        $GLOBALS['xmlrpcI4']='i4';
 
71
        $GLOBALS['xmlrpcInt']='int';
 
72
        $GLOBALS['xmlrpcBoolean']='boolean';
 
73
        $GLOBALS['xmlrpcDouble']='double';
 
74
        $GLOBALS['xmlrpcString']='string';
 
75
        $GLOBALS['xmlrpcDateTime']='dateTime.iso8601';
 
76
        $GLOBALS['xmlrpcBase64']='base64';
 
77
        $GLOBALS['xmlrpcArray']='array';
 
78
        $GLOBALS['xmlrpcStruct']='struct';
 
79
        $GLOBALS['xmlrpcValue']='undefined';
 
80
 
 
81
        $GLOBALS['xmlrpcTypes']=array(
 
82
                $GLOBALS['xmlrpcI4']       => 1,
 
83
                $GLOBALS['xmlrpcInt']      => 1,
 
84
                $GLOBALS['xmlrpcBoolean']  => 1,
 
85
                $GLOBALS['xmlrpcString']   => 1,
 
86
                $GLOBALS['xmlrpcDouble']   => 1,
 
87
                $GLOBALS['xmlrpcDateTime'] => 1,
 
88
                $GLOBALS['xmlrpcBase64']   => 1,
 
89
                $GLOBALS['xmlrpcArray']    => 2,
 
90
                $GLOBALS['xmlrpcStruct']   => 3
 
91
        );
 
92
 
 
93
        $GLOBALS['xmlrpc_valid_parents'] = array(
 
94
                'BOOLEAN' => array('VALUE'),
 
95
                'I4' => array('VALUE'),
 
96
                'INT' => array('VALUE'),
 
97
                'STRING' => array('VALUE'),
 
98
                'DOUBLE' => array('VALUE'),
 
99
                'DATETIME.ISO8601' => array('VALUE'),
 
100
                'BASE64' => array('VALUE'),
 
101
                'ARRAY' => array('VALUE'),
 
102
                'STRUCT' => array('VALUE'),
 
103
                'PARAM' => array('PARAMS'),
 
104
                'METHODNAME' => array('METHODCALL'),
 
105
                'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
 
106
                'MEMBER' => array('STRUCT'),
 
107
                'NAME' => array('MEMBER'),
 
108
                'DATA' => array('ARRAY'),
 
109
                'FAULT' => array('METHODRESPONSE'),
 
110
                'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
 
111
        );
 
112
 
 
113
        $GLOBALS['xmlEntities']=array(
 
114
                'amp'  => '&',
 
115
                'quot' => '"',
 
116
                'lt'   => '<',
 
117
                'gt'   => '>',
 
118
                'apos' => "'"
 
119
        );
 
120
 
 
121
        // tables used for transcoding different charsets into us-ascii xml
 
122
 
 
123
        $GLOBALS['xml_iso88591_Entities']=array();
 
124
        $GLOBALS['xml_iso88591_Entities']['in'] = array();
 
125
        $GLOBALS['xml_iso88591_Entities']['out'] = array();
 
126
        for ($i = 0; $i < 32; $i++)
 
127
        {
 
128
 
 
129
                $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
 
130
                $GLOBALS['xml_iso88591_Entities']['out'][] = "&#$i;";
 
131
        }
 
132
        for ($i = 160; $i < 256; $i++)
 
133
        {
 
134
                $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
 
135
                $GLOBALS['xml_iso88591_Entities']['out'][] = "&#$i;";
 
136
        }
 
137
 
 
138
        /// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159.
 
139
        /// These will NOT be present in true ISO-8859-1, but will save the unwary
 
140
        /// windows user from sending junk.
 
141
/*
 
142
$cp1252_to_htmlent =
 
143
  array(
 
144
   '\x80'=>'&#x20AC;', '\x81'=>'?', '\x82'=>'&#x201A;', '\x83'=>'&#x0192;',
 
145
   '\x84'=>'&#x201E;', '\x85'=>'&#x2026;', '\x86'=>'&#x2020;', \x87'=>'&#x2021;',
 
146
   '\x88'=>'&#x02C6;', '\x89'=>'&#x2030;', '\x8A'=>'&#x0160;', '\x8B'=>'&#x2039;',
 
147
   '\x8C'=>'&#x0152;', '\x8D'=>'?', '\x8E'=>'&#x017D;', '\x8F'=>'?',
 
148
   '\x90'=>'?', '\x91'=>'&#x2018;', '\x92'=>'&#x2019;', '\x93'=>'&#x201C;',
 
149
   '\x94'=>'&#x201D;', '\x95'=>'&#x2022;', '\x96'=>'&#x2013;', '\x97'=>'&#x2014;',
 
150
   '\x98'=>'&#x02DC;', '\x99'=>'&#x2122;', '\x9A'=>'&#x0161;', '\x9B'=>'&#x203A;',
 
151
   '\x9C'=>'&#x0153;', '\x9D'=>'?', '\x9E'=>'&#x017E;', '\x9F'=>'&#x0178;'
 
152
  );
 
153
*/
 
154
 
 
155
        $GLOBALS['xmlrpcerr']['unknown_method']=1;
 
156
        $GLOBALS['xmlrpcstr']['unknown_method']='Unknown method';
 
157
        $GLOBALS['xmlrpcerr']['invalid_return']=2;
 
158
        $GLOBALS['xmlrpcstr']['invalid_return']='Invalid return payload: enable debugging to examine incoming payload';
 
159
        $GLOBALS['xmlrpcerr']['incorrect_params']=3;
 
160
        $GLOBALS['xmlrpcstr']['incorrect_params']='Incorrect parameters passed to method';
 
161
        $GLOBALS['xmlrpcerr']['introspect_unknown']=4;
 
162
        $GLOBALS['xmlrpcstr']['introspect_unknown']="Can't introspect: method unknown";
 
163
        $GLOBALS['xmlrpcerr']['http_error']=5;
 
164
        $GLOBALS['xmlrpcstr']['http_error']="Didn't receive 200 OK from remote server.";
 
165
        $GLOBALS['xmlrpcerr']['no_data']=6;
 
166
        $GLOBALS['xmlrpcstr']['no_data']='No data received from server.';
 
167
        $GLOBALS['xmlrpcerr']['no_ssl']=7;
 
168
        $GLOBALS['xmlrpcstr']['no_ssl']='No SSL support compiled in.';
 
169
        $GLOBALS['xmlrpcerr']['curl_fail']=8;
 
170
        $GLOBALS['xmlrpcstr']['curl_fail']='CURL error';
 
171
        $GLOBALS['xmlrpcerr']['invalid_request']=15;
 
172
        $GLOBALS['xmlrpcstr']['invalid_request']='Invalid request payload';
 
173
        $GLOBALS['xmlrpcerr']['no_curl']=16;
 
174
        $GLOBALS['xmlrpcstr']['no_curl']='No CURL support compiled in.';
 
175
        $GLOBALS['xmlrpcerr']['server_error']=17;
 
176
        $GLOBALS['xmlrpcstr']['server_error']='Internal server error';
 
177
        $GLOBALS['xmlrpcerr']['multicall_error']=18;
 
178
        $GLOBALS['xmlrpcstr']['multicall_error']='Received from server invalid multicall response';
 
179
 
 
180
        $GLOBALS['xmlrpcerr']['multicall_notstruct'] = 9;
 
181
        $GLOBALS['xmlrpcstr']['multicall_notstruct'] = 'system.multicall expected struct';
 
182
        $GLOBALS['xmlrpcerr']['multicall_nomethod']  = 10;
 
183
        $GLOBALS['xmlrpcstr']['multicall_nomethod']  = 'missing methodName';
 
184
        $GLOBALS['xmlrpcerr']['multicall_notstring'] = 11;
 
185
        $GLOBALS['xmlrpcstr']['multicall_notstring'] = 'methodName is not a string';
 
186
        $GLOBALS['xmlrpcerr']['multicall_recursion'] = 12;
 
187
        $GLOBALS['xmlrpcstr']['multicall_recursion'] = 'recursive system.multicall forbidden';
 
188
        $GLOBALS['xmlrpcerr']['multicall_noparams']  = 13;
 
189
        $GLOBALS['xmlrpcstr']['multicall_noparams']  = 'missing params';
 
190
        $GLOBALS['xmlrpcerr']['multicall_notarray']  = 14;
 
191
        $GLOBALS['xmlrpcstr']['multicall_notarray']  = 'params is not an array';
 
192
 
 
193
        $GLOBALS['xmlrpcerr']['cannot_decompress']=103;
 
194
        $GLOBALS['xmlrpcstr']['cannot_decompress']='Received from server compressed HTTP and cannot decompress';
 
195
        $GLOBALS['xmlrpcerr']['decompress_fail']=104;
 
196
        $GLOBALS['xmlrpcstr']['decompress_fail']='Received from server invalid compressed HTTP';
 
197
        $GLOBALS['xmlrpcerr']['dechunk_fail']=105;
 
198
        $GLOBALS['xmlrpcstr']['dechunk_fail']='Received from server invalid chunked HTTP';
 
199
        $GLOBALS['xmlrpcerr']['server_cannot_decompress']=106;
 
200
        $GLOBALS['xmlrpcstr']['server_cannot_decompress']='Received from client compressed HTTP request and cannot decompress';
 
201
        $GLOBALS['xmlrpcerr']['server_decompress_fail']=107;
 
202
        $GLOBALS['xmlrpcstr']['server_decompress_fail']='Received from client invalid compressed HTTP request';
 
203
 
 
204
        // The charset encoding used by the server for received messages and
 
205
        // by the client for received responses when received charset cannot be determined
 
206
        // or is not supported
 
207
        $GLOBALS['xmlrpc_defencoding']='UTF-8';
 
208
        // The encoding used internally by PHP.
 
209
        // String values received as xml will be converted to this, and php strings will be converted to xml
 
210
 
 
211
        // as if having been coded with this
 
212
        $GLOBALS['xmlrpc_internalencoding']='ISO-8859-1';
 
213
 
 
214
        $GLOBALS['xmlrpcName']='XML-RPC for PHP';
 
215
        $GLOBALS['xmlrpcVersion']='2.0RC3';
 
216
 
 
217
        // let user errors start at 800
 
218
        $GLOBALS['xmlrpcerruser']=800;
 
219
        // let XML parse errors start at 100
 
220
        $GLOBALS['xmlrpcerrxml']=100;
 
221
 
 
222
        // formulate backslashes for escaping regexp
 
223
        $GLOBALS['xmlrpc_backslash']=chr(92).chr(92);
 
224
 
 
225
        // used to store state during parsing
 
226
        // quick explanation of components:
 
227
        //   ac - used to accumulate values
 
228
        //   isf - used to indicate a fault
 
229
        //   lv - used to indicate "looking for a value": implements
 
230
        //        the logic to allow values with no types to be strings
 
231
        //   params - used to store parameters in method calls
 
232
        //   method - used to store method name
 
233
        //   stack - array with genealogy of xml elements names:
 
234
        //           used to validate nesting of xmlrpc elements
 
235
 
 
236
        $GLOBALS['_xh']=null;
 
237
 
 
238
        /**
 
239
        * Convert a string to the correct XML representation in a target charset
 
240
        * To help correct communication of non-ascii chars inside strings, regardless
 
241
        * of the charset used when sending requests, parsing them, sending responses
 
242
        * and parsing responses, an option is to convert all non-ascii chars present in the message
 
243
        * into their equivalent 'charset entity'. Charset entities enumerated this way
 
244
        * are independent of the charset encoding used to transmit them, and all XML
 
245
        * parsers are bound to understand them.
 
246
        * Note that in the std case we are not sending a charset encoding mime type
 
247
        * along with http headers, so we are bound by RFC 3023 to emit strict us-ascii.
 
248
        *
 
249
        * @todo do a bit of basic benchmarking (strtr vs. str_replace)
 
250
        * @todo make usage of iconv() or recode_string() or mb_string() where available
 
251
        */
 
252
        function xmlrpc_encode_entitites($data, $src_encoding='', $dest_encoding='')
 
253
        {
 
254
                if ($src_encoding == '')
 
255
                {
 
256
                        // lame, but we know no better...
 
257
                        $src_encoding = $GLOBALS['xmlrpc_internalencoding'];
 
258
                }
 
259
                //if ($dest_encoding == '')
 
260
                //{
 
261
                //      // lame, but we know no better...
 
262
                //      $dest_encoding = 'US-ASCII';
 
263
                //}
 
264
                                switch(strtoupper($src_encoding.'_'.$dest_encoding))
 
265
                                {
 
266
                                        case 'ISO-8859-1_':
 
267
                                        case 'ISO-8859-1_US-ASCII':
 
268
                                                $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
 
269
                                                $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
 
270
                                                break;
 
271
                                        case 'ISO-8859-1_UTF-8':
 
272
                                                $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
 
273
                                                $escaped_data = utf8_encode($escaped_data);
 
274
                                                break;
 
275
                                        case 'ISO-8859-1_ISO-8859-1':
 
276
                                        case 'US-ASCII_US-ASCII':
 
277
                                        case 'US-ASCII_UTF-8':
 
278
                                        case 'US-ASCII_':
 
279
                                        case 'US-ASCII_ISO-8859-1':
 
280
                                        case 'UTF-8_UTF-8':
 
281
                                                $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
 
282
                                                break;
 
283
                                        case 'UTF-8_':
 
284
                                        case 'UTF-8_US-ASCII':
 
285
                                        case 'UTF-8_ISO-8859-1':
 
286
        // NB: this will choke on invalid UTF-8, going most likely beyond EOF
 
287
        $escaped_data = "";
 
288
        // be kind to users creating string xmlrpcvals out of different php types
 
289
        $data = (string) $data;
 
290
        $ns = strlen ($data);
 
291
        for ($nn = 0; $nn < $ns; $nn++)
 
292
        {
 
293
                $ch = $data[$nn];
 
294
                $ii = ord($ch);
 
295
//1 7 0bbbbbbb (127)
 
296
                if ($ii < 128)
 
297
                {
 
298
                        /// @todo shall we replace this with a (suuposedly) faster str_replace?
 
299
                        switch($ii){
 
300
                                case 34:
 
301
                                        $escaped_data .= '&quot;';
 
302
                                        break;
 
303
                                case 38:
 
304
                                        $escaped_data .= '&amp;';
 
305
                                case 39:
 
306
                                        $escaped_data .= '&apos;';
 
307
                                        break;
 
308
                                case 60:
 
309
                                        $escaped_data .= '&lt;';
 
310
                                        break;
 
311
                                case 62:
 
312
                                        $escaped_data .= '&gt;';
 
313
                                        break;
 
314
                                default:
 
315
                                        $escaped_data .= $ch;
 
316
                        } // switch
 
317
                }
 
318
//2 11 110bbbbb 10bbbbbb (2047)
 
319
                else if ($ii>>5 == 6)
 
320
                {
 
321
                        $b1 = ($ii & 31);
 
322
                        $ii = ord($data[$nn+1]);
 
323
                        $b2 = ($ii & 63);
 
324
                        $ii = ($b1 * 64) + $b2;
 
325
                        $ent = sprintf ("&#%d;", $ii);
 
326
                        $escaped_data .= $ent;
 
327
                }
 
328
//3 16 1110bbbb 10bbbbbb 10bbbbbb
 
329
                else if ($ii>>4 == 14)
 
330
                {
 
331
                        $b1 = ($ii & 31);
 
332
                        $ii = ord($data[$nn+1]);
 
333
                        $b2 = ($ii & 63);
 
334
                        $ii = ord($data[$nn+2]);
 
335
                        $b3 = ($ii & 63);
 
336
                        $ii = ((($b1 * 64) + $b2) * 64) + $b3;
 
337
                        $ent = sprintf ("&#%d;", $ii);
 
338
                        $escaped_data .= $ent;
 
339
                }
 
340
//4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
 
341
                else if ($ii>>3 == 30)
 
342
                {
 
343
                        $b1 = ($ii & 31);
 
344
                        $ii = ord($data[$nn+1]);
 
345
                        $b2 = ($ii & 63);
 
346
                        $ii = ord($data[$nn+2]);
 
347
                        $b3 = ($ii & 63);
 
348
                        $ii = ord($data[$nn+3]);
 
349
                        $b4 = ($ii & 63);
 
350
                        $ii = ((((($b1 * 64) + $b2) * 64) + $b3) * 64) + $b4;
 
351
                        $ent = sprintf ("&#%d;", $ii);
 
352
                        $escaped_data .= $ent;
 
353
                }
 
354
        }
 
355
                                                break;
 
356
                                        default:
 
357
                                                $escaped_data = '';
 
358
                                                error_log("Converting from $src_encoding to $dest_encoding: not supported...");
 
359
                                }
 
360
//              } // switch
 
361
                return $escaped_data;
 
362
        }
 
363
 
 
364
        function xmlrpc_se($parser, $name, $attrs)
 
365
        {
 
366
                // if invalid xmlrpc already detected, skip all processing
 
367
                if ($GLOBALS['_xh']['isf'] < 2)
 
368
                {
 
369
                        // check for correct element nesting
 
370
                        // top level element can only be of 2 types
 
371
                        if (count($GLOBALS['_xh']['stack']) == 0)
 
372
                        {
 
373
                                if ($name != 'METHODRESPONSE' && $name != 'METHODCALL')
 
374
                                {
 
375
                                        $GLOBALS['_xh']['isf'] = 2;
 
376
                                        $GLOBALS['_xh']['isf_reason'] = 'missing top level xmlrpc element';
 
377
                                        return;
 
378
                                }
 
379
                        }
 
380
                        else
 
381
                        {
 
382
                                // not top level element: see if parent is OK
 
383
                                $parent = end($GLOBALS['_xh']['stack']);
 
384
                                if (!array_key_exists($name, $GLOBALS['xmlrpc_valid_parents']) || !in_array($parent, $GLOBALS['xmlrpc_valid_parents'][$name]))
 
385
                                {
 
386
                                        $GLOBALS['_xh']['isf'] = 2;
 
387
                                        $GLOBALS['_xh']['isf_reason'] = "xmlrpc element $name cannot be child of $parent";
 
388
                                        return;
 
389
                                }
 
390
                        }
 
391
 
 
392
                        switch($name)
 
393
                        {
 
394
                                case 'STRUCT':
 
395
                                case 'ARRAY':
 
396
                                        // create an empty array to hold child values, and push it onto appropriate stack
 
397
                                        $cur_val = array();
 
398
                                        $cur_val['values'] = array();
 
399
                                        $cur_val['type'] = $name;
 
400
                                        // check for out-of-band information to rebuild php objs
 
401
                                        // and in case it is found, save it
 
402
                                        if (@isset($attrs['PHP_CLASS']))
 
403
                                        {
 
404
                                                $cur_val['php_class'] = $attrs['PHP_CLASS'];
 
405
                                        }
 
406
                                        $GLOBALS['_xh']['valuestack'][] = $cur_val;
 
407
                                        break;
 
408
                                case 'DATA':
 
409
                                case 'METHODCALL':
 
410
                                case 'METHODRESPONSE':
 
411
                                case 'PARAMS':
 
412
                                        // valid elements that add little to processing
 
413
                                        break;
 
414
                                case 'METHODNAME':
 
415
                                case 'NAME':
 
416
                                        $GLOBALS['_xh']['ac']='';
 
417
                                        break;
 
418
                                case 'FAULT':
 
419
                                        $GLOBALS['_xh']['isf']=1;
 
420
                                        break;
 
421
                                case 'VALUE':
 
422
                                        $GLOBALS['_xh']['vt']='value'; // indicator: no value found yet
 
423
                                        $GLOBALS['_xh']['ac']='';
 
424
                                        $GLOBALS['_xh']['lv']=1;
 
425
                                        $GLOBALS['_xh']['php_class']=null;
 
426
                                        break;
 
427
                                case 'I4':
 
428
                                case 'INT':
 
429
                                case 'STRING':
 
430
                                case 'BOOLEAN':
 
431
                                case 'DOUBLE':
 
432
                                case 'DATETIME.ISO8601':
 
433
                                case 'BASE64':
 
434
                                        if ($GLOBALS['_xh']['vt']!='value')
 
435
                                        {
 
436
                                                //two data elements inside a value: an error occurred!
 
437
                                                $GLOBALS['_xh']['isf'] = 2;
 
438
                                                $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
 
439
                                                return;
 
440
                                        }
 
441
 
 
442
                                        $GLOBALS['_xh']['ac']=''; // reset the accumulator
 
443
                                        break;
 
444
                                case 'MEMBER':
 
445
                                        $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
 
446
                                        //$GLOBALS['_xh']['ac']='';
 
447
                                        // Drop trough intentionally
 
448
                                case 'PARAM':
 
449
                                        // clear value type, so we can check later if no value has been passed for this param/member
 
450
                                        $GLOBALS['_xh']['vt']=null;
 
451
                                        break;
 
452
                                default:
 
453
                                        /// INVALID ELEMENT: RAISE ISF so that it is later recognized!!!
 
454
                                        $GLOBALS['_xh']['isf'] = 2;
 
455
                                        $GLOBALS['_xh']['isf_reason'] = "found not-xmlrpc xml element $name";
 
456
                                        break;
 
457
                        }
 
458
 
 
459
                        // Save current element name to stack, to validate nesting
 
460
                        $GLOBALS['_xh']['stack'][] = $name;
 
461
 
 
462
                        if($name!='VALUE')
 
463
                        {
 
464
                                $GLOBALS['_xh']['lv']=0;
 
465
                        }
 
466
                }
 
467
        }
 
468
 
 
469
        function xmlrpc_ee($parser, $name, $rebuild_xmlrpcvals = true)
 
470
        {
 
471
                if ($GLOBALS['_xh']['isf'] < 2)
 
472
                {
 
473
                        // push this element name from stack
 
474
                        // NB: if XML validates, correct opening/closing is guaranteed and
 
475
                        // we do not have to check for $name == $curr_elem.
 
476
                        // we also checked for proper nesting at start of elements...
 
477
                        $curr_elem = array_pop($GLOBALS['_xh']['stack']);
 
478
 
 
479
                        switch($name)
 
480
                        {
 
481
                                case 'STRUCT':
 
482
                                case 'ARRAY':
 
483
                                        // fetch out of stack array of values, and promote it to current value
 
484
                                        $curr_val = array_pop($GLOBALS['_xh']['valuestack']);
 
485
                                        $GLOBALS['_xh']['value'] = $curr_val['values'];
 
486
                                        $GLOBALS['_xh']['vt']=strtolower($name);
 
487
                                        if (isset($curr_val['php_class']))
 
488
                                        {
 
489
                                                $GLOBALS['_xh']['php_class'] = $curr_val['php_class'];
 
490
                                        }
 
491
                                        break;
 
492
                                case 'NAME':
 
493
                                        $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name'] = $GLOBALS['_xh']['ac'];
 
494
                                        break;
 
495
                                case 'BOOLEAN':
 
496
                                case 'I4':
 
497
                                case 'INT':
 
498
                                case 'STRING':
 
499
                                case 'DOUBLE':
 
500
                                case 'DATETIME.ISO8601':
 
501
                                case 'BASE64':
 
502
                                        $GLOBALS['_xh']['vt']=strtolower($name);
 
503
                                        if ($name=='STRING')
 
504
                                        {
 
505
                                                $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
 
506
                                        }
 
507
                                        elseif ($name=='DATETIME.ISO8601')
 
508
                                        {
 
509
                                                /// @todo validate datetime values with a correct format mask?
 
510
                                                $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcDateTime'];
 
511
                                                $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
 
512
                                        }
 
513
                                        elseif ($name=='BASE64')
 
514
                                        {
 
515
                                                /// @todo check for failure of base64 decoding / catch warnings
 
516
                                                $GLOBALS['_xh']['value']=base64_decode($GLOBALS['_xh']['ac']);
 
517
                                        }
 
518
                                        elseif ($name=='BOOLEAN')
 
519
                                        {
 
520
                                                // special case here: we translate boolean 1 or 0 into PHP
 
521
                                                        // constants true or false
 
522
                                                        // NB: this simple checks helps a lot sanitizing input, ie no
 
523
                                                        // security problems around here
 
524
                                                        if ($GLOBALS['_xh']['ac']=='1')
 
525
                                                        {
 
526
                                                                $GLOBALS['_xh']['value']=true;
 
527
                                                        }
 
528
                                                        else
 
529
                                                        {
 
530
                                                                // log if receiveing something strange, even though we set the value to false anyway
 
531
                                                                if ($GLOBALS['_xh']['ac']!='0')
 
532
                                                                        error_log('XML-RPC: invalid value received in BOOLEAN: '.$GLOBALS['_xh']['ac']);
 
533
                                                                $GLOBALS['_xh']['value']=false;
 
534
                                                        }
 
535
                                        }
 
536
                                        elseif ($name=='DOUBLE')
 
537
                                        {
 
538
                                                // we have a DOUBLE
 
539
                                                // we must check that only 0123456789-.<space> are characters here
 
540
                                                if (!ereg("^[+-]?[eE0123456789 \\t.]+$", $GLOBALS['_xh']['ac']))
 
541
                                                {
 
542
                                                        /// @todo: find a better way of throwing an error
 
543
                                                        // than this!
 
544
                                                        error_log('XML-RPC: non numeric value received in DOUBLE: '.$GLOBALS['_xh']['ac']);
 
545
                                                        $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
 
546
                                                }
 
547
                                                else
 
548
                                                {
 
549
                                                        // it's ok, add it on
 
550
                                                        $GLOBALS['_xh']['value']=(double)$GLOBALS['_xh']['ac'];
 
551
                                                }
 
552
                                        }
 
553
                                        else
 
554
                                        {
 
555
                                                // we have an I4/INT
 
556
                                                // we must check that only 0123456789-<space> are characters here
 
557
                                                if (!ereg("^[+-]?[0123456789 \\t]+$", $GLOBALS['_xh']['ac']))
 
558
                                                {
 
559
                                                        /// @todo find a better way of throwing an error
 
560
                                                        // than this!
 
561
                                                        error_log('XML-RPC: non numeric value received in INT: '.$GLOBALS['_xh']['ac']);
 
562
                                                        $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
 
563
                                                }
 
564
                                                else
 
565
                                                {
 
566
                                                        // it's ok, add it on
 
567
                                                        $GLOBALS['_xh']['value']=(int)$GLOBALS['_xh']['ac'];
 
568
                                                }
 
569
                                        }
 
570
                                        $GLOBALS['_xh']['ac']=''; // is this necessary?
 
571
                                        $GLOBALS['_xh']['lv']=3; // indicate we've found a value
 
572
                                        break;
 
573
                                case 'VALUE':
 
574
                                        // This if() detects if no scalar was inside <VALUE></VALUE>
 
575
                                        if ($GLOBALS['_xh']['vt']=='value')
 
576
                                        {
 
577
                                                $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
 
578
                                                $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcString'];
 
579
                                        }
 
580
 
 
581
                                        if ($rebuild_xmlrpcvals)
 
582
                                        {
 
583
                                                // build the xmlrpc val out of the data received, and substitute it
 
584
                                                $temp =& new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']);
 
585
                                                // in case we got info about underlying php class, save it
 
586
                                                // in the object we're rebuilding
 
587
                                                if (isset($GLOBALS['_xh']['php_class']))
 
588
                                                        $temp->_php_class = $GLOBALS['_xh']['php_class'];
 
589
                                                // check if we are inside an array or struct:
 
590
                                                // if value just built is inside an array, let's move it into array on the stack
 
591
                                                $vscount = count($GLOBALS['_xh']['valuestack']);
 
592
                                                if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
 
593
                                                {
 
594
                                                        $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $temp;
 
595
                                                }
 
596
                                                else
 
597
                                                {
 
598
                                                        $GLOBALS['_xh']['value'] = $temp;
 
599
                                                }
 
600
                                        }
 
601
                                        else
 
602
                                        {
 
603
                                                /// @todo this needs to treat correctly php-serialized objects,
 
604
                                                /// since std deserializing is done by php_xmlrpc_decode,
 
605
                                                /// which we will not be calling...
 
606
                                                if (isset($GLOBALS['_xh']['php_class']))
 
607
                                                {
 
608
                                                }
 
609
 
 
610
                                                // check if we are inside an array or struct:
 
611
                                                // if value just built is inside an array, let's move it into array on the stack
 
612
                                                $vscount = count($GLOBALS['_xh']['valuestack']);
 
613
                                                if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
 
614
                                                {
 
615
                                                        $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $GLOBALS['_xh']['value'];
 
616
                                                }
 
617
                                        }
 
618
                                        break;
 
619
                                case 'MEMBER':
 
620
                                        $GLOBALS['_xh']['ac']=''; // is this necessary?
 
621
                                        // add to array in the stack the last element built,
 
622
                                        // unless no VALUE was found
 
623
                                        if ($GLOBALS['_xh']['vt'])
 
624
                                        {
 
625
                                                $vscount = count($GLOBALS['_xh']['valuestack']);
 
626
                                                $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][$GLOBALS['_xh']['valuestack'][$vscount-1]['name']] = $GLOBALS['_xh']['value'];
 
627
                                        } else
 
628
                                                error_log('XML-RPC: missing VALUE inside STRUCT in received xml');
 
629
                                        break;
 
630
                                case 'DATA':
 
631
                                        $GLOBALS['_xh']['ac']=''; // is this necessary?
 
632
                                        break;
 
633
                                case 'PARAM':
 
634
                                        // add to array of params the current value,
 
635
                                        // unless no VALUE was found
 
636
                                        if ($GLOBALS['_xh']['vt'])
 
637
                                        {
 
638
                                                $GLOBALS['_xh']['params'][]=$GLOBALS['_xh']['value'];
 
639
                                                $GLOBALS['_xh']['pt'][]=$GLOBALS['_xh']['vt'];
 
640
                                        }
 
641
                                        else
 
642
                                                error_log('XML-RPC: missing VALUE inside PARAM in received xml');
 
643
                                        break;
 
644
                                case 'METHODNAME':
 
645
                                        $GLOBALS['_xh']['method']=ereg_replace("^[\n\r\t ]+", '', $GLOBALS['_xh']['ac']);
 
646
                                        break;
 
647
                                case 'PARAMS':
 
648
                                case 'FAULT':
 
649
                                case 'METHODCALL':
 
650
                                case 'METHORESPONSE':
 
651
                                        break;
 
652
                                default:
 
653
                                        // End of INVALID ELEMENT!
 
654
                                        // shall we add an assert here for unreachable code???
 
655
                                        break;
 
656
                        }
 
657
                }
 
658
        }
 
659
 
 
660
        function xmlrpc_ee_fast($parser, $name)
 
661
        {
 
662
                xmlrpc_ee($parser, $name, false);
 
663
        }
 
664
 
 
665
        function xmlrpc_cd($parser, $data)
 
666
        {
 
667
                //if(ereg("^[\n\r \t]+$", $data)) return;
 
668
                // print "adding [${data}]\n";
 
669
 
 
670
                // skip processing if xml fault already detected
 
671
                if ($GLOBALS['_xh']['isf'] < 2)
 
672
                {
 
673
                        if($GLOBALS['_xh']['lv']!=3)
 
674
                        {
 
675
                                // "lookforvalue==3" means that we've found an entire value
 
676
                                // and should discard any further character data
 
677
                                if($GLOBALS['_xh']['lv']==1)
 
678
                                {
 
679
                                        // if we've found text and we're just in a <value> then
 
680
                                        // say we've found a value
 
681
                                        $GLOBALS['_xh']['lv']=2;
 
682
                                }
 
683
                                if(!@isset($GLOBALS['_xh']['ac']))
 
684
                                {
 
685
                                        $GLOBALS['_xh']['ac'] = '';
 
686
                                }
 
687
                                $GLOBALS['_xh']['ac'].=$data;
 
688
                        }
 
689
                }
 
690
        }
 
691
 
 
692
        function xmlrpc_dh($parser, $data)
 
693
        {
 
694
                // skip processing if xml fault already detected
 
695
                if ($GLOBALS['_xh']['isf'] < 2)
 
696
                {
 
697
                        if(substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';')
 
698
                        {
 
699
                                if($GLOBALS['_xh']['lv']==1)
 
700
                                {
 
701
                                        $GLOBALS['_xh']['lv']=2;
 
702
                                }
 
703
                                $GLOBALS['_xh']['ac'].=$data;
 
704
                        }
 
705
                }
 
706
        }
 
707
 
 
708
        class xmlrpc_client
 
709
        {
 
710
                var $path;
 
711
                var $server;
 
712
                var $port=0;
 
713
                var $method='http';
 
714
                var $errno;
 
715
                var $errstr;
 
716
                var $debug=0;
 
717
                var $username='';
 
718
                var $password='';
 
719
                var $cert='';
 
720
                var $certpass='';
 
721
                var $key='';
 
722
                var $keypass='';
 
723
                var $verifypeer=1;
 
724
                var $verifyhost=1;
 
725
                var $no_multicall=false;
 
726
                var $proxy = '';
 
727
                var $proxyport=0;
 
728
                var $proxy_user = '';
 
729
                var $proxy_pass = '';
 
730
                var $cookies=array();
 
731
                /**
 
732
                * List of http compression methods accepted by the client for responses.
 
733
                * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
 
734
                *
 
735
                * NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since
 
736
                * in those cases it will be up to CURL to decide the compression methods
 
737
                * it supports. You might check for the presence of 'zlib' in the output of
 
738
                * curl_version() to determine wheter compression is supported or not
 
739
                */
 
740
                var $accepted_compression = array();
 
741
                /**
 
742
                * Name of compression scheme to be used for sending requests.
 
743
                * Either null, gzip or deflate
 
744
                */
 
745
                var $request_compression = '';
 
746
                /**
 
747
                * CURL handle: used for keep-alive connections (PHP 4.3.8 up, see:
 
748
                * http://curl.haxx.se/docs/faq.html#7.3)
 
749
                */
 
750
                var $xmlrpc_curl_handle = null;
 
751
                /// Wheter to use persistent connections for http 1.1 and https
 
752
                var $keepalive = false;
 
753
                /// Charset encodings that can be decoded without problems by the client
 
754
                var $accepted_charset_encodings = array();
 
755
                /// Charset encoding to be used in serializing request. NULL = use ASCII
 
756
                var $request_charset_encoding = '';
 
757
                /**
 
758
                * Decides the content of xmlrpcresp objects returned by calls to send()
 
759
                * valid strings are 'xmlrpcvals', 'phpvals' or 'xml'
 
760
                */
 
761
                var $return_type = 'xmlrpcvals';
 
762
 
 
763
                /**
 
764
                * @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php
 
765
                * @param string $server the server name / ip address
 
766
                * @param integer $port the port the server is listening on, defaults to 80 or 443 depending on protocol used
 
767
                * @param string $method the http protocol variant: defaults to 'http', 'https' and 'http11' can be used if CURL is installed
 
768
                */
 
769
                function xmlrpc_client($path, $server='', $port='', $method='')
 
770
                {
 
771
                        // allow user to specify all params in $path
 
772
                        if($server == '' and $port == '' and $method == '')
 
773
                        {
 
774
                                $parts = parse_url($path);
 
775
                                $server = $parts['host'];
 
776
                                $path = $parts['path'];
 
777
                                if(isset($parts['query']))
 
778
                                {
 
779
                                        $path .= '?'.$parts['query'];
 
780
                                }
 
781
                                if(isset($parts['fragment']))
 
782
                                {
 
783
                                        $path .= '#'.$parts['fragment'];
 
784
                                }
 
785
                                if(isset($parts['port']))
 
786
                                {
 
787
                                        $port = $parts['port'];
 
788
                                }
 
789
                                if(isset($parts['scheme']))
 
790
                                {
 
791
                                        $method = $parts['scheme'];
 
792
                                }
 
793
                                if(isset($parts['user']))
 
794
                                {
 
795
                                        $this->username = $parts['user'];
 
796
                                }
 
797
                                if(isset($parts['pass']))
 
798
                                {
 
799
                                        $this->password = $parts['pass'];
 
800
                                }
 
801
                        }
 
802
                        if($path == '' || $path[0] != '/')
 
803
                        {
 
804
                                $this->path='/'.$path;
 
805
                        }
 
806
                        else
 
807
                        {
 
808
                                $this->path=$path;
 
809
                        }
 
810
                        $this->server=$server;
 
811
                        if($port != '')
 
812
                        {
 
813
                                $this->port=$port;
 
814
                        }
 
815
                        if($method != '')
 
816
                        {
 
817
                                $this->method=$method;
 
818
                        }
 
819
 
 
820
                        // if ZLIB is enabled, let the client by default accept compressed responses
 
821
                        if(function_exists('gzinflate') || (
 
822
                                function_exists('curl_init') && (($info = curl_version()) &&
 
823
                                ((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version'])))
 
824
                        ))
 
825
                        {
 
826
                                $this->accepted_compression = array('gzip', 'deflate');
 
827
                        }
 
828
 
 
829
                        // keepalives: enabled by default ONLY for PHP >= 4.3.8
 
830
                        // (see http://curl.haxx.se/docs/faq.html#7.3)
 
831
                        if(version_compare(phpversion(), '4.3.8') >= 0)
 
832
                        {
 
833
                                $this->keepalive = true;
 
834
                        }
 
835
 
 
836
                        // by default the xml parser can support these 3 charset encodings
 
837
                        $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
 
838
                }
 
839
 
 
840
                /*
 
841
                * Enables/disables the echoing to screen of the xmlrpc responses received
 
842
                * @param integer $debug values 0, 1 and 2 are supported (2 = echo sent msg too, beside received response)
 
843
                * @access public
 
844
                */
 
845
                function setDebug($in)
 
846
                {
 
847
                        $this->debug=$in;
 
848
                }
 
849
 
 
850
                /*
 
851
                * Add some http BASIC AUTH credentials, used by the client to authenticate
 
852
                * @param string $u username
 
853
                * @param string $p password
 
854
                * @access public
 
855
                */
 
856
                function setCredentials($u, $p)
 
857
                {
 
858
                        $this->username=$u;
 
859
                        $this->password=$p;
 
860
                }
 
861
 
 
862
                /*
 
863
                * Add a client-side https certificate
 
864
                * @param string $cert
 
865
                * @param string $certpass
 
866
                * @access public
 
867
                */
 
868
                function setCertificate($cert, $certpass)
 
869
                {
 
870
                        $this->cert = $cert;
 
871
                        $this->certpass = $certpass;
 
872
                }
 
873
 
 
874
                /*
 
875
                * @param string $key     The name of a file containing a private SSL key
 
876
                * @param string $keypass The secret password needed to use the private SSL key
 
877
                * @access public
 
878
                * NB: does not work in older php/curl installs
 
879
                * Thanks to Daniel Convissor
 
880
                */
 
881
                function setKey($key, $keypass)
 
882
                {
 
883
                        $this->key = $key;
 
884
                        $this->keypass = $keypass;
 
885
                }
 
886
 
 
887
                /*
 
888
                * @access public
 
889
                */
 
890
                function setSSLVerifyPeer($i)
 
891
                {
 
892
                        $this->verifypeer = $i;
 
893
                }
 
894
 
 
895
                /*
 
896
                * @access public
 
897
                */
 
898
                function setSSLVerifyHost($i)
 
899
                {
 
900
                        $this->verifyhost = $i;
 
901
                }
 
902
 
 
903
                /**
 
904
                * Set proxy info
 
905
                *
 
906
                * @param    string $proxyhost
 
907
                * @param    string $proxyport Defaults to 8080 for HTTP and 443 for HTTPS
 
908
                * @param    string $proxyusername Leave blank if proxy has public access
 
909
                * @param    string $proxypassword Leave blank if proxy has public access
 
910
                * @access   public
 
911
                */
 
912
                function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '')
 
913
                {
 
914
                        $this->proxy = $proxyhost;
 
915
                        $this->proxyport = $proxyport;
 
916
                        $this->proxy_user = $proxyusername;
 
917
                        $this->proxy_pass = $proxypassword;
 
918
                }
 
919
 
 
920
                /**
 
921
                * Enables/disables reception of compressed xmlrpc responses.
 
922
                * Note that enabling reception of compressed responses merely adds some standard
 
923
                * http headers to xmlrpc requests. It is up to the xmlrpc server to return
 
924
                * compressed responses when receiving such requests.
 
925
                * @param string $compmethod either 'gzip', 'deflate', 'any' or ''
 
926
                * @access   public
 
927
                */
 
928
                function setAcceptedCompression($compmethod)
 
929
                {
 
930
                        if ($compmethod == 'any')
 
931
                                $this->accepted_compression = array('gzip', 'deflate');
 
932
                        else
 
933
                                $this->accepted_compression = array($compmethod);
 
934
                }
 
935
 
 
936
                /**
 
937
                * Enables/disables http compression of xmlrpc request.
 
938
                * Take care when sending compressed requests: servers might not support them
 
939
                * (and automatic fallback to uncompressed requests is not yet implemented)
 
940
                * @param string $compmethod either 'gzip', 'deflate' or ''
 
941
                * @access   public
 
942
                */
 
943
                function setRequestCompression($compmethod)
 
944
                {
 
945
                        $this->request_compression = $compmethod;
 
946
                }
 
947
 
 
948
                /**
 
949
                * Adds a cookie to list of cookies that will be sent to server.
 
950
                * NB: setting any param but name and value will turn the cookie into a 'version 1' cookie:
 
951
                * do not do it unless you know what you are doing
 
952
                * @param string $name
 
953
                * @param string $value
 
954
                * @param string $path
 
955
                * @param string $domain
 
956
                * @param string $port
 
957
                * @access   public
 
958
                *
 
959
                * @todo check correctness of urlencoding cookie value (copied from php way of doing it...)
 
960
                */
 
961
                function setCookie($name, $value='', $path='', $domain='', $port=null)
 
962
                {
 
963
                        $this->cookies[$name]['value'] = urlencode($value);
 
964
                        if ($path || $domain || $port)
 
965
                        {
 
966
                                $this->cookies[$name]['path'] = $path;
 
967
                                $this->cookies[$name]['domain'] = $domain;
 
968
                                $this->cookies[$name]['port'] = $port;
 
969
                                $this->cookies[$name]['version'] = 1;
 
970
                        }
 
971
                        else
 
972
                        {
 
973
                                $this->cookies[$name]['version'] = 0;
 
974
                        }
 
975
                }
 
976
 
 
977
                /**
 
978
                * Send an xmlrpc request
 
979
                * @param mixed $msg The message object, or an array of messages for using multicall, or the complete xml representation of a request
 
980
                * @param integer $timeout Connection timeout, in seconds, If unspecified, a platform specific timeout will apply
 
981
                * @param string $method if left unspecified, the http protocol chosen during creation of the object will be used
 
982
                */
 
983
                function& send($msg, $timeout=0, $method='')
 
984
                {
 
985
                        // if user deos not specify http protocol, use native method of this client
 
986
                        // (i.e. method set during call to constructor)
 
987
                        if($method == '')
 
988
                        {
 
989
                                $method = $this->method;
 
990
                        }
 
991
 
 
992
                        if(is_array($msg))
 
993
                        {
 
994
                                // $msg is an array of xmlrpcmsg's
 
995
                                $r = $this->multicall($msg, $timeout, $method);
 
996
                                return $r;
 
997
                        }
 
998
                        elseif(is_string($msg))
 
999
                        {
 
1000
                                $n =& new xmlrpcmsg('');
 
1001
                                $n->payload = $msg;
 
1002
                                $msg = $n;
 
1003
                        }
 
1004
 
 
1005
                        // where msg is an xmlrpcmsg
 
1006
                        $msg->debug=$this->debug;
 
1007
 
 
1008
                        if($method == 'https')
 
1009
                        {
 
1010
                                $r =& $this->sendPayloadHTTPS(
 
1011
                                        $msg,
 
1012
                                        $this->server,
 
1013
                                        $this->port,
 
1014
                                        $timeout,
 
1015
                                        $this->username,
 
1016
                                        $this->password,
 
1017
                                        $this->cert,
 
1018
                                        $this->certpass,
 
1019
                                        $this->proxy,
 
1020
                                        $this->proxyport,
 
1021
                                        $this->proxy_user,
 
1022
                                        $this->proxy_pass,
 
1023
                                        $this->keepalive,
 
1024
                                        $this->key,
 
1025
                                        $this->keypass
 
1026
                                );
 
1027
                        }
 
1028
                        elseif($method == 'http11')
 
1029
                        {
 
1030
                                $r =& $this->sendPayloadCURL(
 
1031
                                        $msg,
 
1032
                                        $this->server,
 
1033
                                        $this->port,
 
1034
                                        $timeout,
 
1035
                                        $this->username,
 
1036
                                        $this->password,
 
1037
                                        null,
 
1038
                                        null,
 
1039
                                        $this->proxy,
 
1040
                                        $this->proxyport,
 
1041
                                        $this->proxy_user,
 
1042
                                        $this->proxy_pass,
 
1043
                                        'http',
 
1044
                                        $this->keepalive
 
1045
                                );
 
1046
                        }
 
1047
                        else
 
1048
                        {
 
1049
                                $r =& $this->sendPayloadHTTP10(
 
1050
                                        $msg,
 
1051
                                        $this->server,
 
1052
                                        $this->port,
 
1053
                                        $timeout,
 
1054
                                        $this->username,
 
1055
                                        $this->password,
 
1056
                                        $this->proxy,
 
1057
                                        $this->proxyport,
 
1058
                                        $this->proxy_user,
 
1059
                                        $this->proxy_pass
 
1060
                                );
 
1061
                        }
 
1062
 
 
1063
                        return $r;
 
1064
                }
 
1065
 
 
1066
                /**
 
1067
                * @access private
 
1068
                */
 
1069
                function &sendPayloadHTTP10($msg, $server, $port, $timeout=0,$username='', $password='',
 
1070
                        $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='')
 
1071
                {
 
1072
                        if($port==0)
 
1073
                        {
 
1074
                                $port=80;
 
1075
                        }
 
1076
 
 
1077
                        // Only create the payload if it was not created previously
 
1078
                        if(empty($msg->payload))
 
1079
                        {
 
1080
                                $msg->createPayload($this->request_charset_encoding);
 
1081
                        }
 
1082
 
 
1083
                        $payload = $msg->payload;
 
1084
                        // Deflate request body and set appropriate request headers
 
1085
                        if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
 
1086
                        {
 
1087
                                if($this->request_compression == 'gzip')
 
1088
                                {
 
1089
                                        $a = @gzencode($msg->payload);
 
1090
                                        if($a)
 
1091
                                        {
 
1092
                                                $payload = $a;
 
1093
                                                $encoding_hdr = "Content-Encoding: gzip\r\n";
 
1094
                                        }
 
1095
                                }
 
1096
                                else
 
1097
                                {
 
1098
                                        $a = @gzdeflate($msg->payload);
 
1099
                                        if($a)
 
1100
                                        {
 
1101
                                                $payload = $a;
 
1102
                                                $encoding_hdr = "Content-Encoding: deflate\r\n";
 
1103
                                        }
 
1104
                                }
 
1105
                        }
 
1106
                        else
 
1107
                        {
 
1108
                                $encoding_hdr = '';
 
1109
                        }
 
1110
 
 
1111
                        // thanks to Grant Rauscher <grant7@firstworld.net>
 
1112
                        // for this
 
1113
                        $credentials='';
 
1114
                        if($username!='')
 
1115
                        {
 
1116
                                $credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
 
1117
                        }
 
1118
 
 
1119
                        $accepted_encoding = '';
 
1120
                        if(is_array($this->accepted_compression) && count($this->accepted_compression))
 
1121
                        {
 
1122
                                $accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n";
 
1123
                        }
 
1124
 
 
1125
                        $proxy_credentials = '';
 
1126
                        if($proxyhost)
 
1127
                        {
 
1128
                                if($proxyport == 0)
 
1129
                                {
 
1130
                                        $proxyport = 8080;
 
1131
                                }
 
1132
                                $connectserver = $proxyhost;
 
1133
                                $connectport = $proxyport;
 
1134
                                $uri = 'http://'.$server.':'.$port.$this->path;
 
1135
                                if($proxyusername != '')
 
1136
                                {
 
1137
                                        $proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n";
 
1138
                                }
 
1139
                        }
 
1140
                        else
 
1141
                        {
 
1142
                                $connectserver = $server;
 
1143
                                $connectport = $port;
 
1144
                                $uri = $this->path;
 
1145
                        }
 
1146
 
 
1147
                        // Cookie generation, as per rfc2965 (version 1 cookies) or
 
1148
                        // netscape's rules (version 0 cookies)
 
1149
                        $cookieheader='';
 
1150
                        foreach ($this->cookies as $name => $cookie)
 
1151
                        {
 
1152
                                if ($cookie['version'])
 
1153
                                {
 
1154
                                        $cookieheader .= 'Cookie: $Version="' . $cookie['version'] . '"; ';
 
1155
                                        $cookieheader .= $name . '="' . $cookie['value'] . '";';
 
1156
                                        if ($cookie['path'])
 
1157
                                                $cookieheader .= ' $Path="' . $cookie['path'] . '";';
 
1158
                                        if ($cookie['domain'])
 
1159
                                                $cookieheader .= ' $Domain="' . $cookie['domain'] . '";';
 
1160
                                        if ($cookie['port'])
 
1161
                                                $cookieheader .= ' $Port="' . $cookie['domain'] . '";';
 
1162
                                        $cookieheader = substr($cookieheader, 0, -1) . "\r\n";
 
1163
                                }
 
1164
                                else
 
1165
                                {
 
1166
                                        $cookieheader .= 'Cookie: ' . $name . '=' . $cookie['value'] . "\r\n";
 
1167
                                }
 
1168
                        }
 
1169
 
 
1170
                        $op= "POST " . $uri. " HTTP/1.0\r\n" .
 
1171
                                "User-Agent: " . $GLOBALS['xmlrpcName'] . " " . $GLOBALS['xmlrpcVersion'] . "\r\n" .
 
1172
                                "Host: ". $server . "\r\n" .
 
1173
                                $credentials .
 
1174
                                $proxy_credentials .
 
1175
                                $accepted_encoding .
 
1176
                                $encoding_hdr .
 
1177
                                "Accept-Charset: " . implode(',', $this->accepted_charset_encodings) . "\r\n" .
 
1178
                                $cookieheader .
 
1179
                                "Content-Type: " . $msg->content_type . "\r\nContent-Length: " .
 
1180
                                strlen($payload) . "\r\n\r\n" .
 
1181
                                $payload;
 
1182
 
 
1183
 
 
1184
                        if($this->debug > 1)
 
1185
                        {
 
1186
                                print "<PRE>\n---SENDING---\n" . htmlentities($op) . "\n---END---\n</PRE>";
 
1187
                                // let the client see this now in case http times out...
 
1188
                                flush();
 
1189
                        }
 
1190
 
 
1191
                        if($timeout>0)
 
1192
                        {
 
1193
                                $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout);
 
1194
                        }
 
1195
                        else
 
1196
                        {
 
1197
                                $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr);
 
1198
                        }
 
1199
                        if($fp)
 
1200
                        {
 
1201
                                if($timeout>0 && function_exists('stream_set_timeout'))
 
1202
                                {
 
1203
                                        stream_set_timeout($fp, $timeout);
 
1204
                                }
 
1205
                        }
 
1206
                        else
 
1207
                        {
 
1208
                                $this->errstr='Connect error: '.$this->errstr;
 
1209
                                $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')');
 
1210
                                return $r;
 
1211
                        }
 
1212
 
 
1213
                        if(!fputs($fp, $op, strlen($op)))
 
1214
                        {
 
1215
                                $this->errstr='Write error';
 
1216
                                $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr);
 
1217
                                return $r;
 
1218
                        }
 
1219
                        else
 
1220
                        {
 
1221
                                // reset errno and errstr on succesful socket connection
 
1222
                                $this->errstr = '';
 
1223
                        }
 
1224
                        // G. Giunta 2005/10/24: close socket before parsing.
 
1225
                        // should yeld slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
 
1226
                        //$resp=&$msg->parseResponseFile($fp);
 
1227
                        $ipd='';
 
1228
                        while($data=fread($fp, 32768))
 
1229
                        {
 
1230
                                // shall we check for $data === FALSE?
 
1231
                                // as per the manual, it signals an error
 
1232
                                $ipd.=$data;
 
1233
                        }
 
1234
                        fclose($fp);
 
1235
                        $r =& $msg->parseResponse($ipd, false, $this->return_type);
 
1236
                        return $r;
 
1237
 
 
1238
                }
 
1239
 
 
1240
                /**
 
1241
                * @access private
 
1242
                */
 
1243
                function &sendPayloadHTTPS($msg, $server, $port, $timeout=0,$username='', $password='', $cert='',$certpass='',
 
1244
                        $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $keepalive=false, $key='', $keypass='')
 
1245
                {
 
1246
                        $r =& $this->sendPayloadCURL($msg, $server, $port, $timeout, $username, $password, $cert, $certpass,
 
1247
                                $proxyhost, $proxyport, $proxyusername, $proxypassword, 'https', $keepalive, $key, $keypass);
 
1248
                        return $r;
 
1249
                }
 
1250
 
 
1251
                /**
 
1252
                * Contributed by Justin Miller <justin@voxel.net>
 
1253
                * Requires curl to be built into PHP
 
1254
                * NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
 
1255
                * @access private
 
1256
                */
 
1257
                function &sendPayloadCURL($msg, $server, $port, $timeout=0, $username='', $password='', $cert='', $certpass='',
 
1258
                        $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $method='https', $keepalive=false,
 
1259
                        $key='', $keypass='')
 
1260
                {
 
1261
                        if(!function_exists('curl_init'))
 
1262
                        {
 
1263
                                $this->errstr='CURL unavailable on this install';
 
1264
                                $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']);
 
1265
                                return $r;
 
1266
                        }
 
1267
                        if($method == 'https')
 
1268
                        {
 
1269
                                if(($info = curl_version()) &&
 
1270
                                        ((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version']))))
 
1271
                                {
 
1272
                                        $this->errstr='SSL unavailable on this install';
 
1273
                                        $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']);
 
1274
                                        return $r;
 
1275
                                }
 
1276
                        }
 
1277
 
 
1278
                        if($port == 0)
 
1279
                        {
 
1280
                                if($method == 'http')
 
1281
                                {
 
1282
                                        $port = 80;
 
1283
                                }
 
1284
                                else
 
1285
                                {
 
1286
                                        $port = 443;
 
1287
                                }
 
1288
                        }
 
1289
 
 
1290
                        // Only create the payload if it was not created previously
 
1291
                        if(empty($msg->payload))
 
1292
                        {
 
1293
                                $msg->createPayload($this->request_charset_encoding);
 
1294
                        }
 
1295
 
 
1296
                        // Deflate request body and set appropriate request headers
 
1297
                        $payload = $msg->payload;
 
1298
                        if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
 
1299
                        {
 
1300
                                if($this->request_compression == 'gzip')
 
1301
                                {
 
1302
                                        $a = @gzencode($msg->payload);
 
1303
                                        if($a)
 
1304
                                        {
 
1305
                                                $payload = $a;
 
1306
                                                $encoding_hdr = "Content-Encoding: gzip";
 
1307
                                        }
 
1308
                                }
 
1309
                                else
 
1310
                                {
 
1311
                                        $a = @gzdeflate($msg->payload);
 
1312
                                        if($a)
 
1313
                                        {
 
1314
                                                $payload = $a;
 
1315
                                                $encoding_hdr = "Content-Encoding: deflate";
 
1316
                                        }
 
1317
                                }
 
1318
                        }
 
1319
                        else
 
1320
                        {
 
1321
                                $encoding_hdr = '';
 
1322
                        }
 
1323
 
 
1324
                        if($this->debug > 1)
 
1325
                        {
 
1326
                                print "<PRE>\n---SENDING---\n" . htmlentities($payload) . "\n---END---\n</PRE>";
 
1327
                                // let the client see this now in case http times out...
 
1328
                                flush();
 
1329
                        }
 
1330
 
 
1331
                        if(!$keepalive || !$this->xmlrpc_curl_handle)
 
1332
                        {
 
1333
                                $curl = curl_init($method . '://' . $server . ':' . $port . $this->path);
 
1334
                                if($keepalive)
 
1335
                                {
 
1336
                                        $this->xmlrpc_curl_handle = $curl;
 
1337
                                }
 
1338
                        }
 
1339
                        else
 
1340
                        {
 
1341
                                $curl = $this->xmlrpc_curl_handle;
 
1342
                        }
 
1343
 
 
1344
                        // results into variable
 
1345
                        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
 
1346
 
 
1347
                        if($this->debug)
 
1348
                        {
 
1349
                                curl_setopt($curl, CURLOPT_VERBOSE, 1);
 
1350
                        }
 
1351
                        curl_setopt($curl, CURLOPT_USERAGENT, $GLOBALS['xmlrpcName'].' '.$GLOBALS['xmlrpcVersion']);
 
1352
                        // required for XMLRPC: post the data
 
1353
                        curl_setopt($curl, CURLOPT_POST, 1);
 
1354
                        // the data
 
1355
                        curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
 
1356
 
 
1357
                        // return the header too
 
1358
                        curl_setopt($curl, CURLOPT_HEADER, 1);
 
1359
 
 
1360
                        // will only work with PHP >= 5.0
 
1361
                        // NB: if we set an empty string, CURL will add http header indicating
 
1362
                        // ALL methods it is supporting. This is possibly a better option than
 
1363
                        // letting the user tell what curl can / cannot do...
 
1364
                        if(is_array($this->accepted_compression) && count($this->accepted_compression))
 
1365
                        {
 
1366
                                //curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression));
 
1367
                                // empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
 
1368
                                curl_setopt($curl, CURLOPT_ENCODING, '');
 
1369
                        }
 
1370
                        // extra headers
 
1371
                        $headers = array('Content-Type: ' . $msg->content_type , 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings));
 
1372
                        // if no keepalive is wanted, let the server know it in advance
 
1373
                        if(!$keepalive)
 
1374
                        {
 
1375
                                $headers[] = 'Connection: close';
 
1376
                        }
 
1377
                        // request compression header
 
1378
                        if($encoding_hdr)
 
1379
                        {
 
1380
                                $headers[] = $encoding_hdr;
 
1381
                        }
 
1382
 
 
1383
                        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
 
1384
                        // timeout is borked
 
1385
                        if($timeout)
 
1386
                        {
 
1387
                                curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
 
1388
                        }
 
1389
 
 
1390
                        if($username && $password)
 
1391
                        {
 
1392
                                curl_setopt($curl, CURLOPT_USERPWD,"$username:$password");
 
1393
                        }
 
1394
 
 
1395
                        if($method == 'https')
 
1396
                        {
 
1397
                                // set cert file
 
1398
                                if($cert)
 
1399
                                {
 
1400
                                        curl_setopt($curl, CURLOPT_SSLCERT, $cert);
 
1401
                                }
 
1402
                                // set cert password
 
1403
                                if($certpass)
 
1404
                                {
 
1405
                                        curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass);
 
1406
                                }
 
1407
                                // set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
 
1408
                                if($key)
 
1409
                                {
 
1410
                                        curl_setopt($curl, CURLOPT_SSLKEY, $key);
 
1411
                                }
 
1412
                                // set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
 
1413
                                if($keypass)
 
1414
                                {
 
1415
                                        curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $keypass);
 
1416
                                }
 
1417
                                // whether to verify remote host's cert
 
1418
                                curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
 
1419
                                // 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
 
1420
                                curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
 
1421
                        }
 
1422
 
 
1423
                        // proxy info
 
1424
                        if($proxyhost)
 
1425
                        {
 
1426
                                if($proxyport == 0)
 
1427
                                {
 
1428
                                        $proxyport = 8080; // NB: even for HTTPS, local connection is on port 8080
 
1429
                                }
 
1430
                                curl_setopt($curl, CURLOPT_PROXY,$proxyhost.':'.$proxyport);
 
1431
                                //curl_setopt($curl, CURLOPT_PROXYPORT,$proxyport);
 
1432
                                if($proxyusername)
 
1433
                                {
 
1434
                                        curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyusername.':'.$proxypassword);
 
1435
                                }
 
1436
                        }
 
1437
 
 
1438
                        // NB: should we build cookie http headers by hand rather than let CURL do it?
 
1439
                        // the following code does not honour 'expires', 'path' and 'domain' cookie attributes
 
1440
                        // set to clint obj the the user...
 
1441
                        if (count($this->cookies))
 
1442
                        {
 
1443
                                $cookieheader = '';
 
1444
                                foreach ($this->cookies as $name => $cookie)
 
1445
                                {
 
1446
                                        $cookieheader .= $name . '=' . $cookie['value'] . ', ';
 
1447
                                }
 
1448
                                curl_setopt($curl, CURLOPT_COOKIE, substr($cookieheader, 0, -2));
 
1449
                        }
 
1450
 
 
1451
                        $result = curl_exec($curl);
 
1452
 
 
1453
                        if(!$result)
 
1454
                        {
 
1455
                                $this->errstr='no response';
 
1456
                                $resp=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['curl_fail'], $GLOBALS['xmlrpcstr']['curl_fail']. ': '. curl_error($curl));
 
1457
                                if(!$keepalive)
 
1458
                                {
 
1459
                                        curl_close($curl);
 
1460
                                }
 
1461
                        }
 
1462
                        else
 
1463
                        {
 
1464
                                if(!$keepalive)
 
1465
                                {
 
1466
                                        curl_close($curl);
 
1467
                                }
 
1468
                                $resp =& $msg->parseResponse($result, true, $this->return_type);
 
1469
                        }
 
1470
                        return $resp;
 
1471
                }
 
1472
 
 
1473
                /**
 
1474
                * Send an array of request messages and return an array of responses.
 
1475
                * Unless $this->no_multicall has been set to true, it will try first
 
1476
                * to use one single xmlrpc call to server method system.multicall, and
 
1477
                * revert to sending many successive calls in case of failure.
 
1478
                * This failure is also stored in $this->no_multicall for subsequent calls.
 
1479
                * Unfortunately, there is no server error code universally used to denote
 
1480
                * the fact that multicall is unsupported, so there is no way to reliably
 
1481
                * distinguish between that and a temporary failure.
 
1482
                * If you are sure that server supports multicall and do not want to
 
1483
                * fallback to using many single calls, set the fourth parameter to FALSE.
 
1484
                *
 
1485
                * NB: trying to shoehorn extra functionality into existing syntax has resulted
 
1486
                * in pretty much convoluted code...
 
1487
                *
 
1488
                * @access public
 
1489
                * @param array $msgs an array of xmlrpcmsg objects
 
1490
                * @param integer $timeout connection timeout (in seconds)
 
1491
                * @param string $method the http protocol variant to be used
 
1492
                * @param boolen fallback When true, upon receiveing an error during multicall, multiple single calls will be attempted
 
1493
                */
 
1494
                function multicall($msgs, $timeout=0, $method='http', $fallback=true)
 
1495
                {
 
1496
                        if(!$this->no_multicall)
 
1497
                        {
 
1498
                                $results = $this->_try_multicall($msgs, $timeout, $method);
 
1499
                                if(is_array($results))
 
1500
                                {
 
1501
                                        // System.multicall succeeded
 
1502
                                        return $results;
 
1503
                                }
 
1504
                                else
 
1505
                                {
 
1506
                                        // either system.multicall is unsupported by server,
 
1507
                                        // or call failed for some other reason.
 
1508
                                        if ($fallback)
 
1509
                                        {
 
1510
                                                // Don't try it next time...
 
1511
                                                $this->no_multicall = true;
 
1512
                                        }
 
1513
                                        else
 
1514
                                        {
 
1515
                                                if (is_a($result, 'xmlrpcresp'))
 
1516
                                                {
 
1517
                                                        $result = $results;
 
1518
                                                }
 
1519
                                                else
 
1520
                                                {
 
1521
                                                        $result =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['multicall_error'], $GLOBALS['xmlrpcstr']['multicall_error']);
 
1522
                                                }
 
1523
                                        }
 
1524
                                }
 
1525
                        }
 
1526
                        else
 
1527
                        {
 
1528
                                // override fallback, in case careless user tries to do two
 
1529
                                // opposite things at the same time
 
1530
                                $fallback = true;
 
1531
                        }
 
1532
 
 
1533
                        $results = array();
 
1534
                        if ($fallback)
 
1535
                        {
 
1536
                                // system.multicall is (probably) unsupported by server:
 
1537
                                // emulate multicall via multiple requests
 
1538
                                foreach($msgs as $msg)
 
1539
                                {
 
1540
                                        $results[] =& $this->send($msg, $timeout, $method);
 
1541
                                }
 
1542
                        }
 
1543
                        else
 
1544
                        {
 
1545
                                // user does NOT want to fallback on many single calls:
 
1546
                                // since we should always return an array of responses,
 
1547
                                // return an array with the same error repeated n times
 
1548
                                foreach($msgs as $msg)
 
1549
                                {
 
1550
                                        $results[] = $result;
 
1551
                                }
 
1552
                        }
 
1553
                        return $results;
 
1554
                }
 
1555
 
 
1556
                /**
 
1557
                * Attempt to boxcar $msgs via system.multicall.
 
1558
                * Returns either an array of xmlrpcreponses, an xmlrpc error response
 
1559
                * or false (when recived response does not respect valid multiccall syntax)
 
1560
                * @access private
 
1561
                */
 
1562
                function _try_multicall($msgs, $timeout, $method)
 
1563
                {
 
1564
                        // Construct multicall message
 
1565
                        $calls = array();
 
1566
                        foreach($msgs as $msg)
 
1567
                        {
 
1568
                                $call['methodName'] =& new xmlrpcval($msg->method(),'string');
 
1569
                                $numParams = $msg->getNumParams();
 
1570
                                $params = array();
 
1571
                                for($i = 0; $i < $numParams; $i++)
 
1572
                                {
 
1573
                                        $params[$i] = $msg->getParam($i);
 
1574
                                }
 
1575
                                $call['params'] =& new xmlrpcval($params, 'array');
 
1576
                                $calls[] =& new xmlrpcval($call, 'struct');
 
1577
                        }
 
1578
                        $multicall =& new xmlrpcmsg('system.multicall');
 
1579
                        $multicall->addParam(new xmlrpcval($calls, 'array'));
 
1580
 
 
1581
                        // Attempt RPC call
 
1582
                        $result =& $this->send($multicall, $timeout, $method);
 
1583
                        //if(!is_object($result))
 
1584
                        //{
 
1585
                        //      return ($result || 0); // transport failed
 
1586
                        //}
 
1587
 
 
1588
                        if($result->faultCode() != 0)
 
1589
                        {
 
1590
                                // call to system.multicall failed
 
1591
                                return $result;
 
1592
                        }
 
1593
 
 
1594
                        // Unpack responses.
 
1595
                        $rets = $result->value();
 
1596
 
 
1597
                        if ($this->return_type == 'xml')
 
1598
                        {
 
1599
                                        return $rets;
 
1600
                        }
 
1601
                        else if ($this->return_type == 'phpvals')
 
1602
                        {
 
1603
                                ///@todo test this code branch...
 
1604
                                $rets = $result->value();
 
1605
                                if(!is_array($rets))
 
1606
                                {
 
1607
                                        return false;           // bad return type from system.multicall
 
1608
                                }
 
1609
                                $numRets = count($rets);
 
1610
                                if($numRets != count($msgs))
 
1611
                                {
 
1612
                                        return false;           // wrong number of return values.
 
1613
                                }
 
1614
 
 
1615
                                $response = array();
 
1616
                                for($i = 0; $i < $numRets; $i++)
 
1617
                                {
 
1618
                                        $val = $rets[$i];
 
1619
                                        if (!is_array($val)) {
 
1620
                                                return false;
 
1621
                                        }
 
1622
                                        switch(count($val))
 
1623
                                        {
 
1624
                                                case 1:
 
1625
                                                        if(!isset($val[0]))
 
1626
                                                        {
 
1627
                                                                return false;           // Bad value
 
1628
                                                        }
 
1629
                                                        // Normal return value
 
1630
                                                        $response[$i] =& new xmlrpcresp($val[0], 0, '', 'phpvals');
 
1631
                                                        break;
 
1632
                                                case 2:
 
1633
                                                        ///     @todo remove usage of @: it is apparently quite slow
 
1634
                                                        $code = @$val['faultCode'];
 
1635
                                                        if(!is_int($code))
 
1636
                                                        {
 
1637
                                                                return false;
 
1638
                                                        }
 
1639
                                                        $str = @$val['faultString'];
 
1640
                                                        if(!is_string($str))
 
1641
                                                        {
 
1642
                                                                return false;
 
1643
                                                        }
 
1644
                                                        $response[$i] =& new xmlrpcresp(0, $code, $str);
 
1645
                                                        break;
 
1646
                                                default:
 
1647
                                                        return false;
 
1648
                                        }
 
1649
                                }
 
1650
                                return $response;
 
1651
                        }
 
1652
                        else // return type == 'xmlrpcvals'
 
1653
                        {
 
1654
                                $rets = $result->value();
 
1655
                                if($rets->kindOf() != 'array')
 
1656
                                {
 
1657
                                        return false;           // bad return type from system.multicall
 
1658
                                }
 
1659
                                $numRets = $rets->arraysize();
 
1660
                                if($numRets != count($msgs))
 
1661
                                {
 
1662
                                        return false;           // wrong number of return values.
 
1663
                                }
 
1664
 
 
1665
                                $response = array();
 
1666
                                for($i = 0; $i < $numRets; $i++)
 
1667
                                {
 
1668
                                        $val = $rets->arraymem($i);
 
1669
                                        switch($val->kindOf())
 
1670
                                        {
 
1671
                                                case 'array':
 
1672
                                                        if($val->arraysize() != 1)
 
1673
                                                        {
 
1674
                                                                return false;           // Bad value
 
1675
                                                        }
 
1676
                                                        // Normal return value
 
1677
                                                        $response[$i] =& new xmlrpcresp($val->arraymem(0));
 
1678
                                                        break;
 
1679
                                                case 'struct':
 
1680
                                                        $code = $val->structmem('faultCode');
 
1681
                                                        if($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
 
1682
                                                        {
 
1683
                                                                return false;
 
1684
                                                        }
 
1685
                                                        $str = $val->structmem('faultString');
 
1686
                                                        if($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
 
1687
                                                        {
 
1688
                                                                return false;
 
1689
                                                        }
 
1690
                                                        $response[$i] =& new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
 
1691
                                                        break;
 
1692
                                                default:
 
1693
                                                        return false;
 
1694
                                        }
 
1695
                                }
 
1696
                                return $response;
 
1697
                        }
 
1698
                }
 
1699
        } // end class xmlrpc_client
 
1700
 
 
1701
        class xmlrpcresp
 
1702
        {
 
1703
                var $val = 0;
 
1704
                var $valtyp;
 
1705
                var $errno = 0;
 
1706
                var $errstr = '';
 
1707
                var $hdrs = array();
 
1708
                var $_cookies = array();
 
1709
                var $content_type = 'text/xml';
 
1710
 
 
1711
                /**
 
1712
                * @param mixed  $val either an xmlrpcval obj, a php value or the xml serialization of an xmlrpcval (a string)
 
1713
                * @param integer $fcode set it to anything but 0 to create an error response
 
1714
                * @param string $fstr the error string, in case of an error response
 
1715
                * @param string $valtyp either 'xmlrpcvals', 'phpvals' or 'xml'
 
1716
                *
 
1717
                * @todo add check that $val is of correct type???
 
1718
                * NB: as of now we do not do it, since it might be either an xmlrpcval or a plain
 
1719
                * php val, or a complete xml chunk, depending on usage of xmlrpc_client::send() inside which creator is called...
 
1720
                */
 
1721
                function xmlrpcresp($val, $fcode = 0, $fstr = '', $valtyp='')
 
1722
                {
 
1723
                        if($fcode != 0)
 
1724
                        {
 
1725
                                // error response
 
1726
                                $this->errno = $fcode;
 
1727
                                $this->errstr = $fstr;
 
1728
                                //$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
 
1729
                        }
 
1730
                        /*elseif(!is_object($val) || !is_a($val, 'xmlrpcval'))
 
1731
                        {
 
1732
                                // programmer error
 
1733
                                error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to xmlrpcresp. Defaulting to empty value.");
 
1734
                                $this->val =& new xmlrpcval();
 
1735
                        }*/
 
1736
                        else
 
1737
                        {
 
1738
                                // successful response
 
1739
                                $this->val = $val;
 
1740
                                if ($valtyp == '')
 
1741
                                {
 
1742
                                        // user did not declare type of response value: try to guess it
 
1743
                                        if (is_object($this->val) && is_a($this->val, 'xmlrpcval'))
 
1744
                                        {
 
1745
                                                $this->valtyp = 'xmlrpcvals';
 
1746
                                        }
 
1747
                                        else if (is_string($this->val))
 
1748
                                        {
 
1749
                                                $this->valtyp = 'xml';
 
1750
 
 
1751
                                        }
 
1752
                                        else
 
1753
                                        {
 
1754
                                                $this->valtyp = 'phpvals';
 
1755
                                        }
 
1756
                                }
 
1757
                                else
 
1758
                                {
 
1759
                                        // user declares type of resp value: believe him
 
1760
                                        $this->valtyp = $valtyp;
 
1761
                                }
 
1762
                        }
 
1763
                }
 
1764
 
 
1765
                /*
 
1766
                * @return integer the error code of this response (0 for not-error responses)
 
1767
                */
 
1768
                function faultCode()
 
1769
                {
 
1770
                        return $this->errno;
 
1771
                }
 
1772
 
 
1773
                /*
 
1774
                * @return string the error string of this response ('' for not-error responses)
 
1775
                */
 
1776
                function faultString()
 
1777
                {
 
1778
                        return $this->errstr;
 
1779
                }
 
1780
 
 
1781
                /*
 
1782
                * @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
 
1783
                */
 
1784
                function value()
 
1785
                {
 
1786
                        return $this->val;
 
1787
                }
 
1788
 
 
1789
                /**
 
1790
                * Returns an array with the cookies received from the server.
 
1791
                * Array has the form: $cookiename => array ('value' => $val, $attr1 => $val1, $attr2 = $val2, ...)
 
1792
                * with attributes being e.g. 'expires', 'path', domain'.
 
1793
                * NB: cookies sent as 'expired' by the server (i.e. with an expiry date in the past)
 
1794
                * are still present in the array. It is up to the user-defined code to decide
 
1795
                * how to use the received cookies, and wheter they have to be sent back with the next
 
1796
                * request to the server (using xmlrpc_client::setCookie) or not
 
1797
                * @return array array of cookies received from the server
 
1798
                * @access public
 
1799
                */
 
1800
                function cookies()
 
1801
                {
 
1802
                        return $this->_cookies;
 
1803
                }
 
1804
 
 
1805
                /**
 
1806
                * Return xml representation of the response
 
1807
                * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
 
1808
                * @return string the xml representation of the response
 
1809
                */
 
1810
                function serialize($charset_encoding='')
 
1811
                {
 
1812
                        if ($charset_encoding != '')
 
1813
                                $this->content_type = 'text/xml; charset=' . $charset_encoding;
 
1814
                        else
 
1815
                                $this->content_type = 'text/xml';
 
1816
                        $result = "<methodResponse>\n";
 
1817
                        if($this->errno)
 
1818
                        {
 
1819
                                // G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
 
1820
                                // by xml-encoding non ascii chars
 
1821
                                $result .= "<fault>\n" .
 
1822
"<value>\n<struct><member><name>faultCode</name>\n<value><int>" . $this->errno .
 
1823
"</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>" .
 
1824
xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "</string></value>\n</member>\n" .
 
1825
"</struct>\n</value>\n</fault>";
 
1826
                        }
 
1827
                        else
 
1828
                        {
 
1829
                                if(!is_object($this->val) || !is_a($this->val, 'xmlrpcval'))
 
1830
                                {
 
1831
                                        if (is_string($this->val) && $this->valtyp == 'xml')
 
1832
                                        {
 
1833
                                                $result .= "<params>\n<param>\n" .
 
1834
                                                        $this->val .
 
1835
                                                        "</param>\n</params>";
 
1836
                                        }
 
1837
                                        else
 
1838
                                        {
 
1839
                                                /// @todo try to build something serializable?
 
1840
                                                die('cannot serialize xmlrpcresp objects whose content is native php values');
 
1841
                                        }
 
1842
                                }
 
1843
                                else
 
1844
                                {
 
1845
                                        $result .= "<params>\n<param>\n" .
 
1846
                                                $this->val->serialize($charset_encoding) .
 
1847
                                                "</param>\n</params>";
 
1848
                                }
 
1849
                        }
 
1850
                        $result .= "\n</methodResponse>";
 
1851
                        return $result;
 
1852
                }
 
1853
        }
 
1854
 
 
1855
        class xmlrpcmsg
 
1856
        {
 
1857
                var $payload;
 
1858
                var $methodname;
 
1859
                var $params=array();
 
1860
                var $debug=0;
 
1861
                var $content_type = 'text/xml';
 
1862
 
 
1863
                /*
 
1864
                * @param string $meth the name of the method to invoke
 
1865
                * @param array $pars array of parameters to be paased to the method (xmlrpcval objects)
 
1866
                */
 
1867
                function xmlrpcmsg($meth, $pars=0)
 
1868
                {
 
1869
                        $this->methodname=$meth;
 
1870
                        if(is_array($pars) && sizeof($pars)>0)
 
1871
                        {
 
1872
                                for($i=0; $i<sizeof($pars); $i++)
 
1873
                                {
 
1874
                                        $this->addParam($pars[$i]);
 
1875
                                }
 
1876
                        }
 
1877
                }
 
1878
 
 
1879
                function xml_header()
 
1880
                {
 
1881
                        return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n";
 
1882
                }
 
1883
 
 
1884
                function xml_footer()
 
1885
                {
 
1886
                        return "</methodCall>";
 
1887
                }
 
1888
 
 
1889
                function kindOf()
 
1890
                {
 
1891
                        return 'msg';
 
1892
                }
 
1893
 
 
1894
                function createPayload($charset_encoding='')
 
1895
                {
 
1896
                        if ($charset_encoding != '')
 
1897
                                $this->content_type = 'text/xml; charset=' . $charset_encoding;
 
1898
                        else
 
1899
                                $this->content_type = 'text/xml';
 
1900
                        $this->payload=$this->xml_header();
 
1901
                        $this->payload.='<methodName>' . $this->methodname . "</methodName>\n";
 
1902
                        //      if(sizeof($this->params)) {
 
1903
                        $this->payload.="<params>\n";
 
1904
                        for($i=0; $i<sizeof($this->params); $i++)
 
1905
                        {
 
1906
                                $p=$this->params[$i];
 
1907
                                $this->payload.="<param>\n" . $p->serialize($charset_encoding) .
 
1908
                                "</param>\n";
 
1909
                        }
 
1910
                        $this->payload.="</params>\n";
 
1911
                        // }
 
1912
                        $this->payload.=$this->xml_footer();
 
1913
                        //$this->payload=str_replace("\n", "\r\n", $this->payload);
 
1914
                }
 
1915
 
 
1916
                /*
 
1917
                * Gets/sets the xmlrpc method to be invoked
 
1918
                * @param string $meth the method to be set (leave empty not to set it)
 
1919
                * @return string the method that will be invoked
 
1920
                * @access public
 
1921
                */
 
1922
                function method($meth='')
 
1923
                {
 
1924
                        if($meth!='')
 
1925
                        {
 
1926
                                $this->methodname=$meth;
 
1927
                        }
 
1928
                        return $this->methodname;
 
1929
                }
 
1930
 
 
1931
                /*
 
1932
                * @return string the xml representation of the message
 
1933
                */
 
1934
                function serialize($charset_encoding='')
 
1935
                {
 
1936
                        $this->createPayload($charset_encoding);
 
1937
                        return $this->payload;
 
1938
                }
 
1939
 
 
1940
                /*
 
1941
                * Add a parameter to the list of parameters to be used upon method invocation
 
1942
                * @param xmlrpcval $par
 
1943
                * @return boolean false on failure
 
1944
                */
 
1945
                function addParam($par)
 
1946
                {
 
1947
                        // add check: do not add to self params which are not xmlrpcvals
 
1948
                        if(is_object($par) && is_a($par, 'xmlrpcval'))
 
1949
                        {
 
1950
                                $this->params[]=$par;
 
1951
                                return true;
 
1952
                        }
 
1953
                        else
 
1954
                        {
 
1955
                                return false;
 
1956
                        }
 
1957
                }
 
1958
 
 
1959
                /*
 
1960
                * @param integer $i the index of the parameter to fetch (zero based)
 
1961
                * @return xmlrpcval the i-th parameter
 
1962
                */
 
1963
                function getParam($i) { return $this->params[$i]; }
 
1964
 
 
1965
                /*
 
1966
                * @return integer the number of parameters currently set
 
1967
                */
 
1968
                function getNumParams() { return sizeof($this->params); }
 
1969
 
 
1970
                /*
 
1971
                * @access private
 
1972
                * @todo add 2nd & 3rd param to be passed to ParseResponse() ???
 
1973
                */
 
1974
                function &parseResponseFile($fp)
 
1975
                {
 
1976
                        $ipd='';
 
1977
                        while($data=fread($fp, 32768))
 
1978
                        {
 
1979
                                $ipd.=$data;
 
1980
                        }
 
1981
                        //fclose($fp);
 
1982
                        $r =& $this->parseResponse($ipd);
 
1983
                        return $r;
 
1984
                }
 
1985
 
 
1986
                /**
 
1987
                * Parses HTTP headers and separates them from data.
 
1988
                * @access private
 
1989
                */
 
1990
                function &parseResponseHeaders(&$data, $headers_processed=false)
 
1991
                {
 
1992
                                // Strip HTTP 1.1 100 Continue header if present
 
1993
                                while(ereg('^HTTP/1\.1 1[0-9]{2} ', $data))
 
1994
                                {
 
1995
                                        $pos = strpos($data, 'HTTP', 12);
 
1996
                                        // server sent a Continue header without any (valid) content following...
 
1997
                                        // give the client a chance to know it
 
1998
                                        if(!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
 
1999
                                        {
 
2000
                                                break;
 
2001
                                        }
 
2002
                                        $data = substr($data, $pos);
 
2003
                                }
 
2004
                                if(!ereg('^HTTP/[0-9.]+ 200 ', $data))
 
2005
                                {
 
2006
                                        $errstr= substr($data, 0, strpos($data, "\n")-1);
 
2007
                                        error_log('XML-RPC: xmlrpcmsg::parseResponse: HTTP error, got response: ' .$errstr);
 
2008
                                        $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (' . $errstr . ')');
 
2009
                                        return $r;
 
2010
                                }
 
2011
 
 
2012
                                $GLOBALS['_xh']['headers'] = array();
 
2013
                                $GLOBALS['_xh']['cookies'] = array();
 
2014
 
 
2015
                                // be tolerant to usage of \n instead of \r\n to separate headers and data
 
2016
                                // (even though it is not valid http)
 
2017
                                $pos = strpos($data,"\r\n\r\n");
 
2018
                                if($pos || is_int($pos))
 
2019
                                {
 
2020
                                        $bd = $pos+4;
 
2021
                                }
 
2022
                                else
 
2023
                                {
 
2024
                                        $pos = strpos($data,"\n\n");
 
2025
                                        if($pos || is_int($pos))
 
2026
                                        {
 
2027
                                                $bd = $pos+2;
 
2028
                                        }
 
2029
                                        else
 
2030
                                        {
 
2031
                                                // No separation between response headers and body: fault?
 
2032
                                                $bd = 0;
 
2033
                                        }
 
2034
                                }
 
2035
                                // be tolerant to line endings, and extra empty lines
 
2036
                                $ar = split("\r?\n", trim(substr($data, 0, $pos)));
 
2037
                                while(list(,$line) = @each($ar))
 
2038
                                {
 
2039
                                        // take care of multi-line headers and cookies
 
2040
                                        $arr = explode(':',$line,2);
 
2041
                                        if(count($arr) > 1)
 
2042
                                        {
 
2043
                                                $header_name = strtolower(trim($arr[0]));
 
2044
                                                /// @todo some other headers (the ones that allow a CSV list of values)
 
2045
                                                /// do allow many values to be passed using multiple header lines.
 
2046
                                                /// We should add content to $GLOBALS['_xh']['headers'][$header_name]
 
2047
                                                /// instead of replacing it for those...
 
2048
                                                if ($header_name == 'set-cookie' || $header_name == 'set-cookie2')
 
2049
                                                {
 
2050
                                                        if ($header_name == 'set-cookie2')
 
2051
                                                        {
 
2052
                                                                // version 2 cookies:
 
2053
                                                                // there could be many cookies on one line, comma separated
 
2054
                                                                $cookies = explode(',', $arr[1]);
 
2055
                                                        }
 
2056
                                                        else
 
2057
                                                        {
 
2058
                                                                $cookies = array($arr[1]);
 
2059
                                                        }
 
2060
                                                        foreach ($cookies as $cookie)
 
2061
                                                        {
 
2062
                                                                // glue together all received cookies, using a comma to separate them
 
2063
                                                                // (same as php does with getallheaders())
 
2064
                                                                if (isset($GLOBALS['_xh']['headers'][$header_name]))
 
2065
                                                                        $GLOBALS['_xh']['headers'][$header_name] .= ', ' . trim($cookie);
 
2066
                                                                else
 
2067
                                                                        $GLOBALS['_xh']['headers'][$header_name] = trim($cookie);
 
2068
                                                                // parse cookie attributes, in case user wants to coorectly honour then
 
2069
                                                                // feature creep: only allow rfc-compliant cookie attributes?
 
2070
                                                                $cookie = explode(';', $cookie);
 
2071
                                                                foreach ($cookie as $pos => $val)
 
2072
                                                                {
 
2073
                                                                        $val = explode('=', $val, 2);
 
2074
                                                                        $tag = trim($val[0]);
 
2075
                                                                        $val = trim(@$val[1]);
 
2076
                                                                        /// @todo with version 1 cookies, we should strip leading and trailing " chars
 
2077
                                                                        if ($pos == 0)
 
2078
                                                                        {
 
2079
                                                                                $cookiename = $tag;
 
2080
                                                                                $GLOBALS['_xh']['cookies'][$tag] = array();
 
2081
                                                                                $GLOBALS['_xh']['cookies'][$cookiename]['value'] = urldecode($val);
 
2082
                                                                        }
 
2083
                                                                        else
 
2084
                                                                        {
 
2085
                                                                                $GLOBALS['_xh']['cookies'][$cookiename][$tag] = $val;
 
2086
                                                                        }
 
2087
                                                                }
 
2088
                                                        }
 
2089
                                                }
 
2090
                                                else
 
2091
                                                {
 
2092
                                                        $GLOBALS['_xh']['headers'][$header_name] = trim($arr[1]);
 
2093
                                                }
 
2094
                                        }
 
2095
                                        elseif(isset($header_name))
 
2096
                                        {
 
2097
                                                ///     @todo version1 cookies might span multiple lines, thus breaking the parsing above
 
2098
                                                $GLOBALS['_xh']['headers'][$header_name] .= ' ' . trim($line);
 
2099
                                        }
 
2100
                                }
 
2101
                                // rebuild full cookie set
 
2102
                                /*if (isset($GLOBALS['_xh']['headers']['set-cookie']))
 
2103
                                {
 
2104
                                        $cookies = array();
 
2105
                                        $received = explode(';', $GLOBALS['_xh']['headers']['set-cookie']);
 
2106
                                        foreach($received as $cookie)
 
2107
                                        {
 
2108
                                                list($name, $value) = explode('=', $cookie);
 
2109
                                                $name = trim($name);
 
2110
                                                $value = trim($value);
 
2111
                                                // these values are in fact attributes
 
2112
                                                if ($name != 'Comment' && $name != 'Comment' && $name != 'Comment' && $name != 'Comment' && $name != 'Comment' && $name != 'Comment')
 
2113
                                                {
 
2114
                                                        $cookies[$name] = $value;
 
2115
                                                }
 
2116
                                        }
 
2117
                                }*/
 
2118
 
 
2119
                                $data = substr($data, $bd);
 
2120
 
 
2121
                                if($this->debug && count($GLOBALS['_xh']['headers']))
 
2122
                                {
 
2123
                                        print '<PRE>';
 
2124
                                        foreach($GLOBALS['_xh']['headers'] as $header => $value)
 
2125
                                        {
 
2126
                                                print "HEADER: $header: $value\n";
 
2127
                                        }
 
2128
                                        foreach($GLOBALS['_xh']['cookies'] as $header => $value)
 
2129
                                        {
 
2130
                                                print "COOKIE: $header={$value['value']}\n";
 
2131
                                        }
 
2132
                                        print "</PRE>\n";
 
2133
                                }
 
2134
 
 
2135
                                // if CURL was used for the call, http headers have been processed,
 
2136
                                // and dechunking + reinflating have been carried out
 
2137
                                if(!$headers_processed)
 
2138
                                {
 
2139
                                        // Decode chunked encoding sent by http 1.1 servers
 
2140
                                        if(isset($GLOBALS['_xh']['headers']['transfer-encoding']) && $GLOBALS['_xh']['headers']['transfer-encoding'] == 'chunked')
 
2141
                                        {
 
2142
                                                if(!$data = decode_chunked($data))
 
2143
                                                {
 
2144
                                                        error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to rebuild the chunked data received from server');
 
2145
                                                        $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['dechunk_fail'], $GLOBALS['xmlrpcstr']['dechunk_fail']);
 
2146
                                                        return $r;
 
2147
                                                }
 
2148
                                        }
 
2149
 
 
2150
                                        // Decode gzip-compressed stuff
 
2151
                                        // code shamelessly inspired from nusoap library by Dietrich Ayala
 
2152
                                        if(isset($GLOBALS['_xh']['headers']['content-encoding']))
 
2153
                                        {
 
2154
                                                if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' || $GLOBALS['_xh']['headers']['content-encoding'] == 'gzip')
 
2155
                                                {
 
2156
                                                        // if decoding works, use it. else assume data wasn't gzencoded
 
2157
                                                        if(function_exists('gzinflate'))
 
2158
                                                        {
 
2159
                                                                if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' && $degzdata = @gzinflate($data))
 
2160
                                                                {
 
2161
                                                                        $data = $degzdata;
 
2162
                                                                        if($this->debug)
 
2163
                                                                        print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
 
2164
                                                                }
 
2165
                                                                elseif($GLOBALS['_xh']['headers']['content-encoding'] == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
 
2166
                                                                {
 
2167
                                                                        $data = $degzdata;
 
2168
                                                                        if($this->debug)
 
2169
                                                                        print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
 
2170
                                                                }
 
2171
                                                                else
 
2172
                                                                {
 
2173
                                                                        error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to decode the deflated data received from server');
 
2174
                                                                        $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']);
 
2175
                                                                        return $r;
 
2176
                                                                }
 
2177
                                                        }
 
2178
                                                        else
 
2179
                                                        {
 
2180
                                                                error_log('XML-RPC: xmlrpcmsg::parseResponse: the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
 
2181
                                                                $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']);
 
2182
                                                                return $r;
 
2183
                                                        }
 
2184
                                                }
 
2185
                                        }
 
2186
                                } // end of 'if needed, de-chunk, re-inflate response'
 
2187
 
 
2188
                                // real stupid hack to avoid PHP 4 complaining about returning NULL by ref
 
2189
                                $r = null;
 
2190
                                $r =& $r;
 
2191
                                return $r;
 
2192
                }
 
2193
 
 
2194
                /*
 
2195
                * @param string $data the xmlrpc response, eventually including http headers
 
2196
                * @param bool   $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and conseuqent decoding
 
2197
                * @param string $return_type decides return type, i.e. content of response->value(). Either 'xmlrpcvals', 'xml' or 'phpvals'
 
2198
                * @access private
 
2199
                */
 
2200
                function &parseResponse($data='', $headers_processed=false, $return_type='xmlrpcvals')
 
2201
                {
 
2202
                        //$hdrfnd = 0;
 
2203
                        if($this->debug)
 
2204
                        {
 
2205
                                //by maHo, replaced htmlspecialchars with htmlentities
 
2206
                                print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
 
2207
                                $start = strpos($data, '<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
 
2208
                                if ($start)
 
2209
                                {
 
2210
                                        $start += strlen('<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
 
2211
                                        $end = strpos($data, '-->', $start);
 
2212
                                        $comments = substr($data, $start, $end-$start);
 
2213
                                        print "<PRE>---SERVER DEBUG INFO (DECODED) ---\n\t".htmlentities(str_replace("\n", "\n\t", base64_decode($comments)))."\n---END---\n</PRE>";
 
2214
                                }
 
2215
                        }
 
2216
 
 
2217
                        if($data == '')
 
2218
                        {
 
2219
                                error_log('XML-RPC: xmlrpcmsg::parseResponse: no response received from server.');
 
2220
                                $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']);
 
2221
                                return $r;
 
2222
                        }
 
2223
 
 
2224
                        $GLOBALS['_xh']=array();
 
2225
 
 
2226
                        // parse the HTTP headers of the response, if present, and separate them from data
 
2227
                        if(ereg("^HTTP",$data))
 
2228
                        {
 
2229
                                $r =& $this->parseResponseHeaders($data, $headers_processed);
 
2230
                                if ($r)
 
2231
                                {
 
2232
                                        return $r;
 
2233
                                }
 
2234
                        }
 
2235
                        else
 
2236
                        {
 
2237
                                $GLOBALS['_xh']['headers'] = array();
 
2238
                                $GLOBALS['_xh']['cookies'] = array();
 
2239
                        }
 
2240
 
 
2241
 
 
2242
                        // be tolerant of extra whitespace in response body
 
2243
                        $data = trim($data);
 
2244
 
 
2245
                        /// @todo return an error msg if $data=='' ?
 
2246
 
 
2247
                        // be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts)
 
2248
                        // idea from Luca Mariano <luca.mariano@email.it> originally in PEARified version of the lib
 
2249
                        $bd = false;
 
2250
                        // Poor man's version of strrpos for php 4...
 
2251
                        $pos = strpos($data, '</methodResponse>');
 
2252
                        while($pos || is_int($pos))
 
2253
                        {
 
2254
                                $bd = $pos+17;
 
2255
                                $pos = strpos($data, '</methodResponse>', $bd);
 
2256
                        }
 
2257
                        if($bd)
 
2258
                        {
 
2259
                                $data = substr($data, 0, $bd);
 
2260
                        }
 
2261
 
 
2262
                        // if user wants back raw xml, give it to him
 
2263
                        if ($return_type == 'xml')
 
2264
                        {
 
2265
                                $r =& new xmlrpcresp($data, 0, '', 'xml');
 
2266
                                $r->hdrs = $GLOBALS['_xh']['headers'];
 
2267
                                $r->_cookies = $GLOBALS['_xh']['cookies'];
 
2268
                                return $r;
 
2269
                        }
 
2270
 
 
2271
                        // try to 'guestimate' the character encoding of the received response
 
2272
                        $resp_encoding = guess_encoding(@$GLOBALS['_xh']['headers']['content-type'], $data);
 
2273
 
 
2274
                        $GLOBALS['_xh']['stack'] = array();
 
2275
                        $GLOBALS['_xh']['valuestack'] = array();
 
2276
                        $GLOBALS['_xh']['isf']=0;
 
2277
                        $GLOBALS['_xh']['isf_reason']='';
 
2278
                        $GLOBALS['_xh']['ac']='';
 
2279
                        $GLOBALS['_xh']['qt']='';
 
2280
 
 
2281
                        // if response charset encoding is not known / supported, try to use
 
2282
                        // the default encoding and parse the xml anyway, but log a warning...
 
2283
                        if (!in_array($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
 
2284
                        // the following code might be better for mb_string enabled installs, but
 
2285
                        // makes the lib about 200% slower...
 
2286
                        //if (!is_valid_charset($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
 
2287
                        {
 
2288
                                error_log('XML-RPC: xmlrpcmsg::parseResponse: invalid charset encoding of received response: '.$resp_encoding);
 
2289
                                $resp_encoding = $GLOBALS['xmlrpc_defencoding'];
 
2290
                        }
 
2291
                        $parser = xml_parser_create($resp_encoding);
 
2292
                        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
 
2293
                        // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
 
2294
                        // the xml parser to give us back data in the expected charset
 
2295
                        xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
 
2296
 
 
2297
                        if ($return_type == 'phpvals')
 
2298
                        {
 
2299
                                xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
 
2300
                        }
 
2301
                        else
 
2302
                        {
 
2303
                                xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
 
2304
                        }
 
2305
 
 
2306
                        xml_set_character_data_handler($parser, 'xmlrpc_cd');
 
2307
                        xml_set_default_handler($parser, 'xmlrpc_dh');
 
2308
 
 
2309
                        if(!xml_parse($parser, $data, sizeof($data)))
 
2310
                        {
 
2311
                                // thanks to Peter Kocks <peter.kocks@baygate.com>
 
2312
                                if((xml_get_current_line_number($parser)) == 1)
 
2313
                                {
 
2314
                                        $errstr = 'XML error at line 1, check URL';
 
2315
                                }
 
2316
                                else
 
2317
                                {
 
2318
                                        $errstr = sprintf('XML error: %s at line %d',
 
2319
                                                xml_error_string(xml_get_error_code($parser)),
 
2320
                                                xml_get_current_line_number($parser));
 
2321
                                }
 
2322
                                error_log($errstr);
 
2323
                                $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return'].' ('.$errstr.')');
 
2324
                                xml_parser_free($parser);
 
2325
                                if($this->debug)
 
2326
                                {
 
2327
                                        print $errstr;
 
2328
                                }
 
2329
                                $r->hdrs = $GLOBALS['_xh']['headers'];
 
2330
                                $r->_cookies = $GLOBALS['_xh']['cookies'];
 
2331
                                return $r;
 
2332
                        }
 
2333
                        xml_parser_free($parser);
 
2334
                        if ($GLOBALS['_xh']['isf'] > 1)
 
2335
                        {
 
2336
                                if ($this->debug)
 
2337
                                {
 
2338
                                        /// @todo echo something for user?
 
2339
                                }
 
2340
 
 
2341
                                $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
 
2342
                                $GLOBALS['xmlrpcstr']['invalid_return'] . ' ' . $GLOBALS['_xh']['isf_reason']);
 
2343
                        }
 
2344
                        elseif ($return_type == 'xmlrpcvals' && !is_object($GLOBALS['_xh']['value']))
 
2345
                        {
 
2346
                                // then something odd has happened
 
2347
                                // and it's time to generate a client side error
 
2348
                                // indicating something odd went on
 
2349
                                $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
 
2350
                                $GLOBALS['xmlrpcstr']['invalid_return']);
 
2351
                        }
 
2352
                        else
 
2353
                        {
 
2354
                                if ($this->debug)
 
2355
                                {
 
2356
                                        print "<PRE>---PARSED---\n" ;
 
2357
                                        var_export($GLOBALS['_xh']['value']);
 
2358
                                        print "\n---END---</PRE>";
 
2359
                                }                               // note that using =& will raise an error if $GLOBALS['_xh']['st'] does not generate an object.
 
2360
 
 
2361
                                $v =& $GLOBALS['_xh']['value'];
 
2362
 
 
2363
                                if($GLOBALS['_xh']['isf'])
 
2364
                                {
 
2365
                                        if ($return_type == 'xmlrpcvals')
 
2366
                                        {
 
2367
                                                $errno_v = $v->structmem('faultCode');
 
2368
                                                $errstr_v = $v->structmem('faultString');
 
2369
                                                $errno = $errno_v->scalarval();
 
2370
                                                $errstr = $errstr_v->scalarval();
 
2371
                                        }
 
2372
                                        else
 
2373
                                        {
 
2374
                                                $errno = $v['faultCode'];
 
2375
                                                $errstr = $v['faultString'];
 
2376
                                        }
 
2377
 
 
2378
                                        if($errno == 0)
 
2379
                                        {
 
2380
                                                // FAULT returned, errno needs to reflect that
 
2381
                                                $errno = -1;
 
2382
                                        }
 
2383
 
 
2384
                                        $r =& new xmlrpcresp($v, $errno, $errstr);
 
2385
                                }
 
2386
                                else
 
2387
                                {
 
2388
                                        $r=&new xmlrpcresp($v, 0, '', 'phpvals');
 
2389
                                }
 
2390
                        }
 
2391
 
 
2392
                        $r->hdrs = $GLOBALS['_xh']['headers'];
 
2393
                        $r->_cookies = $GLOBALS['_xh']['cookies'];
 
2394
                        return $r;
 
2395
                }
 
2396
        }
 
2397
 
 
2398
        class xmlrpcval
 
2399
        {
 
2400
                var $me=array();
 
2401
                var $mytype=0;
 
2402
                var $_php_class=null;
 
2403
 
 
2404
                function xmlrpcval($val=-1, $type='')
 
2405
                {
 
2406
                        //$this->me=array();
 
2407
                        //$this->mytype=0;
 
2408
                        if($val!==-1 || $type!='')
 
2409
                        {
 
2410
                                if($type=='')
 
2411
                                {
 
2412
                                        $type='string';
 
2413
                                }
 
2414
                                if($GLOBALS['xmlrpcTypes'][$type]==1)
 
2415
                                {
 
2416
                                        $this->addScalar($val,$type);
 
2417
                                }
 
2418
                                elseif($GLOBALS['xmlrpcTypes'][$type]==2)
 
2419
                                {
 
2420
                                        $this->addArray($val);
 
2421
                                }
 
2422
                                elseif($GLOBALS['xmlrpcTypes'][$type]==3)
 
2423
                                {
 
2424
                                        $this->addStruct($val);
 
2425
                                }
 
2426
                        }
 
2427
                }
 
2428
 
 
2429
                function addScalar($val, $type='string')
 
2430
                {
 
2431
                        $typeof=@$GLOBALS['xmlrpcTypes'][$type];
 
2432
                        if($typeof!=1)
 
2433
                        {
 
2434
                                error_log("XML-RPC: xmlrpcval::addScalar: not a scalar type ($typeof)");
 
2435
                                return 0;
 
2436
                        }
 
2437
 
 
2438
                        // coerce booleans into correct values
 
2439
                        // NB: shall we do it for datetimes, integers and doubles, too?
 
2440
                        if($type==$GLOBALS['xmlrpcBoolean'])
 
2441
                        {
 
2442
                                if(strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
 
2443
                                {
 
2444
                                        $val=true;
 
2445
                                }
 
2446
                                else
 
2447
                                {
 
2448
                                        $val=false;
 
2449
                                }
 
2450
                        }
 
2451
 
 
2452
                        switch($this->mytype)
 
2453
                        {
 
2454
                                case 1:
 
2455
                                        error_log('XML-RPC: xmlrpcval::addScalar: scalar xmlrpcval can have only one value');
 
2456
                                        return 0;
 
2457
                                case 3:
 
2458
                                        error_log('XML-RPC: xmlrpcval::addScalar: cannot add anonymous scalar to struct xmlrpcval');
 
2459
                                        return 0;
 
2460
                                case 2:
 
2461
                                        // we're adding a scalar value to an array here
 
2462
                                        //$ar=$this->me['array'];
 
2463
                                        //$ar[]=&new xmlrpcval($val, $type);
 
2464
                                        //$this->me['array']=$ar;
 
2465
                                        // Faster (?) avoid all the costly array-copy-by-val done here...
 
2466
                                        $this->me['array'][]=&new xmlrpcval($val, $type);
 
2467
                                        return 1;
 
2468
                                default:
 
2469
                                        // a scalar, so set the value and remember we're scalar
 
2470
                                        $this->me[$type]=$val;
 
2471
                                        $this->mytype=$typeof;
 
2472
                                        return 1;
 
2473
                        }
 
2474
                }
 
2475
 
 
2476
                /// @todo add some checking for $vals to be an array of xmlrpcvals?
 
2477
                function addArray($vals)
 
2478
                {
 
2479
                        if($this->mytype==0)
 
2480
                        {
 
2481
                                $this->mytype=$GLOBALS['xmlrpcTypes']['array'];
 
2482
                                $this->me['array']=$vals;
 
2483
                                return 1;
 
2484
                        }
 
2485
                        elseif($this->mytype==2)
 
2486
                        {
 
2487
                                // we're adding to an array here
 
2488
                                $this->me['array'] = array_merge($this->me['array'], $vals);
 
2489
                        }
 
2490
                        else
 
2491
                        {
 
2492
                                error_log('XML-RPC: xmlrpcval::addArray: already initialized as a [' . $this->kindOf() . ']');
 
2493
                                return 0;
 
2494
                        }
 
2495
                }
 
2496
 
 
2497
                /// @todo add some checking for $vals to be an array?
 
2498
                function addStruct($vals)
 
2499
                {
 
2500
                        if($this->mytype==0)
 
2501
                        {
 
2502
                                $this->mytype=$GLOBALS['xmlrpcTypes']['struct'];
 
2503
                                $this->me['struct']=$vals;
 
2504
                                return 1;
 
2505
                        }
 
2506
                        elseif($this->mytype==3)
 
2507
                        {
 
2508
                                // we're adding to a struct here
 
2509
                                $this->me['struct'] = array_merge($this->me['struct'], $vals);
 
2510
                        }
 
2511
                        else
 
2512
                        {
 
2513
                                error_log('XML-RPC: xmlrpcval::addStruct: already initialized as a [' . $this->kindOf() . ']');
 
2514
                                return 0;
 
2515
                        }
 
2516
                }
 
2517
 
 
2518
                // poor man's version of print_r ???
 
2519
                // DEPRECATED!
 
2520
                function dump($ar)
 
2521
                {
 
2522
                        foreach($ar as $key => $val)
 
2523
                        {
 
2524
                                echo "$key => $val<br />";
 
2525
                                if($key == 'array')
 
2526
                                {
 
2527
                                        while(list($key2, $val2) = each($val))
 
2528
                                        {
 
2529
                                                echo "-- $key2 => $val2<br />";
 
2530
                                        }
 
2531
                                }
 
2532
                        }
 
2533
                }
 
2534
 
 
2535
                function kindOf()
 
2536
                {
 
2537
                        switch($this->mytype)
 
2538
                        {
 
2539
                                case 3:
 
2540
                                        return 'struct';
 
2541
                                        break;
 
2542
                                case 2:
 
2543
                                        return 'array';
 
2544
                                        break;
 
2545
                                case 1:
 
2546
                                        return 'scalar';
 
2547
                                        break;
 
2548
                                default:
 
2549
                                        return 'undef';
 
2550
                        }
 
2551
                }
 
2552
 
 
2553
                function serializedata($typ, $val, $charset_encoding='')
 
2554
                {
 
2555
                        $rs='';
 
2556
                        switch(@$GLOBALS['xmlrpcTypes'][$typ])
 
2557
                        {
 
2558
                                case 3:
 
2559
                                        // struct
 
2560
                                        if ($this->_php_class)
 
2561
                                        {
 
2562
                                                $rs.='<struct php_class="' . $this->_php_class . "\">\n";
 
2563
                                        }
 
2564
                                        else
 
2565
                                        {
 
2566
                                                $rs.="<struct>\n";
 
2567
                                        }
 
2568
                                        foreach($val as $key2 => $val2)
 
2569
                                        {
 
2570
                                                $rs.="<member><name>${key2}</name>\n";
 
2571
                                                //$rs.=$this->serializeval($val2);
 
2572
                                                $rs.=$val2->serialize();
 
2573
                                                $rs.="</member>\n";
 
2574
                                        }
 
2575
                                        $rs.='</struct>';
 
2576
                                        break;
 
2577
                                case 2:
 
2578
                                        // array
 
2579
                                        $rs.="<array>\n<data>\n";
 
2580
                                        for($i=0; $i<sizeof($val); $i++)
 
2581
                                        {
 
2582
                                                //$rs.=$this->serializeval($val[$i]);
 
2583
                                                $rs.=$val[$i]->serialize();
 
2584
                                        }
 
2585
                                        $rs.="</data>\n</array>";
 
2586
                                        break;
 
2587
                                case 1:
 
2588
                                        switch($typ)
 
2589
                                        {
 
2590
                                                case $GLOBALS['xmlrpcBase64']:
 
2591
                                                        $rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
 
2592
                                                        break;
 
2593
                                                case $GLOBALS['xmlrpcBoolean']:
 
2594
                                                        $rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
 
2595
                                                        break;
 
2596
                                                case $GLOBALS['xmlrpcString']:
 
2597
                                                        // G. Giunta 2005/2/13: do NOT use htmlentities, since
 
2598
                                                        // it will produce named html entities, which are invalid xml
 
2599
                                                        $rs.="<${typ}>" . xmlrpc_encode_entitites($val, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding). "</${typ}>";
 
2600
                                                        // $rs.="<${typ}>" . htmlentities($val). "</${typ}>";
 
2601
                                                        break;
 
2602
                                                case $GLOBALS['xmlrpcInt']:
 
2603
                                                case $GLOBALS['xmlrpcI4']:
 
2604
                                                        $rs.="<${typ}>".(int)$val."</${typ}>";
 
2605
                                                        break;
 
2606
                                                case $GLOBALS['xmlrpcDouble']:
 
2607
                                                        $rs.="<${typ}>".(double)$val."</${typ}>";
 
2608
                                                        break;
 
2609
                                                default:
 
2610
                                                        // no standard type value should arrive here, but provide a possibility
 
2611
                                                        // for xmlrpcvals of unknown type...
 
2612
                                                        $rs.="<${typ}>${val}</${typ}>";
 
2613
                                        }
 
2614
                                        break;
 
2615
                                default:
 
2616
                                        break;
 
2617
                        }
 
2618
                        return $rs;
 
2619
                }
 
2620
 
 
2621
                /**
 
2622
                * Return xml representation of the value
 
2623
                * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
 
2624
                */
 
2625
                function serialize($charset_encoding='')
 
2626
                {
 
2627
                        // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
 
2628
                        //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
 
2629
                        //{
 
2630
                                reset($this->me);
 
2631
                                list($typ, $val) = each($this->me);
 
2632
                                return '<value>' . $this->serializedata($typ, $val, $charset_encoding) . "</value>\n";
 
2633
                        //}
 
2634
                }
 
2635
 
 
2636
                // DEPRECATED
 
2637
                function serializeval($o)
 
2638
                {
 
2639
                        // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
 
2640
                        //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
 
2641
                        //{
 
2642
                                $ar=$o->me;
 
2643
                                reset($ar);
 
2644
                                list($typ, $val) = each($ar);
 
2645
                                return '<value>' . $this->serializedata($typ, $val) . "</value>\n";
 
2646
                        //}
 
2647
                }
 
2648
 
 
2649
                /**
 
2650
                * Checks wheter a struct member with a given name is present.
 
2651
                * Works only on xmlrpcvals of type struct.
 
2652
                * @param string $m the name of the struct member to be looked up
 
2653
                * @return boolean
 
2654
                */
 
2655
                function structmemexists($m)
 
2656
                {
 
2657
                        return array_key_exists($this->me['struct'][$m]);
 
2658
                }
 
2659
 
 
2660
                /*
 
2661
                * Returns the value of a given struct member (an xmlrpcval object in itself).
 
2662
                * Will raise a php warning if struct member of given name does not exist
 
2663
                * @param string $m the name of the struct member to be looked up
 
2664
                * @return xmlrpcval
 
2665
                */
 
2666
                function structmem($m)
 
2667
                {
 
2668
                        return $this->me['struct'][$m];
 
2669
                }
 
2670
 
 
2671
                function structreset()
 
2672
                {
 
2673
                        reset($this->me['struct']);
 
2674
                }
 
2675
 
 
2676
                function structeach()
 
2677
                {
 
2678
                        return each($this->me['struct']);
 
2679
                }
 
2680
 
 
2681
                // DEPRECATED! this code looks like it is very fragile and has not been fixed
 
2682
                // for a long long time. Shall we remove it for 2.0?
 
2683
                function getval()
 
2684
                {
 
2685
                        // UNSTABLE
 
2686
                        reset($this->me);
 
2687
                        list($a,$b)=each($this->me);
 
2688
                        // contributed by I Sofer, 2001-03-24
 
2689
                        // add support for nested arrays to scalarval
 
2690
                        // i've created a new method here, so as to
 
2691
                        // preserve back compatibility
 
2692
 
 
2693
                        if(is_array($b))
 
2694
                        {
 
2695
                                @reset($b);
 
2696
                                while(list($id,$cont) = @each($b))
 
2697
                                {
 
2698
                                        $b[$id] = $cont->scalarval();
 
2699
                                }
 
2700
                        }
 
2701
 
 
2702
                        // add support for structures directly encoding php objects
 
2703
                        if(is_object($b))
 
2704
                        {
 
2705
                                $t = get_object_vars($b);
 
2706
                                @reset($t);
 
2707
                                while(list($id,$cont) = @each($t))
 
2708
                                {
 
2709
                                        $t[$id] = $cont->scalarval();
 
2710
                                }
 
2711
                                @reset($t);
 
2712
                                while(list($id,$cont) = @each($t))
 
2713
                                {
 
2714
                                        //@eval('$b->'.$id.' = $cont;');
 
2715
                                        @$b->$id = $cont;
 
2716
                                }
 
2717
                        }
 
2718
                        // end contrib
 
2719
                        return $b;
 
2720
                }
 
2721
 
 
2722
                /**
 
2723
                * Returns the value of a scalar xmlrpcval
 
2724
                * @return mixed
 
2725
                */
 
2726
                function scalarval()
 
2727
                {
 
2728
                        reset($this->me);
 
2729
                        list(,$b)=each($this->me);
 
2730
                        return $b;
 
2731
                }
 
2732
 
 
2733
                /**
 
2734
                * Returns the type of the xmlrpcval.
 
2735
                * For integers, 'int' is always returned in place of 'i4'
 
2736
                * @return string
 
2737
                */
 
2738
                function scalartyp()
 
2739
                {
 
2740
                        reset($this->me);
 
2741
                        list($a,$b)=each($this->me);
 
2742
                        if($a==$GLOBALS['xmlrpcI4'])
 
2743
                        {
 
2744
                                $a=$GLOBALS['xmlrpcInt'];
 
2745
                        }
 
2746
                        return $a;
 
2747
                }
 
2748
 
 
2749
                /**
 
2750
                * Returns the m-th member of an xmlrpcval of struct type
 
2751
                * @param integer $m the index of the value to be retrieved (zero based)
 
2752
                * @return xmlrpcval
 
2753
                */
 
2754
                function arraymem($m)
 
2755
                {
 
2756
                        return $this->me['array'][$m];
 
2757
                }
 
2758
 
 
2759
                /**
 
2760
                * Returns the number of members in an xmlrpcval of array type
 
2761
                * @return integer
 
2762
                */
 
2763
                function arraysize()
 
2764
                {
 
2765
                        return count($this->me['array']);
 
2766
                }
 
2767
 
 
2768
                /**
 
2769
                * Returns the number of members in an xmlrpcval of struct type
 
2770
                * @return integer
 
2771
                */
 
2772
                function structsize()
 
2773
                {
 
2774
                        return count($this->me['struct']);
 
2775
                }
 
2776
        }
 
2777
 
 
2778
 
 
2779
        // date helpers
 
2780
        function iso8601_encode($timet, $utc=0)
 
2781
        {
 
2782
                // return an ISO8601 encoded string
 
2783
                // really, timezones ought to be supported
 
2784
                // but the XML-RPC spec says:
 
2785
                //
 
2786
                // "Don't assume a timezone. It should be specified by the server in its
 
2787
                // documentation what assumptions it makes about timezones."
 
2788
                //
 
2789
                // these routines always assume localtime unless
 
2790
                // $utc is set to 1, in which case UTC is assumed
 
2791
                // and an adjustment for locale is made when encoding
 
2792
                if(!$utc)
 
2793
                {
 
2794
                        $t=strftime("%Y%m%dT%H:%M:%S", $timet);
 
2795
                }
 
2796
                else
 
2797
                {
 
2798
                        if(function_exists('gmstrftime'))
 
2799
                        {
 
2800
                                // gmstrftime doesn't exist in some versions
 
2801
                                // of PHP
 
2802
                                $t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
 
2803
                        }
 
2804
                        else
 
2805
                        {
 
2806
                                $t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z'));
 
2807
                        }
 
2808
                }
 
2809
                return $t;
 
2810
        }
 
2811
 
 
2812
        function iso8601_decode($idate, $utc=0)
 
2813
        {
 
2814
                // return a timet in the localtime, or UTC
 
2815
                $t=0;
 
2816
                if(ereg("([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})", $idate, $regs))
 
2817
                {
 
2818
                        if($utc)
 
2819
                        {
 
2820
                                $t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
 
2821
                        }
 
2822
                        else
 
2823
                        {
 
2824
                                $t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
 
2825
                        }
 
2826
                }
 
2827
                return $t;
 
2828
        }
 
2829
 
 
2830
        /**
 
2831
        * Takes an xmlrpc value in PHP xmlrpcval object format
 
2832
        * and translates it into native PHP types.
 
2833
        * Works with xmlrpc message objects as input, too.
 
2834
        *
 
2835
        * @author Dan Libby (dan@libby.com)
 
2836
        *
 
2837
        * @param  xmlrpcval $xmlrpc_val
 
2838
        * @param  array     $options    if 'decode_php_objs' is set in the options array, xmlrpc structs can be decoded into php objects
 
2839
        * @return mixed
 
2840
        */
 
2841
        function php_xmlrpc_decode($xmlrpc_val, $options=array())
 
2842
        {
 
2843
                switch($xmlrpc_val->kindOf())
 
2844
                {
 
2845
                        case 'scalar':
 
2846
                                return $xmlrpc_val->scalarval();
 
2847
                        case 'array':
 
2848
                                $size = $xmlrpc_val->arraysize();
 
2849
                                $arr = array();
 
2850
                                for($i = 0; $i < $size; $i++)
 
2851
                                {
 
2852
                                        $arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i), $options);
 
2853
                                }
 
2854
                                return $arr;
 
2855
                        case 'struct':
 
2856
                                $xmlrpc_val->structreset();
 
2857
                                // If user said so, try to rebuild php objects for specific struct vals.
 
2858
                                /// @todo should we raise a warning for class not found?
 
2859
                                // shall we check for proper subclass of xmlrpcval instead of
 
2860
                                // presence of _php_class to detect what we can do?
 
2861
                                if (in_array('decode_php_objs', $options) && $xmlrpc_val->_php_class != ''
 
2862
                                        && class_exists($xmlrpc_val->_php_class))
 
2863
                                {
 
2864
                                        $obj = @new $xmlrpc_val->_php_class;
 
2865
                                        while(list($key,$value)=$xmlrpc_val->structeach())
 
2866
                                        {
 
2867
                                                $obj->$key = php_xmlrpc_decode($value, $options);
 
2868
                                        }
 
2869
                                        return $obj;
 
2870
                                }
 
2871
                                else
 
2872
                                {
 
2873
                                        $arr = array();
 
2874
                                        while(list($key,$value)=$xmlrpc_val->structeach())
 
2875
                                        {
 
2876
                                                $arr[$key] = php_xmlrpc_decode($value, $options);
 
2877
                                        }
 
2878
                                        return $arr;
 
2879
                                }
 
2880
                        case 'msg':
 
2881
                                $paramcount = $xmlrpc_val->getNumParams();
 
2882
                                $arr = array();
 
2883
                                for($i = 0; $i < $paramcount; $i++)
 
2884
                                {
 
2885
                                        $arr[] = php_xmlrpc_decode($xmlrpc_val->getParam($i));
 
2886
                                }
 
2887
                                return $arr;
 
2888
                        }
 
2889
        }
 
2890
 
 
2891
        if(function_exists('xmlrpc_decode'))
 
2892
        {
 
2893
                define('XMLRPC_EPI_ENABLED','1');
 
2894
        }
 
2895
        else
 
2896
        {
 
2897
                define('XMLRPC_EPI_ENABLED','0');
 
2898
        }
 
2899
 
 
2900
        /**
 
2901
        * Takes native php types and encodes them into xmlrpc PHP object format.
 
2902
        * It will not re-encode xmlrpcval objects.
 
2903
        * Feature creep -- could support more types via optional type argument
 
2904
        * (string => datetime support has been added, ??? => base64 not yet)
 
2905
        *
 
2906
        * @author Dan Libby (dan@libby.com)
 
2907
        *
 
2908
        * @param mixed $php_val the value to be converted into an xmlrpcval object
 
2909
        * @param array $options can include 'encode_php_objs' and 'auto_dates'
 
2910
        * @return xmlrpcval
 
2911
        */
 
2912
        function &php_xmlrpc_encode($php_val, $options=array())
 
2913
        {
 
2914
                $type = gettype($php_val);
 
2915
                $xmlrpc_val =& new xmlrpcval;
 
2916
 
 
2917
                switch($type)
 
2918
                {
 
2919
                        case 'array':
 
2920
                                // PHP arrays can be encoded to either xmlrpc structs or arrays,
 
2921
                                // depending on wheter they are hashes or plain 0..n integer indexed
 
2922
                                // A shorter one-liner would be
 
2923
                                // $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
 
2924
                                // but execution time skyrockets!
 
2925
                                $j = 0;
 
2926
                                $arr = array();
 
2927
                                $ko = false;
 
2928
                                foreach($php_val as $key => $val)
 
2929
                                {
 
2930
                                        $arr[$key] =& php_xmlrpc_encode($val, $options);
 
2931
                                        if(!$ko && $key !== $j)
 
2932
                                        {
 
2933
                                                $ko = true;
 
2934
                                        }
 
2935
                                        $j++;
 
2936
                                }
 
2937
                                if($ko)
 
2938
                                {
 
2939
                                        $xmlrpc_val->addStruct($arr);
 
2940
                                }
 
2941
                                else
 
2942
                                {
 
2943
                                        $xmlrpc_val->addArray($arr);
 
2944
                                }
 
2945
                                break;
 
2946
                        case 'object':
 
2947
                                if(is_a($php_val, 'xmlrpcval'))
 
2948
                                {
 
2949
                                        $xmlrpc_val = $php_val;
 
2950
                                }
 
2951
                                else
 
2952
                                {
 
2953
                                        $arr = array();
 
2954
                                        while(list($k,$v) = each($php_val))
 
2955
                                        {
 
2956
                                                $arr[$k] = php_xmlrpc_encode($v, $options);
 
2957
                                        }
 
2958
                                        $xmlrpc_val->addStruct($arr);
 
2959
                                        if (in_array('encode_php_objs', $options))
 
2960
                                        {
 
2961
                                                // let's save original class name into xmlrpcval:
 
2962
                                                // might be useful later on...
 
2963
                                                $xmlrpc_val->_php_class = get_class($php_val);
 
2964
                                        }
 
2965
                                }
 
2966
                                break;
 
2967
                        case 'integer':
 
2968
                                $xmlrpc_val->addScalar($php_val, $GLOBALS['xmlrpcInt']);
 
2969
                                break;
 
2970
                        case 'double':
 
2971
                                $xmlrpc_val->addScalar($php_val, $GLOBALS['xmlrpcDouble']);
 
2972
                                break;
 
2973
                        case 'string':
 
2974
                                if (in_array('auto_dates', $options) && ereg("^[0-9]{8}\T{1}[0-9]{2}\:[0-9]{2}\:[0-9]{2}$", $php_val))
 
2975
                                        $xmlrpc_val->addScalar($php_val, $GLOBALS['xmlrpcDateTime']);
 
2976
                                else
 
2977
                                        $xmlrpc_val->addScalar($php_val, $GLOBALS['xmlrpcString']);
 
2978
                                break;
 
2979
                                // <G_Giunta_2001-02-29>
 
2980
                                // Add support for encoding/decoding of booleans, since they are supported in PHP
 
2981
                        case 'boolean':
 
2982
                                $xmlrpc_val->addScalar($php_val, $GLOBALS['xmlrpcBoolean']);
 
2983
                                break;
 
2984
                                // </G_Giunta_2001-02-29>
 
2985
                        // catch "resource", "NULL", "user function", "unknown type"
 
2986
                        //case 'unknown type':
 
2987
                        default:
 
2988
                                // giancarlo pinerolo <ping@alt.it>
 
2989
                                // it has to return
 
2990
                                // an empty object in case (which is already
 
2991
                                // at this point), not a boolean.
 
2992
                                break;
 
2993
                        }
 
2994
                        return $xmlrpc_val;
 
2995
        }
 
2996
 
 
2997
        /**
 
2998
        * decode a string that is encoded w/ "chunked" transfer encoding
 
2999
        * as defined in rfc2068 par. 19.4.6
 
3000
        * code shamelessly stolen from nusoap library by Dietrich Ayala
 
3001
        *
 
3002
        * @param   string $buffer the string to be decoded
 
3003
        * @return  string
 
3004
        */
 
3005
        function decode_chunked($buffer)
 
3006
        {
 
3007
                // length := 0
 
3008
                $length = 0;
 
3009
                $new = '';
 
3010
 
 
3011
                // read chunk-size, chunk-extension (if any) and crlf
 
3012
                // get the position of the linebreak
 
3013
                $chunkend = strpos($buffer,"\r\n") + 2;
 
3014
                $temp = substr($buffer,0,$chunkend);
 
3015
                $chunk_size = hexdec( trim($temp) );
 
3016
                $chunkstart = $chunkend;
 
3017
                // while(chunk-size > 0) {
 
3018
                while($chunk_size > 0)
 
3019
                {
 
3020
                        $chunkend = strpos($buffer, "\r\n", $chunkstart + $chunk_size);
 
3021
 
 
3022
                        // just in case we got a broken connection
 
3023
                        if($chunkend == false)
 
3024
                        {
 
3025
                                $chunk = substr($buffer,$chunkstart);
 
3026
                                // append chunk-data to entity-body
 
3027
                                $new .= $chunk;
 
3028
                                $length += strlen($chunk);
 
3029
                                break;
 
3030
                        }
 
3031
 
 
3032
                        // read chunk-data and crlf
 
3033
                        $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
 
3034
                        // append chunk-data to entity-body
 
3035
                        $new .= $chunk;
 
3036
                        // length := length + chunk-size
 
3037
                        $length += strlen($chunk);
 
3038
                        // read chunk-size and crlf
 
3039
                        $chunkstart = $chunkend + 2;
 
3040
 
 
3041
                        $chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
 
3042
                        if($chunkend == false)
 
3043
                        {
 
3044
                                break; //just in case we got a broken connection
 
3045
                        }
 
3046
                        $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
 
3047
                        $chunk_size = hexdec( trim($temp) );
 
3048
                        $chunkstart = $chunkend;
 
3049
                }
 
3050
                return $new;
 
3051
        }
 
3052
 
 
3053
        /**
 
3054
        * Given a string defining a php type or phpxmlrpc type (loosely defined: strings
 
3055
        * accepted come from javadoc blocks), return corresponding phpxmlrpc type.
 
3056
        * NB: for php 'resource' types returns empty string, since resources cannot be serialized;
 
3057
        * for php class names returns 'struct', since php objects can be serialized as xmlrpc structs
 
3058
        * @param string $phptype
 
3059
        * @return string
 
3060
        */
 
3061
        function php_2_xmlrpc_type($phptype)
 
3062
        {
 
3063
                switch(strtolower($phptype))
 
3064
                {
 
3065
                        case 'string':
 
3066
                                return $GLOBALS['xmlrpcString'];
 
3067
                        case 'integer':
 
3068
                        case $GLOBALS['xmlrpcInt']: // 'int'
 
3069
                        case $GLOBALS['xmlrpcI4']:
 
3070
                                return $GLOBALS['xmlrpcInt'];
 
3071
                        case 'double':
 
3072
                                return $GLOBALS['xmlrpcDouble'];
 
3073
                        case 'boolean':
 
3074
                                return $GLOBALS['xmlrpcBoolean'];
 
3075
                        case 'array':
 
3076
                                return $GLOBALS['xmlrpcArray'];
 
3077
                        case 'object':
 
3078
                                return $GLOBALS['xmlrpcStruct'];
 
3079
                        case $GLOBALS['xmlrpcBase64']:
 
3080
                        case $GLOBALS['xmlrpcStruct']:
 
3081
                                return strtolower($phptype);
 
3082
                        case 'resource':
 
3083
                                return '';
 
3084
                        default:
 
3085
                                if(class_exists($phptype))
 
3086
                                {
 
3087
                                        return $GLOBALS['xmlrpcStruct'];
 
3088
                                }
 
3089
                                else
 
3090
                                {
 
3091
                                        // unknown: might be any xmlrpc type
 
3092
                                        return $GLOBALS['xmlrpcValue'];
 
3093
                                }
 
3094
                }
 
3095
        }
 
3096
 
 
3097
        /**
 
3098
        * Given a user-defined PHP function, create a PHP 'wrapper' function that can
 
3099
        * be exposed as xmlrpc method from an xmlrpc_server object and called from remote
 
3100
        * clients.
 
3101
        *
 
3102
        * Since php is a typeless language, to infer types of input and output parameters,
 
3103
        * it relies on parsing the javadoc-style comment block associated with the given
 
3104
        * function. Usage of xmlrpc native types (such as datetime.dateTime.iso8601 and base64)
 
3105
        * in the @param tag is also allowed, if you need the php function to receive/send
 
3106
        * data in that particular format (note that base64 enncoding/decoding is transparently
 
3107
        * carried out by the lib, while datetime vals are passed around as strings)
 
3108
        *
 
3109
        * Known limitations:
 
3110
        * - requires PHP 5.0.3 +
 
3111
        * - only works for user-defined functions, not for PHP internal functions
 
3112
        *   (reflection does not support retrieving number/type of params for those)
 
3113
        * - functions returning php objects will generate special xmlrpc responses:
 
3114
        *   when the xmlrpc decoding of those responses is carried out by this same lib, using
 
3115
        *   the appropriate param in php_xmlrpc_decode, the php objects will be rebuilt.
 
3116
        *   In short: php objects can be serialized, too (except for their resource members),
 
3117
        *   using this function.
 
3118
        *   Other libs might choke on the very same xml that will be generated in this case
 
3119
        *   (i.e. it has a nonstandard attribute on struct element tags)
 
3120
        * - usage of javadoc @param tags using param names in a different order from the
 
3121
        *   function prototype is not considered valid (to be fixed?)
 
3122
        *
 
3123
        * Note that since rel. 2.0RC3 the preferred method to have the server call 'standard'
 
3124
        * php functions (ie. functions not expecting a single xmlrpcmsg obj as parameter)
 
3125
        * is by making use of the functions_parameters_type class member.
 
3126
        *
 
3127
        * @param string $funcname the name of the PHP user function to be exposed as xmlrpc method; array($obj, 'methodname') might be ok too, in the future...
 
3128
        * @return false on error, or an array containing the name of the new php function,
 
3129
        *         its signature and docs, to be used in the server dispatch map
 
3130
        *
 
3131
        * @todo decide how to deal with params passed by ref: bomb out or allow?
 
3132
        * @todo finish using javadoc info to build method sig if all params are named but out of order
 
3133
        * @done switch to some automagic object encoding scheme
 
3134
        * @todo add a check for params of 'resource' type
 
3135
        * @todo add some trigger_errors when returning false?
 
3136
        * @todo what to do when the PHP function returns NULL? we are currently returning bogus responses!!!
 
3137
        */
 
3138
        function wrap_php_function($funcname, $newfuncname='')
 
3139
        {
 
3140
                if(version_compare(phpversion(), '5.0.3') == -1)
 
3141
                {
 
3142
                        // up to php 5.0.3 some useful reflection methods were missing
 
3143
                        return false;
 
3144
                }
 
3145
                if((is_array($funcname) && !method_exists($funcname[0], $funcname[1])) || !function_exists($funcname))
 
3146
                {
 
3147
                        return false;
 
3148
                }
 
3149
                else
 
3150
                {
 
3151
                        // determine name of new php function
 
3152
                        if($newfuncname == '')
 
3153
                        {
 
3154
                                if(is_array($funcname))
 
3155
                                {
 
3156
                                        $xmlrpcfuncname = "xmlrpc_".implode('_', $funcname);
 
3157
                                }
 
3158
                                else
 
3159
                                {
 
3160
                                        $xmlrpcfuncname = "xmlrpc_$funcname";
 
3161
                                }
 
3162
                        }
 
3163
                        else
 
3164
                        {
 
3165
                                $xmlrpcfuncname = $newfuncname;
 
3166
                        }
 
3167
                        while(function_exists($xmlrpcfuncname))
 
3168
                        {
 
3169
                                $xmlrpcfuncname .= 'x';
 
3170
                        }
 
3171
                        $code = "function $xmlrpcfuncname(\$msg) {\n";
 
3172
 
 
3173
                        // start to introspect PHP code
 
3174
                        $func =& new ReflectionFunction($funcname);
 
3175
                        if($func->isInternal())
 
3176
                        {
 
3177
                                // Note: from PHP 5.1.0 onward, we will possibly be able to use invokeargs
 
3178
                                // instead of getparameters to fully reflect internal php functions ?
 
3179
                                return false;
 
3180
                        }
 
3181
 
 
3182
                        // retrieve parameter names, types and description from javadoc comments
 
3183
 
 
3184
                        // function description
 
3185
                        $desc = '';
 
3186
                        // type of return val: by default 'any'
 
3187
                        $returns = $GLOBALS['xmlrpcValue'];
 
3188
                        // type + name of function parameters
 
3189
                        $paramDocs = array();
 
3190
 
 
3191
                        $docs = $func->getDocComment();
 
3192
                        if($docs != '')
 
3193
                        {
 
3194
                                $docs = explode("\n", $docs);
 
3195
                                $i = 0;
 
3196
                                foreach($docs as $doc)
 
3197
                                {
 
3198
                                        $doc = trim($doc, " \r\t/*");
 
3199
                                        if(strlen($doc) && strpos($doc, '@') !== 0 && !$i)
 
3200
                                        {
 
3201
                                                if($desc)
 
3202
                                                {
 
3203
                                                        $desc .= "\n";
 
3204
                                                }
 
3205
                                                $desc .= $doc;
 
3206
                                        }
 
3207
                                        elseif(strpos($doc, '@param') === 0)
 
3208
                                        {
 
3209
                                                // syntax: @param type [$name] desc
 
3210
                                                if(preg_match('/@param\s+(\S+)(\s+\$\S+)?\s+(.+)/', $doc, $matches))
 
3211
                                                {
 
3212
                                                        if(strpos($matches[1], '|'))
 
3213
                                                        {
 
3214
                                                                //$paramDocs[$i]['type'] = explode('|', $matches[1]);
 
3215
                                                                $paramDocs[$i]['type'] = 'mixed';
 
3216
                                                        }
 
3217
                                                        else
 
3218
                                                        {
 
3219
                                                                $paramDocs[$i]['type'] = $matches[1];
 
3220
                                                        }
 
3221
                                                        $paramDocs[$i]['name'] = trim($matches[2]);
 
3222
                                                        $paramDocs[$i]['doc'] = $matches[3];
 
3223
                                                }
 
3224
                                                $i++;
 
3225
                                        }
 
3226
                                        elseif(strpos($doc, '@return') === 0)
 
3227
                                        {
 
3228
                                                $returns = preg_split("/\s+/", $doc);
 
3229
                                                if(isset($returns[1]))
 
3230
                                                {
 
3231
                                                        $returns = php_2_xmlrpc_type($returns[1]);
 
3232
                                                }
 
3233
                                        }
 
3234
                                }
 
3235
                        }
 
3236
 
 
3237
                        // start introspection of actual function prototype and building of PHP code
 
3238
                        // to be eval'd
 
3239
                        $params = $func->getParameters();
 
3240
 
 
3241
                        $innercode = '';
 
3242
                        $i = 0;
 
3243
                        $parsvariations = array();
 
3244
                        $pars = array();
 
3245
                        $pnum = count($params);
 
3246
                        foreach($params as $param)
 
3247
                        {
 
3248
                                if (isset($paramDocs[$i]['name']) && $paramDocs[$i]['name'] && strtolower($paramDocs[$i]['name']) != '$'.strtolower($param->getName()))
 
3249
                                {
 
3250
                                        // param name from phpdoc info does not match param definition!
 
3251
                                        $paramDocs[$i]['type'] = 'mixed';
 
3252
                                }
 
3253
 
 
3254
                                if($param->isOptional())
 
3255
                                {
 
3256
                                        // this particular parameter is optional. save as valid previous list of parameters
 
3257
                                        $innercode .= "if (\$paramcount > $i) {\n";
 
3258
                                        $parsvariations[] = $pars;
 
3259
                                }
 
3260
                                $innercode .= "\$p$i = \$msg->getParam($i);\n";
 
3261
                                $innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_xmlrpc_decode(\$p$i);\n";
 
3262
                                $pars[] = "\$p$i";
 
3263
                                $i++;
 
3264
                                if($param->isOptional())
 
3265
                                {
 
3266
                                        $innercode .= "}\n";
 
3267
                                }
 
3268
                                if($i == $pnum)
 
3269
                                {
 
3270
                                        // last allowed parameters combination
 
3271
                                        $parsvariations[] = $pars;
 
3272
                                }
 
3273
                        }
 
3274
 
 
3275
                        $sigs = array();
 
3276
                        if(count($parsvariations) == 0)
 
3277
                        {
 
3278
                                // only known good synopsis = no parameters
 
3279
                                $parsvariations[] = array();
 
3280
                                $minpars = 0;
 
3281
                        }
 
3282
                        else
 
3283
                        {
 
3284
                                $minpars = count($parsvariations[0]);
 
3285
                        }
 
3286
 
 
3287
                        if($minpars)
 
3288
                        {
 
3289
                                // add to code the check for min params number
 
3290
                                // NB: this check needs to be done BEFORE decoding param values
 
3291
                                $innercode = "\$paramcount = \$msg->getNumParams();\n" .
 
3292
                                "if (\$paramcount < $minpars) return new xmlrpcresp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}');\n" . $innercode;
 
3293
                        }
 
3294
                        else
 
3295
                        {
 
3296
                                $innercode = "\$paramcount = \$msg->getNumParams();\n" . $innercode;
 
3297
                        }
 
3298
 
 
3299
                        $innercode .= "\$np = false;";
 
3300
                        foreach($parsvariations as $pars)
 
3301
                        {
 
3302
                                $innercode .= "if (\$paramcount == " . count($pars) . ") \$retval = $funcname(" . implode(',', $pars) . "); else\n";
 
3303
                                // build a 'generic' signature (only use an appropriate return type)
 
3304
                                $sig = array($returns);
 
3305
                                for($i=0; $i < count($pars); $i++)
 
3306
                                {
 
3307
                                        if (isset($paramDocs[$i]['type']))
 
3308
                                        {
 
3309
                                                $sig[] = php_2_xmlrpc_type($paramDocs[$i]['type']);
 
3310
                                        }
 
3311
                                        else
 
3312
                                        {
 
3313
                                                $sig[] = $GLOBALS['xmlrpcValue'];
 
3314
                                        }
 
3315
                                }
 
3316
                                $sigs[] = $sig;
 
3317
                        }
 
3318
                        $innercode .= "\$np = true;\n";
 
3319
                        $innercode .= "if (\$np) return new xmlrpcresp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}'); else\n";
 
3320
                        //$innercode .= "if (\$_xmlrpcs_error_occurred) return new xmlrpcresp(0, $GLOBALS['xmlrpcerr']user, \$_xmlrpcs_error_occurred); else\n";
 
3321
                        if($returns == $GLOBALS['xmlrpcDateTime'] || $returns == $GLOBALS['xmlrpcBase64'])
 
3322
                        {
 
3323
                                $innercode .= "return new xmlrpcresp(new xmlrpcval(\$retval, '$returns'));";
 
3324
                        }
 
3325
                        else
 
3326
                        {
 
3327
                                $innercode .= "return new xmlrpcresp(php_xmlrpc_encode(\$retval, array('encode_php_objs')));";
 
3328
                        }
 
3329
                        // shall we exclude functions returning by ref?
 
3330
                        // if($func->returnsReference())
 
3331
                        //  return false;
 
3332
                        $code = $code . $innercode . "\n}\n \$allOK=1;";
 
3333
                        //print_r($code);
 
3334
                        $allOK = 0;
 
3335
                        eval($code);
 
3336
                        // alternative
 
3337
                        //$xmlrpcfuncname = create_function('$m', $innercode);
 
3338
 
 
3339
                        if(!$allOK)
 
3340
                        {
 
3341
                                return false;
 
3342
                        }
 
3343
 
 
3344
                        /// @todo examine if $paramDocs matches $parsvariations and build array for
 
3345
                        /// usage as method signature, plus put together a nice string for docs
 
3346
 
 
3347
                        $ret = array('function' => $xmlrpcfuncname, 'signature' => $sigs, 'docstring' => $desc);
 
3348
                        return $ret;
 
3349
                }
 
3350
        }
 
3351
 
 
3352
        /**
 
3353
        * Given an xmlrpc client and a method name, register a php wrapper function
 
3354
        * that will call it and return results using native php types for both
 
3355
        * params and results. The generated php function will return an xmlrpcresp
 
3356
        * oject for failed xmlrpc calls
 
3357
        *
 
3358
        * Known limitations:
 
3359
        * - server must support system.methodsignature for the wanted xmlrpc method
 
3360
        * - for methods that expose many signatures, only one can be picked (we
 
3361
        *   could in priciple check if signatures differ only by number of params
 
3362
        *   and not by type, but it would be more complication than we can spare time)
 
3363
        * - nested xmlrpc params: the caller of the generated php function has to
 
3364
        *   encode on its own the params passed to the php function if these are structs
 
3365
        *   or arrays whose (sub)members include values of type datetime or base64
 
3366
        *
 
3367
        * Notes: the connection properties of the given client will be copied
 
3368
        * and reused for the connection used during the call to the generated
 
3369
        * php function.
 
3370
        * Calling the generated php function 'might' be slow: a new xmlrpc client
 
3371
        * is created on every invocation and an xmlrpc-connection opened+closed.
 
3372
        * An extra 'debug' param is appended to param list of xmlrpc method, useful
 
3373
        * for debugging purposes.
 
3374
        *
 
3375
        * @param xmlrpc_client $client     an xmlrpc client set up correctly to communicate with target server
 
3376
        * @param string        $methodname the xmlrpc method to be mapped to a php function
 
3377
        * @param integer       $signum     the index of the method signature to use in mapping (if method exposes many sigs)
 
3378
        * @return string                   the name of the generated php function (or false)
 
3379
        */
 
3380
        function wrap_xmlrpc_method($client, $methodname, $signum=0, $timeout=0, $protocol='', $newfuncname='')
 
3381
        {
 
3382
                $msg =& new xmlrpcmsg('system.methodSignature');
 
3383
                $msg->addparam(new xmlrpcval($methodname));
 
3384
                $response =& $client->send($msg, $timeout, $protocol);
 
3385
                if(!$response || $response->faultCode())
 
3386
                {
 
3387
                        return false;
 
3388
                }
 
3389
                else
 
3390
                {
 
3391
                        $desc = $response->value();
 
3392
                        if($desc->kindOf() != 'array' || $desc->arraysize() <= $signum)
 
3393
                        {
 
3394
                                return false;
 
3395
                        }
 
3396
                        else
 
3397
                        {
 
3398
                                if($newfuncname != '')
 
3399
                                {
 
3400
                                        $xmlrpcfuncname = $newfuncname;
 
3401
                                }
 
3402
                                else
 
3403
                                {
 
3404
                                        $xmlrpcfuncname = 'xmlrpc_'.str_replace('.', '_', $methodname);
 
3405
                                }
 
3406
                                while(function_exists($xmlrpcfuncname))
 
3407
                                {
 
3408
                                        $xmlrpcfuncname .= 'x';
 
3409
                                }
 
3410
                                $desc = $desc->arraymem($signum);
 
3411
                                $code = "function $xmlrpcfuncname (";
 
3412
                                $innercode = "\$client =& new xmlrpc_client('$client->path', '$client->server');\n";
 
3413
                                // copy all client fields to the client that will be generated runtime
 
3414
                                // (this provides for future expansion of client obj)
 
3415
                                foreach($client as $fld => $val)
 
3416
                                {
 
3417
                                        if($fld != 'debug' && $fld != 'return_type')
 
3418
                                        {
 
3419
                                                $val = var_export($val, true);
 
3420
                                                $innercode .= "\$client->$fld = $val;\n";
 
3421
                                        }
 
3422
                                }
 
3423
                                $innercode .= "\$client->setDebug(\$debug);\n";
 
3424
                                $innercode .= "\$client->return_type = 'xmlrpcvals';\n";
 
3425
                                $innercode .= "\$msg =& new xmlrpcmsg('$methodname');\n";
 
3426
 
 
3427
                                // param parsing
 
3428
                                $plist = array();
 
3429
                                $pcount = $desc->arraysize();
 
3430
                                for($i = 1; $i < $pcount; $i++)
 
3431
                                {
 
3432
                                        $plist[] = "\$p$i";
 
3433
                                        $ptype = $desc->arraymem($i);
 
3434
                                        $ptype = $ptype->scalarval();
 
3435
                                        if($ptype == 'dateTime.iso8601' || $ptype == 'base64')
 
3436
                                        {
 
3437
                                                $innercode .= "\$p$i =& new xmlrpcval(\$p$i, '$ptype');\n";
 
3438
                                        }
 
3439
                                        else
 
3440
                                        {
 
3441
                                                $innercode .= "\$p$i =& php_xmlrpc_encode(\$p$i);\n";
 
3442
                                        }
 
3443
                                        $innercode .= "\$msg->addparam(\$p$i);\n";
 
3444
                                }
 
3445
                                $plist[] = '$debug = 0';
 
3446
                                $plist = implode(',', $plist);
 
3447
 
 
3448
                                $innercode .= "\$res =& \$client->send(\$msg, $timeout, '$protocol');\n";
 
3449
                                $innercode .= "if (\$res->faultcode()) return \$res; else return php_xmlrpc_decode(\$res->value(), array('decode_php_objs'));";
 
3450
 
 
3451
                                $code = $code . $plist. ") {\n" . $innercode . "\n}\n\$allOK=1;";
 
3452
                                //print_r($code);
 
3453
                                $allOK = 0;
 
3454
                                eval($code);
 
3455
                                // alternative
 
3456
                                //$xmlrpcfuncname = create_function('$m', $innercode);
 
3457
                                if($allOK)
 
3458
                                {
 
3459
                                        return $xmlrpcfuncname;
 
3460
                                }
 
3461
                                else
 
3462
                                {
 
3463
                                        return false;
 
3464
                                }
 
3465
                        }
 
3466
                }
 
3467
        }
 
3468
 
 
3469
        /**
 
3470
        * xml charset encoding guessing helper function.
 
3471
        * Tries to determine the charset encoding of an XML chunk
 
3472
        * received over HTTP.
 
3473
 
 
3474
        * NB: according to the spec (RFC 3023, if text/xml content-type is received over HTTP without a content-type,
 
3475
 
 
3476
        * we SHOULD assume it is strictly US-ASCII. But we try to be more tolerant of unconforming (legacy?) clients/servers,
 
3477
 
 
3478
        * which will be most probably using UTF-8 anyway...
 
3479
        *
 
3480
        * @param string $httpheaders the http Content-type header
 
3481
        * @param string $xmlchunk xml content buffer
 
3482
        * @param string $encoding_prefs comma separated list of character encodings to be used as default (when mb extension is enabled)
 
3483
        *
 
3484
        * @todo explore usage of mb_http_input(): does it detect http headers + post data? if so, use it instead of hand-detection!!!
 
3485
        */
 
3486
        function guess_encoding($httpheader='', $xmlchunk='', $encoding_prefs=null)
 
3487
        {
 
3488
                // discussion: see http://www.yale.edu/pclt/encoding/
 
3489
                // 1 - test if encoding is specified in HTTP HEADERS
 
3490
 
 
3491
                //Details:
 
3492
                // LWS:           (\13\10)?( |\t)+
 
3493
                // token:         (any char but excluded stuff)+
 
3494
                // header:        Content-type = ...; charset=value(; ...)*
 
3495
                //   where value is of type token, no LWS allowed between 'charset' and value
 
3496
                // Note: we do not check for invalid chars in VALUE:
 
3497
                //   this had better be done using pure ereg as below
 
3498
 
 
3499
                /// @todo this test will pass if ANY header has charset specification, not only Content-Type. Fix it?
 
3500
                if(eregi(";((\\xD\\xA)?[ \\x9]+)*charset=", $httpheader))
 
3501
                {
 
3502
                        /// @BUG if charset is received uppercase, this line will fail!
 
3503
                        $in = strpos($httpheader, 'charset=')+8;
 
3504
                        $out = strpos($httpheader, ';', $in) ? strpos($httpheader, ';', $in) : strlen($httpheader);
 
3505
                        return strtoupper(trim(substr($httpheader, $in, $out-$in)));
 
3506
                }
 
3507
 
 
3508
                // 2 - scan the first bytes of the data for a UTF-16 (or other) BOM pattern
 
3509
                //     (source: http://www.w3.org/TR/2000/REC-xml-20001006)
 
3510
                //     NOTE: actually, according to the spec, even if we find the BOM and determine
 
3511
                //     an encoding, we should check if there is an encoding specified
 
3512
                //     in the xml declaration, and verify if they match.
 
3513
                /// @todo implement check as described above?
 
3514
                /// @todo implement check for first bytes of string even without a BOM? (It sure looks harder than for cases WITH a BOM)
 
3515
                if(@ereg("^(\\x00\\x00\\xFE\\xFF|\\xFF\\xFE\\x00\\x00|\\x00\\x00\\xFF\\xFE|\\xFE\\xFF\\x00\\x00)", $xmlchunk))
 
3516
                //  if (preg_match("/^(\\x00\\x00\\xFE\\xFF|\\xFF\\xFE\\x00\\x00|\\x00\\x00\\xFF\\xFE|\\xFE\\xFF\\x00\\x00)/", $xmlchunk))
 
3517
                {
 
3518
                        return 'UCS-4';
 
3519
                }
 
3520
                elseif(ereg("^(\\xFE\\xFF|\\xFF\\xFE)", $xmlchunk))
 
3521
                {
 
3522
                        return 'UTF-16';
 
3523
                }
 
3524
                elseif(ereg("^(\\xEF\\xBB\\xBF)", $xmlchunk))
 
3525
                {
 
3526
                        return 'UTF-8';
 
3527
                }
 
3528
 
 
3529
                // 3 - test if encoding is specified in the xml declaration
 
3530
                // Details:
 
3531
                // SPACE:         (#x20 | #x9 | #xD | #xA)+ === [ \x9\xD\xA]+
 
3532
                // EQ:            SPACE?=SPACE? === [ \x9\xD\xA]*=[ \x9\xD\xA]*
 
3533
                if (ereg("^<\?xml".
 
3534
                        "[ \\x9\\xD\\xA]+" . "version"  . "[ \\x9\\xD\\xA]*=[ \\x9\\xD\\xA]*" . "((\"[a-zA-Z0-9_.:-]+\")|('[a-zA-Z0-9_.:-]+'))".
 
3535
                        "[ \\x9\\xD\\xA]+" . "encoding" . "[ \\x9\\xD\\xA]*=[ \\x9\\xD\\xA]*" . "((\"[A-Za-z][A-Za-z0-9._-]*\")|('[A-Za-z][A-Za-z0-9._-]*'))",
 
3536
                        $xmlchunk, $regs))
 
3537
                {
 
3538
                        return strtoupper(substr($regs[4], 1, strlen($regs[4])-2));
 
3539
                }
 
3540
 
 
3541
                // 4 - if mbstring is available, let it do the guesswork
 
3542
                // NB: we favour finding an encoding that is compatible with what we can process
 
3543
                if(extension_loaded('mbstring'))
 
3544
                {
 
3545
                        if($encoding_prefs)
 
3546
                        {
 
3547
                                $enc = mb_detect_encoding($xmlchunk, $encoding_prefs);
 
3548
                        }
 
3549
                        else
 
3550
                        {
 
3551
                                $enc = mb_detect_encoding($xmlchunk);
 
3552
                        }
 
3553
                        // NB: mb_detect likes to call it ascii, xml parser likes to call it US_ASCII...
 
3554
                        // IANA also likes better US-ASCII, so go with it
 
3555
                        if($enc == 'ASCII')
 
3556
                        {
 
3557
                                $enc = 'US-'.$enc;
 
3558
                        }
 
3559
                        return $enc;
 
3560
                }
 
3561
                else
 
3562
                {
 
3563
                        // no encoding specified: as per HTTP1.1 assume it is iso-8859-1?
 
3564
                        // Both RFC 2616 (HTTP 1.1) and 1945(http 1.0) clearly state that for text/xxx content types
 
3565
                        // this should be the standard. And we should be getting text/xml as request and response.
 
3566
                        // BUT we have to be backward compatible with the lib, which always used UTF-8 as default...
 
3567
                        return $GLOBALS['xmlrpc_defencoding'];
 
3568
                }
 
3569
        }
 
3570
 
 
3571
/**
 
3572
* Checks if a given charset encoding is present in a list of encodings or
 
3573
* if it is a valid subset of any encoding in the list
 
3574
* @param string $encoding  charset to be tested
 
3575
* @param mixed  $validlist comma separated list of valid charsets (or array of charsets)
 
3576
*/
 
3577
function is_valid_charset($encoding, $validlist)
 
3578
{
 
3579
        $charset_supersets = array(
 
3580
    'US-ASCII' => array ('ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
 
3581
                         'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8',
 
3582
                         'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-11', 'ISO-8859-12',
 
3583
                         'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'UTF-8',
 
3584
                         'EUC-JP', 'EUC-', 'EUC-KR', 'EUC-CN')
 
3585
        );
 
3586
        if (is_string($validlist))
 
3587
                $validlist = split(',', $validlist);
 
3588
        if (@in_array(strtoupper($encoding), $validlist))
 
3589
                return true;
 
3590
        else
 
3591
        {
 
3592
                if (array_key_exists($encoding, $charset_supersets))
 
3593
                        foreach ($validlist as $allowed)
 
3594
                                if (in_array($allowed, $charset_supersets[$encoding]))
 
3595
                                        return true;
 
3596
                return false;
 
3597
        }
 
3598
}
 
3599
 
 
3600
?>
 
 
b'\\ No newline at end of file'