Project

General

Profile

Download (55.3 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2

    
3
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4

    
5
/**
6
 * PHP implementation of the XML-RPC protocol
7
 *
8
 * This is a PEAR-ified version of Useful inc's XML-RPC for PHP.
9
 * It has support for HTTP transport, proxies and authentication.
10
 *
11
 * PHP versions 4 and 5
12
 *
13
 * LICENSE: License is granted to use or modify this software
14
 * ("XML-RPC for PHP") for commercial or non-commercial use provided the
15
 * copyright of the author is preserved in any distributed or derivative work.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESSED OR
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 *
28
 * @category   Web Services
29
 * @package    XML_RPC
30
 * @author     Edd Dumbill <edd@usefulinc.com>
31
 * @author     Stig Bakken <stig@php.net>
32
 * @author     Martin Jansen <mj@php.net>
33
 * @author     Daniel Convissor <danielc@php.net>
34
 * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
35
 * @version    CVS: $Id$
36
 * @link       http://pear.php.net/package/XML_RPC
37
 */
38

    
39

    
40
if (!function_exists('xml_parser_create')) {
41
    PEAR::loadExtension('xml');
42
}
43

    
44
/**#@+
45
 * Error constants
46
 */
47
/**
48
 * Parameter values don't match parameter types
49
 */
50
define('XML_RPC_ERROR_INVALID_TYPE', 101);
51
/**
52
 * Parameter declared to be numeric but the values are not
53
 */
54
define('XML_RPC_ERROR_NON_NUMERIC_FOUND', 102);
55
/**
56
 * Communication error
57
 */
58
define('XML_RPC_ERROR_CONNECTION_FAILED', 103);
59
/**
60
 * The array or struct has already been started
61
 */
62
define('XML_RPC_ERROR_ALREADY_INITIALIZED', 104);
63
/**
64
 * Incorrect parameters submitted
65
 */
66
define('XML_RPC_ERROR_INCORRECT_PARAMS', 105);
67
/**
68
 * Programming error by developer
69
 */
70
define('XML_RPC_ERROR_PROGRAMMING', 106);
71
/**#@-*/
72

    
73

    
74
/**
75
 * Data types
76
 * @global string $GLOBALS['XML_RPC_I4']
77
 */
78
$GLOBALS['XML_RPC_I4'] = 'i4';
79

    
80
/**
81
 * Data types
82
 * @global string $GLOBALS['XML_RPC_Int']
83
 */
84
$GLOBALS['XML_RPC_Int'] = 'int';
85

    
86
/**
87
 * Data types
88
 * @global string $GLOBALS['XML_RPC_Boolean']
89
 */
90
$GLOBALS['XML_RPC_Boolean'] = 'boolean';
91

    
92
/**
93
 * Data types
94
 * @global string $GLOBALS['XML_RPC_Double']
95
 */
96
$GLOBALS['XML_RPC_Double'] = 'double';
97

    
98
/**
99
 * Data types
100
 * @global string $GLOBALS['XML_RPC_String']
101
 */
102
$GLOBALS['XML_RPC_String'] = 'string';
103

    
104
/**
105
 * Data types
106
 * @global string $GLOBALS['XML_RPC_DateTime']
107
 */
108
$GLOBALS['XML_RPC_DateTime'] = 'dateTime.iso8601';
109

    
110
/**
111
 * Data types
112
 * @global string $GLOBALS['XML_RPC_Base64']
113
 */
114
$GLOBALS['XML_RPC_Base64'] = 'base64';
115

    
116
/**
117
 * Data types
118
 * @global string $GLOBALS['XML_RPC_Array']
119
 */
120
$GLOBALS['XML_RPC_Array'] = 'array';
121

    
122
/**
123
 * Data types
124
 * @global string $GLOBALS['XML_RPC_Struct']
125
 */
126
$GLOBALS['XML_RPC_Struct'] = 'struct';
127

    
128

    
129
/**
130
 * Data type meta-types
131
 * @global array $GLOBALS['XML_RPC_Types']
132
 */
133
$GLOBALS['XML_RPC_Types'] = array(
134
    $GLOBALS['XML_RPC_I4']       => 1,
135
    $GLOBALS['XML_RPC_Int']      => 1,
136
    $GLOBALS['XML_RPC_Boolean']  => 1,
137
    $GLOBALS['XML_RPC_String']   => 1,
138
    $GLOBALS['XML_RPC_Double']   => 1,
139
    $GLOBALS['XML_RPC_DateTime'] => 1,
140
    $GLOBALS['XML_RPC_Base64']   => 1,
141
    $GLOBALS['XML_RPC_Array']    => 2,
142
    $GLOBALS['XML_RPC_Struct']   => 3,
143
);
144

    
145

    
146
/**
147
 * Error message numbers
148
 * @global array $GLOBALS['XML_RPC_err']
149
 */
150
$GLOBALS['XML_RPC_err'] = array(
151
    'unknown_method'      => 1,
152
    'invalid_return'      => 2,
153
    'incorrect_params'    => 3,
154
    'introspect_unknown'  => 4,
155
    'http_error'          => 5,
156
    'not_response_object' => 6,
157
    'invalid_request'     => 7,
158
);
159

    
160
/**
161
 * Error message strings
162
 * @global array $GLOBALS['XML_RPC_str']
163
 */
164
$GLOBALS['XML_RPC_str'] = array(
165
    'unknown_method'      => 'Unknown method',
166
    'invalid_return'      => 'Invalid return payload: enable debugging to examine incoming payload',
167
    'incorrect_params'    => 'Incorrect parameters passed to method',
168
    'introspect_unknown'  => 'Can\'t introspect: method unknown',
169
    'http_error'          => 'Didn\'t receive 200 OK from remote server.',
170
    'not_response_object' => 'The requested method didn\'t return an XML_RPC_Response object.',
171
    'invalid_request'     => 'Invalid request payload',
172
);
173

    
174

    
175
/**
176
 * Default XML encoding (ISO-8859-1, UTF-8 or US-ASCII)
177
 * @global string $GLOBALS['XML_RPC_defencoding']
178
 */
179
$GLOBALS['XML_RPC_defencoding'] = 'UTF-8';
180

    
181
/**
182
 * User error codes start at 800
183
 * @global int $GLOBALS['XML_RPC_erruser']
184
 */
185
$GLOBALS['XML_RPC_erruser'] = 800;
186

    
187
/**
188
 * XML parse error codes start at 100
189
 * @global int $GLOBALS['XML_RPC_errxml']
190
 */
191
$GLOBALS['XML_RPC_errxml'] = 100;
192

    
193

    
194
/**
195
 * Compose backslashes for escaping regexp
196
 * @global string $GLOBALS['XML_RPC_backslash']
197
 */
198
$GLOBALS['XML_RPC_backslash'] = chr(92) . chr(92);
199

    
200

    
201
/**
202
 * Valid parents of XML elements
203
 * @global array $GLOBALS['XML_RPC_valid_parents']
204
 */
205
$GLOBALS['XML_RPC_valid_parents'] = array(
206
    'BOOLEAN' => array('VALUE'),
207
    'I4' => array('VALUE'),
208
    'INT' => array('VALUE'),
209
    'STRING' => array('VALUE'),
210
    'DOUBLE' => array('VALUE'),
211
    'DATETIME.ISO8601' => array('VALUE'),
212
    'BASE64' => array('VALUE'),
213
    'ARRAY' => array('VALUE'),
214
    'STRUCT' => array('VALUE'),
215
    'PARAM' => array('PARAMS'),
216
    'METHODNAME' => array('METHODCALL'),
217
    'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
218
    'MEMBER' => array('STRUCT'),
219
    'NAME' => array('MEMBER'),
220
    'DATA' => array('ARRAY'),
221
    'FAULT' => array('METHODRESPONSE'),
222
    'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
223
);
224

    
225

    
226
/**
227
 * Stores state during parsing
228
 *
229
 * quick explanation of components:
230
 *   + ac     = accumulates values
231
 *   + qt     = decides if quotes are needed for evaluation
232
 *   + cm     = denotes struct or array (comma needed)
233
 *   + isf    = indicates a fault
234
 *   + lv     = indicates "looking for a value": implements the logic
235
 *               to allow values with no types to be strings
236
 *   + params = stores parameters in method calls
237
 *   + method = stores method name
238
 *
239
 * @global array $GLOBALS['XML_RPC_xh']
240
 */
241
$GLOBALS['XML_RPC_xh'] = array();
242

    
243

    
244
/**
245
 * Start element handler for the XML parser
246
 *
247
 * @return void
248
 */
249
function XML_RPC_se($parser_resource, $name, $attrs)
250
{
251
    global $XML_RPC_xh, $XML_RPC_DateTime, $XML_RPC_String, $XML_RPC_valid_parents;
252
    $parser = (int) $parser_resource;
253

    
254
    // if invalid xmlrpc already detected, skip all processing
255
    if ($XML_RPC_xh[$parser]['isf'] >= 2) {
256
        return;
257
    }
258

    
259
    // check for correct element nesting
260
    // top level element can only be of 2 types
261
    if (count($XML_RPC_xh[$parser]['stack']) == 0) {
262
        if ($name != 'METHODRESPONSE' && $name != 'METHODCALL') {
263
            $XML_RPC_xh[$parser]['isf'] = 2;
264
            $XML_RPC_xh[$parser]['isf_reason'] = 'missing top level xmlrpc element';
265
            return;
266
        }
267
    } else {
268
        // not top level element: see if parent is OK
269
        if (!in_array($XML_RPC_xh[$parser]['stack'][0], $XML_RPC_valid_parents[$name])) {
270
            $name = preg_replace('[^a-zA-Z0-9._-]', '', $name);
271
            $XML_RPC_xh[$parser]['isf'] = 2;
272
            $XML_RPC_xh[$parser]['isf_reason'] = "xmlrpc element $name cannot be child of {$XML_RPC_xh[$parser]['stack'][0]}";
273
            return;
274
        }
275
    }
276

    
277
    switch ($name) {
278
    case 'STRUCT':
279
        $XML_RPC_xh[$parser]['cm']++;
280

    
281
        // turn quoting off
282
        $XML_RPC_xh[$parser]['qt'] = 0;
283

    
284
        $cur_val = array();
285
        $cur_val['value'] = array();
286
        $cur_val['members'] = 1;
287
        array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val);
288
        break;
289

    
290
    case 'ARRAY':
291
        $XML_RPC_xh[$parser]['cm']++;
292

    
293
        // turn quoting off
294
        $XML_RPC_xh[$parser]['qt'] = 0;
295

    
296
        $cur_val = array();
297
        $cur_val['value'] = array();
298
        $cur_val['members'] = 0;
299
        array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val);
300
        break;
301

    
302
    case 'NAME':
303
        $XML_RPC_xh[$parser]['ac'] = '';
304
        break;
305

    
306
    case 'FAULT':
307
        $XML_RPC_xh[$parser]['isf'] = 1;
308
        break;
309

    
310
    case 'PARAM':
311
        $XML_RPC_xh[$parser]['valuestack'] = array();
312
        break;
313

    
314
    case 'VALUE':
315
        $XML_RPC_xh[$parser]['lv'] = 1;
316
        $XML_RPC_xh[$parser]['vt'] = $XML_RPC_String;
317
        $XML_RPC_xh[$parser]['ac'] = '';
318
        $XML_RPC_xh[$parser]['qt'] = 0;
319
        // look for a value: if this is still 1 by the
320
        // time we reach the first data segment then the type is string
321
        // by implication and we need to add in a quote
322
        break;
323

    
324
    case 'I4':
325
    case 'INT':
326
    case 'STRING':
327
    case 'BOOLEAN':
328
    case 'DOUBLE':
329
    case 'DATETIME.ISO8601':
330
    case 'BASE64':
331
        $XML_RPC_xh[$parser]['ac'] = ''; // reset the accumulator
332

    
333
        if ($name == 'DATETIME.ISO8601' || $name == 'STRING') {
334
            $XML_RPC_xh[$parser]['qt'] = 1;
335

    
336
            if ($name == 'DATETIME.ISO8601') {
337
                $XML_RPC_xh[$parser]['vt'] = $XML_RPC_DateTime;
338
            }
339

    
340
        } elseif ($name == 'BASE64') {
341
            $XML_RPC_xh[$parser]['qt'] = 2;
342
        } else {
343
            // No quoting is required here -- but
344
            // at the end of the element we must check
345
            // for data format errors.
346
            $XML_RPC_xh[$parser]['qt'] = 0;
347
        }
348
        break;
349

    
350
    case 'MEMBER':
351
        $XML_RPC_xh[$parser]['ac'] = '';
352
        break;
353

    
354
    case 'DATA':
355
    case 'METHODCALL':
356
    case 'METHODNAME':
357
    case 'METHODRESPONSE':
358
    case 'PARAMS':
359
        // valid elements that add little to processing
360
        break;
361
    }
