~katiekitty/+junk/solidstate

« back to all changes in this revision

Viewing changes to solidstate/branches/v0.4.2/xmlrpc/.svn/text-base/IXR.php.svn-base

  • Committer: root
  • Date: 2010-01-13 07:44:31 UTC
  • Revision ID: root@ds3-vamp.cs-monitor.cz.cc-20100113074431-kt8ceoeznpjg22x7
Reviving the project

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/* 
 
3
   IXR - The Inutio XML-RPC Library - (c) Incutio Ltd 2002
 
4
   Version 1.61 - Simon Willison, 11th July 2003 (htmlentities -> htmlspecialchars)
 
5
   Site:   http://scripts.incutio.com/xmlrpc/
 
6
   Manual: http://scripts.incutio.com/xmlrpc/manual.php
 
7
   Made available under the Artistic License: http://www.opensource.org/licenses/artistic-license.php
 
8
*/
 
9
 
 
10
class IXR_Value {
 
11
    var $data;
 
12
    var $type;
 
13
    function IXR_Value ($data, $type = false) {
 
14
        $this->data = $data;
 
15
        if (!$type) {
 
16
            $type = $this->calculateType();
 
17
        }
 
18
        $this->type = $type;
 
19
        if ($type == 'struct') {
 
20
            /* Turn all the values in the array in to new IXR_Value objects */
 
21
            foreach ($this->data as $key => $value) {
 
22
                $this->data[$key] = new IXR_Value($value);
 
23
            }
 
24
        }
 
25
        if ($type == 'array') {
 
26
            for ($i = 0, $j = count($this->data); $i < $j; $i++) {
 
27
                $this->data[$i] = new IXR_Value($this->data[$i]);
 
28
            }
 
29
        }
 
30
    }
 
31
    function calculateType() {
 
32
        if ($this->data === true || $this->data === false) {
 
33
            return 'boolean';
 
34
        }
 
35
        if (is_integer($this->data)) {
 
36
            return 'int';
 
37
        }
 
38
        if (is_double($this->data)) {
 
39
            return 'double';
 
40
        }
 
41
        // Deal with IXR object types base64 and date
 
42
        if (is_object($this->data) && is_a($this->data, 'IXR_Date')) {
 
43
            return 'date';
 
44
        }
 
45
        if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) {
 
46
            return 'base64';
 
47
        }
 
48
        // If it is a normal PHP object convert it in to a struct
 
49
        if (is_object($this->data)) {
 
50
            
 
51
            $this->data = get_object_vars($this->data);
 
52
            return 'struct';
 
53
        }
 
54
        if (!is_array($this->data)) {
 
55
            return 'string';
 
56
        }
 
57
        /* We have an array - is it an array or a struct ? */
 
58
        if ($this->isStruct($this->data)) {
 
59
            return 'struct';
 
60
        } else {
 
61
            return 'array';
 
62
        }
 
63
    }
 
64
    function getXml() {
 
65
        /* Return XML for this value */
 
66
        switch ($this->type) {
 
67
            case 'boolean':
 
68
                return '<boolean>'.(($this->data) ? '1' : '0').'</boolean>';
 
69
                break;
 
70
            case 'int':
 
71
                return '<int>'.$this->data.'</int>';
 
72
                break;
 
73
            case 'double':
 
74
                return '<double>'.$this->data.'</double>';
 
75
                break;
 
76
            case 'string':
 
77
                return '<string>'.htmlspecialchars($this->data).'</string>';
 
78
                break;
 
79
            case 'array':
 
80
                $return = '<array><data>'."\n";
 
81
                foreach ($this->data as $item) {
 
82
                    $return .= '  <value>'.$item->getXml()."</value>\n";
 
83
                }
 
84
                $return .= '</data></array>';
 
85
                return $return;
 
86
                break;
 
87
            case 'struct':
 
88
                $return = '<struct>'."\n";
 
89
                foreach ($this->data as $name => $value) {
 
90
                    $return .= "  <member><name>$name</name><value>";
 
91
                    $return .= $value->getXml()."</value></member>\n";
 
92
                }
 
93
                $return .= '</struct>';
 
94
                return $return;
 
95
                break;
 
96
            case 'date':
 
97
            case 'base64':
 
98
                return $this->data->getXml();
 
99
                break;
 
100
        }
 
101
        return false;
 
102
    }
 
