Project

General

Profile

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

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

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

    
40

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

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

    
74

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

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

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

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

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

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

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

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

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

    
129

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

    
146

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

    
173

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

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

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

    
192

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

    
199

    
200
/**
201
 * Stores state during parsing
202
 *
203
 * quick explanation of components:
204
 *   + st     = builds up a string for evaluation
205
 *   + ac     = accumulates values
206
 *   + qt     = decides if quotes are needed for evaluation
207
 *   + cm     = denotes struct or array (comma needed)
208
 *   + isf    = indicates a fault
209
 *   + lv     = indicates "looking for a value": implements the logic
210
 *               to allow values with no types to be strings
211
 *   + params = stores parameters in method calls
212
 *   + method = stores method name
213
 *
214
 * @global array $GLOBALS['XML_RPC_xh']
215
 */
216
$GLOBALS['XML_RPC_xh'] = array();
217

    
218

    
219
/**
220
 * Start element handler for the XML parser
221
 *
222
 * @return void
223
 */
224
function XML_RPC_se($parser_resource, $name, $attrs)
225
{
226
    global $XML_RPC_xh, $XML_RPC_DateTime, $XML_RPC_String;
227
    $parser = (int) $parser_resource;
228

    
229
    switch ($name) {
230
    case 'STRUCT':
231
    case 'ARRAY':
232
        $XML_RPC_xh[$parser]['st'] .= 'array(';
233
        $XML_RPC_xh[$parser]['cm']++;
234
        // this last line turns quoting off
235
        // this means if we get an empty array we'll
236
        // simply get a bit of whitespace in the eval
237
        $XML_RPC_xh[$parser]['qt'] = 0;
238
        break;
239

    
240
    case 'NAME':
241
        $XML_RPC_xh[$parser]['st'] .= '"';
242
        $XML_RPC_xh[$parser]['ac'] = '';
243
        break;
244

    
245
    case 'FAULT':
246
        $XML_RPC_xh[$parser]['isf'] = 1;
247
        break;
248

    
249
    case 'PARAM':
250
        $XML_RPC_xh[$parser]['st'] = '';
251
        break;
252

    
253
    case 'VALUE':
254
        $XML_RPC_xh[$parser]['st'] .= 'new XML_RPC_Value(';
255
        $XML_RPC_xh[$parser]['lv'] = 1;
256
        $XML_RPC_xh[$parser]['vt'] = $XML_RPC_String;
257
        $XML_RPC_xh[$parser]['ac'] = '';
258
        $XML_RPC_xh[$parser]['qt'] = 0;
259
        // look for a value: if this is still 1 by the
260
        // time we reach the first data segment then the type is string
261
        // by implication and we need to add in a quote
262
        break;
263

    
264
    case 'I4':
265
    case 'INT':
266
    case 'STRING':
267
    case 'BOOLEAN':
268
    case 'DOUBLE':
269
    case 'DATETIME.ISO8601':
270
    case 'BASE64':
271
        $XML_RPC_xh[$parser]['ac'] = ''; // reset the accumulator
272

    
273
        if ($name == 'DATETIME.ISO8601' || $name == 'STRING') {
274
            $XML_RPC_xh[$parser]['qt'] = 1;
275

    
276
            if ($name == 'DATETIME.ISO8601') {
277
                $XML_RPC_xh[$parser]['vt'] = $XML_RPC_DateTime;
278
            }
279

    
280
        } elseif ($name == 'BASE64') {
281
            $XML_RPC_xh[$parser]['qt'] = 2;
282
        } else {
283
            // No quoting is required here -- but
284
            // at the end of the element we must check
285
            // for data format errors.
286
            $XML_RPC_xh[$parser]['qt'] = 0;
287
        }
288
        break;
289

    
290
    case 'MEMBER':
291
        $XML_RPC_xh[$parser]['ac'] = '';
292
    }
293

    
294
    if ($name != 'VALUE') {
295
        $XML_RPC_xh[$parser]['lv'] = 0;
296
    }
297
}
298

    
299
/**
300
 * End element handler for the XML parser
301
 *
302
 * @return void
303
 */
304
function XML_RPC_ee($parser_resource, $name)
305
{
306
    global $XML_RPC_xh, $XML_RPC_Types, $XML_RPC_String;
307
    $parser = (int) $parser_resource;
308

    
309
    switch ($name) {
310
    case 'STRUCT':
311
    case 'ARRAY':
312
        if ($XML_RPC_xh[$parser]['cm']
313
            && substr($XML_RPC_xh[$parser]['st'], -1) == ',')
314
        {
315
            $XML_RPC_xh[$parser]['st'] = substr($XML_RPC_xh[$parser]['st'], 0, -1);
316
        }
317

    
318
        $XML_RPC_xh[$parser]['st'] .= ')';
319
        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
320
        $XML_RPC_xh[$parser]['cm']--;
321
        break;
322

    
323
    case 'NAME':
324
        $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'] . '" => ';
325
        break;
326

    
327
    case 'BOOLEAN':
328
        // special case here: we translate boolean 1 or 0 into PHP
329
        // constants true or false
330
        if ($XML_RPC_xh[$parser]['ac'] == '1') {
331
            $XML_RPC_xh[$parser]['ac'] = 'true';
332
        } else {
333
            $XML_RPC_xh[$parser]['ac'] = 'false';
334
        }
335

    
336
        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
337
        // Drop through intentionally.
338

    
339
    case 'I4':
340
    case 'INT':
341
    case 'STRING':
342
    case 'DOUBLE':
343
    case 'DATETIME.ISO8601':
344
    case 'BASE64':
345
        if ($XML_RPC_xh[$parser]['qt'] == 1) {
346
            // we use double quotes rather than single so backslashification works OK
347
            $XML_RPC_xh[$parser]['st'] .= '"' . $XML_RPC_xh[$parser]['ac'] . '"';
348
        } elseif ($XML_RPC_xh[$parser]['qt'] == 2) {
349
            $XML_RPC_xh[$parser]['st'] .= 'base64_decode("'
350
                                        . $XML_RPC_xh[$parser]['ac'] . '")';
351
        } elseif ($name == 'BOOLEAN') {
352
            $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'];
353
        } else {
354
            // we have an I4, INT or a DOUBLE
355
            // we must check that only 0123456789-.<space> are characters here
356
            if (!ereg("^[+-]?[0123456789 \t\.]+$", $XML_RPC_xh[$parser]['ac'])) {
357
                XML_RPC_Base::raiseError('Non-numeric value received in INT or DOUBLE',
358
                                         XML_RPC_ERROR_NON_NUMERIC_FOUND);
359
                $XML_RPC_xh[$parser]['st'] .= 'XML_RPC_ERROR_NON_NUMERIC_FOUND';
360
            } else {
361
                // it's ok, add it on
362
                $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'];
363
            }
364
        }
365

    
366
        $XML_RPC_xh[$parser]['ac'] = '';
367
        $XML_RPC_xh[$parser]['qt'] = 0;
368
        $XML_RPC_xh[$parser]['lv'] = 3; // indicate we've found a value
369
        break;
370

    
371
    case 'VALUE':
372
        // deal with a string value
373
        if (strlen($XML_RPC_xh[$parser]['ac']) > 0 &&
374
            $XML_RPC_xh[$parser]['vt'] == $XML_RPC_String) {
375

    
376
            $XML_RPC_xh[$parser]['st'] .= '"' . $XML_RPC_xh[$parser]['ac'] . '"';
377
        }
378

    
379
        // This if () detects if no scalar was inside <VALUE></VALUE>
380
        // and pads an empty "".
381
        if ($XML_RPC_xh[$parser]['st'][strlen($XML_RPC_xh[$parser]['st'])-1] == '(') {
382
            $XML_RPC_xh[$parser]['st'] .= '""';
383
        }
384
        $XML_RPC_xh[$parser]['st'] .= ", '" . $XML_RPC_xh[$parser]['vt'] . "')";
385
        if ($XML_RPC_xh[$parser]['cm']) {
386
            $XML_RPC_xh[$parser]['st'] .= ',';
387
        }
388
        break;
389

    
390
    case 'MEMBER':
391
        $XML_RPC_xh[$parser]['ac'] = '';
392
        $XML_RPC_xh[$parser]['qt'] = 0;
393
        break;
394

    
395
    case 'DATA':
396
        $XML_RPC_xh[$parser]['ac'] = '';
397
        $XML_RPC_xh[$parser]['qt'] = 0;
398
        break;
399

    
400
    case 'PARAM':
401
        $XML_RPC_xh[$parser]['params'][] = $XML_RPC_xh[$parser]['st'];
402
        break;
403

    
404
    case 'METHODNAME':
405
    case 'RPCMETHODNAME':
406
        $XML_RPC_xh[$parser]['method'] = ereg_replace("^[\n\r\t ]+", '',
407
                                                      $XML_RPC_xh[$parser]['ac']);
408
        break;
409
    }
410

    
411
    // if it's a valid type name, set the type
412
    if (isset($XML_RPC_Types[strtolower($name)])) {
413
        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
414
    }
415
}
416

    
417
/**
418
 * Character data handler for the XML parser
419
 *
420
 * @return void
421
 */