362

    
363

    
364
    // Save current element to stack
365
    array_unshift($XML_RPC_xh[$parser]['stack'], $name);
366

    
367
    if ($name != 'VALUE') {
368
        $XML_RPC_xh[$parser]['lv'] = 0;
369
    }
370
}
371

    
372
/**
373
 * End element handler for the XML parser
374
 *
375
 * @return void
376
 */
377
function XML_RPC_ee($parser_resource, $name)
378
{
379
    global $XML_RPC_xh, $XML_RPC_Types, $XML_RPC_String;
380
    $parser = (int) $parser_resource;
381

    
382
    if ($XML_RPC_xh[$parser]['isf'] >= 2) {
383
        return;
384
    }
385

    
386
    // push this element from stack
387
    // NB: if XML validates, correct opening/closing is guaranteed and
388
    // we do not have to check for $name == $curr_elem.
389
    // we also checked for proper nesting at start of elements...
390
    $curr_elem = array_shift($XML_RPC_xh[$parser]['stack']);
391

    
392
    switch ($name) {
393
    case 'STRUCT':
394
    case 'ARRAY':
395
    $cur_val = array_shift($XML_RPC_xh[$parser]['valuestack']);
396
    $XML_RPC_xh[$parser]['value'] = $cur_val['value'];
397
        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
398
        $XML_RPC_xh[$parser]['cm']--;
399
        break;
400

    
401
    case 'NAME':
402
    $XML_RPC_xh[$parser]['valuestack'][0]['name'] = $XML_RPC_xh[$parser]['ac'];
403
        break;
404

    
405
    case 'BOOLEAN':
406
        // special case here: we translate boolean 1 or 0 into PHP
407
        // constants true or false
408
        if ($XML_RPC_xh[$parser]['ac'] == '1') {
409
            $XML_RPC_xh[$parser]['ac'] = 'true';
410
        } else {
411
            $XML_RPC_xh[$parser]['ac'] = 'false';
412
        }
413

    
414
        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
415
        // Drop through intentionally.
416

    
417
    case 'I4':
418
    case 'INT':
419
    case 'STRING':
420
    case 'DOUBLE':
421
    case 'DATETIME.ISO8601':
422
    case 'BASE64':
423
        if ($XML_RPC_xh[$parser]['qt'] == 1) {
424
            // we use double quotes rather than single so backslashification works OK
425
            $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac'];
426
        } elseif ($XML_RPC_xh[$parser]['qt'] == 2) {
427
            $XML_RPC_xh[$parser]['value'] = base64_decode($XML_RPC_xh[$parser]['ac']);
428
        } elseif ($name == 'BOOLEAN') {
429
            $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac'];
430
        } else {
431
            // we have an I4, INT or a DOUBLE
432
            // we must check that only 0123456789-.<space> are characters here
433
            if (!ereg("^[+-]?[0123456789 \t\.]+$", $XML_RPC_xh[$parser]['ac'])) {
434
                XML_RPC_Base::raiseError('Non-numeric value received in INT or DOUBLE',
435
                                         XML_RPC_ERROR_NON_NUMERIC_FOUND);
436
                $XML_RPC_xh[$parser]['value'] = XML_RPC_ERROR_NON_NUMERIC_FOUND;
437
            } else {
438
                // it's ok, add it on
439
                $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac'];
440
            }
441
        }
442

    
443
        $XML_RPC_xh[$parser]['ac'] = '';
444
        $XML_RPC_xh[$parser]['qt'] = 0;
445
        $XML_RPC_xh[$parser]['lv'] = 3; // indicate we've found a value
446
        break;
447

    
448
    case 'VALUE':
449
        // deal with a string value
450
        if (strlen($XML_RPC_xh[$parser]['ac']) > 0 &&
451
            $XML_RPC_xh[$parser]['vt'] == $XML_RPC_String) {
452
            $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac'];
453
        } elseif ($XML_RPC_xh[$parser]['lv'] == 1) {
454
            // The <value> element was empty.
455
            $XML_RPC_xh[$parser]['value'] = '';
456
        }
457

    
458
        $temp = new XML_RPC_Value($XML_RPC_xh[$parser]['value'], $XML_RPC_xh[$parser]['vt']);
459

    
460
        $cur_val = array_shift($XML_RPC_xh[$parser]['valuestack']);
461
        if (is_array($cur_val)) {
462
            if ($cur_val['members']==0) {
463
                $cur_val['value'][] = $temp;
464
            } else {
465
                $XML_RPC_xh[$parser]['value'] = $temp;
466
            }
467
            array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val);
468
        } else {
469
            $XML_RPC_xh[$parser]['value'] = $temp;
470
        }
471
        break;
472

    
473
    case 'MEMBER':
474
        $XML_RPC_xh[$parser]['ac'] = '';
475
        $XML_RPC_xh[$parser]['qt'] = 0;
476

    
477
        $cur_val = array_shift($XML_RPC_xh[$parser]['valuestack']);
478
        if (is_array($cur_val)) {
479
            if ($cur_val['members']==1) {
480
                $cur_val['value'][$cur_val['name']] = $XML_RPC_xh[$parser]['value'];
481
            }
482
            array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val);
483
        }
