blob: eaec87a6d8ce1f344b8f9e3d48d3a80294b38bb3 [file] [log] [blame]
adminb0dd10f2006-08-25 17:25:49 +00001<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * Code Igniter
4 *
5 * An open source application development framework for PHP 4.3.2 or newer
6 *
7 * @package CodeIgniter
8 * @author Rick Ellis
9 * @copyright Copyright (c) 2006, pMachine, Inc.
10 * @license http://www.codeignitor.com/user_guide/license.html
11 * @link http://www.codeigniter.com
12 * @since Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * XML-RPC server class
20 *
21 * @package CodeIgniter
22 * @subpackage Libraries
23 * @category XML-RPC
24 * @author Paul Burdick
25 * @link http://www.codeigniter.com/user_guide/libraries/xmlrpc.html
26 */
27class CI_XML_RPC_Server extends CI_XML_RPC
28{
29 var $methods = array(); //array of methods mapped to function names and signatures
30 var $debug_msg = ''; // Debug Message
31 var $system_methods = array(); // XML RPC Server methods
32 var $controller_obj;
33
34
35 //-------------------------------------
36 // Constructor, more or less
37 //-------------------------------------
38
39 function CI_XML_RPC_Server($config=array())
40 {
41 parent::CI_XML_RPC();
42 $this->set_system_methods();
43
44 if (isset($config['functions']) && is_array($config['functions']))
45 {
46 $this->methods = $config['functions'];
47 }
48
49 log_message('debug', "XML-RPC Server Class Initialized");
50 }
51
52 //-------------------------------------
53 // Initialize Prefs and Serve
54 //-------------------------------------
55
56 function initialize($config=array())
57 {
58 if (isset($config['functions']) && is_array($config['functions']))
59 {
60 $this->methods = $config['functions'];
61 }
62
63 if (isset($config['debug']))
64 {
65 $this->debug = $config['debug'];
66 }
67 }
68
69 //-------------------------------------
70 // Setting of System Methods
71 //-------------------------------------
72
73 function set_system_methods ()
74 {
75 $system_methods = array(
76 'system.listMethods' => array(
77 'function' => 'this.listMethods',
78 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString), array($this->xmlrpcArray)),
79 'docstring' => 'Returns an array of available methods on this server'),
80 'system.methodHelp' => array(
81 'function' => 'this.methodHelp',
82 'signature' => array(array($this->xmlrpcString, $this->xmlrpcString)),
83 'docstring' => 'Returns a documentation string for the specified method'),
84 'system.methodSignature' => array(
85 'function' => 'this.methodSignature',
86 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString)),
87 'docstring' => 'Returns an array describing the return type and required parameters of a method'),
88 'system.multicall' => array(
89 'function' => 'this.multicall',
90 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcArray)),
91 'docstring' => 'Combine multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details')
92 );
93 }
94
95
96 //-------------------------------------
97 // Main Server Function
98 //-------------------------------------
99
100 function serve()
101 {
102 $r = $this->parseRequest();
103 $payload = '<?xml version="1.0" encoding="'.$this->xmlrpc_defencoding.'"?'.'>'."\n";
104 $payload .= $this->debug_msg;
105 $payload .= $r->prepare_response();
106
107 header("Content-Type: text/xml");
108 header("Content-Length: ".strlen($payload));
109 echo $payload;
110 }
111
112 //-------------------------------------
113 // Add Method to Class
114 //-------------------------------------
115
116 function add_to_map($methodname,$function,$sig,$doc)
117 {
118 $this->methods[$methodname] = array(
119 'function' => $function,
120 'signature' => $sig,
121 'docstring' => $doc
122 );
123 }
124
125
126 //-------------------------------------
127 // Parse Server Request
128 //-------------------------------------
129
130 function parseRequest($data='')
131 {
132 global $HTTP_RAW_POST_DATA;
133
134 //-------------------------------------
135 // Get Data
136 //-------------------------------------
137
138 if ($data == '')
139 {
140 $data = $HTTP_RAW_POST_DATA;
141 }
142
143
144 //-------------------------------------
145 // Set up XML Parser
146 //-------------------------------------
147
148 $parser = xml_parser_create($this->xmlrpc_defencoding);
149 $parser_object = new XML_RPC_Message("filler");
150
151 $parser_object->xh[$parser] = array();
152 $parser_object->xh[$parser]['isf'] = 0;
153 $parser_object->xh[$parser]['isf_reason'] = '';
154 $parser_object->xh[$parser]['params'] = array();
155 $parser_object->xh[$parser]['stack'] = array();
156 $parser_object->xh[$parser]['valuestack'] = array();
157 $parser_object->xh[$parser]['method'] = '';
158
159 xml_set_object($parser, $parser_object);
160 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
161 xml_set_element_handler($parser, 'open_tag', 'closing_tag');
162 xml_set_character_data_handler($parser, 'character_data');
163 //xml_set_default_handler($parser, 'default_handler');
164
165
166 //-------------------------------------
167 // PARSE + PROCESS XML DATA
168 //-------------------------------------
169
170 if ( ! xml_parse($parser, $data, 1))
171 {
172 // return XML error as a faultCode
173 $r = new XML_RPC_Response(0,
174 $this->xmlrpcerrxml + xml_get_error_code($parser),
175 sprintf('XML error: %s at line %d',
176 xml_error_string(xml_get_error_code($parser)),
177 xml_get_current_line_number($parser)));
178 xml_parser_free($parser);
179 }
180 elseif($parser_object->xh[$parser]['isf'])
181 {
182 return new XML_RPC_Response(0,
183 $this->xmlrpcerr['invalid_return'],
184 $this->xmlrpcstr['invalid_retrun']);
185 }
186 else
187 {
188 xml_parser_free($parser);
189
190 $m = new XML_RPC_Message($parser_object->xh[$parser]['method']);
191 $plist='';
192
193 for($i=0; $i < sizeof($parser_object->xh[$parser]['params']); $i++)
194 {
195 $plist .= "$i - " . print_r(get_object_vars($parser_object->xh[$parser]['params'][$i]), TRUE). ";\n";
196
197 $m->addParam($parser_object->xh[$parser]['params'][$i]);
198 }
199
200 if ($this->debug === TRUE)
201 {
202 echo "<pre>";
203 echo "---PLIST---\n" . $plist . "\n---PLIST END---\n\n";
204 echo "</pre>";
205 }
206
207 $r = $this->execute($m);
208 }
209
210 //-------------------------------------
211 // SET DEBUGGING MESSAGE
212 //-------------------------------------
213
214 if ($this->debug === TRUE)
215 {
216 $this->debug_msg = "<!-- DEBUG INFO:\n\n".$plist."\n END DEBUG-->\n";
217 }
218
219 return $r;
220 }
221
222 //-------------------------------------
223 // Executes the Method
224 //-------------------------------------
225
226 function execute($m)
227 {
228 $methName = $m->method_name;
229
230 // Check to see if it is a system call
231 // If so, load the system_methods
232 $sysCall = ereg("^system\.", $methName);
233 $methods = $sysCall ? $this->system_methods : $this->methods;
234
235 //-------------------------------------
236 // Check for Function
237 //-------------------------------------
238
239 if (!isset($methods[$methName]['function']))
240 {
241 return new XML_RPC_Response(0,
242 $this->xmlrpcerr['unknown_method'],
243 $this->xmlrpcstr['unknown_method']);
244 }
245 else
246 {
247 // See if we are calling function in an object
248
249 $method_parts = explode(".",$methods[$methName]['function']);
250 $objectCall = (isset($method_parts['1']) && $method_parts['1'] != "") ? true : false;
251
252 if ($objectCall && !is_callable(array($method_parts['0'],$method_parts['1'])))
253 {
254 return new XML_RPC_Response(0,
255 $this->xmlrpcerr['unknown_method'],
256 $this->xmlrpcstr['unknown_method']);
257 }
258 elseif (!$objectCall && !is_callable($methods[$methName]['function']))
259 {
260 return new XML_RPC_Response(0,
261 $this->xmlrpcerr['unknown_method'],
262 $this->xmlrpcstr['unknown_method']);
263 }
264 }
265
266 //-------------------------------------
267 // Checking Methods Signature
268 //-------------------------------------
269
270 if (isset($methods[$methName]['signature']))
271 {
272 $sig = $methods[$methName]['signature'];
273 for($i=0; $i<sizeof($sig); $i++)
274 {
275 $current_sig = $sig[$i];
276
277 if (sizeof($current_sig) == sizeof($m->params)+1)
278 {
279 for($n=0; $n < sizeof($m->params); $n++)
280 {
281 $p = $m->params[$n];
282 $pt = ($p->kindOf() == 'scalar') ? $p->scalartyp() : $p->kindOf();
283
284 if ($pt != $current_sig[$n+1])
285 {
286 $pno = $n+1;
287 $wanted = $current_sig[$n+1];
288
289 return new XML_RPC_Response(0,
290 $this->xmlrpcerr['incorrect_params'],
291 $this->xmlrpcstr['incorrect_params'] .
292 ": Wanted {$wanted}, got {$pt} at param {$pno})");
293 }
294 }
295 }
296 }
297 }
298
299 //-------------------------------------
300 // Calls the Function
301 //-------------------------------------
302
303 if ($objectCall)
304 {
305 if ($method_parts['1'] == "this")
306 {
307 return call_user_func(array($this, $method_parts['0']), $m);
308 }
309 else
310 {
311 $obj =& get_instance();
312 return $obj->$method_parts['1']($m);
313 //$class = new $method_parts['0'];
314 //return $class->$method_parts['1']($m);
315 //return call_user_func(array(&$method_parts['0'],$method_parts['1']), $m);
316 }
317 }
318 else
319 {
320 return call_user_func($methods[$methName]['function'], $m);
321 }
322 }
323
324
325 //-------------------------------------
326 // Server Function: List Methods
327 //-------------------------------------
328
329 function listMethods($m)
330 {
331 $v = new XML_RPC_Values();
332 $output = array();
333 foreach($this->$methods as $key => $value)
334 {
335 $output[] = new XML_RPC_Values($key, 'string');
336 }
337
338 foreach($this->system_methods as $key => $value)
339 {
340 $output[]= new XML_RPC_Values($key, 'string');
341 }
342
343 $v->addArray($output);
344 return new XML_RPC_Response($v);
345 }
346
347 //-------------------------------------
348 // Server Function: Return Signature for Method
349 //-------------------------------------
350
351 function methodSignature($m)
352 {
353 $methName = $m->getParam(0);
354 $method_name = $methName->scalarval();
355
356 $methods = ereg("^system\.", $method_name) ? $this->system_methods : $this->methods;
357
358 if (isset($methods[$method_name]))
359 {
360 if ($methods[$method_name]['signature'])
361 {
362 $sigs = array();
363 $signature = $methods[$method_name]['signature'];
364
365 for($i=0; $i < sizeof($signature); $i++)
366 {
367 $cursig = array();
368 $inSig = $signature[$i];
369 for($j=0; $j<sizeof($inSig); $j++)
370 {
371 $cursig[]= new XML_RPC_Values($inSig[$j], 'string');
372 }
373 $sigs[]= new XML_RPC_Values($cursig, 'array');
374 }
375 $r = new XML_RPC_Response(new XML_RPC_Values($sigs, 'array'));
376 }
377 else
378 {
379 $r = new XML_RPC_Response(new XML_RPC_Values('undef', 'string'));
380 }
381 }
382 else
383 {
384 $r = new XML_RPC_Response(0,$this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);
385 }
386 return $r;
387 }
388
389 //-------------------------------------
390 // Server Function: Doc String for Method
391 //-------------------------------------
392
393 function methodHelp($m)
394 {
395 $methName = $m->getParam(0);
396 $method_name = $methName->scalarval();
397
398 $methods = ereg("^system\.", $method_name) ? $this->system_methods : $this->methods;
399
400 if (isset($methods[$methName]))
401 {
402 $docstring = isset($methods[$method_name]['docstring']) ? $methods[$method_name]['docstring'] : '';
403 $r = new XML_RPC_Response(new XML_RPC_Values($docstring, 'string'));
404 }
405 else
406 {
407 $r = new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);
408 }
409 return $r;
410 }
411
412 //-------------------------------------
413 // Server Function: Multi-call
414 //-------------------------------------
415
416 function multicall($m)
417 {
418 $calls = $m->getParam(0);
419 list($a,$b)=each($calls->me);
420 $result = array();
421
422 for ($i = 0; $i < sizeof($b); $i++)
423 {
424 $call = $calls->me['array'][$i];
425 $result[$i] = $this->do_multicall($call);
426 }
427
428 return new XML_RPC_Response(new XML_RPC_Values($result, 'array'));
429 }
430
431
432 //-------------------------------------
433 // Multi-call Function: Error Handling
434 //-------------------------------------
435
436 function multicall_error($err)
437 {
438 $str = is_string($err) ? $this->xmlrpcstr["multicall_${err}"] : $err->faultString();
439 $code = is_string($err) ? $this->xmlrpcerr["multicall_${err}"] : $err->faultCode();
440
441 $struct['faultCode'] = new XML_RPC_Values($code, 'int');
442 $struct['faultString'] = new XML_RPC_Values($str, 'string');
443
444 return new XML_RPC_Values($struct, 'struct');
445 }
446
447
448 //-------------------------------------
449 // Multi-call Function: Processes method
450 //-------------------------------------
451
452 function do_multicall($call)
453 {
454 if ($call->kindOf() != 'struct')
455 return $this->multicall_error('notstruct');
456 elseif (!$methName = $call->me['struct']['methodName'])
457 return $this->multicall_error('nomethod');
458
459 list($scalar_type,$scalar_value)=each($methName->me);
460 $scalar_type = $scalar_type == $this->xmlrpcI4 ? $this->xmlrpcInt : $scalar_type;
461
462 if ($methName->kindOf() != 'scalar' || $scalar_type != 'string')
463 return $this->multicall_error('notstring');
464 elseif ($scalar_value == 'system.multicall')
465 return $this->multicall_error('recursion');
466 elseif (!$params = $call->me['struct']['params'])
467 return $this->multicall_error('noparams');
468 elseif ($params->kindOf() != 'array')
469 return $this->multicall_error('notarray');
470
471 list($a,$b)=each($params->me);
472 $numParams = sizeof($b);
473
474 $msg = new XML_RPC_Message($scalar_value);
475 for ($i = 0; $i < $numParams; $i++)
476 {
477 $msg->params[] = $params->me['array'][$i];
478 }
479
480 $result = $this->execute($msg);
481
482 if ($result->faultCode() != 0)
483 {
484 return $this->multicall_error($result);
485 }
486
487 return new XML_RPC_Values(array($result->value()), 'array');
488 }
489
490}
491// END XML_RPC_Server class
492?>