422
function XML_RPC_cd($parser_resource, $data)
423
{
424
    global $XML_RPC_xh, $XML_RPC_backslash;
425
    $parser = (int) $parser_resource;
426

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

    
431
        if ($XML_RPC_xh[$parser]['lv'] == 1) {
432
            // if we've found text and we're just in a <value> then
433
            // turn quoting on, as this will be a string
434
            $XML_RPC_xh[$parser]['qt'] = 1;
435
            // and say we've found a value
436
            $XML_RPC_xh[$parser]['lv'] = 2;
437
        }
438

    
439
        // replace characters that eval would
440
        // do special things with
441
        if (!isset($XML_RPC_xh[$parser]['ac'])) {
442
            $XML_RPC_xh[$parser]['ac'] = '';
443
        }
444
        $XML_RPC_xh[$parser]['ac'] .= str_replace('$', '\$',
445
            str_replace('"', '\"', str_replace(chr(92),
446
            $XML_RPC_backslash, $data)));
447
    }
448
}
449

    
450
/**
451
 * The common methods and properties for all of the XML_RPC classes
452
 *
453
 * @category   Web Services
454
 * @package    XML_RPC
455
 * @author     Edd Dumbill <edd@usefulinc.com>
456
 * @author     Stig Bakken <stig@php.net>
457
 * @author     Martin Jansen <mj@php.net>
458
 * @author     Daniel Convissor <danielc@php.net>
459
 * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
460
 * @version    Release: 1.3.1
461
 * @link       http://pear.php.net/package/XML_RPC
462
 */
463
class XML_RPC_Base {
464

    
465
    /**
466
     * PEAR Error handling
467
     *
468
     * @return object  PEAR_Error object
469
     */
470
    function raiseError($msg, $code)
471
    {
472
        include_once 'PEAR.php';
473
        if (is_object(@$this)) {
474
            return PEAR::raiseError(get_class($this) . ': ' . $msg, $code);
475
        } else {
476
            return PEAR::raiseError('XML_RPC: ' . $msg, $code);
477
        }
478
    }
479

    
480
    /**
481
     * Tell whether something is a PEAR_Error object
482
     *
483
     * @param mixed $value  the item to check
484
     *
485
     * @return bool  whether $value is a PEAR_Error object or not
486
     *
487
     * @access public
488
     */
489
    function isError($value)
490
    {
491
        return is_a($value, 'PEAR_Error');
492
    }
493
}
494

    
495
/**
496
 * The methods and properties for submitting XML RPC requests
497
 *
498
 * @category   Web Services
499
 * @package    XML_RPC
500
 * @author     Edd Dumbill <edd@usefulinc.com>
501
 * @author     Stig Bakken <stig@php.net>
502
 * @author     Martin Jansen <mj@php.net>
503
 * @author     Daniel Convissor <danielc@php.net>
504
 * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
505
 * @version    Release: 1.3.1
506
 * @link       http://pear.php.net/package/XML_RPC
507
 */