484
        break;
485

    
486
    case 'DATA':
487
        $XML_RPC_xh[$parser]['ac'] = '';
488
        $XML_RPC_xh[$parser]['qt'] = 0;
489
        break;
490

    
491
    case 'PARAM':
492
        $XML_RPC_xh[$parser]['params'][] = $XML_RPC_xh[$parser]['value'];
493
        break;
494

    
495
    case 'METHODNAME':
496
    case 'RPCMETHODNAME':
497
        $XML_RPC_xh[$parser]['method'] = ereg_replace("^[\n\r\t ]+", '',
498
                                                      $XML_RPC_xh[$parser]['ac']);
499
        break;
500
    }
501

    
502
    // if it's a valid type name, set the type
503
    if (isset($XML_RPC_Types[strtolower($name)])) {
504
        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
505
    }
506
}
507

    
508
/**
509
 * Character data handler for the XML parser
510
 *
511
 * @return void
512
 */
513
function XML_RPC_cd($parser_resource, $data)
514
{
515
    global $XML_RPC_xh, $XML_RPC_backslash;
516
    $parser = (int) $parser_resource;
517

    
518
    if ($XML_RPC_xh[$parser]['lv'] != 3) {
519
        // "lookforvalue==3" means that we've found an entire value
520
        // and should discard any further character data
521

    
522
        if ($XML_RPC_xh[$parser]['lv'] == 1) {
523
            // if we've found text and we're just in a <value> then
524
            // turn quoting on, as this will be a string
525
            $XML_RPC_xh[$parser]['qt'] = 1;
526
            // and say we've found a value
527
            $XML_RPC_xh[$parser]['lv'] = 2;
528
        }
529

    
530
        // replace characters that eval would
531
        // do special things with
532
        if (!isset($XML_RPC_xh[$parser]['ac'])) {
533
            $XML_RPC_xh[$parser]['ac'] = '';
534
        }
535
        $XML_RPC_xh[$parser]['ac'] .= $data;
536
    }
537
}
538

    
539
/**
540
 * The common methods and properties for all of the XML_RPC classes
541
 *
542
 * @category   Web Services
543
 * @package    XML_RPC
544
 * @author     Edd Dumbill <edd@usefulinc.com>
545
 * @author     Stig Bakken <stig@php.net>
546
 * @author     Martin Jansen <mj@php.net>
547
 * @author     Daniel Convissor <danielc@php.net>
548
 * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
549
 * @version    Release: 1.4.2
550
 * @link       http://pear.php.net/package/XML_RPC
551
 */
552
class XML_RPC_Base {
553

    
554
    /**
555
     * PEAR Error handling
556
     *
557
     * @return object  PEAR_Error object
558
     */
559
    function raiseError($msg, $code)
560
    {
561
        include_once 'PEAR.inc';
562
        if (is_object(@$this)) {
563
            return PEAR::raiseError(get_class($this) . ': ' . $msg, $code);
564
        } else {
565
            return PEAR::raiseError('XML_RPC: ' . $msg, $code);
566
        }
567
    }
568

    
569
    /**
570
     * Tell whether something is a PEAR_Error object
571
     *
572
     * @param mixed $value  the item to check
573
     *
574
     * @return bool  whether $value is a PEAR_Error object or not
575
     *
576
     * @access public
577
     */
578
    function isError($value)
579
    {
580
        return is_a($value, 'PEAR_Error');
581
    }
582
}
583

    
584
/**
585
 * The methods and properties for submitting XML RPC requests
586
 *
587
 * @category   Web Services
588
 * @package    XML_RPC
589
 * @author     Edd Dumbill <edd@usefulinc.com>
590
 * @author     Stig Bakken <stig@php.net>
591
 * @author     Martin Jansen <mj@php.net>
592
 * @author     Daniel Convissor <danielc@php.net>
593
 * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
594
 * @version    Release: 1.4.2
595
 * @link       http://pear.php.net/package/XML_RPC
596
 */
