Project

General

Profile

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

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

    
6
/**
7
 * Server commands for our 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
/**
42
 * Pull in the XML_RPC class
43
 */
44
require_once 'xmlrpc_client.inc';
45

    
46

    
47
/**
48
 * signature for system.listMethods: return = array,
49
 * parameters = a string or nothing
50
 * @global array $GLOBALS['XML_RPC_Server_listMethods_sig']
51
 */
52
$GLOBALS['XML_RPC_Server_listMethods_sig'] = array(
53
    array($GLOBALS['XML_RPC_Array'],
54
          $GLOBALS['XML_RPC_String']
55
    ),
56
    array($GLOBALS['XML_RPC_Array'])
57
);
58

    
59
/**
60
 * docstring for system.listMethods
61
 * @global string $GLOBALS['XML_RPC_Server_listMethods_doc']
62
 */
63
$GLOBALS['XML_RPC_Server_listMethods_doc'] = 'This method lists all the'
64
        . ' methods that the XML-RPC server knows how to dispatch';
65

    
66
/**
67
 * signature for system.methodSignature: return = array,
68
 * parameters = string
69
 * @global array $GLOBALS['XML_RPC_Server_methodSignature_sig']
70
 */
71
$GLOBALS['XML_RPC_Server_methodSignature_sig'] = array(
72
    array($GLOBALS['XML_RPC_Array'],
73
          $GLOBALS['XML_RPC_String']
74
    )
75
);
76

    
77
/**
78
 * docstring for system.methodSignature
79
 * @global string $GLOBALS['XML_RPC_Server_methodSignature_doc']
80
 */
81
$GLOBALS['XML_RPC_Server_methodSignature_doc'] = 'Returns an array of known'
82
        . ' signatures (an array of arrays) for the method name passed. If'
83
        . ' no signatures are known, returns a none-array (test for type !='
84
        . ' array to detect missing signature)';
85

    
86
/**
87
 * signature for system.methodHelp: return = string,
88
 * parameters = string
89
 * @global array $GLOBALS['XML_RPC_Server_methodHelp_sig']
90
 */
91
$GLOBALS['XML_RPC_Server_methodHelp_sig'] = array(
92
    array($GLOBALS['XML_RPC_String'],
93
          $GLOBALS['XML_RPC_String']
94
    )
95
);
96

    
97
/**
98
 * docstring for methodHelp
99
 * @global string $GLOBALS['XML_RPC_Server_methodHelp_doc']
100
 */
101
$GLOBALS['XML_RPC_Server_methodHelp_doc'] = 'Returns help text if defined'
102
        . ' for the method passed, otherwise returns an empty string';
103

    
104
/**
105
 * dispatch map for the automatically declared XML-RPC methods.
106
 * @global array $GLOBALS['XML_RPC_Server_dmap']
107
 */
108
$GLOBALS['XML_RPC_Server_dmap'] = array(
109
    'system.listMethods' => array(
110
        'function'  => 'XML_RPC_Server_listMethods',
111
        'signature' => $GLOBALS['XML_RPC_Server_listMethods_sig'],
112
        'docstring' => $GLOBALS['XML_RPC_Server_listMethods_doc']
113
    ),
114
    'system.methodHelp' => array(
115
        'function'  => 'XML_RPC_Server_methodHelp',
116
        'signature' => $GLOBALS['XML_RPC_Server_methodHelp_sig'],
117
        'docstring' => $GLOBALS['XML_RPC_Server_methodHelp_doc']
118
    ),
119
    'system.methodSignature' => array(
120
        'function'  => 'XML_RPC_Server_methodSignature',
121
        'signature' => $GLOBALS['XML_RPC_Server_methodSignature_sig'],
122
        'docstring' => $GLOBALS['XML_RPC_Server_methodSignature_doc']
123
    )
124
);
125

    
126
/**
127
 * @global string $GLOBALS['XML_RPC_Server_debuginfo']
128
 */
129
$GLOBALS['XML_RPC_Server_debuginfo'] = '';
130

    
131

    
132
/**
133
 * Lists all the methods that the XML-RPC server knows how to dispatch
134
 *
135
 * @return object  a new XML_RPC_Response object
136
 */