103
    function isStruct($array) {
 
104
        /* Nasty function to check if an array is a struct or not */
 
105
        $expected = 0;
 
106
        foreach ($array as $key => $value) {
 
107
            if ((string)$key != (string)$expected) {
 
108
                return true;
 
109
            }
 
110
            $expected++;
 
111
        }
 
112
        return false;
 
113
    }
 
114
}
 
115
 
 
116
 
 
117
class IXR_Message {
 
118
    var $message;
 
119
    var $messageType;  // methodCall / methodResponse / fault
 
120
    var $faultCode;
 
121
    var $faultString;
 
122
    var $methodName;
 
123
    var $params;
 
124
    // Current variable stacks
 
125
    var $_arraystructs = array();   // The stack used to keep track of the current array/struct
 
126
    var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array
 
127
    var $_currentStructName = array();  // A stack as well
 
128
    var $_param;
 
129
    var $_value;
 
130
    var $_currentTag;
 
131
    var $_currentTagContents;
 
132
    // The XML parser
 
133
    var $_parser;
 
134
    function IXR_Message ($message) {
 
135
        $this->message = $message;
 
136
    }
 
137
    function parse() {
 
138
        // first remove the XML declaration
 
139
        $this->message = preg_replace('/<\?xml(.*)?\?'.'>/', '', $this->message);
 
140
        if (trim($this->message) == '') {
 
141
            return false;
 
142
        }
 
143
        $this->_parser = xml_parser_create();
 
144
        // Set XML parser to take the case of tags in to account
 
145
        xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
 
146
        // Set XML parser callback functions
 
147
        xml_set_object($this->_parser, $this);
 
148
        xml_set_element_handler($this->_parser, 'tag_open', 'tag_close');
 
149
        xml_set_character_data_handler($this->_parser, 'cdata');
 
150
        if (!xml_parse($this->_parser, $this->message)) {
 
151
            /* die(sprintf('XML error: %s at line %d',
 
152
                xml_error_string(xml_get_error_code($this->_parser)),
 
153
                xml_get_current_line_number($this->_parser))); */
 
154
            return false;
 
155
        }
 
156
        xml_parser_free($this->_parser);
 
157
        // Grab the error messages, if any
 
158
        if ($this->messageType == 'fault') {
 
159
            $this->faultCode = $this->params[0]['faultCode'];
 
160
            $this->faultString = $this->params[0]['faultString'];
 
161
        }
 
162
        return true;
 
163
    }
 
164
    function tag_open($parser, $tag, $attr) {
 
165
        $this->currentTag = $tag;
 
166
        switch($tag) {
 
167
            case 'methodCall':
 
168
            case 'methodResponse':
 
169
            case 'fault':
 
170
                $this->messageType = $tag;
 
171
                break;
 
172
            /* Deal with stacks of arrays and structs */
 
173
            case 'data':    // data is to all intents and puposes more interesting than array
 
174
                $this->_arraystructstypes[] = 'array';
 
175
                $this->_arraystructs[] = array();
 
176
                break;
 
177
            case 'struct':
 
178
                $this->_arraystructstypes[] = 'struct';
 
179
                $this->_arraystructs[] = array();
 
180
                break;
 
181
        }
 
182
    }
 
183
    function cdata($parser, $cdata) {
 
184
        $this->_currentTagContents .= $cdata;
 
185
    }
 
186
    function tag_close($parser, $tag) {
 
187
        $valueFlag = false;
 
188
        switch($tag) {
 
189
            case 'int':
 
190
            case 'i4':
 
191
                $value = (int)trim($this->_currentTagContents);
 
192
                $this->_currentTagContents = '';
 
193
                $valueFlag = true;
 
194
                break;
 
195
            case 'double':
 
196
                $value = (double)trim($this->_currentTagContents);
 
197
                $this->_currentTagContents = '';
 
198
                $valueFlag = true;
 
199
                break;
 
200
            case 'string':
 
201
                $value = (string)trim($this->_currentTagContents);
 
202
                $this->_currentTagContents = '';
 
203
                $valueFlag = true;
 
204
                break;
 
205
            case 'dateTime.iso8601':
 
206
                $value = new IXR_Date(trim($this->_currentTagContents));
 
207
                // $value = $iso->getTimestamp();
 
208
                $this->_currentTagContents = '';
 
209
                $valueFlag = true;
 
210
                break;
 
211
            case 'value':
 
212
                // "If no type is indicated, the type is string."
 
213
                if (trim($this->_currentTagContents) != '') {
 
214
                    $value = (string)$this->_currentTagContents;
 
215
                    $this->_currentTagContents = '';
 
216
                    $valueFlag = true;
 
217
                }
 
218
                break;
 
219
            case 'boolean':
 
220
                $value = (boolean)trim($this->_currentTagContents);
 
221
                $this->_currentTagContents = '';
 
222
                $valueFlag = true;
 
223
                break;
 
224
            case 'base64':
 
225
                $value = base64_decode($this->_currentTagContents);
 
226
                $this->_currentTagContents = '';
 
227
                $valueFlag = true;
 
228
                break;
 
229
            /* Deal with stacks of arrays and structs */
 
230
            case 'data':
 
231
            case 'struct':
 
232
                $value = array_pop($this->_arraystructs);
 
233
                array_pop($this->_arraystructstypes);
 
234
                $valueFlag = true;
 
235
                break;
 
236
            case 'member':
 
237
                array_pop($this->_currentStructName);
 
238
                break;
 
239
            case 'name':
 
240
                $this->_currentStructName[] = trim($this->_currentTagContents);
 
241
                $this->_currentTagContents = '';
 
242
                break;
 
243
            case 'methodName':
 
244
                $this->methodName = trim($this->_currentTagContents);
 
245
                $this->_currentTagContents = '';
 
246
                break;
 
247
        }
 
248
        if ($valueFlag) {
 
249
            /*
 
250
            if (!is_array($value) && !is_object($value)) {
 
251
                $value = trim($value);
 
252
            }
 
253
            */
 
254
            if (count($this->_arraystructs) > 0) {
 
255
                // Add value to struct or array
 
256
                if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {
 
257
                    // Add to struct
 
258
                    
 
259
$this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] 
 
260
= $value;
 
261
                } else {
 
262
                    // Add to array
 
263
                    $this->_arraystructs[count($this->_arraystructs)-1][] = $value;
 
264
                }
 
265
            } else {
 
266
                // Just add as a paramater
 
267
                $this->params[] = $value;
 
268
            }
 
269
        }
 
270
    }       
 