597
class XML_RPC_Client extends XML_RPC_Base {
598

    
599
    /**
600
     * The path and name of the RPC server script you want the request to go to
601
     * @var string
602
     */
603
    var $path = '';
604

    
605
    /**
606
     * The name of the remote server to connect to
607
     * @var string
608
     */
609
    var $server = '';
610

    
611
    /**
612
     * The protocol to use in contacting the remote server
613
     * @var string
614
     */
615
    var $protocol = 'http://';
616

    
617
    /**
618
     * The port for connecting to the remote server
619
     *
620
     * The default is 80 for http:// connections
621
     * and 443 for https:// and ssl:// connections.
622
     *
623
     * @var integer
624
     */
625
    var $port = 80;
626

    
627
    /**
628
     * A user name for accessing the RPC server
629
     * @var string
630
     * @see XML_RPC_Client::setCredentials()
631
     */
632
    var $username = '';
633

    
634
    /**
635
     * A password for accessing the RPC server
636
     * @var string
637
     * @see XML_RPC_Client::setCredentials()
638
     */
639
    var $password = '';
640

    
641
    /**
642
     * The name of the proxy server to use, if any
643
     * @var string
644
     */
645
    var $proxy = '';
646

    
647
    /**
648
     * The protocol to use in contacting the proxy server, if any
649
     * @var string
650
     */
651
    var $proxy_protocol = 'http://';
652

    
653
    /**
654
     * The port for connecting to the proxy server
655
     *
656
     * The default is 8080 for http:// connections
657
     * and 443 for https:// and ssl:// connections.
658
     *
659
     * @var integer
660
     */
661
    var $proxy_port = 8080;
662

    
663
    /**
664
     * A user name for accessing the proxy server
665
     * @var string
666
     */
667
    var $proxy_user = '';
668

    
669
    /**
670
     * A password for accessing the proxy server
671
     * @var string
672
     */
673
    var $proxy_pass = '';
674

    
675
    /**
676
     * The error number, if any
677
     * @var integer
678
     */
679
    var $errno = 0;
680

    
681
    /**
682
     * The error message, if any
683
     * @var string
684
     */
685
    var $errstr = '';
686

    
687
    /**
688
     * The current debug mode (1 = on, 0 = off)
689
     * @var integer
690
     */
691
    var $debug = 0;
692

    
693
    /**
694
     * The HTTP headers for the current request.
695
     * @var string
696
     */
697
    var $headers = '';
698

    
699

    
700
    /**
701
     * Sets the object's properties
702
     *
703
     * @param string  $path        the path and name of the RPC server script
704
     *                              you want the request to go to
705
     * @param string  $server      the URL of the remote server to connect to.
706
     *                              If this parameter doesn't specify a
707
     *                              protocol and $port is 443, ssl:// is
708
     *                              assumed.
709
     * @param integer $port        a port for connecting to the remote server.
710
     *                              Defaults to 80 for http:// connections and
711
     *                              443 for https:// and ssl:// connections.
712
     * @param string  $proxy       the URL of the proxy server to use, if any.
713
     *                              If this parameter doesn't specify a
714
     *                              protocol and $port is 443, ssl:// is
715
     *                              assumed.
716
     * @param integer $proxy_port  a port for connecting to the remote server.
717
     *                              Defaults to 8080 for http:// connections and
718
     *                              443 for https:// and ssl:// connections.
719
     * @param string  $proxy_user  a user name for accessing the proxy server
720
     * @param string  $proxy_pass  a password for accessing the proxy server
721
     *
722
     * @return void
723
     */
724
    function XML_RPC_Client($path, $server, $port = 0,
725
                            $proxy = '', $proxy_port = 0,
726
                            $proxy_user = '', $proxy_pass = '')
727
    {
728
        $this->path       = $path;
729
        $this->proxy_user = $proxy_user;
730
        $this->proxy_pass = $proxy_pass;
731

    
732
		$match = "";
733
        preg_match('@^(http://|https://|ssl://)?(.*)$@', $server, $match);
734
        if ($match[1] == '') {
735
            if ($port == 443) {
736
                $this->server   = $match[2];
737
                $this->protocol = 'ssl://';
738
                $this->port     = 443;
739
            } else {
740
                $this->server = $match[2];
741
                if ($port) {
742
                    $this->port = $port;
743
                }
744
            }
745
        } elseif ($match[1] == 'http://') {
746
            $this->server = $match[2];
747
            if ($port) {
748
                $this->port = $port;
749
            }
750
        } else {
751
            $this->server   = $match[2];
752
            $this->protocol = 'ssl://';
753
            if ($port) {
754
                $this->port = $port;
755
            } else {
756
                $this->port = 443;
757
            }
758
        }
759

    
760
        if ($proxy) {
761
            preg_match('@^(http://|https://|ssl://)?(.*)$@', $proxy, $match);
762
            if ($match[1] == '') {
763
                if ($proxy_port == 443) {
764
                    $this->proxy          = $match[2];
765
                    $this->proxy_protocol = 'ssl://';
766
                    $this->proxy_port     = 443;
767
                } else {
768
                    $this->proxy = $match[2];
769
                    if ($proxy_port) {
770
                        $this->proxy_port = $proxy_port;
771
                    }
772
                }
773
            } elseif ($match[1] == 'http://') {
774
                $this->proxy = $match[2];
775
                if ($proxy_port) {
776
                    $this->proxy_port = $proxy_port;
777
                }
778
            } else {
779
                $this->proxy          = $match[2];
780
                $this->proxy_protocol = 'ssl://';
781
                if ($proxy_port) {
782
                    $this->proxy_port = $proxy_port;
783
                } else {
784
                    $this->proxy_port = 443;
785
                }
786
            }
787
        }
788
    }
789

    
790
    /**
791
     * Change the current debug mode
792
     *
793
     * @param int $in  where 1 = on, 0 = off
794
     *
795
     * @return void
796
     */
797
    function setDebug($in)
798
    {
799
        if ($in) {
800
            $this->debug = 1;
801
        } else {
802
            $this->debug = 0;
803
        }
804
    }
805

    
806
    /**
807
     * Set username and password properties for connecting to the RPC server
808
     *
809
     * @param string $u  the user name
810
     * @param string $p  the password
811
     *
812
     * @return void
813
     *
814
     * @see XML_RPC_Client::$username, XML_RPC_Client::$password
815
     */
816
    function setCredentials($u, $p)
817
    {
818
        $this->username = $u;
819
        $this->password = $p;
820
    }
821

    
822
    /**
823
     * Transmit the RPC request via HTTP 1.0 protocol
824
     *
825
     * @param object $msg       the XML_RPC_Message object
826
     * @param int    $timeout   how many seconds to wait for the request
827
     *
828
     * @return object  an XML_RPC_Response object.  0 is returned if any
829
     *                  problems happen.
830
     *
831
     * @see XML_RPC_Message, XML_RPC_Client::XML_RPC_Client(),
832
     *      XML_RPC_Client::setCredentials()
833
     */
834
    function send($msg, $timeout = 0)
835
    {
836
        if (!is_a($msg, 'XML_RPC_Message')) {
837
            $this->errstr = 'send()\'s $msg parameter must be an'
838
                          . ' XML_RPC_Message object.';
839
            $this->raiseError($this->errstr, XML_RPC_ERROR_PROGRAMMING);
840
            return 0;
841
        }
842
        $msg->debug = $this->debug;
843
        return $this->sendPayloadHTTP10($msg, $this->server, $this->port,
844
                                        $timeout, $this->username,
845
                                        $this->password);
846
    }
847

    
848
    /**
849
     * Transmit the RPC request via HTTP 1.0 protocol
850
     *
851
     * Requests should be sent using XML_RPC_Client send() rather than
852
     * calling this method directly.
853
     *
854
     * @param object $msg       the XML_RPC_Message object
855
     * @param string $server    the server to send the request to
856
     * @param int    $port      the server port send the request to
857
     * @param int    $timeout   how many seconds to wait for the request
858
     *                           before giving up
859
     * @param string $username  a user name for accessing the RPC server
860
     * @param string $password  a password for accessing the RPC server
861
     *
862
     * @return object  an XML_RPC_Response object.  0 is returned if any
863
     *                  problems happen.
864
     *
865
     * @access protected
866
     * @see XML_RPC_Client::send()
867
     */
868
    function sendPayloadHTTP10($msg, $server, $port, $timeout = 0,
869
                               $username = '', $password = '')
870
    {
871
        /*
872
         * If we're using a proxy open a socket to the proxy server
873
         * instead to the xml-rpc server
874
         */
875
        if ($this->proxy) {
876
            if ($this->proxy_protocol == 'http://') {
877
                $protocol = '';
878
            } else {
879
                $protocol = $this->proxy_protocol;
880
            }
881
            if ($timeout > 0) {
882
                $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port,
883
                                 $this->errno, $this->errstr, $timeout);
884
            } else {
885
                $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port,
886
                                 $this->errno, $this->errstr);
887
            }
888
        } else {
889
            if ($this->protocol == 'http://') {
890
                $protocol = '';
891
            } else {
892
                $protocol = $this->protocol;
893
            }
894
            if ($timeout > 0) {
895
                $fp = @fsockopen($protocol . $server, $port,
896
                                 $this->errno, $this->errstr, $timeout);
897
            } else {
898
                $fp = @fsockopen($protocol . $server, $port,
899
                                 $this->errno, $this->errstr);
900
            }
901
        }
902

    
903
        /*
904
         * Just raising the error without returning it is strange,
905
         * but keep it here for backwards compatibility.
906
         */
907
        if (!$fp && $this->proxy) {
908
            $this->raiseError('Connection to proxy server '
909
                              . $this->proxy . ':' . $this->proxy_port
910
                              . ' failed. ' . $this->errstr,
911
                              XML_RPC_ERROR_CONNECTION_FAILED);
912
            return 0;
913
        } elseif (!$fp) {
914
            $this->raiseError('Connection to RPC server '
915
                              . $server . ':' . $port
916
                              . ' failed. ' . $this->errstr,
917
                              XML_RPC_ERROR_CONNECTION_FAILED);
918
            return 0;
919
        }
920

    
921
        if ($timeout) {
922
            /*
923
             * Using socket_set_timeout() because stream_set_timeout()
924
             * was introduced in 4.3.0, but we need to support 4.2.0.
925
             */
926
            socket_set_timeout($fp, $timeout);
927
        }
928

    
929
        // Pre-emptive BC hacks for fools calling sendPayloadHTTP10() directly
930
        if ($username != $this->username) {
931
            $this->setCredentials($username, $password);
932
        }
933

    
934
        // Only create the payload if it was not created previously
935
        if (empty($msg->payload)) {
936
            $msg->createPayload();
937
        }
938
        $this->createHeaders($msg);
939

    
940
        $op  = $this->headers . "\r\n\r\n";
941
        $op .= $msg->payload;
942

    
943
        if (!fputs($fp, $op, strlen($op))) {
944
            $this->errstr = 'Write error';
945
            return 0;
946
        }
947
        $resp = $msg->parseResponseFile($fp);
948

    
949
        $meta = socket_get_status($fp);
950
        if ($meta['timed_out']) {
951
            fclose($fp);
952
            $this->errstr = 'RPC server did not send response before timeout.';
953
            $this->raiseError($this->errstr, XML_RPC_ERROR_CONNECTION_FAILED);
954
            return 0;
955
        }
956

    
957
        fclose($fp);
958
        return $resp;
959
    }
960

    
961
    /**
962
     * Determines the HTTP headers and puts it in the $headers property
963
     *
964
     * @param object $msg       the XML_RPC_Message object
965
     *
966
     * @return boolean  TRUE if okay, FALSE if the message payload isn't set.
967
     *
968
     * @access protected
969
     */
970
    function createHeaders($msg)