137
function XML_RPC_Server_listMethods($server, $m)
138
{
139
    global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
140

    
141
    $v = new XML_RPC_Value();
142
    $outAr = array();
143
    foreach ($server->dmap as $key => $val) {
144
        $outAr[] = new XML_RPC_Value($key, 'string');
145
    }
146
    foreach ($XML_RPC_Server_dmap as $key => $val) {
147
        $outAr[] = new XML_RPC_Value($key, 'string');
148
    }
149
    $v->addArray($outAr);
150
    return new XML_RPC_Response($v);
151
}
152

    
153
/**
154
 * Returns an array of known signatures (an array of arrays)
155
 * for the given method
156
 *
157
 * If no signatures are known, returns a none-array
158
 * (test for type != array to detect missing signature)
159
 *
160
 * @return object  a new XML_RPC_Response object
161
 */
162
function XML_RPC_Server_methodSignature($server, $m)
163
{
164
    global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
165

    
166
    $methName = $m->getParam(0);
167
    $methName = $methName->scalarval();
168
    if (strpos($methName, 'system.') === 0) {
169
        $dmap = $XML_RPC_Server_dmap;
170
        $sysCall = 1;
171
    } else {
172
        $dmap = $server->dmap;
173
        $sysCall = 0;
174
    }
175
    //  print "<!-- ${methName} -->\n";
176
    if (isset($dmap[$methName])) {
177
        if ($dmap[$methName]['signature']) {
178
            $sigs = array();
179
            $thesigs = $dmap[$methName]['signature'];
180
            for ($i = 0; $i < sizeof($thesigs); $i++) {
181
                $cursig = array();
182
                $inSig = $thesigs[$i];
183
                for ($j = 0; $j < sizeof($inSig); $j++) {
184
                    $cursig[] = new XML_RPC_Value($inSig[$j], 'string');
185
                }
186
                $sigs[] = new XML_RPC_Value($cursig, 'array');
187
            }
188
            $r = new XML_RPC_Response(new XML_RPC_Value($sigs, 'array'));
189
        } else {
190
            $r = new XML_RPC_Response(new XML_RPC_Value('undef', 'string'));
191
        }
192
    } else {
193
        $r = new XML_RPC_Response(0, $XML_RPC_err['introspect_unknown'],
194
                                  $XML_RPC_str['introspect_unknown']);
195
    }
196
    return $r;
197
}
198

    
199
/**
200
 * Returns help text if defined for the method passed, otherwise returns
201
 * an empty string
202
 *
203
 * @return object  a new XML_RPC_Response object
204
 */
205
function XML_RPC_Server_methodHelp($server, $m)
206
{
207
    global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
208

    
209
    $methName = $m->getParam(0);
210
    $methName = $methName->scalarval();
211
    if (strpos($methName, 'system.') === 0) {
212
        $dmap = $XML_RPC_Server_dmap;
213
        $sysCall = 1;
214
    } else {
215
        $dmap = $server->dmap;
216
        $sysCall = 0;
217
    }
218
    //  print "<!-- ${methName} -->\n";
219
    if (isset($dmap[$methName])) {
220
        if ($dmap[$methName]['docstring']) {
221
            $r = new XML_RPC_Response(new XML_RPC_Value($dmap[$methName]['docstring']),
222
                                                        'string');
223
        } else {
224
            $r = new XML_RPC_Response(new XML_RPC_Value('', 'string'));
225
        }
226
    } else {
227
        $r = new XML_RPC_Response(0, $XML_RPC_err['introspect_unknown'],
228
                                     $XML_RPC_str['introspect_unknown']);
229
    }
230
    return $r;
231
}
232

    
233
/**
234
 * @return void
235
 */
236
function XML_RPC_Server_debugmsg($m)
237
{
238
    global $XML_RPC_Server_debuginfo;
239
    $XML_RPC_Server_debuginfo = $XML_RPC_Server_debuginfo . $m . "\n";
240
}
241

    
242

    
243
/**
244
 * A server for receiving and replying to XML RPC requests
245
 *
246
 * <code>
247
 * $server = new XML_RPC_Server(
248
 *     array(
249
 *         'isan8' =>
250
 *             array(
251
 *                 'function' => 'is_8',
252
 *                 'signature' =>
253
 *                      array(
254
 *                          array('boolean', 'int'),
255
 *                          array('boolean', 'int', 'boolean'),
256
 *                          array('boolean', 'string'),
257
 *                          array('boolean', 'string', 'boolean'),
258
 *                      ),
259
 *                 'docstring' => 'Is the value an 8?'
260
 *             ),
261
 *     ),
262
 *     1,
263
 *     0
264
 * ); 
265
 * </code>
266
 *
267
 * @category   Web Services
268
 * @package    XML_RPC
269
 * @author     Edd Dumbill <edd@usefulinc.com>
270
 * @author     Stig Bakken <stig@php.net>
271
 * @author     Martin Jansen <mj@php.net>
272
 * @author     Daniel Convissor <danielc@php.net>
273
 * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
274
 * @version    Release: 1.3.1
275
 * @link       http://pear.php.net/package/XML_RPC
276
 */
