Project

General

Profile

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

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

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

    
45

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

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

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

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

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

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

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

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

    
130

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

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

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

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

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

    
208
    $methName = $m->getParam(0);
209
    $methName = $methName->scalarval();
210
    if (strpos($methName, 'system.') === 0) {
211
        $dmap = $XML_RPC_Server_dmap;
212
        $sysCall = 1;
213
    } else {
214
        $dmap = $server->dmap;
215
        $sysCall = 0;
216
    }
217

    
218
    if (isset($dmap[$methName])) {
219
        if ($dmap[$methName]['docstring']) {
220
            $r = new XML_RPC_Response(new XML_RPC_Value($dmap[$methName]['docstring']),
221
                                                        'string');
222
        } else {
223
            $r = new XML_RPC_Response(new XML_RPC_Value('', 'string'));
224
        }
225
    } else {
226
        $r = new XML_RPC_Response(0, $XML_RPC_err['introspect_unknown'],
227
                                     $XML_RPC_str['introspect_unknown']);
228
    }
229
    return $r;
230
}
231

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

    
241

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

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

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

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

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

    
309

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

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

    
343
        $this->dmap = $dispMap;
344

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

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

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

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

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

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

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

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

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

    
499
        if ($data == '') {
500
            $data = $HTTP_RAW_POST_DATA;
501
        }
502

    
503
        $this->encoding = XML_RPC_Message::getEncoding($data);
504
        $parser_resource = xml_parser_create($this->encoding);
505
        $parser = (int) $parser_resource;
506

    
507
        $XML_RPC_xh[$parser] = array();
508
        $XML_RPC_xh[$parser]['cm']     = 0;
509
        $XML_RPC_xh[$parser]['isf']    = 0;
510
        $XML_RPC_xh[$parser]['params'] = array();
511
        $XML_RPC_xh[$parser]['method'] = '';
512
        $XML_RPC_xh[$parser]['stack'] = array();	
513
        $XML_RPC_xh[$parser]['valuestack'] = array();	
514

    
515
        $plist = '';
516

    
517
        // decompose incoming XML into request structure
518

    
519
        xml_parser_set_option($parser_resource, XML_OPTION_CASE_FOLDING, true);
520
        xml_set_element_handler($parser_resource, 'XML_RPC_se', 'XML_RPC_ee');
521
        xml_set_character_data_handler($parser_resource, 'XML_RPC_cd');
522
        if (!xml_parse($parser_resource, $data, 1)) {
523
            // return XML error as a faultCode
524
            $r = new XML_RPC_Response(0,
525
                                      $XML_RPC_errxml+xml_get_error_code($parser_resource),
526
                                      sprintf('XML error: %s at line %d',
527
                                              xml_error_string(xml_get_error_code($parser_resource)),
528
                                              xml_get_current_line_number($parser_resource)));
529
            xml_parser_free($parser_resource);
530
        } elseif ($XML_RPC_xh[$parser]['isf']>1) {
531
            $r = new XML_RPC_Response(0,
532
                                      $XML_RPC_err['invalid_request'],
533
                                      $XML_RPC_str['invalid_request']
534
                                      . ': '
535
                                      . $XML_RPC_xh[$parser]['isf_reason']);
536
            xml_parser_free($parser_resource);
537
        } else {
538
            xml_parser_free($parser_resource);
539
            $m = new XML_RPC_Message($XML_RPC_xh[$parser]['method']);
540
            // now add parameters in
541
            for ($i = 0; $i < sizeof($XML_RPC_xh[$parser]['params']); $i++) {
542
                // print '<!-- ' . $XML_RPC_xh[$parser]['params'][$i]. "-->\n";
543
                $plist .= "$i - " . var_export($XML_RPC_xh[$parser]['params'][$i], true) . " \n";
544
                $m->addParam($XML_RPC_xh[$parser]['params'][$i]);
545
            }
546

    
547
            if ($this->debug) {
548
                XML_RPC_Server_debugmsg($plist);
549
            }
550

    
551
            // now to deal with the method
552
            $methName = $XML_RPC_xh[$parser]['method'];
553
            if (strpos($methName, 'system.') === 0) {
554
                $dmap = $XML_RPC_Server_dmap;
555
                $sysCall = 1;
556
            } else {
557
                $dmap = $this->dmap;
558
                $sysCall = 0;
559
            }
560

    
561
            if (isset($dmap[$methName]['function'])
562
                && is_string($dmap[$methName]['function'])
563
                && strpos($dmap[$methName]['function'], '::') !== false)
564
            {
565
                $dmap[$methName]['function'] =
566
                        explode('::', $dmap[$methName]['function']);
567
            }
568

    
569
            if (isset($dmap[$methName]['function'])
570
                && is_callable($dmap[$methName]['function']))
571
            {
572
                // dispatch if exists
573
                if (isset($dmap[$methName]['signature'])) {
574
                    $sr = $this->verifySignature($m,
575
                                                 $dmap[$methName]['signature'] );
576
                }
577
                if (!isset($dmap[$methName]['signature']) || $sr[0]) {
578
                    // if no signature or correct signature
579
                    if ($sysCall) {
580
                        $r = call_user_func($dmap[$methName]['function'], $this, $m);
581
                    } else {
582
                        $r = call_user_func($dmap[$methName]['function'], $m);
583
                    }
584
                    if (!is_a($r, 'XML_RPC_Response')) {
585
                        $r = new XML_RPC_Response(0, $XML_RPC_err['not_response_object'],
586
                                                  $XML_RPC_str['not_response_object']);
587
                    }
588
                } else {
589
                    $r = new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'],
590
                                              $XML_RPC_str['incorrect_params']
591
                                              . ': ' . $sr[1]);
592
                }
593
            } else {
594
                // else prepare error response
595
                $r = new XML_RPC_Response(0, $XML_RPC_err['unknown_method'],
596
                                          $XML_RPC_str['unknown_method']);
597
            }
598
        }
599
        return $r;
600
    }
601

    
602
    /**
603
     * Echos back the input packet as a string value
604
     *
605
     * @return void
606
     *
607
     * Useful for debugging.
608
     */
609
    function echoInput()
610
    {
611
        global $HTTP_RAW_POST_DATA;
612

    
613
        $r = new XML_RPC_Response(0);
614
        $r->xv = new XML_RPC_Value("'Aha said I: '" . $HTTP_RAW_POST_DATA, 'string');
615
        print $r->serialize();
616
    }
617
}
618

    
619
/*
620
 * Local variables:
621
 * tab-width: 4
622
 * c-basic-offset: 4
623
 * c-hanging-comment-ender-p: nil
624
 * End:
625
 */
626

    
627
?>
(27-27/27)