971
    {
972
        if (empty($msg->payload)) {
973
            return false;
974
        }
975
        if ($this->proxy) {
976
            $this->headers = 'POST ' . $this->protocol . $this->server;
977
            if ($this->proxy_port) {
978
                $this->headers .= ':' . $this->port;
979
            }
980
        } else {
981
           $this->headers = 'POST ';
982
        }
983
        $this->headers .= $this->path. " HTTP/1.0\r\n";
984

    
985
        $this->headers .= "User-Agent: PEAR XML_RPC\r\n";
986
        $this->headers .= 'Host: ' . $this->server . "\r\n";
987

    
988
        if ($this->proxy && $this->proxy_user) {
989
            $this->headers .= 'Proxy-Authorization: Basic '
990
                     . base64_encode("$this->proxy_user:$this->proxy_pass")
991
                     . "\r\n";
992
        }
993

    
994
        // thanks to Grant Rauscher <grant7@firstworld.net> for this
995
        if ($this->username) {
996
            $this->headers .= 'Authorization: Basic '
997
                     . base64_encode("$this->username:$this->password")
998
                     . "\r\n";
999
        }
1000

    
1001
        $this->headers .= "Content-Type: text/xml\r\n";
1002
        $this->headers .= 'Content-Length: ' . strlen($msg->payload);
1003
        return true;
1004
    }
1005
}
1006

    
1007
/**
1008
 * The methods and properties for interpreting responses to XML RPC requests
1009
 *
1010
 * @category   Web Services
1011
 * @package    XML_RPC
1012
 * @author     Edd Dumbill <edd@usefulinc.com>
1013
 * @author     Stig Bakken <stig@php.net>
1014
 * @author     Martin Jansen <mj@php.net>
1015
 * @author     Daniel Convissor <danielc@php.net>
1016
 * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
1017
 * @version    Release: 1.4.2
1018
 * @link       http://pear.php.net/package/XML_RPC
1019
 */
1020
class XML_RPC_Response extends XML_RPC_Base
1021
{
1022
    var $xv;
1023
    var $fn;
1024
    var $fs;
1025
    var $hdrs;
1026

    
1027
    /**
1028
     * @return void
1029
     */
1030
    function XML_RPC_Response($val, $fcode = 0, $fstr = '')
1031
    {
1032
        if ($fcode != 0) {
1033
            $this->fn = $fcode;
1034
            $this->fs = htmlspecialchars($fstr);
1035
        } else {
1036
            $this->xv = $val;
1037
        }
1038
    }
1039

    
1040
    /**
1041
     * @return int  the error code
1042
     */
1043
    function faultCode()
1044
    {
1045
        if (isset($this->fn)) {
1046
            return $this->fn;
1047
        } else {
1048
            return 0;
1049
        }
1050
    }
1051

    
1052
    /**
1053
     * @return string  the error string
1054
     */
1055
    function faultString()
1056
    {
1057
        return $this->fs;
1058
    }
1059

    
1060
    /**
1061
     * @return mixed  the value
1062
     */
1063
    function value()
1064
    {
1065
        return $this->xv;
1066
    }
1067

    
1068
    /**
1069
     * @return string  the error message in XML format
1070
     */
1071
    function serialize()
1072
    {
1073
        $rs = "<methodResponse>\n";
1074
        if ($this->fn) {
1075
            $rs .= "<fault>
1076
  <value>
1077
    <struct>
1078
      <member>
1079
        <name>faultCode</name>
1080
        <value><int>" . $this->fn . "</int></value>
1081
      </member>
1082
      <member>
1083
        <name>faultString</name>
1084
        <value><string>" . $this->fs . "</string></value>
1085
      </member>
1086
    </struct>
1087
  </value>
1088
</fault>";
1089
        } else {
1090
            $rs .= "<params>\n<param>\n" . $this->xv->serialize() .
1091
        "</param>\n</params>";
1092
        }
1093
        $rs .= "\n</methodResponse>";
1094
        return $rs;
1095
    }
1096
}
1097

    
1098
/**
1099
 * The methods and properties for composing XML RPC messages
1100
 *
1101
 * @category   Web Services
1102
 * @package    XML_RPC
1103
 * @author     Edd Dumbill <edd@usefulinc.com>
1104
 * @author     Stig Bakken <stig@php.net>
1105
 * @author     Martin Jansen <mj@php.net>
1106
 * @author     Daniel Convissor <danielc@php.net>
1107
 * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
1108
 * @version    Release: 1.4.2
1109
 * @link       http://pear.php.net/package/XML_RPC
1110
 */
1111
class XML_RPC_Message extends XML_RPC_Base
1112
{
1113
    /**
1114
     * The current debug mode (1 = on, 0 = off)
1115
     * @var integer
1116
     */
1117
    var $debug = 0;
1118

    
1119
    /**
1120
     * The encoding to be used for outgoing messages
1121
     *
1122
     * Defaults to the value of <var>$GLOBALS['XML_RPC_defencoding']</var>
1123
     *
1124
     * @var string
1125
     * @see XML_RPC_Message::setSendEncoding(),
1126
     *      $GLOBALS['XML_RPC_defencoding'], XML_RPC_Message::xml_header()
1127
     */
1128
    var $send_encoding = '';
1129

    
1130
    /**
1131
     * The method presently being evaluated
1132
     * @var string
1133
     */
1134
    var $methodname = '';
1135

    
1136
    /**
1137
     * @var array
1138
     */
1139
    var $params = array();
1140

    
1141
    /**
1142
     * The XML message being generated
1143
     * @var string
1144
     */
1145
    var $payload = '';
1146

    
1147
    /**
1148
     * @return void
1149
     */
1150
    function XML_RPC_Message($meth, $pars = 0)
1151
    {
1152
        $this->methodname = $meth;
1153
        if (is_array($pars) && sizeof($pars) > 0) {
1154
            for ($i = 0; $i < sizeof($pars); $i++) {
1155
                $this->addParam($pars[$i]);
1156
            }
1157
        }
1158
    }
1159

    
1160
    /**
1161
     * Produces the XML declaration including the encoding attribute
1162
     *
1163
     * The encoding is determined by this class' <var>$send_encoding</var>
1164
     * property.  If the <var>$send_encoding</var> property is not set, use
1165
     * <var>$GLOBALS['XML_RPC_defencoding']</var>.
1166
     *
1167
     * @return string  the XML declaration and <methodCall> element
1168
     *
1169
     * @see XML_RPC_Message::setSendEncoding(),
1170
     *      XML_RPC_Message::$send_encoding, $GLOBALS['XML_RPC_defencoding']
1171
     */
1172
    function xml_header()
1173
    {
1174
        global $XML_RPC_defencoding;
1175
        if (!$this->send_encoding) {
1176
            $this->send_encoding = $XML_RPC_defencoding;
1177
        }
1178
        return '<?xml version="1.0" encoding="' . $this->send_encoding . '"?>'
1179
               . "\n<methodCall>\n";
1180
    }
1181

    
1182
    /**
1183
     * @return string  the closing </methodCall> tag
1184
     */
1185
    function xml_footer()
1186
    {
1187
        return "</methodCall>\n";
1188
    }
1189

    
1190
    /**
1191
     * @return void
1192
     *
1193
     * @uses XML_RPC_Message::xml_header(), XML_RPC_Message::xml_footer()
1194
     */
1195
    function createPayload()
1196
    {
1197
        $this->payload = $this->xml_header();
1198
        $this->payload .= '<methodName>' . $this->methodname . "</methodName>\n";
1199
        $this->payload .= "<params>\n";
1200
        for ($i = 0; $i < sizeof($this->params); $i++) {
1201
            $p = $this->params[$i];
1202
            $this->payload .= "<param>\n" . $p->serialize() . "</param>\n";
1203
        }
1204
        $this->payload .= "</params>\n";
1205
        $this->payload .= $this->xml_footer();
1206
        $this->payload = ereg_replace("[\r\n]+", "\r\n", $this->payload);
1207
    }
1208

    
1209
    /**
1210
     * @return string  the name of the method
1211
     */
1212
    function method($meth = '')
1213
    {
1214
        if ($meth != '') {
1215
            $this->methodname = $meth;
1216
        }
1217
        return $this->methodname;
1218
    }
1219

    
1220
    /**
1221
     * @return string  the payload
1222
     */
1223
    function serialize()
1224
    {
1225
        $this->createPayload();
1226
        return $this->payload;
1227
    }
1228

    
1229
    /**
1230
     * @return void
1231
     */
1232
    function addParam($par)
1233
    {
1234
        $this->params[] = $par;
1235
    }
1236

    
1237
    /**
1238
     * Obtains an XML_RPC_Value object for the given parameter
1239
     *
1240
     * @param int $i  the index number of the parameter to obtain
1241
     *
1242
     * @return object  the XML_RPC_Value object.
1243
     *                  If the parameter doesn't exist, an XML_RPC_Response object.
1244
     *
1245
     * @since Returns XML_RPC_Response object on error since Release 1.3.0
1246
     */
1247
    function getParam($i)
1248
    {
1249
        global $XML_RPC_err, $XML_RPC_str;
1250

    
1251
        if (isset($this->params[$i])) {
1252
            return $this->params[$i];
1253
        } else {
1254
            $this->raiseError('The submitted request did not contain this parameter',
1255
                              XML_RPC_ERROR_INCORRECT_PARAMS);
1256
            return new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'],
1257
                                        $XML_RPC_str['incorrect_params']);
1258
        }
1259
    }