508
class XML_RPC_Client extends XML_RPC_Base {
509

    
510
    /**
511
     * The path and name of the RPC server script you want the request to go to
512
     * @var string
513
     */
514
    var $path = '';
515

    
516
    /**
517
     * The name of the remote server to connect to
518
     * @var string
519
     */
520
    var $server = '';
521

    
522
    /**
523
     * The protocol to use in contacting the remote server
524
     * @var string
525
     */
526
    var $protocol = 'http://';
527

    
528
    /**
529
     * The port for connecting to the remote server
530
     *
531
     * The default is 80 for http:// connections
532
     * and 443 for https:// and ssl:// connections.
533
     *
534
     * @var integer
535
     */
536
    var $port = 80;
537

    
538
    /**
539
     * A user name for accessing the RPC server
540
     * @var string
541
     * @see XML_RPC_Client::setCredentials()
542
     */
543
    var $username = '';
544

    
545
    /**
546
     * A password for accessing the RPC server
547
     * @var string
548
     * @see XML_RPC_Client::setCredentials()
549
     */
550
    var $password = '';
551

    
552
    /**
553
     * The name of the proxy server to use, if any
554
     * @var string
555
     */
556
    var $proxy = '';
557

    
558
    /**
559
     * The protocol to use in contacting the proxy server, if any
560
     * @var string
561
     */
562
    var $proxy_protocol = 'http://';
563

    
564
    /**
565
     * The port for connecting to the proxy server
566
     *
567
     * The default is 8080 for http:// connections
568
     * and 443 for https:// and ssl:// connections.
569
     *
570
     * @var integer
571
     */
572
    var $proxy_port = 8080;
573

    
574
    /**
575
     * A user name for accessing the proxy server
576
     * @var string
577
     */
578
    var $proxy_user = '';
579

    
580
    /**
581
     * A password for accessing the proxy server
582
     * @var string
583
     */
584
    var $proxy_pass = '';
585

    
586
    /**
587
     * The error number, if any
588
     * @var integer
589
     */
590
    var $errno = 0;
591

    
592
    /**
593
     * The error message, if any
594
     * @var string
595
     */
596
    var $errstring = '';
597

    
598
    /**
599
     * The current debug mode (1 = on, 0 = off)
600
     * @var integer
601
     */
602
    var $debug = 0;
603

    
604
    /**
605
     * The HTTP headers for the current request.
606
     * @var string
607
     */
608
    var $headers = '';
609

    
610

    
611
    /**
612
     * Sets the object's properties
613
     *
614
     * @param string  $path        the path and name of the RPC server script
615
     *                              you want the request to go to
616
     * @param string  $server      the URL of the remote server to connect to.
617
     *                              If this parameter doesn't specify a
618
     *                              protocol and $port is 443, ssl:// is
619
     *                              assumed.
620
     * @param integer $port        a port for connecting to the remote server.
621
     *                              Defaults to 80 for http:// connections and
622
     *                              443 for https:// and ssl:// connections.
623
     * @param string  $proxy       the URL of the proxy server to use, if any.
624
     *                              If this parameter doesn't specify a
625
     *                              protocol and $port is 443, ssl:// is
626
     *                              assumed.
627
     * @param integer $proxy_port  a port for connecting to the remote server.
628
     *                              Defaults to 8080 for http:// connections and
629
     *                              443 for https:// and ssl:// connections.
630
     * @param string  $proxy_user  a user name for accessing the proxy server
631
     * @param string  $proxy_pass  a password for accessing the proxy server
632
     *
633
     * @return void
634
     */
635
    function XML_RPC_Client($path, $server, $port = 0,
636
                            $proxy = '', $proxy_port = 0,
637
                            $proxy_user = '', $proxy_pass = '')
638
    {
639
        $this->path       = $path;
640
        $this->proxy_user = $proxy_user;
641
        $this->proxy_pass = $proxy_pass;
642

    
643
        preg_match('@^(http://|https://|ssl://)?(.*)$@', $server, $match);
644
        if ($match[1] == '') {
645
            if ($port == 443) {
646
                $this->server   = $match[2];
647
                $this->protocol = 'ssl://';
648
                $this->port     = 443;
649
            } else {
650
                $this->server = $match[2];
651
                if ($port) {
652
                    $this->port = $port;
653
                }
654
            }
655
        } elseif ($match[1] == 'http://') {
656
            $this->server = $match[2];
657
            if ($port) {
658
                $this->port = $port;
659
            }
660
        } else {
661
            $this->server   = $match[2];
662
            $this->protocol = 'ssl://';
663
            if ($port) {
664
                $this->port = $port;
665
            } else {
666
                $this->port = 443;
667
            }
668
        }
669

    
670
        if ($proxy) {
671
            preg_match('@^(http://|https://|ssl://)?(.*)$@', $proxy, $match);
672
            if ($match[1] == '') {
673
                if ($proxy_port == 443) {
674
                    $this->proxy          = $match[2];
675
                    $this->proxy_protocol = 'ssl://';
676
                    $this->proxy_port     = 443;
677
                } else {
678
                    $this->proxy = $match[2];
679
                    if ($proxy_port) {
680
                        $this->proxy_port = $proxy_port;
681
                    }
682
                }
683
            } elseif ($match[1] == 'http://') {
684
                $this->proxy = $match[2];
685
                if ($proxy_port) {
686
                    $this->proxy_port = $proxy_port;
687
                }
688
            } else {
689
                $this->proxy          = $match[2];
690
                $this->proxy_protocol = 'ssl://';
691
                if ($proxy_port) {
692
                    $this->proxy_port = $proxy_port;
693
                } else {
694
                    $this->proxy_port = 443;
695
                }
696
            }
697
        }
698
    }
699

    
700
    /**
701
     * Change the current debug mode
702
     *
703
     * @param int $in  where 1 = on, 0 = off
704
     *
705
     * @return void
706
     */
707
    function setDebug($in)
708
    {
709
        if ($in) {
710
            $this->debug = 1;
711
        } else {
712
            $this->debug = 0;
713
        }
714
    }
715

    
716
    /**
717
     * Set username and password properties for connecting to the RPC server
718
     *
719
     * @param string $u  the user name
720
     * @param string $p  the password
721
     *
722
     * @return void
723
     *
724
     * @see XML_RPC_Client::$username, XML_RPC_Client::$password
725
     */
726
    function setCredentials($u, $p)
727
    {
728
        $this->username = $u;
729
        $this->password = $p;
730
    }
731

    
732
    /**
733
     * Transmit the RPC request via HTTP 1.0 protocol
734
     *
735
     * @param object $msg       the XML_RPC_Message object
736
     * @param int    $timeout   how many seconds to wait for the request
737
     *
738
     * @return object  an XML_RPC_Response object.  0 is returned if any
739
     *                  problems happen.
740
     *
741
     * @see XML_RPC_Message, XML_RPC_Client::XML_RPC_Client(),
742
     *      XML_RPC_Client::setCredentials()
743
     */
744
    function send($msg, $timeout = 0)
745
    {
746
        if (strtolower(get_class($msg)) != 'xml_rpc_message') {
747
            $this->errstr = 'send()\'s $msg parameter must be an'
748
                          . ' XML_RPC_Message object.';
749
            $this->raiseError($this->errstr, XML_RPC_ERROR_PROGRAMMING);
750
            return 0;
751
        }
752
        $msg->debug = $this->debug;
753
        return $this->sendPayloadHTTP10($msg, $this->server, $this->port,
754
                                        $timeout, $this->username,
755
                                        $this->password);
756
    }
757

    
758
    /**
759
     * Transmit the RPC request via HTTP 1.0 protocol
760
     *
761
     * Requests should be sent using XML_RPC_Client send() rather than
762
     * calling this method directly.
763
     *
764
     * @param object $msg       the XML_RPC_Message object
765
     * @param string $server    the server to send the request to
766
     * @param int    $port      the server port send the request to
767
     * @param int    $timeout   how many seconds to wait for the request
768
     *                           before giving up
769
     * @param string $username  a user name for accessing the RPC server
770
     * @param string $password  a password for accessing the RPC server
771
     *
772
     * @return object  an XML_RPC_Response object.  0 is returned if any
773
     *                  problems happen.
774
     *
775
     * @access protected
776
     * @see XML_RPC_Client::send()
777
     */
778
    function sendPayloadHTTP10($msg, $server, $port, $timeout = 0,
779
                               $username = '', $password = '')
780
    {
781
        /*
782
         * If we're using a proxy open a socket to the proxy server
783
         * instead to the xml-rpc server
784
         */
785
        if ($this->proxy) {
786
            if ($this->proxy_protocol == 'http://') {
787
                $protocol = '';
788
            } else {
789
                $protocol = $this->proxy_protocol;
790
            }
791
            if ($timeout > 0) {
792
                $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port,
793
                                 $this->errno, $this->errstr, $timeout);
794
            } else {
795
                $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port,
796
                                 $this->errno, $this->errstr);
797
            }
798
        } else {
799
            if ($this->protocol == 'http://') {
800
                $protocol = '';
801
            } else {
802
                $protocol = $this->protocol;
803
            }
804
            if ($timeout > 0) {
805
                $fp = @fsockopen($protocol . $server, $port,
806
                                 $this->errno, $this->errstr, $timeout);
807
            } else {
808
                $fp = @fsockopen($protocol . $server, $port,
809
                                 $this->errno, $this->errstr);
810
            }
811
        }
