Project

General

Profile

Download (15.3 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
/* $Id$ */
4

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

    
38

    
39
/**
40
 * Pull in the XML_RPC class
41
 */
42
require_once 'xmlrpc_client.inc';
43

    
44

    
45
/**
46
 * listMethods: either a string, or nothing
47
 * @global array $GLOBALS['XML_RPC_Server_listMethods_sig']
48
 */
49
$GLOBALS['XML_RPC_Server_listMethods_sig'] = array(
50
    array($GLOBALS['XML_RPC_Array'],
51
          $GLOBALS['XML_RPC_String']
52
    ),
53
    array($GLOBALS['XML_RPC_Array'])
54
);
55

    
56
/**
57
 * @global string $GLOBALS['XML_RPC_Server_listMethods_doc']
58
 */
59
$GLOBALS['XML_RPC_Server_listMethods_doc'] = 'This method lists all the'
60
        . ' methods that the XML-RPC server knows how to dispatch';
61

    
62
/**
63
 * @global array $GLOBALS['XML_RPC_Server_methodSignature_sig']
64
 */
65
$GLOBALS['XML_RPC_Server_methodSignature_sig'] = array(
66
    array($GLOBALS['XML_RPC_Array'],
67
          $GLOBALS['XML_RPC_String']
68
    )
69
);
70

    
71
/**
72
 * @global string $GLOBALS['XML_RPC_Server_methodSignature_doc']
73
 */
74
$GLOBALS['XML_RPC_Server_methodSignature_doc'] = 'Returns an array of known'
75
        . ' signatures (an array of arrays) for the method name passed. If'
76
        . ' no signatures are known, returns a none-array (test for type !='
77
        . ' array to detect missing signature)';
78

    
79
/**
80
 * @global array $GLOBALS['XML_RPC_Server_methodHelp_sig']
81
 */
82
$GLOBALS['XML_RPC_Server_methodHelp_sig'] = array(
83
    array($GLOBALS['XML_RPC_String'],
84
          $GLOBALS['XML_RPC_String']
85
    )
86
);
87

    
88
/**
89
 * @global string $GLOBALS['XML_RPC_Server_methodHelp_doc']
90
 */
91
$GLOBALS['XML_RPC_Server_methodHelp_doc'] = 'Returns help text if defined'
92
        . ' for the method passed, otherwise returns an empty string';
93

    
94
/**
95
 * @global array $GLOBALS['XML_RPC_Server_dmap']
96
 */
97
$GLOBALS['XML_RPC_Server_dmap'] = array(
98
    'system.listMethods' => array(
99
        'function'  => 'XML_RPC_Server_listMethods',
100
        'signature' => $GLOBALS['XML_RPC_Server_listMethods_sig'],
101
        'docstring' => $GLOBALS['XML_RPC_Server_listMethods_doc']
102
    ),
103
    'system.methodHelp' => array(
104
        'function'  => 'XML_RPC_Server_methodHelp',
105
        'signature' => $GLOBALS['XML_RPC_Server_methodHelp_sig'],
106
        'docstring' => $GLOBALS['XML_RPC_Server_methodHelp_doc']
107
    ),
108
    'system.methodSignature' => array(
109
        'function'  => 'XML_RPC_Server_methodSignature',
110
        'signature' => $GLOBALS['XML_RPC_Server_methodSignature_sig'],
111
        'docstring' => $GLOBALS['XML_RPC_Server_methodSignature_doc']
112
    )
113
);
114

    
115
/**
116
 * @global string $GLOBALS['XML_RPC_Server_debuginfo']
117
 */
118
$GLOBALS['XML_RPC_Server_debuginfo'] = '';
119

    
120

    
121
/**
122
 * Lists all the methods that the XML-RPC server knows how to dispatch
123
 *
124
 * @return object  a new XML_RPC_Response object
125
 */
126
function XML_RPC_Server_listMethods($server, $m)
127
{
128
    global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
129

    
130
    $v = new XML_RPC_Value();
131
    $dmap = $server->dmap;
132
    $outAr = array();
133
    for (reset($dmap); list($key, $val) = each($dmap); ) {
134
        $outAr[] = new XML_RPC_Value($key, 'string');
135
    }
136
    $dmap = $XML_RPC_Server_dmap;
137
    for (reset($dmap); list($key, $val) = each($dmap); ) {
138
        $outAr[] = new XML_RPC_Value($key, 'string');
139
    }
140
    $v->addArray($outAr);
141
    return new XML_RPC_Response($v);
142
}
143

    
144
/**
145
 * Returns an array of known signatures (an array of arrays)
146
 * for the given method
147
 *
148
 * If no signatures are known, returns a none-array
149
 * (test for type != array to detect missing signature)
150
 *
151
 * @return object  a new XML_RPC_Response object
152
 */
153
function XML_RPC_Server_methodSignature($server, $m)
154
{
155
    global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
156

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

    
190
/**
191
 * Returns help text if defined for the method passed, otherwise returns
192
 * an empty string
193
 *
194
 * @return object  a new XML_RPC_Response object
195
 */
196
function XML_RPC_Server_methodHelp($server, $m)
197
{
198
    global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
199

    
200
    $methName = $m->getParam(0);
201
    $methName = $methName->scalarval();
202
    if (strpos($methName, 'system.') === 0) {
203
        $dmap = $XML_RPC_Server_dmap;
204
        $sysCall = 1;
205
    } else {
206
        $dmap = $server->dmap;
207
        $sysCall = 0;
208
    }
209
    //  print "<!-- ${methName} -->\n";
210
    if (isset($dmap[$methName])) {
211
        if ($dmap[$methName]['docstring']) {
212
            $r = new XML_RPC_Response(new XML_RPC_Value($dmap[$methName]['docstring']),
213
                                                        'string');
214
        } else {
215
            $r = new XML_RPC_Response(new XML_RPC_Value('', 'string'));
216
        }
217
    } else {
218
        $r = new XML_RPC_Response(0, $XML_RPC_err['introspect_unknown'],
219
                                     $XML_RPC_str['introspect_unknown']);
220
    }
221
    return $r;
222
}
223

    
224
/**
225
 * @return void
226
 */
227
function XML_RPC_Server_debugmsg($m)
228
{
229
    global $XML_RPC_Server_debuginfo;
230
    $XML_RPC_Server_debuginfo = $XML_RPC_Server_debuginfo . $m . "\n";
231
}
232

    
233

    
234
/**
235
 *
236
 *
237
 * @category   Web Services
238
 * @package    XML_RPC
239
 * @author     Edd Dumbill <edd@usefulinc.com>
240
 * @author     Stig Bakken <stig@php.net>
241
 * @author     Martin Jansen <mj@php.net>
242
 * @copyright  1999-2001 Edd Dumbill
243
 * @version    Release: @package_version@
244
 * @link       http://pear.php.net/package/XML_RPC
245
 */
246
class XML_RPC_Server
247
{
248
    var $dmap = array();
249
    var $encoding = '';
250
    var $debug = 0;
251

    
252
    /**
253
     * @return void
254
     */
255
    function XML_RPC_Server($dispMap, $serviceNow = 1, $debug = 0)
256
    {
257
        global $HTTP_RAW_POST_DATA;
258

    
259
        if ($debug) {
260
            $this->debug = 1;
261
        } else {
262
            $this->debug = 0;
263
        }
264

    
265
        // dispMap is a despatch array of methods
266
        // mapped to function names and signatures
267
        // if a method
268
        // doesn't appear in the map then an unknown
269
        // method error is generated
270
        $this->dmap = $dispMap;
271
        if ($serviceNow) {
272
            $this->service();
273
        }
274
    }
275

    
276
    /**
277
     * @return string  the debug information if debug debug mode is on
278
     */
279
    function serializeDebug()
280
    {
281
        global $XML_RPC_Server_debuginfo, $HTTP_RAW_POST_DATA;
282

    
283
        if ($this->debug) {
284
            XML_RPC_Server_debugmsg('vvv POST DATA RECEIVED BY SERVER vvv' . "\n"
285
                                    . $HTTP_RAW_POST_DATA
286
                                    . "\n" . '^^^ END POST DATA ^^^');
287
        }
288

    
289
        if ($XML_RPC_Server_debuginfo != '') {
290
            return "<!-- PEAR XML_RPC SERVER DEBUG INFO:\n\n"
291
                   . preg_replace('/-(?=-)/', '- ', $XML_RPC_Server_debuginfo)
292
                   . "-->\n";
293
        } else {
294
            return '';
295
        }
296
    }
297

    
298
    /**
299
     * Print out the result
300
     *
301
     * The encoding and content-type are determined by
302
     * XML_RPC_Message::getEncoding()
303
     *
304
     * @return void
305
     *
306
     * @see XML_RPC_Message::getEncoding()
307
     */
308
    function service()
309
    {
310
        $r = $this->parseRequest();
311
        $payload = '<?xml version="1.0" encoding="'
312
                 . $this->encoding . '"?>' . "\n"
313
                 . $this->serializeDebug()
314
                 . $r->serialize();
315
        header('Content-Length: ' . strlen($payload));
316
        header('Content-Type: text/xml; charset=' . $this->encoding);
317
        print $payload;
318
    }
319

    
320
    /**
321
     * @return array
322
     */
323
    function verifySignature($in, $sig)
324
    {
325
        for ($i = 0; $i < sizeof($sig); $i++) {
326
            // check each possible signature in turn
327
            $cursig = $sig[$i];
328
            if (sizeof($cursig) == $in->getNumParams() + 1) {
329
                $itsOK = 1;
330
                for ($n = 0; $n < $in->getNumParams(); $n++) {
331
                    $p = $in->getParam($n);
332
                    // print "<!-- $p -->\n";
333
                    if ($p->kindOf() == 'scalar') {
334
                        $pt = $p->scalartyp();
335
                    } else {
336
                        $pt = $p->kindOf();
337
                    }
338
                    // $n+1 as first type of sig is return type
339
                    if ($pt != strtolower($cursig[$n+1])) {
340
                        $itsOK = 0;
341
                        $pno = $n+1;
342
                        $wanted = $cursig[$n+1];
343
                        $got = $pt;
344
                        break;
345
                    }
346
                }
347
                if ($itsOK) {
348
                    return array(1);
349
                }
350
            }
351
        }
352
        return array(0, "Wanted ${wanted}, got ${got} at param ${pno})");
353
    }
354

    
355
    /**
356
     * @return object  a new XML_RPC_Response object
357
     */
358
    function parseRequest($data = '')
359
    {
360
        global $XML_RPC_xh, $HTTP_RAW_POST_DATA,
361
                $XML_RPC_err, $XML_RPC_str, $XML_RPC_errxml,
362
                $XML_RPC_defencoding, $XML_RPC_Server_dmap;
363

    
364
        if ($data == '') {
365
            $data = $HTTP_RAW_POST_DATA;
366
        }
367

    
368
        $this->encoding = XML_RPC_Message::getEncoding($data);
369
        $parser_resource = xml_parser_create($this->encoding);
370
        $parser = (int) $parser_resource;
371

    
372
        $XML_RPC_xh[$parser] = array();
373
        $XML_RPC_xh[$parser]['st']     = '';
374
        $XML_RPC_xh[$parser]['cm']     = 0;
375
        $XML_RPC_xh[$parser]['isf']    = 0;
376
        $XML_RPC_xh[$parser]['params'] = array();
377
        $XML_RPC_xh[$parser]['method'] = '';
378

    
379
        $plist = '';
380

    
381
        // decompose incoming XML into request structure
382

    
383
        xml_parser_set_option($parser_resource, XML_OPTION_CASE_FOLDING, true);
384
        xml_set_element_handler($parser_resource, 'XML_RPC_se', 'XML_RPC_ee');
385
        xml_set_character_data_handler($parser_resource, 'XML_RPC_cd');
386
        if (!xml_parse($parser_resource, $data, 1)) {
387
            // return XML error as a faultCode
388
            $r = new XML_RPC_Response(0,
389
                                      $XML_RPC_errxml+xml_get_error_code($parser_resource),
390
                                      sprintf('XML error: %s at line %d',
391
                                              xml_error_string(xml_get_error_code($parser_resource)),
392
                                              xml_get_current_line_number($parser_resource)));
393
            xml_parser_free($parser_resource);
394
        } else {
395
            xml_parser_free($parser_resource);
396
            $m = new XML_RPC_Message($XML_RPC_xh[$parser]['method']);
397
            // now add parameters in
398
            for ($i = 0; $i < sizeof($XML_RPC_xh[$parser]['params']); $i++) {
399
                // print '<!-- ' . $XML_RPC_xh[$parser]['params'][$i]. "-->\n";
400
                $plist .= "$i - " . $XML_RPC_xh[$parser]['params'][$i] . " \n";
401
                eval('$m->addParam(' . $XML_RPC_xh[$parser]['params'][$i] . ');');
402
            }
403
            XML_RPC_Server_debugmsg($plist);
404

    
405
            // now to deal with the method
406
            $methName = $XML_RPC_xh[$parser]['method'];
407
            if (strpos($methName, 'system.') === 0) {
408
                $dmap = $XML_RPC_Server_dmap;
409
                $sysCall = 1;
410
            } else {
411
                $dmap = $this->dmap;
412
                $sysCall = 0;
413
            }
414

    
415
            if (isset($dmap[$methName]['function'])
416
                && is_string($dmap[$methName]['function'])
417
                && strpos($dmap[$methName]['function'], '::') !== false)
418
            {
419
                $dmap[$methName]['function'] =
420
                        explode('::', $dmap[$methName]['function']);
421
            }
422

    
423
            if (isset($dmap[$methName]['function'])
424
                && is_callable($dmap[$methName]['function']))
425
            {
426
                // dispatch if exists
427
                if (isset($dmap[$methName]['signature'])) {
428
                    $sr = $this->verifySignature($m,
429
                                                 $dmap[$methName]['signature'] );
430
                }
431
                if ( (!isset($dmap[$methName]['signature'])) || $sr[0]) {
432
                    // if no signature or correct signature
433
                    if ($sysCall) {
434
                        $r = call_user_func($dmap[$methName]['function'], $this, $m);
435
                    } else {
436
                        $r = call_user_func($dmap[$methName]['function'], $m);
437
                    }
438
                } else {
439
                    $r = new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'],
440
                                              $XML_RPC_str['incorrect_params']
441
                                              . ': ' . $sr[1]);
442
                }
443
            } else {
444
                // else prepare error response
445
                $r = new XML_RPC_Response(0, $XML_RPC_err['unknown_method'],
446
                                          $XML_RPC_str['unknown_method']);
447
            }
448
        }
449
        return $r;
450
    }
451

    
452
    /**
453
     * Echos back the input packet as a string value
454
     *
455
     * @return void
456
     *
457
     * Useful for debugging.
458
     */
459
    function echoInput() {
460
        global $HTTP_RAW_POST_DATA;
461

    
462
        $r = new XML_RPC_Response(0);
463
        $r->xv = new XML_RPC_Value("'Aha said I: '" . $HTTP_RAW_POST_DATA, 'string');
464
        print $r->serialize();
465
    }
466
}
467

    
468
/*
469
 * Local variables:
470
 * tab-width: 4
471
 * c-basic-offset: 4
472
 * c-hanging-comment-ender-p: nil
473
 * End:
474
 */
475

    
476
?>
(19-19/19)