271
}
 
272
 
 
273
 
 
274
class IXR_Server {
 
275
    var $data;
 
276
    var $callbacks = array();
 
277
    var $message;
 
278
    var $capabilities;
 
279
    function IXR_Server($callbacks = false, $data = false) {
 
280
        $this->setCapabilities();
 
281
        if ($callbacks) {
 
282
            $this->callbacks = $callbacks;
 
283
        }
 
284
        $this->setCallbacks();
 
285
        $this->serve($data);
 
286
    }
 
287
    function serve($data = false) {
 
288
        if (!$data) {
 
289
            global $HTTP_RAW_POST_DATA;
 
290
            if (!$HTTP_RAW_POST_DATA) {
 
291
               die('XML-RPC server accepts POST requests only.');
 
292
            }
 
293
            $data = $HTTP_RAW_POST_DATA;
 
294
        }
 
295
        $this->message = new IXR_Message($data);
 
296
        if (!$this->message->parse()) {
 
297
            $this->error(-32700, 'parse error. not well formed');
 
298
        }
 
299
        if ($this->message->messageType != 'methodCall') {
 
300
            $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a 
 
301
methodCall');
 
302
        }
 
303
        $result = $this->call($this->message->methodName, $this->message->params);
 
304
        // Is the result an error?
 
305
        if (is_a($result, 'IXR_Error')) {
 
306
            $this->error($result);
 
307
        }
 
308
        // Encode the result
 
309
        $r = new IXR_Value($result);
 
310
        $resultxml = $r->getXml();
 
311
        // Create the XML
 
312
        $xml = <<<EOD
 
313
<methodResponse>
 
314
  <params>
 
315
    <param>
 
316
      <value>
 
317
        $resultxml
 
318
      </value>
 
319
    </param>
 
320
  </params>
 
321
</methodResponse>
 
322
 
 
323
EOD;
 
324
        // Send it
 
325
        $this->output($xml);
 
326
    }
 
327
    function call($methodname, $args) {
 
328
        if (!$this->hasMethod($methodname)) {
 
329
            return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.');
 
330
        }
 
331
        $method = $this->callbacks[$methodname];
 
332
        // Perform the callback and send the response
 
333
        if (count($args) == 1) {
 
334
            // If only one paramater just send that instead of the whole array
 
335
            $args = $args[0];
 
336
        }
 
337
        // Are we dealing with a function or a method?
 
338
        if (substr($method, 0, 5) == 'this:') {
 
339
            // It's a class method - check it exists
 
340
            $method = substr($method, 5);
 
341
            if (!method_exists($this, $method)) {
 
342
                return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not 
 
343
exist.');
 
344
            }
 
345
            // Call the method
 
346
            $result = $this->$method($args);
 
347
        } else {
 
348
            // It's a function - does it exist?
 
349
            if (!function_exists($method)) {
 
350
                return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.');
 
351
            }
 
352
            // Call the function
 
353
            $result = $method($args);
 
354
        }
 
355
        return $result;
 
356
    }
 
357
 
 
358
    function error($error, $message = false) {
 
359
        // Accepts either an error object or an error code and message
 
360
        if ($message && !is_object($error)) {
 
361
            $error = new IXR_Error($error, $message);
 
362
        }
 
363
        $this->output($error->getXml());
 
364
    }
 
365
    function output($xml) {
 
366
        $xml = '<?xml version="1.0"?>'."\n".$xml;
 
367
        $length = strlen($xml);
 
368
        header('Connection: close');
 
369
        header('Content-Length: '.$length);
 
370
        header('Content-Type: text/xml');
 
371
        header('Date: '.date('r'));
 
372
        echo $xml;
 
373
        exit;
 
374
    }
 
375
    function hasMethod($method) {
 
376
        return in_array($method, array_keys($this->callbacks));
 
377
    }
 
378
    function setCapabilities() {
 
379
        // Initialises capabilities array
 
380
        $this->capabilities = array(
 
381
            'xmlrpc' => array(
 
382
                'specUrl' => 'http://www.xmlrpc.com/spec',
 
383
                'specVersion' => 1
 
384
            ),
 
385
            'faults_interop' => array(
 
386
                'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
 
387
                'specVersion' => 20010516
 
388
            ),
 
389
            'system.multicall' => array(
 
390
                'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
 
391
                'specVersion' => 1
 
392
            ),
 
393
        );   
 
394
    }
 
395
    function getCapabilities($args) {
 
396
        return $this->capabilities;
 
397
    }
 
398
    function setCallbacks() {
 
399
        $this->callbacks['system.getCapabilities'] = 'this:getCapabilities';
 
400
        $this->callbacks['system.listMethods'] = 'this:listMethods';
 
401
        $this->callbacks['system.multicall'] = 'this:multiCall';
 
402
    }
 
403
    function listMethods($args) {
 
404
        // Returns a list of methods - uses array_reverse to ensure user defined
 
405
        // methods are listed before server defined methods
 
406
        return array_reverse(array_keys($this->callbacks));
 
407
    }
 
408
    function multiCall($methodcalls) {
 
409
        // See http://www.xmlrpc.com/discuss/msgReader$1208
 
410
        $return = array();
 
411
        foreach ($methodcalls as $call) {
 
412
            $method = $call['methodName'];
 
413
            $params = $call['params'];
 
414
            if ($method == 'system.multicall') {
 
415
                $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden');
 
416
            } else {
 
417
                $result = $this->call($method, $params);
 
418
            }
 
419
            if (is_a($result, 'IXR_Error')) {
 
420
                $return[] = array(
 
421
                    'faultCode' => $result->code,
 
422
                    'faultString' => $result->message
 
423
                );
 
424
            } else {
 
425
                $return[] = array($result);
 
426
            }
 
427
        }
 
428
        return $return;
 
429
    }
 
430
}
 