812

    
813
        /*
814
         * Just raising the error without returning it is strange,
815
         * but keep it here for backwards compatibility.
816
         */
817
        if (!$fp && $this->proxy) {
818
            $this->raiseError('Connection to proxy server '
819
                              . $this->proxy . ':' . $this->proxy_port
820
                              . ' failed. ' . $this->errstr,
821
                              XML_RPC_ERROR_CONNECTION_FAILED);
822
            return 0;
823
        } elseif (!$fp) {
824
            $this->raiseError('Connection to RPC server '
825
                              . $server . ':' . $port
826
                              . ' failed. ' . $this->errstr,
827
                              XML_RPC_ERROR_CONNECTION_FAILED);
828
            return 0;
829
        }
830

    
831
        if ($timeout) {
832
            stream_set_timeout($fp, $timeout);
833
        }
834

    
835
        // Pre-emptive BC hacks for fools calling sendPayloadHTTP10() directly
836
        if ($username != $this->username) {
837
            $this->setCredentials($username, $password);
838
        }
839

    
840
        // Only create the payload if it was not created previously
841
        if (empty($msg->payload)) {
842
            $msg->createPayload();
843
        }
844
        $this->createHeaders($msg);
845

    
846
        $op  = $this->headers . "\r\n\r\n";
847
        $op .= $msg->payload;
848

    
849
        if (!fputs($fp, $op, strlen($op))) {
850
            $this->errstr = 'Write error';
851
            return 0;
852
        }
853
        $resp = $msg->parseResponseFile($fp);
854

    
855
        $meta = stream_get_meta_data($fp);
856
        if ($meta['timed_out']) {
857
            fclose($fp);
858
            $this->errstr = 'RPC server did not send response before timeout.';
859
            $this->raiseError($this->errstr, XML_RPC_ERROR_CONNECTION_FAILED);
860
            return 0;
861
        }
862

    
863
        fclose($fp);
864
        return $resp;
865
    }
866

    
867
    /**
868
     * Determines the HTTP headers and puts it in the $headers property
869
     *
870
     * @param object $msg       the XML_RPC_Message object
871
     *
872
     * @return boolean  TRUE if okay, FALSE if the message payload isn't set.
873
     *
874
     * @access protected
875
     */
876
    function createHeaders($msg)
877
    {
878
        if (empty($msg->payload)) {
879
            return false;
880
        }
881
        if ($this->proxy) {
882
            $this->headers = 'POST ' . $this->protocol . $this->server;
883
            if ($this->proxy_port) {
884
                $this->headers .= ':' . $this->port;
885
            }
886
        } else {
887
           $this->headers = 'POST ';
888
        }
889
        $this->headers .= $this->path. " HTTP/1.0\r\n";
890
        
891
        $this->headers .= "User-Agent: PEAR XML_RPC\r\n";
892
        $this->headers .= 'Host: ' . $this->server . "\r\n";
893

    
894
        if ($this->proxy && $this->proxy_user) {
895
            $this->headers .= 'Proxy-Authorization: Basic '
896
                     . base64_encode("$this->proxy_user:$this->proxy_pass")
897
                     . "\r\n";
898
        }
899

    
900
        // thanks to Grant Rauscher <grant7@firstworld.net> for this
901
        if ($this->username) {
902
            $this->headers .= 'Authorization: Basic '
903
                     . base64_encode("$this->username:$this->password")
904
                     . "\r\n";
905
        }
906

    
907
        $this->headers .= "Content-Type: text/xml\r\n";
908
        $this->headers .= 'Content-Length: ' . strlen($msg->payload);
909
        return true;
910
    }
911
}
912

    
913
/**
914
 * The methods and properties for interpreting responses to XML RPC requests
915
 *
916
 * @category   Web Services
917
 * @package    XML_RPC
918
 * @author     Edd Dumbill <edd@usefulinc.com>
919
 * @author     Stig Bakken <stig@php.net>
920
 * @author     Martin Jansen <mj@php.net>
921
 * @author     Daniel Convissor <danielc@php.net>
922
 * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
923
 * @version    Release: 1.3.1
924
 * @link       http://pear.php.net/package/XML_RPC
925
 */
926
class XML_RPC_Response extends XML_RPC_Base
927
{
928
    var $xv;
929
    var $fn;
930
    var $fs;
931
    var $hdrs;
932

    
933
    /**
934
     * @return void
935
     */
936
    function XML_RPC_Response($val, $fcode = 0, $fstr = '')
937
    {
938
        if ($fcode != 0) {
939
            $this->fn = $fcode;
940
            $this->fs = htmlspecialchars($fstr);
941
        } else {
942
            $this->xv = $val;
943
        }
944
    }
945

    
946
    /**
947
     * @return int  the error code
948
     */
949
    function faultCode()
950
    {
951
        if (isset($this->fn)) {
952
            return $this->fn;
953
        } else {
954
            return 0;
955
        }
956
    }
957

    
958
    /**
959
     * @return string  the error string
960
     */
961
    function faultString()
962
    {
963
        return $this->fs;
964
    }
965

    
966
    /**
967
     * @return mixed  the value
968
     */
969
    function value()
970
    {
971
        return $this->xv;
972
    }
973

    
974
    /**
975
     * @return string  the error message in XML format
976
     */
977
    function serialize()
978
    {
979
        $rs = "<methodResponse>\n";
980
        if ($this->fn) {
981
            $rs .= "<fault>
982
  <value>
983
    <struct>
984
      <member>
985
        <name>faultCode</name>
986
        <value><int>" . $this->fn . "</int></value>
987
      </member>
988
      <member>
989
        <name>faultString</name>
990
        <value><string>" . $this->fs . "</string></value>
991
      </member>
992
    </struct>
993
  </value>
994
</fault>";
995
        } else {
996
            $rs .= "<params>\n<param>\n" . $this->xv->serialize() .
997
        "</param>\n</params>";
998
        }
999
        $rs .= "\n</methodResponse>";
1000
        return $rs;
1001
    }
1002
}
1003

    
1004
/**
1005
 * The methods and properties for composing XML RPC messages
1006
 *
1007
 * @category   Web Services
1008
 * @package    XML_RPC
1009
 * @author     Edd Dumbill <edd@usefulinc.com>
1010
 * @author     Stig Bakken <stig@php.net>
1011
 * @author     Martin Jansen <mj@php.net>
1012
 * @author     Daniel Convissor <danielc@php.net>
1013
 * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
1014
 * @version    Release: 1.3.1
1015
 * @link       http://pear.php.net/package/XML_RPC
1016
 */
