00001 <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 if ( ! function_exists('xml_parser_create'))
00017 {
00018 show_error('Your PHP installation does not support XML');
00019 }
00020
00021 if ( ! class_exists('CI_Xmlrpc'))
00022 {
00023 show_error('You must load the Xmlrpc class before loading the Xmlrpcs class in order to create a server.');
00024 }
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 class CI_Xmlrpcs extends CI_Xmlrpc
00038 {
00039 var $methods = array();
00040 var $debug_msg = '';
00041 var $system_methods = array();
00042 var $controller_obj;
00043
00044 var $object = FALSE;
00045
00046
00047
00048
00049
00050
00051 function CI_Xmlrpcs($config=array())
00052 {
00053 parent::CI_Xmlrpc();
00054 $this->set_system_methods();
00055
00056 if (isset($config['functions']) && is_array($config['functions']))
00057 {
00058 $this->methods = array_merge($this->methods, $config['functions']);
00059 }
00060
00061 log_message('debug', "XML-RPC Server Class Initialized");
00062 }
00063
00064
00065
00066
00067
00068 function initialize($config=array())
00069 {
00070 if (isset($config['functions']) && is_array($config['functions']))
00071 {
00072 $this->methods = array_merge($this->methods, $config['functions']);
00073 }
00074
00075 if (isset($config['debug']))
00076 {
00077 $this->debug = $config['debug'];
00078 }
00079
00080 if (isset($config['object']) && is_object($config['object']))
00081 {
00082 $this->object = $config['object'];
00083 }
00084 }
00085
00086
00087
00088
00089
00090 function set_system_methods ()
00091 {
00092 $this->methods = array(
00093 'system.listMethods' => array(
00094 'function' => 'this.listMethods',
00095 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString), array($this->xmlrpcArray)),
00096 'docstring' => 'Returns an array of available methods on this server'),
00097 'system.methodHelp' => array(
00098 'function' => 'this.methodHelp',
00099 'signature' => array(array($this->xmlrpcString, $this->xmlrpcString)),
00100 'docstring' => 'Returns a documentation string for the specified method'),
00101 'system.methodSignature' => array(
00102 'function' => 'this.methodSignature',
00103 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString)),
00104 'docstring' => 'Returns an array describing the return type and required parameters of a method'),
00105 'system.multicall' => array(
00106 'function' => 'this.multicall',
00107 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcArray)),
00108 'docstring' => 'Combine multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details')
00109 );
00110 }
00111
00112
00113
00114
00115
00116
00117 function serve()
00118 {
00119 $r = $this->parseRequest();
00120 $payload = '<?xml version="1.0" encoding="'.$this->xmlrpc_defencoding.'"?'.'>'."\n";
00121 $payload .= $this->debug_msg;
00122 $payload .= $r->prepare_response();
00123
00124 header("Content-Type: text/xml");
00125 header("Content-Length: ".strlen($payload));
00126 echo $payload;
00127 }
00128
00129
00130
00131
00132
00133 function add_to_map($methodname,$function,$sig,$doc)
00134 {
00135 $this->methods[$methodname] = array(
00136 'function' => $function,
00137 'signature' => $sig,
00138 'docstring' => $doc
00139 );
00140 }
00141
00142
00143
00144
00145
00146
00147 function parseRequest($data='')
00148 {
00149 global $HTTP_RAW_POST_DATA;
00150
00151
00152
00153
00154
00155 if ($data == '')
00156 {
00157 $data = $HTTP_RAW_POST_DATA;
00158 }
00159
00160
00161
00162
00163
00164 $parser = xml_parser_create($this->xmlrpc_defencoding);
00165 $parser_object = new XML_RPC_Message("filler");
00166
00167 $parser_object->xh[$parser] = array();
00168 $parser_object->xh[$parser]['isf'] = 0;
00169 $parser_object->xh[$parser]['isf_reason'] = '';
00170 $parser_object->xh[$parser]['params'] = array();
00171 $parser_object->xh[$parser]['stack'] = array();
00172 $parser_object->xh[$parser]['valuestack'] = array();
00173 $parser_object->xh[$parser]['method'] = '';
00174
00175 xml_set_object($parser, $parser_object);
00176 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
00177 xml_set_element_handler($parser, 'open_tag', 'closing_tag');
00178 xml_set_character_data_handler($parser, 'character_data');
00179
00180
00181
00182
00183
00184
00185
00186 if ( ! xml_parse($parser, $data, 1))
00187 {
00188
00189 $r = new XML_RPC_Response(0,
00190 $this->xmlrpcerrxml + xml_get_error_code($parser),
00191 sprintf('XML error: %s at line %d',
00192 xml_error_string(xml_get_error_code($parser)),
00193 xml_get_current_line_number($parser)));
00194 xml_parser_free($parser);
00195 }
00196 elseif($parser_object->xh[$parser]['isf'])
00197 {
00198 return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']);
00199 }
00200 else
00201 {
00202 xml_parser_free($parser);
00203
00204 $m = new XML_RPC_Message($parser_object->xh[$parser]['method']);
00205 $plist='';
00206
00207 for($i=0; $i < sizeof($parser_object->xh[$parser]['params']); $i++)
00208 {
00209 if ($this->debug === TRUE)
00210 {
00211 $plist .= "$i - " . print_r(get_object_vars($parser_object->xh[$parser]['params'][$i]), TRUE). ";\n";
00212 }
00213
00214 $m->addParam($parser_object->xh[$parser]['params'][$i]);
00215 }
00216
00217 if ($this->debug === TRUE)
00218 {
00219 echo "<pre>";
00220 echo "---PLIST---\n" . $plist . "\n---PLIST END---\n\n";
00221 echo "</pre>";
00222 }
00223
00224 $r = $this->_execute($m);
00225 }
00226
00227
00228
00229
00230
00231 if ($this->debug === TRUE)
00232 {
00233 $this->debug_msg = "<!-- DEBUG INFO:\n\n".$plist."\n END DEBUG-->\n";
00234 }
00235
00236 return $r;
00237 }
00238
00239
00240
00241
00242
00243 function _execute($m)
00244 {
00245 $methName = $m->method_name;
00246
00247
00248 $system_call = (strncmp($methName, 'system', 5) == 0) ? TRUE : FALSE;
00249
00250
00251
00252
00253
00254 if ( ! isset($this->methods[$methName]['function']))
00255 {
00256 return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
00257 }
00258
00259
00260
00261
00262
00263 $method_parts = explode(".", $this->methods[$methName]['function']);
00264 $objectCall = (isset($method_parts['1']) && $method_parts['1'] != "") ? TRUE : FALSE;
00265
00266 if ($system_call === TRUE)
00267 {
00268 if ( ! is_callable(array($this,$method_parts['1'])))
00269 {
00270 return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
00271 }
00272 }
00273 else
00274 {
00275 if ($objectCall && ! is_callable(array($method_parts['0'],$method_parts['1'])))
00276 {
00277 return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
00278 }
00279 elseif ( ! $objectCall && ! is_callable($this->methods[$methName]['function']))
00280 {
00281 return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
00282 }
00283 }
00284
00285
00286
00287
00288
00289 if (isset($this->methods[$methName]['signature']))
00290 {
00291 $sig = $this->methods[$methName]['signature'];
00292 for($i=0; $i<sizeof($sig); $i++)
00293 {
00294 $current_sig = $sig[$i];
00295
00296 if (sizeof($current_sig) == sizeof($m->params)+1)
00297 {
00298 for($n=0; $n < sizeof($m->params); $n++)
00299 {
00300 $p = $m->params[$n];
00301 $pt = ($p->kindOf() == 'scalar') ? $p->scalarval() : $p->kindOf();
00302
00303 if ($pt != $current_sig[$n+1])
00304 {
00305 $pno = $n+1;
00306 $wanted = $current_sig[$n+1];
00307
00308 return new XML_RPC_Response(0,
00309 $this->xmlrpcerr['incorrect_params'],
00310 $this->xmlrpcstr['incorrect_params'] .
00311 ": Wanted {$wanted}, got {$pt} at param {$pno})");
00312 }
00313 }
00314 }
00315 }
00316 }
00317
00318
00319
00320
00321
00322 if ($objectCall === TRUE)
00323 {
00324 if ($method_parts[0] == "this" && $system_call == TRUE)
00325 {
00326 return call_user_func(array($this, $method_parts[1]), $m);
00327 }
00328 else
00329 {
00330 if ($this->object === FALSE)
00331 {
00332 $CI =& get_instance();
00333 return $CI->$method_parts['1']($m);
00334 }
00335 else
00336 {
00337 return $this->object->$method_parts['1']($m);
00338
00339 }
00340 }
00341 }
00342 else
00343 {
00344 return call_user_func($this->methods[$methName]['function'], $m);
00345 }
00346 }
00347
00348
00349
00350
00351
00352
00353 function listMethods($m)
00354 {
00355 $v = new XML_RPC_Values();
00356 $output = array();
00357
00358 foreach($this->methods as $key => $value)
00359 {
00360 $output[] = new XML_RPC_Values($key, 'string');
00361 }
00362
00363 foreach($this->system_methods as $key => $value)
00364 {
00365 $output[]= new XML_RPC_Values($key, 'string');
00366 }
00367
00368 $v->addArray($output);
00369 return new XML_RPC_Response($v);
00370 }
00371
00372
00373
00374
00375
00376 function methodSignature($m)
00377 {
00378 $parameters = $m->output_parameters();
00379 $method_name = $parameters[0];
00380
00381 if (isset($this->methods[$method_name]))
00382 {
00383 if ($this->methods[$method_name]['signature'])
00384 {
00385 $sigs = array();
00386 $signature = $this->methods[$method_name]['signature'];
00387
00388 for($i=0; $i < sizeof($signature); $i++)
00389 {
00390 $cursig = array();
00391 $inSig = $signature[$i];
00392 for($j=0; $j<sizeof($inSig); $j++)
00393 {
00394 $cursig[]= new XML_RPC_Values($inSig[$j], 'string');
00395 }
00396 $sigs[]= new XML_RPC_Values($cursig, 'array');
00397 }
00398 $r = new XML_RPC_Response(new XML_RPC_Values($sigs, 'array'));
00399 }
00400 else
00401 {
00402 $r = new XML_RPC_Response(new XML_RPC_Values('undef', 'string'));
00403 }
00404 }
00405 else
00406 {
00407 $r = new XML_RPC_Response(0,$this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);
00408 }
00409 return $r;
00410 }
00411
00412
00413
00414
00415
00416 function methodHelp($m)
00417 {
00418 $parameters = $m->output_parameters();
00419 $method_name = $parameters[0];
00420
00421 if (isset($this->methods[$method_name]))
00422 {
00423 $docstring = isset($this->methods[$method_name]['docstring']) ? $this->methods[$method_name]['docstring'] : '';
00424
00425 return new XML_RPC_Response(new XML_RPC_Values($docstring, 'string'));
00426 }
00427 else
00428 {
00429 return new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']);
00430 }
00431 }
00432
00433
00434
00435
00436
00437 function multicall($m)
00438 {
00439
00440 return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']);
00441
00442 $parameters = $m->output_parameters();
00443 $calls = $parameters[0];
00444
00445 $result = array();
00446
00447 foreach ($calls as $value)
00448 {
00449
00450
00451 $m = new XML_RPC_Message($value[0]);
00452 $plist='';
00453
00454 for($i=0; $i < sizeof($value[1]); $i++)
00455 {
00456 $m->addParam(new XML_RPC_Values($value[1][$i], 'string'));
00457 }
00458
00459 $attempt = $this->_execute($m);
00460
00461 if ($attempt->faultCode() != 0)
00462 {
00463 return $attempt;
00464 }
00465
00466 $result[] = new XML_RPC_Values(array($attempt->value()), 'array');
00467 }
00468
00469 return new XML_RPC_Response(new XML_RPC_Values($result, 'array'));
00470 }
00471
00472
00473
00474
00475
00476
00477 function multicall_error($err)
00478 {
00479 $str = is_string($err) ? $this->xmlrpcstr["multicall_${err}"] : $err->faultString();
00480 $code = is_string($err) ? $this->xmlrpcerr["multicall_${err}"] : $err->faultCode();
00481
00482 $struct['faultCode'] = new XML_RPC_Values($code, 'int');
00483 $struct['faultString'] = new XML_RPC_Values($str, 'string');
00484
00485 return new XML_RPC_Values($struct, 'struct');
00486 }
00487
00488
00489
00490
00491
00492
00493 function do_multicall($call)
00494 {
00495 if ($call->kindOf() != 'struct')
00496 return $this->multicall_error('notstruct');
00497 elseif ( ! $methName = $call->me['struct']['methodName'])
00498 return $this->multicall_error('nomethod');
00499
00500 list($scalar_type,$scalar_value)=each($methName->me);
00501 $scalar_type = $scalar_type == $this->xmlrpcI4 ? $this->xmlrpcInt : $scalar_type;
00502
00503 if ($methName->kindOf() != 'scalar' OR $scalar_type != 'string')
00504 return $this->multicall_error('notstring');
00505 elseif ($scalar_value == 'system.multicall')
00506 return $this->multicall_error('recursion');
00507 elseif ( ! $params = $call->me['struct']['params'])
00508 return $this->multicall_error('noparams');
00509 elseif ($params->kindOf() != 'array')
00510 return $this->multicall_error('notarray');
00511
00512 list($a,$b)=each($params->me);
00513 $numParams = sizeof($b);
00514
00515 $msg = new XML_RPC_Message($scalar_value);
00516 for ($i = 0; $i < $numParams; $i++)
00517 {
00518 $msg->params[] = $params->me['array'][$i];
00519 }
00520
00521 $result = $this->_execute($msg);
00522
00523 if ($result->faultCode() != 0)
00524 {
00525 return $this->multicall_error($result);
00526 }
00527
00528 return new XML_RPC_Values(array($result->value()), 'array');
00529 }
00530
00531 }
00532
00533
00534
00535
00536