431
 
 
432
class IXR_Request {
 
433
    var $method;
 
434
    var $args;
 
435
    var $xml;
 
436
    function IXR_Request($method, $args) {
 
437
        $this->method = $method;
 
438
        $this->args = $args;
 
439
        $this->xml = <<<EOD
 
440
<?xml version="1.0"?>
 
441
<methodCall>
 
442
<methodName>{$this->method}</methodName>
 
443
<params>
 
444
 
 
445
EOD;
 
446
        foreach ($this->args as $arg) {
 
447
            $this->xml .= '<param><value>';
 
448
            $v = new IXR_Value($arg);
 
449
            $this->xml .= $v->getXml();
 
450
            $this->xml .= "</value></param>\n";
 
451
        }
 
452
        $this->xml .= '</params></methodCall>';
 
453
    }
 
454
    function getLength() {
 
455
        return strlen($this->xml);
 
456
    }
 
457
    function getXml() {
 
458
        return $this->xml;
 
459
    }
 
460
}
 
461
 
 
462
 
 
463
class IXR_Client {
 
464
    var $server;
 
465
    var $port;
 
466
    var $path;
 
467
    var $useragent;
 
468
    var $response;
 
469
    var $message = false;
 
470
    var $debug = false;
 
471
    // Storage place for an error message
 
472
    var $error = false;
 
473
    function IXR_Client($server, $path = false, $port = 80) {
 
474
        if (!$path) {
 
475
            // Assume we have been given a URL instead
 
476
            $bits = parse_url($server);
 
477
            $this->server = $bits['host'];
 
478
            $this->port = isset($bits['port']) ? $bits['port'] : 80;
 
479
            $this->path = isset($bits['path']) ? $bits['path'] : '/';
 
480
            // Make absolutely sure we have a path
 
481
            if (!$this->path) {
 
482
                $this->path = '/';
 
483
            }
 
484
        } else {
 
485
            $this->server = $server;
 
486
            $this->path = $path;
 
487
            $this->port = $port;
 
488
        }
 
489
        $this->useragent = 'The Incutio XML-RPC PHP Library';
 
490
    }
 
491
    function query() {
 
492
        $args = func_get_args();
 
493
        $method = array_shift($args);
 
494
        $request = new IXR_Request($method, $args);
 
495
        $length = $request->getLength();
 
496
        $xml = $request->getXml();
 
497
        $r = "\r\n";
 
498
        $request  = "POST {$this->path} HTTP/1.0$r";
 
499
        $request .= "Host: {$this->server}$r";
 
500
        $request .= "Content-Type: text/xml$r";
 
501
        $request .= "User-Agent: {$this->useragent}$r";
 
502
        $request .= "Content-length: {$length}$r$r";
 
503
        $request .= $xml;
 
504
        // Now send the request
 
505
        if ($this->debug) {
 
506
            echo '<pre>'.htmlspecialchars($request)."\n</pre>\n\n";
 
507
        }
 
508
        $fp = @fsockopen($this->server, $this->port);
 
509
        if (!$fp) {
 
510
            $this->error = new IXR_Error(-32300, 'transport error - could not open socket');
 
511
            return false;
 
512
        }
 
513
        fputs($fp, $request);
 
514
        $contents = '';
 
515
        $gotFirstLine = false;
 
516
        $gettingHeaders = true;
 
517
        while (!feof($fp)) {
 
518
            $line = fgets($fp, 4096);
 
519
            if (!$gotFirstLine) {
 
520
                // Check line for '200'
 
521
                if (strstr($line, '200') === false) {
 
522
                    $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200');
 
523
                    return false;
 
524
                }
 
525
                $gotFirstLine = true;
 
526
            }
 
527
            if (trim($line) == '') {
 
528
                $gettingHeaders = false;
 
529
            }
 
530
            if (!$gettingHeaders) {
 
531
                $contents .= trim($line)."\n";
 
532
            }
 
533
        }
 
534
        if ($this->debug) {
 
535
            echo '<pre>'.htmlspecialchars($contents)."\n</pre>\n\n";
 
536
        }
 
537
        // Now parse what we've got back
 
538
        $this->message = new IXR_Message($contents);
 
539
        if (!$this->message->parse()) {
 
540
            // XML error
 
541
            $this->error = new IXR_Error(-32700, 'parse error. not well formed');
 
542
            return false;
 
543
        }
 
544
        // Is the message a fault?
 
545
        if ($this->message->messageType == 'fault') {
 
546
            $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString);
 
547
            return false;
 
548
        }
 
549
        // Message must be OK
 
550
        return true;
 
551
    }
 