1017
class XML_RPC_Message extends XML_RPC_Base
1018
{
1019
    /**
1020
     * The current debug mode (1 = on, 0 = off)
1021
     * @var integer
1022
     */
1023
    var $debug = 0;
1024

    
1025
    /**
1026
     * The encoding to be used for outgoing messages
1027
     *
1028
     * Defaults to the value of <var>$GLOBALS['XML_RPC_defencoding']</var>
1029
     *
1030
     * @var string
1031
     * @see XML_RPC_Message::setSendEncoding(),
1032
     *      $GLOBALS['XML_RPC_defencoding'], XML_RPC_Message::xml_header()
1033
     */
1034
    var $send_encoding = '';
1035

    
1036
    /**
1037
     * The method presently being evaluated
1038
     * @var string
1039
     */
1040
    var $methodname = '';
1041

    
1042
    /**
1043
     * @var array
1044
     */
1045
    var $params = array();
1046

    
1047
    /**
1048
     * The XML message being generated
1049
     * @var string
1050
     */
1051
    var $payload = '';
1052

    
1053
    /**
1054
     * @return void
1055
     */
1056
    function XML_RPC_Message($meth, $pars = 0)
1057
    {
1058
        $this->methodname = $meth;
1059
        if (is_array($pars) && sizeof($pars) > 0) {
1060
            for ($i = 0; $i < sizeof($pars); $i++) {
1061
                $this->addParam($pars[$i]);
1062
            }
1063
        }
1064
    }
1065

    
1066
    /**
1067
     * Produces the XML declaration including the encoding attribute
1068
     *
1069
     * The encoding is determined by this class' <var>$send_encoding</var>
1070
     * property.  If the <var>$send_encoding</var> property is not set, use
1071
     * <var>$GLOBALS['XML_RPC_defencoding']</var>.
1072
     *
1073
     * @return string  the XML declaration and <methodCall> element
1074
     *
1075
     * @see XML_RPC_Message::setSendEncoding(),
1076
     *      XML_RPC_Message::$send_encoding, $GLOBALS['XML_RPC_defencoding']
1077
     */
1078
    function xml_header()
1079
    {
1080
        global $XML_RPC_defencoding;
1081
        if (!$this->send_encoding) {
1082
            $this->send_encoding = $XML_RPC_defencoding;
1083
        }
1084
        return '<?xml version="1.0" encoding="' . $this->send_encoding . '"?>'
1085
               . "\n<methodCall>\n";
1086
    }
1087

    
1088
    /**
1089
     * @return string  the closing </methodCall> tag
1090
     */
1091
    function xml_footer()
1092
    {
1093
        return "</methodCall>\n";
1094
    }
1095

    
1096
    /**
1097
     * @return void
1098
     *
1099
     * @uses XML_RPC_Message::xml_header(), XML_RPC_Message::xml_footer()
1100
     */
1101
    function createPayload()
1102
    {
1103
        $this->payload = $this->xml_header();
1104
        $this->payload .= '<methodName>' . $this->methodname . "</methodName>\n";
1105
        $this->payload .= "<params>\n";
1106
        for ($i = 0; $i < sizeof($this->params); $i++) {
1107
            $p = $this->params[$i];
1108
            $this->payload .= "<param>\n" . $p->serialize() . "</param>\n";
1109
        }
1110
        $this->payload .= "</params>\n";
1111
        $this->payload .= $this->xml_footer();
1112
        $this->payload = ereg_replace("[\r\n]+", "\r\n", $this->payload);
1113
    }
1114

    
1115
    /**
1116
     * @return string  the name of the method
1117
     */
1118
    function method($meth = '')
1119
    {
1120
        if ($meth != '') {
1121
            $this->methodname = $meth;
1122
        }
1123
        return $this->methodname;
1124
    }
1125

    
1126
    /**
1127
     * @return string  the payload
1128
     */
1129
    function serialize()
1130
    {
1131
        $this->createPayload();
1132
        return $this->payload;
1133
    }
1134

    
1135
    /**
1136
     * @return void
1137
     */
1138
    function addParam($par)
1139
    {
1140
        $this->params[] = $par;
1141
    }
1142

    
1143
    /**
1144
     * Obtains an XML_RPC_Value object for the given parameter
1145
     *
1146
     * @param int $i  the index number of the parameter to obtain
1147
     *
1148
     * @return object  the XML_RPC_Value object.
1149
     *                  If the parameter doesn't exist, an XML_RPC_Response object.
1150
     *
1151
     * @since Returns XML_RPC_Response object on error since Release 1.3.0
1152
     */
1153
    function getParam($i)
1154
    {
1155
        global $XML_RPC_err, $XML_RPC_str;
1156

    
1157
        if (isset($this->params[$i])) {
1158
            return $this->params[$i];
1159
        } else {
1160
            $this->raiseError('The submitted request did not contain this parameter',
1161
                              XML_RPC_ERROR_INCORRECT_PARAMS);
1162
            return new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'],
1163
                                        $XML_RPC_str['incorrect_params']);
1164
        }
1165
    }
1166

    
1167
    /**
1168
     * @return int  the number of parameters
1169
     */
1170
    function getNumParams()
1171
    {
1172
        return sizeof($this->params);
1173
    }
1174

    
1175
    /**
1176
     * Sets the XML declaration's encoding attribute
1177
     *
1178
     * @param string $type  the encoding type (ISO-8859-1, UTF-8 or US-ASCII)
1179
     *
1180
     * @return void
1181
     *
1182
     * @see XML_RPC_Message::$send_encoding, XML_RPC_Message::xml_header()
1183
     * @since Method available since Release 1.2.0
1184
     */
1185
    function setSendEncoding($type)
1186
    {
1187
        $this->send_encoding = $type;
1188
    }
1189

    
1190
    /**
1191
     * Determine the XML's encoding via the encoding attribute
1192
     * in the XML declaration
1193
     *
1194
     * If the encoding parameter is not set or is not ISO-8859-1, UTF-8
1195
     * or US-ASCII, $XML_RPC_defencoding will be returned.
1196
     *
1197
     * @param string $data  the XML that will be parsed
1198
     *
1199
     * @return string  the encoding to be used
1200
     *
1201
     * @link   http://php.net/xml_parser_create
1202
     * @since  Method available since Release 1.2.0
1203
     */
1204
    function getEncoding($data)
1205
    {
1206
        global $XML_RPC_defencoding;
1207

    
1208
        if (preg_match('/<\?xml[^>]*\s*encoding\s*=\s*[\'"]([^"\']*)[\'"]/i',
1209
                       $data, $match))
1210
        {
1211
            $match[1] = trim(strtoupper($match[1]));
1212
            switch ($match[1]) {
1213
                case 'ISO-8859-1':
1214
                case 'UTF-8':
1215
                case 'US-ASCII':
1216
                    return $match[1];
1217
                    break;
1218

    
1219
                default:
1220
                    return $XML_RPC_defencoding;
1221
            }
1222
        } else {
1223
            return $XML_RPC_defencoding;
1224
        }
1225
    }
1226

    
1227
    /**
1228
     * @return object  a new XML_RPC_Response object
1229
     */
1230
    function parseResponseFile($fp)
1231
    {
1232
        $ipd = '';
1233
        while ($data = @fread($fp, 8192)) {
1234
            $ipd .= $data;
1235
        }
1236
        return $this->parseResponse($ipd);
1237
    }
1238

    
1239
    /**
1240
     * @return object  a new XML_RPC_Response object
1241
     */
1242
    function parseResponse($data = '')