277
class XML_RPC_Server
278
{
279
    /**
280
     * The dispatch map, listing the methods this server provides.
281
     * @var array
282
     */
283
    var $dmap = array();
284

    
285
    /**
286
     * The present response's encoding
287
     * @var string
288
     * @see XML_RPC_Message::getEncoding()
289
     */
290
    var $encoding = '';
291

    
292
    /**
293
     * Debug mode (0 = off, 1 = on)
294
     * @var integer
295
     */
296
    var $debug = 0;
297

    
298
    /**
299
     * The response's HTTP headers
300
     * @var string
301
     */
302
    var $server_headers = '';
303

    
304
    /**
305
     * The response's XML payload
306
     * @var string
307
     */
308
    var $server_payload = '';
309

    
310

    
311
    /**
312
     * Constructor for the XML_RPC_Server class
313
     *
314
     * @param array $dispMap   the dispatch map. An associative array
315
     *                          explaining each function. The keys of the main
316
     *                          array are the procedure names used by the
317
     *                          clients. The value is another associative array
318
     *                          that contains up to three elements:
319
     *                            + The 'function' element's value is the name
320
     *                              of the function or method that gets called.
321
     *                              To define a class' method: 'class::method'.
322
     *                            + The 'signature' element (optional) is an
323
     *                              array describing the return values and
324
     *                              parameters
325
     *                            + The 'docstring' element (optional) is a
326
     *                              string describing what the method does
327
     * @param int $serviceNow  should the HTTP response be sent now?
328
     *                          (1 = yes, 0 = no)
329
     * @param int $debug       should debug output be displayed?
330
     *                          (1 = yes, 0 = no)
331
     *
332
     * @return void
333
     */
334
    function XML_RPC_Server($dispMap, $serviceNow = 1, $debug = 0)
335
    {
336
        global $HTTP_RAW_POST_DATA;
337

    
338
        if ($debug) {
339
            $this->debug = 1;
340
        } else {
341
            $this->debug = 0;
342
        }
343

    
344
        $this->dmap = $dispMap;
345

    
346
        if ($serviceNow) {
347
            $this->service();
348
        } else {
349
            $this->createServerPayload();
350
            $this->createServerHeaders();
351
        }
352
    }
353

    
354
    /**
355
     * @return string  the debug information if debug debug mode is on
356
     */
357
    function serializeDebug()
358
    {
359
        global $XML_RPC_Server_debuginfo, $HTTP_RAW_POST_DATA;
360

    
361
        if ($this->debug) {
362
            XML_RPC_Server_debugmsg('vvv POST DATA RECEIVED BY SERVER vvv' . "\n"
363
                                    . $HTTP_RAW_POST_DATA
364
                                    . "\n" . '^^^ END POST DATA ^^^');
365
        }
366

    
367
        if ($XML_RPC_Server_debuginfo != '') {
368
            return "<!-- PEAR XML_RPC SERVER DEBUG INFO:\n\n"
369
                   . preg_replace('/-(?=-)/', '- ', $XML_RPC_Server_debuginfo)
370
                   . "-->\n";
371
        } else {
372
            return '';
373
        }
374
    }
375

    
376
    /**
377
     * Sends the response
378
     *
379
     * The encoding and content-type are determined by
380
     * XML_RPC_Message::getEncoding()
381
     *
382
     * @return void
383
     *
384
     * @uses XML_RPC_Server::createServerPayload(),
385
     *       XML_RPC_Server::createServerHeaders()
386
     */
387
    function service()
388
    {
389
        $this->createServerPayload();
390
        $this->createServerHeaders();
391
        header($this->server_headers);
392
        print $this->server_payload;
393
    }
394

    
395
    /**
396
     * Generates the payload and puts it in the $server_payload property
397
     *
398
     * @return void
399
     *
400
     * @uses XML_RPC_Server::parseRequest(), XML_RPC_Server::$encoding,
401
     *       XML_RPC_Response::serialize(), XML_RPC_Server::serializeDebug()
402
     */
403
    function createServerPayload()
404
    {
405
        $r = $this->parseRequest();
406
        $this->server_payload = '<?xml version="1.0" encoding="'
407
                              . $this->encoding . '"?>' . "\n"
408
                              . $this->serializeDebug()
409
                              . $r->serialize();
410
    }
411

    
412
    /**
413
     * Determines the HTTP headers and puts them in the $server_headers
414
     * property
415
     *
416
     * @return boolean  TRUE if okay, FALSE if $server_payload isn't set.
417
     *
418
     * @uses XML_RPC_Server::createServerPayload(),
419
     *       XML_RPC_Server::$server_headers
420
     */
421
    function createServerHeaders()
422
    {
423
        if (!$this->server_payload) {
424
            return false;
425
        }
426
        $this->server_headers = 'Content-Length: '
427
                              . strlen($this->server_payload) . "\r\n"
428
                              . 'Content-Type: text/xml;'
429
                              . ' charset=' . $this->encoding;
430
        return true;
431
    }
432

    
433
    /**
434
     * @return array
435
     */
436
    function verifySignature($in, $sig)
437
    {
438
        for ($i = 0; $i < sizeof($sig); $i++) {
439
            // check each possible signature in turn
440
            $cursig = $sig[$i];
441
            if (sizeof($cursig) == $in->getNumParams() + 1) {
442
                $itsOK = 1;
443
                for ($n = 0; $n < $in->getNumParams(); $n++) {
444
                    $p = $in->getParam($n);
445
                    // print "<!-- $p -->\n";
446
                    if ($p->kindOf() == 'scalar') {
447
                        $pt = $p->scalartyp();
448
                    } else {
449
                        $pt = $p->kindOf();
450
                    }
451
                    // $n+1 as first type of sig is return type
452
                    if ($pt != $cursig[$n+1]) {
453
                        $itsOK = 0;
454
                        $pno = $n+1;
455
                        $wanted = $cursig[$n+1];
456
                        $got = $pt;
457
                        break;
458
                    }
459
                }
460
                if ($itsOK) {
461
                    return array(1);
462
                }
463
            }
464
        }
465
        if (isset($wanted)) {
466
            return array(0, "Wanted ${wanted}, got ${got} at param ${pno}");
467
        } else {
468
            $allowed = array();
469
            foreach ($sig as $val) {
470
                end($val);
471
                $allowed[] = key($val);
472
            }
473
            $allowed = array_unique($allowed);
474
            $last = count($allowed) - 1;
475
            if ($last > 0) {
476
                $allowed[$last] = 'or ' . $allowed[$last];
477
            }
478
            return array(0,
479
                         'Signature permits ' . implode(', ', $allowed)
480
                                . ' parameters but the request had '
481
                                . $in->getNumParams());
482
        }
483
    }
484

    
485
    /**
486
     * @return object  a new XML_RPC_Response object
487
     *
488
     * @uses XML_RPC_Message::getEncoding(), XML_RPC_Server::$encoding
489
     */
490
    function parseRequest($data = '')
491
    {
492
        global $XML_RPC_xh, $HTTP_RAW_POST_DATA,
493
                $XML_RPC_err, $XML_RPC_str, $XML_RPC_errxml,
494
                $XML_RPC_defencoding, $XML_RPC_Server_dmap;
495

    
496
        if ($data == '') {
497
            $data = $HTTP_RAW_POST_DATA;
498
        }
499

    
500
        $this->encoding = XML_RPC_Message::getEncoding($data);
501
        $parser_resource = xml_parser_create($this->encoding);
502
        $parser = (int) $parser_resource;
503

    
504
        $XML_RPC_xh[$parser] = array();
505
        $XML_RPC_xh[$parser]['st']     = '';
506
        $XML_RPC_xh[$parser]['cm']     = 0;
507
        $XML_RPC_xh[$parser]['isf']    = 0;
508
        $XML_RPC_xh[$parser]['params'] = array();
509
        $XML_RPC_xh[$parser]['method'] = '';
510

    
511
        $plist = '';
512

    
513
        // decompose incoming XML into request structure
514

    
515
        xml_parser_set_option($parser_resource, XML_OPTION_CASE_FOLDING, true);
516
        xml_set_element_handler($parser_resource, 'XML_RPC_se', 'XML_RPC_ee');
517
        xml_set_character_data_handler($parser_resource, 'XML_RPC_cd');
518
        if (!xml_parse($parser_resource, $data, 1)) {
519
            // return XML error as a faultCode
520
            $r = new XML_RPC_Response(0,
521
                                      $XML_RPC_errxml+xml_get_error_code($parser_resource),
522
                                      sprintf('XML error: %s at line %d',
523
                                              xml_error_string(xml_get_error_code($parser_resource)),
524
                                              xml_get_current_line_number($parser_resource)));
525
            xml_parser_free($parser_resource);
526
        } else {
527
            xml_parser_free($parser_resource);
528
            $m = new XML_RPC_Message($XML_RPC_xh[$parser]['method']);
529
            // now add parameters in
530
            for ($i = 0; $i < sizeof($XML_RPC_xh[$parser]['params']); $i++) {
531
                // print '<!-- ' . $XML_RPC_xh[$parser]['params'][$i]. "-->\n";
532
                $plist .= "$i - " . $XML_RPC_xh[$parser]['params'][$i] . " \n";
533
                eval('$m->addParam(' . $XML_RPC_xh[$parser]['params'][$i] . ');');
534
            }
535
            XML_RPC_Server_debugmsg($plist);
536

    
537
            // now to deal with the method
538
            $methName = $XML_RPC_xh[$parser]['method'];
539
            if (strpos($methName, 'system.') === 0) {
540
                $dmap = $XML_RPC_Server_dmap;
541
                $sysCall = 1;
542
            } else {
543
                $dmap = $this->dmap;
544
                $sysCall = 0;
545
            }
546

    
547
            if (isset($dmap[$methName]['function'])
548
                && is_string($dmap[$methName]['function'])
549
                && strpos($dmap[$methName]['function'], '::') !== false)
550
            {
551
                $dmap[$methName]['function'] =
552
                        explode('::', $dmap[$methName]['function']);
553
            }
554

    
555
            if (isset($dmap[$methName]['function'])
556
                && is_callable($dmap[$methName]['function']))
557
            {
558
                // dispatch if exists
559
                if (isset($dmap[$methName]['signature'])) {
560
                    $sr = $this->verifySignature($m,
561
                                                 $dmap[$methName]['signature'] );
562
                }
563
                if (!isset($dmap[$methName]['signature']) || $sr[0]) {
564
                    // if no signature or correct signature
565
                    if ($sysCall) {
566
                        $r = call_user_func($dmap[$methName]['function'], $this, $m);
567
                    } else {
568
                        $r = call_user_func($dmap[$methName]['function'], $m);
569
                    }
570
                    if (!is_a($r, 'XML_RPC_Response')) {
571
                        $r = new XML_RPC_Response(0, $XML_RPC_err['not_response_object'],
572
                                                  $XML_RPC_str['not_response_object']);
573
                    }
574
                } else {
575
                    $r = new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'],
576
                                              $XML_RPC_str['incorrect_params']
577
                                              . ': ' . $sr[1]);
578
                }
579
            } else {
580
                // else prepare error response
581
                $r = new XML_RPC_Response(0, $XML_RPC_err['unknown_method'],
582
                                          $XML_RPC_str['unknown_method']);
583
            }
584
        }
585
        return $r;
586
    }
587

    
588
    /**
589
     * Echos back the input packet as a string value
590
     *
591
     * @return void
592
     *
593
     * Useful for debugging.
594
     */
595
    function echoInput()
596
    {
597
        global $HTTP_RAW_POST_DATA;
598

    
599
        $r = new XML_RPC_Response(0);
600
        $r->xv = new XML_RPC_Value("'Aha said I: '" . $HTTP_RAW_POST_DATA, 'string');
601
        print $r->serialize();
602
    }
603
}
604

    
605
/*
606
 * Local variables:
607
 * tab-width: 4
608
 * c-basic-offset: 4
609
 * c-hanging-comment-ender-p: nil
610
 * End:
611
 */
612

    
613
?>
(23-23/23)