552
    function getResponse() {
 
553
        // methodResponses can only have one param - return that
 
554
        return $this->message->params[0];
 
555
    }
 
556
    function isError() {
 
557
        return (is_object($this->error));
 
558
    }
 
559
    function getErrorCode() {
 
560
        return $this->error->code;
 
561
    }
 
562
    function getErrorMessage() {
 
563
        return $this->error->message;
 
564
    }
 
565
}
 
566
 
 
567
 
 
568
class IXR_Error {
 
569
    var $code;
 
570
    var $message;
 
571
    function IXR_Error($code, $message) {
 
572
        $this->code = $code;
 
573
        $this->message = $message;
 
574
    }
 
575
    function getXml() {
 
576
        $xml = <<<EOD
 
577
<methodResponse>
 
578
  <fault>
 
579
    <value>
 
580
      <struct>
 
581
        <member>
 
582
          <name>faultCode</name>
 
583
          <value><int>{$this->code}</int></value>
 
584
        </member>
 
585
        <member>
 
586
          <name>faultString</name>
 
587
          <value><string>{$this->message}</string></value>
 
588
        </member>
 
589
      </struct>
 
590
    </value>
 
591
  </fault>
 
592
</methodResponse> 
 
593
 
 
594
EOD;
 
595
        return $xml;
 
596
    }
 
597
}
 