1260

    
1261
    /**
1262
     * @return int  the number of parameters
1263
     */
1264
    function getNumParams()
1265
    {
1266
        return sizeof($this->params);
1267
    }
1268

    
1269
    /**
1270
     * Sets the XML declaration's encoding attribute
1271
     *
1272
     * @param string $type  the encoding type (ISO-8859-1, UTF-8 or US-ASCII)
1273
     *
1274
     * @return void
1275
     *
1276
     * @see XML_RPC_Message::$send_encoding, XML_RPC_Message::xml_header()
1277
     * @since Method available since Release 1.2.0
1278
     */
1279
    function setSendEncoding($type)
1280
    {
1281
        $this->send_encoding = $type;
1282
    }
1283

    
1284
    /**
1285
     * Determine the XML's encoding via the encoding attribute
1286
     * in the XML declaration
1287
     *
1288
     * If the encoding parameter is not set or is not ISO-8859-1, UTF-8
1289
     * or US-ASCII, $XML_RPC_defencoding will be returned.
1290
     *
1291
     * @param string $data  the XML that will be parsed
1292
     *
1293
     * @return string  the encoding to be used
1294
     *
1295
     * @link   http://php.net/xml_parser_create
1296
     * @since  Method available since Release 1.2.0
1297
     */
1298
    function getEncoding($data)
1299
    {
1300
        global $XML_RPC_defencoding;
1301
		$match = "";
1302
        if (preg_match('/<\?xml[^>]*\s*encoding\s*=\s*[\'"]([^"\']*)[\'"]/i',
1303
                       $data, $match))
1304
        {
1305
            $match[1] = trim(strtoupper($match[1]));
1306
            switch ($match[1]) {
1307
                case 'ISO-8859-1':
1308
                case 'UTF-8':
1309
                case 'US-ASCII':
1310
                    return $match[1];
1311
                    break;
1312

    
1313
                default:
1314
                    return $XML_RPC_defencoding;
1315
            }
1316
        } else {
1317
            return $XML_RPC_defencoding;
1318
        }
1319
    }
1320

    
1321
    /**
1322
     * @return object  a new XML_RPC_Response object
1323
     */
1324
    function parseResponseFile($fp)
1325
    {
1326
        $ipd = '';
1327
        while ($data = @fread($fp, 8192)) {
1328
            $ipd .= $data;
1329
        }
1330
        return $this->parseResponse($ipd);
1331
    }
1332

    
1333
    /**
1334
     * @return object  a new XML_RPC_Response object
1335
     */
1336
    function parseResponse($data = '')
1337
    {
1338
        global $XML_RPC_xh, $XML_RPC_err, $XML_RPC_str, $XML_RPC_defencoding;
1339

    
1340
        $encoding = $this->getEncoding($data);
1341
        $parser_resource = xml_parser_create($encoding);
1342
        $parser = (int) $parser_resource;
1343

    
1344
        $XML_RPC_xh = array();
1345
        $XML_RPC_xh[$parser] = array();
1346

    
1347
        $XML_RPC_xh[$parser]['cm'] = 0;
1348
        $XML_RPC_xh[$parser]['isf'] = 0;
1349
        $XML_RPC_xh[$parser]['ac'] = '';
1350
        $XML_RPC_xh[$parser]['qt'] = '';
1351
        $XML_RPC_xh[$parser]['stack'] = array();
1352
        $XML_RPC_xh[$parser]['valuestack'] = array();
1353

    
1354
        xml_parser_set_option($parser_resource, XML_OPTION_CASE_FOLDING, true);
1355
        xml_set_element_handler($parser_resource, 'XML_RPC_se', 'XML_RPC_ee');
1356
        xml_set_character_data_handler($parser_resource, 'XML_RPC_cd');
1357

    
1358
        $hdrfnd = 0;
1359
        if ($this->debug) {
1360
            print "\n<pre>---GOT---\n";
1361
            print isset($_SERVER['SERVER_PROTOCOL']) ? htmlspecialchars($data) : $data;
1362
            print "\n---END---</pre>\n";
1363
        }
1364

    
1365
        // See if response is a 200 or a 100 then a 200, else raise error.
1366
        // But only do this if we're using the HTTP protocol.
1367
        if (ereg('^HTTP', $data) &&
1368
            !ereg('^HTTP/[0-9\.]+ 200 ', $data) &&
1369
            !preg_match('@^HTTP/[0-9\.]+ 10[0-9]([A-Za-z ]+)?[\r\n]+HTTP/[0-9\.]+ 200@', $data))
1370
        {
1371
                $errstr = substr($data, 0, strpos($data, "\n") - 1);
1372
                error_log('HTTP error, got response: ' . $errstr);
1373
                $r = new XML_RPC_Response(0, $XML_RPC_err['http_error'],
1374
                                          $XML_RPC_str['http_error'] . ' (' .
1375
                                          $errstr . ')');
1376
                xml_parser_free($parser_resource);
1377
                return $r;
1378
        }
1379

    
1380
        // gotta get rid of headers here
1381
        if (!$hdrfnd && ($brpos = strpos($data,"\r\n\r\n"))) {
1382
            $XML_RPC_xh[$parser]['ha'] = substr($data, 0, $brpos);
1383
            $data = substr($data, $brpos + 4);
1384
            $hdrfnd = 1;
1385
        }
1386

    
1387
        /*
1388
         * be tolerant of junk after methodResponse
1389
         * (e.g. javascript automatically inserted by free hosts)
1390
         * thanks to Luca Mariano <luca.mariano@email.it>
1391
         */
1392
        $data = substr($data, 0, strpos($data, "</methodResponse>") + 17);
1393

    
1394
        if (!xml_parse($parser_resource, $data, sizeof($data))) {
1395
            // thanks to Peter Kocks <peter.kocks@baygate.com>
1396
            if (xml_get_current_line_number($parser_resource) == 1) {
1397
                $errstr = 'XML error at line 1, check URL';
1398
            } else {
1399
                $errstr = sprintf('XML error: %s at line %d',
1400
                                  xml_error_string(xml_get_error_code($parser_resource)),
1401
                                  xml_get_current_line_number($parser_resource));
1402
            }
1403
            error_log($errstr);
1404
            $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'],
1405
                                      $XML_RPC_str['invalid_return']);
1406
            xml_parser_free($parser_resource);
1407
            return $r;
1408
        }
1409

    
1410
        xml_parser_free($parser_resource);
1411

    
1412
        if ($this->debug) {
1413
            print "\n<pre>---PARSED---\n";
1414
            var_dump($XML_RPC_xh[$parser]['value']);
1415
            print "---END---</pre>\n";
1416
        }
1417

    
1418
        if ($XML_RPC_xh[$parser]['isf'] > 1) {
1419
            $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'],
1420
                                      $XML_RPC_str['invalid_return'].' '.$XML_RPC_xh[$parser]['isf_reason']);
1421
        } elseif (!is_object($XML_RPC_xh[$parser]['value'])) {
1422
            // then something odd has happened
1423
            // and it's time to generate a client side error
1424
            // indicating something odd went on
1425
            $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'],
1426
                                      $XML_RPC_str['invalid_return']);
1427
        } else {
1428
            $v = $XML_RPC_xh[$parser]['value'];
1429
            $allOK=1;
1430
            if ($XML_RPC_xh[$parser]['isf']) {
1431
                $f = $v->structmem('faultCode');
1432
                $fs = $v->structmem('faultString');
1433
                $r = new XML_RPC_Response($v, $f->scalarval(),
1434
                                          $fs->scalarval());
1435
            } else {
1436
                $r = new XML_RPC_Response($v);
1437
            }
1438
        }