1243
    {
1244
        global $XML_RPC_xh, $XML_RPC_err, $XML_RPC_str, $XML_RPC_defencoding;
1245

    
1246
        $encoding = $this->getEncoding($data);
1247
        $parser_resource = xml_parser_create($encoding);
1248
        $parser = (int) $parser_resource;
1249

    
1250
        $XML_RPC_xh[$parser] = array();
1251

    
1252
        $XML_RPC_xh[$parser]['st'] = '';
1253
        $XML_RPC_xh[$parser]['cm'] = 0;
1254
        $XML_RPC_xh[$parser]['isf'] = 0;
1255
        $XML_RPC_xh[$parser]['ac'] = '';
1256
        $XML_RPC_xh[$parser]['qt'] = '';
1257

    
1258
        xml_parser_set_option($parser_resource, XML_OPTION_CASE_FOLDING, true);
1259
        xml_set_element_handler($parser_resource, 'XML_RPC_se', 'XML_RPC_ee');
1260
        xml_set_character_data_handler($parser_resource, 'XML_RPC_cd');
1261

    
1262
        $hdrfnd = 0;
1263
        if ($this->debug) {
1264
            print "<PRE>---GOT---\n";
1265
            print isset($_SERVER['SERVER_PROTOCOL']) ? htmlspecialchars($data) : $data;
1266
            print "\n---END---\n</PRE>";
1267
        }
1268

    
1269
        // See if response is a 200 or a 100 then a 200, else raise error.
1270
        // But only do this if we're using the HTTP protocol.
1271
        if (ereg('^HTTP', $data) &&
1272
            !ereg('^HTTP/[0-9\.]+ 200 ', $data) &&
1273
            !preg_match('@^HTTP/[0-9\.]+ 10[0-9]([A-Za-z ]+)?[\r\n]+HTTP/[0-9\.]+ 200@', $data))
1274
        {
1275
                $errstr = substr($data, 0, strpos($data, "\n") - 1);
1276
                //error_log('HTTP error, got response: ' . $errstr);
1277
                $r = new XML_RPC_Response(0, $XML_RPC_err['http_error'],
1278
                                          $XML_RPC_str['http_error'] . ' (' .
1279
                                          $errstr . ')');
1280
                xml_parser_free($parser_resource);
1281
                return $r;
1282
        }
1283
        // gotta get rid of headers here
1284

    
1285

    
1286
        if (!$hdrfnd && ($brpos = strpos($data,"\r\n\r\n"))) {
1287
            $XML_RPC_xh[$parser]['ha'] = substr($data, 0, $brpos);
1288
            $data = substr($data, $brpos + 4);
1289
            $hdrfnd = 1;
1290
        }
1291

    
1292
        /*
1293
         * be tolerant of junk after methodResponse
1294
         * (e.g. javascript automatically inserted by free hosts)
1295
         * thanks to Luca Mariano <luca.mariano@email.it>
1296
         */
1297
        $data = substr($data, 0, strpos($data, "</methodResponse>") + 17);
1298

    
1299
        if (!xml_parse($parser_resource, $data, sizeof($data))) {
1300
            // thanks to Peter Kocks <peter.kocks@baygate.com>
1301
            if (xml_get_current_line_number($parser_resource) == 1) {
1302
                $errstr = 'XML error at line 1, check URL';
1303
            } else {
1304
                $errstr = sprintf('XML error: %s at line %d',
1305
                                  xml_error_string(xml_get_error_code($parser_resource)),
1306
                                  xml_get_current_line_number($parser_resource));
1307
            }
1308
            //error_log($errstr);
1309
            $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'],
1310
                                      $XML_RPC_str['invalid_return']);
1311
            xml_parser_free($parser_resource);
1312
            return $r;
1313
        }
1314
        xml_parser_free($parser_resource);
1315
        if ($this->debug) {
1316
            print '<PRE>---EVALING---[' .
1317
            strlen($XML_RPC_xh[$parser]['st']) . " chars]---\n" .
1318
            htmlspecialchars($XML_RPC_xh[$parser]['st']) . ";\n---END---</PRE>";
1319
        }
1320
        if (strlen($XML_RPC_xh[$parser]['st']) == 0) {
1321
            // then something odd has happened
1322
            // and it's time to generate a client side error
1323
            // indicating something odd went on
1324
            $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'],
1325
                                      $XML_RPC_str['invalid_return']);
1326
        } else {
1327
            eval('$v=' . $XML_RPC_xh[$parser]['st'] . '; $allOK=1;');
1328
            if ($XML_RPC_xh[$parser]['isf']) {
1329
                $f = $v->structmem('faultCode');
1330
                $fs = $v->structmem('faultString');
1331
                $r = new XML_RPC_Response($v, $f->scalarval(),
1332
                                          $fs->scalarval());
1333
            } else {
1334
                $r = new XML_RPC_Response($v);
1335
            }
1336
        }
1337
        $r->hdrs = split("\r?\n", $XML_RPC_xh[$parser]['ha'][1]);
1338
        return $r;
1339
    }
1340
}
1341

    
1342
/**
1343
 * The methods and properties that represent data in XML RPC format
1344
 *
1345
 * @category   Web Services
1346
 * @package    XML_RPC
1347
 * @author     Edd Dumbill <edd@usefulinc.com>
1348
 * @author     Stig Bakken <stig@php.net>
1349
 * @author     Martin Jansen <mj@php.net>
1350
 * @author     Daniel Convissor <danielc@php.net>
1351
 * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
1352
 * @version    Release: 1.3.1
1353
 * @link       http://pear.php.net/package/XML_RPC
1354
 */