598
 
 
599
 
 
600
class IXR_Date {
 
601
    var $year;
 
602
    var $month;
 
603
    var $day;
 
604
    var $hour;
 
605
    var $minute;
 
606
    var $second;
 
607
    function IXR_Date($time) {
 
608
        // $time can be a PHP timestamp or an ISO one
 
609
        if (is_numeric($time)) {
 
610
            $this->parseTimestamp($time);
 
611
        } else {
 
612
            $this->parseIso($time);
 
613
        }
 
614
    }
 
615
    function parseTimestamp($timestamp) {
 
616
        $this->year = date('Y', $timestamp);
 
617
        $this->month = date('Y', $timestamp);
 
618
        $this->day = date('Y', $timestamp);
 
619
        $this->hour = date('H', $timestamp);
 
620
        $this->minute = date('i', $timestamp);
 
621
        $this->second = date('s', $timestamp);
 
622
    }
 
623
    function parseIso($iso) {
 
624
        $this->year = substr($iso, 0, 4);
 
625
        $this->month = substr($iso, 4, 2); 
 
626
        $this->day = substr($iso, 6, 2);
 
627
        $this->hour = substr($iso, 9, 2);
 
628
        $this->minute = substr($iso, 12, 2);
 
629
        $this->second = substr($iso, 15, 2);
 
630
    }
 
631
    function getIso() {
 
632
        return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second;
 
633
    }
 
634
    function getXml() {
 
635
        return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>';
 
636
    }
 
637
    function getTimestamp() {
 
638
        return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year);
 
639
    }
 
640
}
 
641
 
 
642
 
 
643
class IXR_Base64 {
 
644
    var $data;
 
645
    function IXR_Base64($data) {
 
646
        $this->data = $data;
 
647
    }
 
648
    function getXml() {
 
649
        return '<base64>'.base64_encode($this->data).'</base64>';
 
650
    }
 
651
}
 