1439
        $r->hdrs = split("\r?\n", $XML_RPC_xh[$parser]['ha'][1]);
1440
        return $r;
1441
    }
1442
}
1443

    
1444
/**
1445
 * The methods and properties that represent data in XML RPC format
1446
 *
1447
 * @category   Web Services
1448
 * @package    XML_RPC
1449
 * @author     Edd Dumbill <edd@usefulinc.com>
1450
 * @author     Stig Bakken <stig@php.net>
1451
 * @author     Martin Jansen <mj@php.net>
1452
 * @author     Daniel Convissor <danielc@php.net>
1453
 * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
1454
 * @version    Release: 1.4.2
1455
 * @link       http://pear.php.net/package/XML_RPC
1456
 */
1457
class XML_RPC_Value extends XML_RPC_Base
1458
{
1459
    var $me = array();
1460
    var $mytype = 0;
1461

    
1462
    /**
1463
     * @return void
1464
     */
1465
    function XML_RPC_Value($val = -1, $type = '')
1466
    {
1467
        global $XML_RPC_Types;
1468
        $this->me = array();
1469
        $this->mytype = 0;
1470
        if ($val != -1 || $type != '') {
1471
            if ($type == '') {
1472
                $type = 'string';
1473
            }
1474
            if (!array_key_exists($type, $XML_RPC_Types)) {
1475
                // XXX
1476
                // need some way to report this error
1477
            } elseif ($XML_RPC_Types[$type] == 1) {
1478
                $this->addScalar($val, $type);
1479
            } elseif ($XML_RPC_Types[$type] == 2) {
1480
                $this->addArray($val);
1481
            } elseif ($XML_RPC_Types[$type] == 3) {
1482
                $this->addStruct($val);
1483
            }
1484
        }
1485
    }
1486

    
1487
    /**
1488
     * @return int  returns 1 if successful or 0 if there are problems
1489
     */
1490
    function addScalar($val, $type = 'string')
1491
    {
1492
        global $XML_RPC_Types, $XML_RPC_Boolean;
1493

    
1494
        if ($this->mytype == 1) {
1495
            $this->raiseError('Scalar can have only one value',
1496
                              XML_RPC_ERROR_INVALID_TYPE);
1497
            return 0;
1498
        }
1499
        $typeof = $XML_RPC_Types[$type];
1500
        if ($typeof != 1) {
1501
            $this->raiseError("Not a scalar type (${typeof})",
1502
                              XML_RPC_ERROR_INVALID_TYPE);
1503
            return 0;
1504
        }
1505

    
1506
        if ($type == $XML_RPC_Boolean) {
1507
            if (strcasecmp($val, 'true') == 0
1508
                || $val == 1
1509
                || ($val == true && strcasecmp($val, 'false')))
1510
            {
1511
                $val = 1;
1512
            } else {
1513
                $val = 0;
1514
            }
1515
        }
1516

    
1517
        if ($this->mytype == 2) {
1518
            // we're adding to an array here
1519
            $ar = $this->me['array'];
1520
            $ar[] = new XML_RPC_Value($val, $type);
1521
            $this->me['array'] = $ar;
1522
        } else {
1523
            // a scalar, so set the value and remember we're scalar
1524
            $this->me[$type] = $val;
1525
            $this->mytype = $typeof;
1526
        }
1527
        return 1;
1528
    }
1529

    
1530
    /**
1531
     * @return int  returns 1 if successful or 0 if there are problems
1532
     */
1533
    function addArray($vals)
1534
    {
1535
        global $XML_RPC_Types;
1536
        if ($this->mytype != 0) {
1537
            $this->raiseError(
1538
                    'Already initialized as a [' . $this->kindOf() . ']',
1539
                    XML_RPC_ERROR_ALREADY_INITIALIZED);
1540
            return 0;
1541
        }
1542
        $this->mytype = $XML_RPC_Types['array'];
1543
        $this->me['array'] = $vals;
1544
        return 1;
1545
    }
1546

    
1547
    /**
1548
     * @return int  returns 1 if successful or 0 if there are problems
1549
     */
1550
    function addStruct($vals)
1551
    {
1552
        global $XML_RPC_Types;
1553
        if ($this->mytype != 0) {
1554
            $this->raiseError(
1555
                    'Already initialized as a [' . $this->kindOf() . ']',
1556
                    XML_RPC_ERROR_ALREADY_INITIALIZED);
1557
            return 0;
1558
        }
1559
        $this->mytype = $XML_RPC_Types['struct'];
1560
        $this->me['struct'] = $vals;
1561
        return 1;
1562
    }
1563

    
1564
    /**
1565
     * @return void
1566
     */
1567
    function dump($ar)
1568
    {
1569
        reset($ar);
1570
        foreach ($ar as $key => $val) {
1571
            echo "$key => $val<br />";
1572
            if ($key == 'array') {
1573
                foreach ($val as $key2 => $val2) {
1574
                    echo "-- $key2 => $val2<br />";
1575
                }
1576
            }
1577
        }
1578
    }
1579

    
1580
    /**
1581
     * @return string  the data type of the current value
1582
     */
1583
    function kindOf()
1584
    {
1585
        switch ($this->mytype) {
1586
        case 3:
1587
            return 'struct';
1588

    
1589
        case 2:
1590
            return 'array';
1591

    
1592
        case 1:
1593
            return 'scalar';
1594

    
1595
        default:
1596
            return 'undef';
1597
        }
1598
    }
1599

    
1600
    /**
1601
     * @return string  the data in XML format
1602
     */
1603
    function serializedata($typ, $val)
1604
    {
1605
        $rs = '';
1606
        global $XML_RPC_Types, $XML_RPC_Base64, $XML_RPC_String, $XML_RPC_Boolean;
1607
        if (!array_key_exists($typ, $XML_RPC_Types)) {
1608
            // XXX
1609
            // need some way to report this error
1610
            return;
1611
        }
1612
        switch ($XML_RPC_Types[$typ]) {
1613
        case 3:
1614
            // struct
1615
            $rs .= "<struct>\n";
1616
            reset($val);
1617
            foreach ($val as $key2 => $val2) {
1618
                $rs .= "<member><name>${key2}</name>\n";
1619
                $rs .= $this->serializeval($val2);
1620
                $rs .= "</member>\n";
1621
            }
1622
            $rs .= '</struct>';
1623
            break;
1624

    
1625
        case 2:
1626
            // array
1627
            $rs .= "<array>\n<data>\n";
1628
            for ($i = 0; $i < sizeof($val); $i++) {
1629
                $rs .= $this->serializeval($val[$i]);
1630
            }
1631
            $rs .= "</data>\n</array>";
1632
            break;
1633

    
1634
        case 1:
1635
            switch ($typ) {
1636
            case $XML_RPC_Base64:
1637
                $rs .= "<${typ}>" . base64_encode($val) . "</${typ}>";
1638
                break;
1639
            case $XML_RPC_Boolean:
1640
                $rs .= "<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
1641
                break;
1642
            case $XML_RPC_String:
1643
                $rs .= "<${typ}>" . htmlspecialchars($val). "</${typ}>";
1644
                break;
1645
            default:
1646
                $rs .= "<${typ}>${val}</${typ}>";
1647
            }
1648
        }
1649
        return $rs;
1650
    }
1651

    
1652
    /**
1653
     * @return string  the data in XML format
1654
     */
1655
    function serialize()
1656
    {
1657
        return $this->serializeval($this);
1658
    }
1659

    
1660
    /**
1661
     * @return string  the data in XML format
1662
     */
1663
    function serializeval($o)
1664
    {
1665
        if (!is_object($o) || empty($o->me) || !is_array($o->me)) {
1666
            return '';
1667
        }
1668
        $ar = $o->me;
1669
        reset($ar);
1670
        list($typ, $val) = each($ar);
1671
        return '<value>' .  $this->serializedata($typ, $val) .  "</value>\n";
1672
    }
1673

    
1674
    /**
1675
     * @return mixed  the contents of the element requested
1676
     */
1677
    function structmem($m)
1678
    {
1679
        return $this->me['struct'][$m];
1680
    }
1681

    
1682
    /**
1683
     * @return void
1684
     */
1685
    function structreset()
1686
    {
1687
        reset($this->me['struct']);
1688
    }
1689

    
1690
    /**
1691
     * @return  the key/value pair of the struct's current element
1692
     */
1693
    function structeach()
1694
    {
1695
        return each($this->me['struct']);
1696
    }
1697

    
1698
    /**
1699
     * @return mixed  the current value
1700
     */
1701
    function getval()
1702
    {
1703
        // UNSTABLE
1704
        global $XML_RPC_BOOLEAN, $XML_RPC_Base64;
1705

    
1706
        reset($this->me);
1707
        $b = current($this->me);
1708

    
1709
        // contributed by I Sofer, 2001-03-24
1710
        // add support for nested arrays to scalarval
1711
        // i've created a new method here, so as to
1712
        // preserve back compatibility
1713

    
1714
        if (is_array($b)) {
1715
            foreach ($b as $id => $cont) {
1716
                $b[$id] = $cont->scalarval();
1717
            }
1718
        }
1719

    
1720
        // add support for structures directly encoding php objects
1721
        if (is_object($b)) {
1722
            $t = get_object_vars($b);
1723
            foreach ($t as $id => $cont) {
1724
                $t[$id] = $cont->scalarval();
1725
            }
1726
            foreach ($t as $id => $cont) {
1727
                $b->$id = $cont;
1728
            }
1729
        }
1730

    
1731
        // end contrib
1732
        return $b;
1733
    }
1734

    
1735
    /**
1736
     * @return mixed
1737
     */
1738
    function scalarval()
1739
    {
1740
        global $XML_RPC_Boolean, $XML_RPC_Base64;
1741
        reset($this->me);
1742
        return current($this->me);
1743
    }
1744

    
1745
    /**
1746
     * @return string
1747
     */
1748
    function scalartyp()
1749
    {
1750
        global $XML_RPC_I4, $XML_RPC_Int;
1751
        reset($this->me);
1752
        $a = key($this->me);
1753
        if ($a == $XML_RPC_I4) {
1754
            $a = $XML_RPC_Int;
1755
        }
1756
        return $a;
1757
    }
1758

    
1759
    /**
1760
     * @return mixed  the struct's current element
1761
     */
1762
    function arraymem($m)
1763
    {
1764
        return $this->me['array'][$m];
1765
    }
1766

    
1767
    /**
1768
     * @return int  the number of elements in the array
1769
     */
1770
    function arraysize()
1771
    {
1772
        reset($this->me);
1773
        list($a, $b) = each($this->me);
1774
        return sizeof($b);
1775
    }
1776

    
1777
    /**
1778
     * Determines if the item submitted is an XML_RPC_Value object
1779
     *
1780
     * @param mixed $val  the variable to be evaluated
1781
     *
1782
     * @return bool  TRUE if the item is an XML_RPC_Value object
1783
     *
1784
     * @static
1785
     * @since Method available since Release 1.3.0
1786
     */
1787
    function isValue($val)
1788
    {
1789
        return (strtolower(get_class($val)) == 'xml_rpc_value');
1790
    }
1791
}
1792

    
1793
/**
1794
 * Return an ISO8601 encoded string
1795
 *
1796
 * While timezones ought to be supported, the XML-RPC spec says:
1797
 *
1798
 * "Don't assume a timezone. It should be specified by the server in its
1799
 * documentation what assumptions it makes about timezones."
1800
 *
1801
 * This routine always assumes localtime unless $utc is set to 1, in which
1802
 * case UTC is assumed and an adjustment for locale is made when encoding.
1803
 *
1804
 * @return string  the formatted date
1805
 */