1355
class XML_RPC_Value extends XML_RPC_Base
1356
{
1357
    var $me = array();
1358
    var $mytype = 0;
1359

    
1360
    /**
1361
     * @return void
1362
     */
1363
    function XML_RPC_Value($val = -1, $type = '')
1364
    {
1365
        global $XML_RPC_Types;
1366
        $this->me = array();
1367
        $this->mytype = 0;
1368
        if ($val != -1 || $type != '') {
1369
            if ($type == '') {
1370
                $type = 'string';
1371
            }
1372
            if (!array_key_exists($type, $XML_RPC_Types)) {
1373
                // XXX
1374
                // need some way to report this error
1375
            } elseif ($XML_RPC_Types[$type] == 1) {
1376
                $this->addScalar($val, $type);
1377
            } elseif ($XML_RPC_Types[$type] == 2) {
1378
                $this->addArray($val);
1379
            } elseif ($XML_RPC_Types[$type] == 3) {
1380
                $this->addStruct($val);
1381
            }
1382
        }
1383
    }
1384

    
1385
    /**
1386
     * @return int  returns 1 if successful or 0 if there are problems
1387
     */
1388
    function addScalar($val, $type = 'string')
1389
    {
1390
        global $XML_RPC_Types, $XML_RPC_Boolean;
1391

    
1392
        if ($this->mytype == 1) {
1393
            $this->raiseError('Scalar can have only one value',
1394
                              XML_RPC_ERROR_INVALID_TYPE);
1395
            return 0;
1396
        }
1397
        $typeof = $XML_RPC_Types[$type];
1398
        if ($typeof != 1) {
1399
            $this->raiseError("Not a scalar type (${typeof})",
1400
                              XML_RPC_ERROR_INVALID_TYPE);
1401
            return 0;
1402
        }
1403

    
1404
        if ($type == $XML_RPC_Boolean) {
1405
            if (strcasecmp($val, 'true') == 0
1406
                || $val == 1
1407
                || ($val == true && strcasecmp($val, 'false')))
1408
            {
1409
                $val = 1;
1410
            } else {
1411
                $val = 0;
1412
            }
1413
        }
1414

    
1415
        if ($this->mytype == 2) {
1416
            // we're adding to an array here
1417
            $ar = $this->me['array'];
1418
            $ar[] = new XML_RPC_Value($val, $type);
1419
            $this->me['array'] = $ar;
1420
        } else {
1421
            // a scalar, so set the value and remember we're scalar
1422
            $this->me[$type] = $val;
1423
            $this->mytype = $typeof;
1424
        }
1425
        return 1;
1426
    }
1427

    
1428
    /**
1429
     * @return int  returns 1 if successful or 0 if there are problems
1430
     */
1431
    function addArray($vals)
1432
    {
1433
        global $XML_RPC_Types;
1434
        if ($this->mytype != 0) {
1435
            $this->raiseError(
1436
                    'Already initialized as a [' . $this->kindOf() . ']',
1437
                    XML_RPC_ERROR_ALREADY_INITIALIZED);
1438
            return 0;
1439
        }
1440
        $this->mytype = $XML_RPC_Types['array'];
1441
        $this->me['array'] = $vals;
1442
        return 1;
1443
    }
1444

    
1445
    /**
1446
     * @return int  returns 1 if successful or 0 if there are problems
1447
     */
1448
    function addStruct($vals)
1449
    {
1450
        global $XML_RPC_Types;
1451
        if ($this->mytype != 0) {
1452
            $this->raiseError(
1453
                    'Already initialized as a [' . $this->kindOf() . ']',
1454
                    XML_RPC_ERROR_ALREADY_INITIALIZED);
1455
            return 0;
1456
        }
1457
        $this->mytype = $XML_RPC_Types['struct'];
1458
        $this->me['struct'] = $vals;
1459
        return 1;
1460
    }
1461

    
1462
    /**
1463
     * @return void
1464
     */
1465
    function dump($ar)
1466
    {
1467
        reset($ar);
1468
        foreach ($ar as $key => $val) {
1469
            echo "$key => $val<br />";
1470
            if ($key == 'array') {
1471
                foreach ($val as $key2 => $val2) {
1472
                    echo "-- $key2 => $val2<br />";
1473
                }
1474
            }
1475
        }
1476
    }
1477

    
1478
    /**
1479
     * @return string  the data type of the current value
1480
     */
1481
    function kindOf()
1482
    {
1483
        switch ($this->mytype) {
1484
        case 3:
1485
            return 'struct';
1486

    
1487
        case 2:
1488
            return 'array';
1489

    
1490
        case 1:
1491
            return 'scalar';
1492

    
1493
        default:
1494
            return 'undef';
1495
        }
1496
    }
1497

    
1498
    /**
1499
     * @return string  the data in XML format
1500
     */
1501
    function serializedata($typ, $val)
1502
    {
1503
        $rs = '';
1504
        global $XML_RPC_Types, $XML_RPC_Base64, $XML_RPC_String, $XML_RPC_Boolean;
1505
        if (!array_key_exists($typ, $XML_RPC_Types)) {
1506
            // XXX
1507
            // need some way to report this error
1508
            return;
1509
        }
1510
        switch ($XML_RPC_Types[$typ]) {
1511
        case 3:
1512
            // struct
1513
            $rs .= "<struct>\n";
1514
            reset($val);
1515
            foreach ($val as $key2 => $val2) {
1516
                $rs .= "<member><name>${key2}</name>\n";
1517
                $rs .= $this->serializeval($val2);
1518
                $rs .= "</member>\n";
1519
            }
1520
            $rs .= '</struct>';
1521
            break;
1522

    
1523
        case 2:
1524
            // array
1525
            $rs .= "<array>\n<data>\n";
1526
            for ($i = 0; $i < sizeof($val); $i++) {
1527
                $rs .= $this->serializeval($val[$i]);
1528
            }
1529
            $rs .= "</data>\n</array>";
1530
            break;
1531

    
1532
        case 1:
1533
            switch ($typ) {
1534
            case $XML_RPC_Base64:
1535
                $rs .= "<${typ}>" . base64_encode($val) . "</${typ}>";
1536
                break;
1537
            case $XML_RPC_Boolean:
1538
                $rs .= "<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
1539
                break;
1540
            case $XML_RPC_String:
1541
                $rs .= "<${typ}>" . htmlspecialchars($val). "</${typ}>";
1542
                break;
1543
            default:
1544
                $rs .= "<${typ}>${val}</${typ}>";
1545
            }
1546
        }
1547
        return $rs;
1548
    }
1549

    
1550
    /**
1551
     * @return string  the data in XML format
1552
     */
1553
    function serialize()
1554
    {
1555
        return $this->serializeval($this);
1556
    }
1557

    
1558
    /**
1559
     * @return string  the data in XML format
1560
     */
1561
    function serializeval($o)
1562
    {
1563
        $rs = '';
1564
        $ar = $o->me;
1565
        reset($ar);
1566
        list($typ, $val) = each($ar);
1567
        $rs .= '<value>';
1568
        $rs .= $this->serializedata($typ, $val);
1569
        $rs .= "</value>\n";
1570
        return $rs;
1571
    }
1572

    
1573
    /**
1574
     * @return mixed  the contents of the element requested
1575
     */
1576
    function structmem($m)
1577
    {
1578
        return $this->me['struct'][$m];
1579
    }
1580

    
1581
    /**
1582
     * @return void
1583
     */
1584
    function structreset()
1585
    {
1586
        reset($this->me['struct']);
1587
    }
1588

    
1589
    /**
1590
     * @return  the key/value pair of the struct's current element
1591
     */
1592
    function structeach()
1593
    {
1594
        return each($this->me['struct']);
1595
    }
1596

    
1597
    /**
1598
     * @return mixed  the current value
1599
     */
1600
    function getval()
1601
    {
1602
        // UNSTABLE
1603
        global $XML_RPC_BOOLEAN, $XML_RPC_Base64;
1604

    
1605
        reset($this->me);
1606
        $b = current($this->me);
1607

    
1608
        // contributed by I Sofer, 2001-03-24
1609
        // add support for nested arrays to scalarval
1610
        // i've created a new method here, so as to
1611
        // preserve back compatibility
1612

    
1613
        if (is_array($b)) {
1614
            foreach ($b as $id => $cont) {
1615
                $b[$id] = $cont->scalarval();
1616
            }
1617
        }
1618

    
1619
        // add support for structures directly encoding php objects
1620
        if (is_object($b)) {
1621
            $t = get_object_vars($b);
1622
            foreach ($t as $id => $cont) {
1623
                $t[$id] = $cont->scalarval();
1624
            }
1625
            foreach ($t as $id => $cont) {
1626
                eval('$b->'.$id.' = $cont;');
1627
            }
1628
        }
1629

    
1630
        // end contrib
1631
        return $b;
1632
    }
1633

    
1634
    /**
1635
     * @return mixed
1636
     */
1637
    function scalarval()
1638
    {
1639
        global $XML_RPC_Boolean, $XML_RPC_Base64;
1640
        reset($this->me);
1641
        return current($this->me);
1642
    }
1643

    
1644
    /**
1645
     * @return string
1646
     */
1647
    function scalartyp()
1648
    {
1649
        global $XML_RPC_I4, $XML_RPC_Int;
1650
        reset($this->me);
1651
        $a = key($this->me);
1652
        if ($a == $XML_RPC_I4) {
1653
            $a = $XML_RPC_Int;
1654
        }
1655
        return $a;
1656
    }
1657

    
1658
    /**
1659
     * @return mixed  the struct's current element
1660
     */
1661
    function arraymem($m)
1662
    {
1663
        return $this->me['array'][$m];
1664
    }
1665

    
1666
    /**
1667
     * @return int  the number of elements in the array
1668
     */
1669
    function arraysize()
1670
    {
1671
        reset($this->me);
1672
        list($a, $b) = each($this->me);
1673
        return sizeof($b);
1674
    }
1675

    
1676
    /**
1677
     * Determines if the item submitted is an XML_RPC_Value object
1678
     *
1679
     * @param mixed $val  the variable to be evaluated
1680
     *
1681
     * @return bool  TRUE if the item is an XML_RPC_Value object
1682
     *
1683
     * @static
1684
     * @since Method available since Release 1.3.0
1685
     */
1686
    function isValue($val)
1687
    {
1688
        return (strtolower(get_class($val)) == 'xml_rpc_value');
1689
    }
1690
}
1691

    
1692
/**
1693
 * Return an ISO8601 encoded string
1694
 *
1695
 * While timezones ought to be supported, the XML-RPC spec says:
1696
 *
1697
 * "Don't assume a timezone. It should be specified by the server in its
1698
 * documentation what assumptions it makes about timezones."
1699
 *
1700
 * This routine always assumes localtime unless $utc is set to 1, in which
1701
 * case UTC is assumed and an adjustment for locale is made when encoding.
1702
 *
1703
 * @return string  the formatted date
1704
 */