652
 
 
653
 
 
654
class IXR_IntrospectionServer extends IXR_Server {
 
655
    var $signatures;
 
656
    var $help;
 
657
    function IXR_IntrospectionServer() {
 
658
        $this->setCallbacks();
 
659
        $this->setCapabilities();
 
660
        $this->capabilities['introspection'] = array(
 
661
            'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html',
 
662
            'specVersion' => 1
 
663
        );
 
664
        $this->addCallback(
 
665
            'system.methodSignature', 
 
666
            'this:methodSignature', 
 
667
            array('array', 'string'), 
 
668
            'Returns an array describing the return type and required parameters of a method'
 
669
        );
 
670
        $this->addCallback(
 
671
            'system.getCapabilities', 
 
672
            'this:getCapabilities', 
 
673
            array('struct'), 
 
674
            'Returns a struct describing the XML-RPC specifications supported by this server'
 
675
        );
 
676
        $this->addCallback(
 
677
            'system.listMethods', 
 
678
            'this:listMethods', 
 
679
            array('array'), 
 
680
            'Returns an array of available methods on this server'
 
681
        );
 
682
        $this->addCallback(
 
683
            'system.methodHelp', 
 
684
            'this:methodHelp', 
 
685
            array('string', 'string'), 
 
686
            'Returns a documentation string for the specified method'
 
687
        );
 
688
    }
 
689
    function addCallback($method, $callback, $args, $help) {
 
690
        $this->callbacks[$method] = $callback;
 
691
        $this->signatures[$method] = $args;
 
692
        $this->help[$method] = $help;
 
693
    }
 
694
    function call($methodname, $args) {
 
695
        // Make sure it's in an array
 
696
        if ($args && !is_array($args)) {
 
697
            $args = array($args);
 
698
        }
 
699
        // Over-rides default call method, adds signature check
 
700
        if (!$this->hasMethod($methodname)) {
 
701
            return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not 
 
702
specified.');
 
703
        }
 
704
        $method = $this->callbacks[$methodname];
 
705
        $signature = $this->signatures[$methodname];
 
706
        $returnType = array_shift($signature);
 
707
        // Check the number of arguments
 
708
        if (count($args) != count($signature)) {
 
709
            // print 'Num of args: '.count($args).' Num in signature: '.count($signature);
 
710
            return new IXR_Error(-32602, 'server error. wrong number of method parameters');
 
711
        }
 
712
        // Check the argument types
 
713
        $ok = true;
 
714
        $argsbackup = $args;
 
715
        for ($i = 0, $j = count($args); $i < $j; $i++) {
 
716
            $arg = array_shift($args);
 
717
            $type = array_shift($signature);
 
718
            switch ($type) {
 
719
                case 'int':
 
720
                case 'i4':
 
721
                    if (is_array($arg) || !is_int($arg)) {
 
722
                        $ok = false;
 
723
                    }
 
724
                    break;
 
725
                case 'base64':
 
726
                case 'string':
 
727
                    if (!is_string($arg)) {
 
728
                        $ok = false;
 
729
                    }
 
730
                    break;
 
731
                case 'boolean':
 
732
                    if ($arg !== false && $arg !== true) {
 
733
                        $ok = false;
 
734
                    }
 
735
                    break;
 
736
                case 'float':
 
737
                case 'double':
 
738
                    if (!is_float($arg)) {
 
739
                        $ok = false;
 
740
                    }
 
741
                    break;
 
742
                case 'date':
 
743
                case 'dateTime.iso8601':
 
744
                    if (!is_a($arg, 'IXR_Date')) {
 
745
                        $ok = false;
 
746
                    }
 
747
                    break;
 
748
            }
 
749
            if (!$ok) {
 
750
                return new IXR_Error(-32602, 'server error. invalid method parameters');
 
751
            }
 
752
        }
 
753
        // It passed the test - run the "real" method call
 
754
        return parent::call($methodname, $argsbackup);
 
755
    }
 
756
    function methodSignature($method) {
 
757
        if (!$this->hasMethod($method)) {
 
758
            return new IXR_Error(-32601, 'server error. requested method "'.$method.'" not specified.');
 
759
        }
 
760
        // We should be returning an array of types
 
761
        $types = $this->signatures[$method];
 
762
        $return = array();
 
763
        foreach ($types as $type) {
 
764
            switch ($type) {
 
765
                case 'string':
 
766
                    $return[] = 'string';
 
767
                    break;
 
768
                case 'int':
 
769
                case 'i4':
 
770
                    $return[] = 42;
 
771
                    break;
 
772
                case 'double':
 
773
                    $return[] = 3.1415;
 
774
                    break;
 
775
                case 'dateTime.iso8601':
 
776
                    $return[] = new IXR_Date(time());
 
777
                    break;
 
778
                case 'boolean':
 
779
                    $return[] = true;
 
780
                    break;
 
781
                case 'base64':
 
782
                    $return[] = new IXR_Base64('base64');
 
783
                    break;
 
784
                case 'array':
 
785
                    $return[] = array('array');
 
786
                    break;
 
787
                case 'struct':
 
788
                    $return[] = array('struct' => 'struct');
 
789
                    break;
 
790
            }
 
791
        }
 
792
        return $return;
 
793
    }
 
794
    function methodHelp($method) {
 
795
        return $this->help[$method];
 
796
    }
 
797
}
 
798
 
 
799
 
 
800
class IXR_ClientMulticall extends IXR_Client {
 
801
    var $calls = array();
 
802
    function IXR_ClientMulticall($server, $path = false, $port = 80) {
 
803
        parent::IXR_Client($server, $path, $port);
 
804
        $this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)';
 
805
    }
 
806
    function addCall() {
 
807
        $args = func_get_args();
 
808
        $methodName = array_shift($args);
 
809
        $struct = array(
 
810
            'methodName' => $methodName,
 
811
            'params' => $args
 
812
        );
 
813
        $this->calls[] = $struct;
 
814
    }
 
815
    function query() {
 
816
        // Prepare multicall, then call the parent::query() method
 
817
        return parent::query('system.multicall', $this->calls);
 
818
    }
 
819
}
 
820
 
 
821
?>
 
 
b'\\ No newline at end of file'