1806
function XML_RPC_iso8601_encode($timet, $utc = 0)
1807
{
1808
    if (!$utc) {
1809
        $t = strftime('%Y%m%dT%H:%M:%S', $timet);
1810
    } else {
1811
        if (function_exists('gmstrftime')) {
1812
            // gmstrftime doesn't exist in some versions
1813
            // of PHP
1814
            $t = gmstrftime('%Y%m%dT%H:%M:%S', $timet);
1815
        } else {
1816
            $t = strftime('%Y%m%dT%H:%M:%S', $timet - date('Z'));
1817
        }
1818
    }
1819
    return $t;
1820
}
1821

    
1822
/**
1823
 * Convert a datetime string into a Unix timestamp
1824
 *
1825
 * While timezones ought to be supported, the XML-RPC spec says:
1826
 *
1827
 * "Don't assume a timezone. It should be specified by the server in its
1828
 * documentation what assumptions it makes about timezones."
1829
 *
1830
 * This routine always assumes localtime unless $utc is set to 1, in which
1831
 * case UTC is assumed and an adjustment for locale is made when encoding.
1832
 *
1833
 * @return int  the unix timestamp of the date submitted
1834
 */
1835
function XML_RPC_iso8601_decode($idate, $utc = 0)
1836
{
1837
    $t = 0;
1838
    $regs = "";
1839
    if (ereg('([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})', $idate, $regs)) {
1840
        if ($utc) {
1841
            $t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
1842
        } else {
1843
            $t = mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
1844
        }
1845
    }
1846
    return $t;
1847
}
1848

    
1849
/**
1850
 * Converts an XML_RPC_Value object into native PHP types
1851
 *
1852
 * @param object $XML_RPC_val  the XML_RPC_Value object to decode
1853
 *
1854
 * @return mixed  the PHP values
1855
 */
1856
function XML_RPC_decode($XML_RPC_val)
1857
{
1858
    $kind = $XML_RPC_val->kindOf();
1859

    
1860
    if ($kind == 'scalar') {
1861
        return $XML_RPC_val->scalarval();
1862

    
1863
    } elseif ($kind == 'array') {
1864
        $size = $XML_RPC_val->arraysize();
1865
        $arr = array();
1866
        for ($i = 0; $i < $size; $i++) {
1867
            $arr[] = XML_RPC_decode($XML_RPC_val->arraymem($i));
1868
        }
1869
        return $arr;
1870

    
1871
    } elseif ($kind == 'struct') {
1872
        $XML_RPC_val->structreset();
1873
        $arr = array();
1874
        while (list($key, $value) = $XML_RPC_val->structeach()) {
1875
            $arr[$key] = XML_RPC_decode($value);
1876
        }
1877
        return $arr;
1878
    }
1879
}
1880

    
1881
/**
1882
 * Converts native PHP types into an XML_RPC_Value object
1883
 *
1884
 * @param mixed $php_val  the PHP value or variable you want encoded
1885
 *
1886
 * @return object  the XML_RPC_Value object
1887
 */
1888
function XML_RPC_encode($php_val)
1889
{
1890
    global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double, $XML_RPC_String,
1891
           $XML_RPC_Array, $XML_RPC_Struct;
1892

    
1893
    $type = gettype($php_val);
1894
    $XML_RPC_val = new XML_RPC_Value;
1895

    
1896
    switch ($type) {
1897
    case 'array':
1898
        if (empty($php_val)) {
1899
            $XML_RPC_val->addArray($php_val);
1900
            break;
1901
        }
1902
        $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
1903
        if (empty($tmp)) {
1904
           $arr = array();
1905
           foreach ($php_val as $k => $v) {
1906
               $arr[$k] = XML_RPC_encode($v);
1907
           }
1908
           $XML_RPC_val->addArray($arr);
1909
           break;
1910
        }
1911
        // fall though if it's not an enumerated array
1912

    
1913
    case 'object':
1914
        $arr = array();
1915
        foreach ($php_val as $k => $v) {
1916
            $arr[$k] = XML_RPC_encode($v);
1917
        }
1918
        $XML_RPC_val->addStruct($arr);
1919
        break;
1920

    
1921
    case 'integer':
1922
        $XML_RPC_val->addScalar($php_val, $XML_RPC_Int);
1923
        break;
1924

    
1925
    case 'double':
1926
        $XML_RPC_val->addScalar($php_val, $XML_RPC_Double);
1927
        break;
1928

    
1929
    case 'string':
1930
    case 'NULL':
1931
        $XML_RPC_val->addScalar($php_val, $XML_RPC_String);
1932
        break;
1933

    
1934
    case 'boolean':
1935
        // Add support for encoding/decoding of booleans, since they
1936
        // are supported in PHP
1937
        // by <G_Giunta_2001-02-29>
1938
        $XML_RPC_val->addScalar($php_val, $XML_RPC_Boolean);
1939
        break;
1940

    
1941
    case 'unknown type':
1942
    default:
1943
        $XML_RPC_val = false;
1944
    }
1945
    return $XML_RPC_val;
1946
}
1947

    
1948
/*
1949
 * Local variables:
1950
 * tab-width: 4
1951
 * c-basic-offset: 4
1952
 * c-hanging-comment-ender-p: nil
1953
 * End:
1954
 */
1955

    
1956
?>
(26-26/27)