1705
function XML_RPC_iso8601_encode($timet, $utc = 0)
1706
{
1707
    if (!$utc) {
1708
        $t = strftime('%Y%m%dT%H:%M:%S', $timet);
1709
    } else {
1710
        if (function_exists('gmstrftime')) {
1711
            // gmstrftime doesn't exist in some versions
1712
            // of PHP
1713
            $t = gmstrftime('%Y%m%dT%H:%M:%S', $timet);
1714
        } else {
1715
            $t = strftime('%Y%m%dT%H:%M:%S', $timet - date('Z'));
1716
        }
1717
    }
1718
    return $t;
1719
}
1720

    
1721
/**
1722
 * Convert a datetime string into a Unix timestamp
1723
 *
1724
 * While timezones ought to be supported, the XML-RPC spec says:
1725
 *
1726
 * "Don't assume a timezone. It should be specified by the server in its
1727
 * documentation what assumptions it makes about timezones."
1728
 *
1729
 * This routine always assumes localtime unless $utc is set to 1, in which
1730
 * case UTC is assumed and an adjustment for locale is made when encoding.
1731
 *
1732
 * @return int  the unix timestamp of the date submitted
1733
 */
1734
function XML_RPC_iso8601_decode($idate, $utc = 0)
1735
{
1736
    $t = 0;
1737
    if (ereg('([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})', $idate, $regs)) {
1738
        if ($utc) {
1739
            $t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
1740
        } else {
1741
            $t = mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
1742
        }
1743
    }
1744
    return $t;
1745
}
1746

    
1747
/**
1748
 * Converts an XML_RPC_Value object into native PHP types
1749
 *
1750
 * @param object $XML_RPC_val  the XML_RPC_Value object to decode
1751
 *
1752
 * @return mixed  the PHP values
1753
 */
1754
function XML_RPC_decode($XML_RPC_val)
1755
{
1756
    $kind = $XML_RPC_val->kindOf();
1757

    
1758
    if ($kind == 'scalar') {
1759
        return $XML_RPC_val->scalarval();
1760

    
1761
    } elseif ($kind == 'array') {
1762
        $size = $XML_RPC_val->arraysize();
1763
        $arr = array();
1764
        for ($i = 0; $i < $size; $i++) {
1765
            $arr[] = XML_RPC_decode($XML_RPC_val->arraymem($i));
1766
        }
1767
        return $arr;
1768

    
1769
    } elseif ($kind == 'struct') {
1770
        $XML_RPC_val->structreset();
1771
        $arr = array();
1772
        while (list($key, $value) = $XML_RPC_val->structeach()) {
1773
            $arr[$key] = XML_RPC_decode($value);
1774
        }
1775
        return $arr;
1776
    }
1777
}
1778

    
1779
/**
1780
 * Converts native PHP types into an XML_RPC_Value object
1781
 *
1782
 * @param mixed $php_val  the PHP value or variable you want encoded
1783
 *
1784
 * @return object  the XML_RPC_Value object
1785
 */
1786
function XML_RPC_encode($php_val)
1787
{
1788
    global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double, $XML_RPC_String,
1789
           $XML_RPC_Array, $XML_RPC_Struct;
1790

    
1791
    $type = gettype($php_val);
1792
    $XML_RPC_val = new XML_RPC_Value;
1793

    
1794
    switch ($type) {
1795
    case 'array':
1796
        if (empty($php_val)) {
1797
            $XML_RPC_val->addArray($php_val);
1798
            break;
1799
        }
1800
        $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
1801
        if (empty($tmp)) {
1802
           $arr = array();
1803
           foreach ($php_val as $k => $v) {
1804
               $arr[$k] = XML_RPC_encode($v);
1805
           }
1806
           $XML_RPC_val->addArray($arr);
1807
           break;
1808
        }
1809
        // fall though if it's not an enumerated array
1810

    
1811
    case 'object':
1812
        $arr = array();
1813
        foreach ($php_val as $k => $v) {
1814
            $arr[$k] = XML_RPC_encode($v);
1815
        }
1816
        $XML_RPC_val->addStruct($arr);
1817
        break;
1818

    
1819
    case 'integer':
1820
        $XML_RPC_val->addScalar($php_val, $XML_RPC_Int);
1821
        break;
1822

    
1823
    case 'double':
1824
        $XML_RPC_val->addScalar($php_val, $XML_RPC_Double);
1825
        break;
1826

    
1827
    case 'string':
1828
    case 'NULL':
1829
        $XML_RPC_val->addScalar($php_val, $XML_RPC_String);
1830
        break;
1831

    
1832
    case 'boolean':
1833
        // Add support for encoding/decoding of booleans, since they
1834
        // are supported in PHP
1835
        // by <G_Giunta_2001-02-29>
1836
        $XML_RPC_val->addScalar($php_val, $XML_RPC_Boolean);
1837
        break;
1838

    
1839
    case 'unknown type':
1840
    default:
1841
        $XML_RPC_val = false;
1842
    }
1843
    return $XML_RPC_val;
1844
}
1845

    
1846
/*
1847
 * Local variables:
1848
 * tab-width: 4
1849
 * c-basic-offset: 4
1850
 * c-hanging-comment-ender-p: nil
1851
 * End:
1852
 */
1853

    
1854
?>
(22-22/23)