So, habe mal versucht, den Converter ans Laufen zu kriegen, habe allerdings Probleme.
Code: Alles auswählen
<?php
/**
* An XML-RPC server
*
* @author Donal McMullan donal@catalyst.net.nz
* @version 0.0.1
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package mnet
*/
/**
* This class represents the handler for XML-RPC requests, and dispatches
* to extensions to handle specific calls
*
* The server will read the required data directly off the POST stream, all
* you need to do to dispatch a request to the server is call:
* mnet_xmlrpc_server::handle_request();
*
* Once invoked, the server will output directly back to the output buffer,
* and will not return.
*
*/
class mnet_xmlrpc_server {
// stores the current remotely connected client
private static $remote_client;
/**
* Called statically, this invokes the XMLRPC server and asks it to
* parse the current request
*
*/
public static function handle_request() {
$instance = new mnet_xmlrpc_server();
$instance->_handle_request();
}
/**
* This method returns true if the MNet server is currently handling a
* valid request. Your code may check this before performing an action that
* should only be dispatched by XML-RPC, as it with return true only iff
* a valid remote endpoint has called in.
*
* Tip: Use this as an alternative to checking the global self::$remote_client
* in earlier versions of MNet
*
* @return bool true if we have a valid remote endpoint making a request currently
*/
public static function is_in_request() {
return isset(self::$remote_client);
}
/**
* Returns the currently connected client
*
* @return mnet_remote_client the currently connected client, or null if none
*/
public static function get_current_client() {
return self::$remote_client;
}
/**
* Empty private constructor - invoke using static handle_request() instead
*
*/
private function __construct() {
}
/**
* After instanciating a singleton of this class, this
* method takes care of actually processing the incoming
* request
*
*/
private function _handle_request() {
global $MNET, $MNET_SYSTEM;
$MNET_SYSTEM->log(MNET_LOG_DEBUG, 'Handling incoming request (unknown so far)');
// Set up output headers
header('Content-type: text/xml; charset=utf-8', true);
try {
// Ensure $_SERVER exists
if (!isset($_SERVER)) {
throw new xmlrpc_fault(712, 'phperror');
}
// PHP 5.2.2: $HTTP_RAW_POST_DATA not populated bug:
// http://bugs.php.net/bug.php?id=41293
if (empty($HTTP_RAW_POST_DATA)) {
$HTTP_RAW_POST_DATA = file_get_contents('php://input');
}
// Check that something was POSTed
if (empty($HTTP_RAW_POST_DATA)) {
throw new xmlrpc_fault(712, 'norequest');
}
// Set the $remote_client to null for now since we don't yet know who we're talking to
self::$remote_client = null;
// Construct an mnet_message out of the incoming request
$request = new mnet_message($HTTP_RAW_POST_DATA);
if ($request->get_state() == mnet_message::STATE_INVALID) {
// the incoming request isn't valid XML
$MNET_SYSTEM->log(MNET_LOG_ERR, "Incoming XMLRPC request isn't valid XML");
throw new xmlrpc_fault(712, 'invalidxml');
}
else if ($request->get_state() == mnet_message::STATE_CLEARTEXT) {
// Cleartext XML-RPC is only allowed for system/keyswap
$method = null; // modifed by reference
$params = xmlrpc_decode_request($xmlrpcrequest, $method);
if ($method != 'system/keyswap') {
// Not allowed
throw new xmlrpc_fault(7021, 'forbidden transport');
}
}
// The request is either encrypted or signed, either way it should have a wwwroot
$wwwroot = $request->get_wwwroot();
if (empty($wwwroot)) {
throw new xmlrpc_fault(7020, 'no wwwroot found');
}
self::$remote_client = $MNET_SYSTEM->get_peer_by_wwwroot($wwwroot);
if (!self::$remote_client) {
throw new xmlrpc_fault(7020, 'unknown wwwroot');
}
// Now continue checking the message
if ($request->get_state() == mnet_message::STATE_SIGNED) {
// This message is signed but not encrypted. If we trust the host
// then this is OK
if (!self::$remote_client->plaintext_is_ok()) {
throw new xmlrpc_fault(7021, 'forbidden transport');
}
}
else if ($request->get_state() == mnet_message::STATE_ENCRYPTED) {
// the message is encrypted. attempt to unlock it with our private key
try {
$request->decrypt($MNET->get_keypair_pem());
}
catch (mnet_message_encryption_error $e) {
// our current key didn't work, hunt through our history for one that might
$keypair_history = $MNET_SYSTEM->get_keypair_history();
foreach ($keypair_history as $keypair) {
try {
$request->decrypt($keypair['keypair_PEM']);
// if we get here -> success
// Now we need to inform the client that their public key for us
// is out-of-date. We do this by raising a '7025' XMLRPC fault, including
// our latest public key in the faultString. This fault will be encrypted
// using our older private key that matches the key the client used to
// make this request, therefore the client knows they can trust the result
// still
$MNET_SYSTEM->log(MNET_LOG_NOTICE, 'Replying with 7025 to supply '.self::$remote_client.' with new pubkey');
exit($this->fault_xml(7025, $MNET->get_public_key_cert(), $keypair['keypair_PEM']));
}
catch (mnet_message_encryption_error $e2) {} // ignore
}
// If we get here, none of our keys worked, error out
$MNET_SYSTEM->log(MNET_LOG_ERR, 'Unable to find a working keypair for this request');
throw new xmlrpc_fault(7023, 'encryption invalid (unable to find a working keypair)');
}
}
// We expect the message to be in the signed state
if ($request->get_state() != mnet_message::STATE_SIGNED) {
// this is rather unexpected... (breaking mnet protocol)
$MNET_SYSTEM->log(MNET_LOG_ERR, 'Request is not signed when it is expected to be');
throw new xmlrpc_fault(711, 'message not signed');
}
// Decryption was fine, check the signature is valid for this client
if (!$request->check_signature(self::$remote_client->get_public_key())) {
$MNET_SYSTEM->log(MNET_LOG_NOTICE, 'Incoming request is not signed with the expected key, checking for an updated pubkey');
// Call the client back - if their public key has changed, they'll send the 7025 fault (see above)
self::$remote_client->check_for_key_update();
if (!$request->check_signature(self::$remote_client->get_public_key())) {
$MNET_SYSTEM->log(MNET_LOG_ERR, 'No new key -> signature is invalid');
throw new xmlrpc_fault(710, 'signature not recognised');
}
}
// if we get here, the signature is valid
self::$remote_client->touch();
$request->strip_signature();
// dispatch the XML-RPC request - will output the result directly
$this->dispatch($request->get_xml());
}
catch (Exception $e) {
if (get_class($e) != 'xmlrpc_fault') {
// convert it into one
$e = new xmlrpc_fault(712, 'Uncaught exception at server: '.$e);
}
// convert the XML-RPC fault exception to a real fault
exit($this->fault_xml($e->getCode(), $e->getMessage(), $MNET->get_keypair_pem()));
}
}
/**
* Construct an XML-RPC fault message, encrypting towards the provided
* private key if supplied
*
* @param int $code The ID code of the error message
* @param string $text The error message
* @param resource $privatekey The private key that should be used to sign the response (or null to not sign or encrypt the response)
* @return string $text The XML text of the error message
*/
private function fault_xml($code, $text, $privatekey) {
if (!is_numeric($code)) {
$code = 0;
}
$code = intval($code);
$return = $this->prepare_response('<?xml version="1.0"?>
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>'.intval($code).'</int></value>
</member>
<member>
<name>faultString</name>
<value><string>'.htmlspecialchars($text).'</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>', $privatekey);
return $return;
}
/**
* Package a response in any required envelope, and return it to the client
*
* @param string $response_xml The XMLRPC response string
* @param resource $privatekey The private key to sign the response with, or null to not sign/encrypt
* @return string The encoded response string
*/
private function prepare_response($response_xml, $privatekey) {
global $MNET, $MNET_SYSTEM;
$response = new mnet_message($response_xml);
if ($privatekey && is_object(self::$remote_client)) {
$response->sign($privatekey);
$response->encrypt(self::$remote_client->get_public_key());
}
return $response->get_xml();
}
/**
* If security checks are passed, dispatch the request to the extension/method
*
* @param string $payload The XML-RPC request
* @return No return val - just echo the response
*/
private function dispatch($payload) {
global $MNET, $MNET_SYSTEM;
// xmlrpc_decode_request returns an array of parameters, and the $method
// variable (which is passed by reference) is instantiated with the value from
// the methodName tag in the xml payload
// xmlrpc_decode_request($xml, &$method)
$params = xmlrpc_decode_request($payload, $method);
// $method is in the form extensionname/function or system/function
// $params is an array of parameters. A parameter might itself be an array.
// Whitelist characters that are permitted in a method name
// The method name must not begin with a / - avoid absolute paths
// A dot character . is only allowed in the filename, i.e. something.php
if (0 == preg_match("@^[A-Za-z0-9]+/[A-Za-z0-9/_-]+(\.php/)?[A-Za-z0-9_-]+$@", $method)) {
throw new xmlrpc_fault(713, 'nosuchfunction');
}
if (preg_match("/^system\./", $method)) {
$callstack = explode('.', $method);
} else {
$callstack = explode('/', $method);
// the callstack in this instance might be a legacy call to the "custom" extension,
// passing array('mod', 'forum', 'lib.php', 'forum_add_instance');
// Or in modern times, array('mnet', 'extname', 'methodname')
// If it starts with the first element 'mnet' then we know it's going to name an extension
// rather than a file path
}
// will store the required call params
$extension = null;
$method = null;
if ($callstack[0] == 'system' && count($callstack) == 2) {
// Special case - map this to ths system extension
$extension = 'system';
$method = $callstack[1];
}
else if ($callstack[0] == 'mnet' && count($callstack) == 3) {
// Specifies an MNet-2 extension name directly
$extension = $callstack[1];
$method = $callstack[2];
}
else if (in_array($callstack[0], array('auth', 'enrol', 'portfolio'))) {
// this is a call to a legacy service from an MNet-1 client. Map it to
// the appropriate new extension
switch ($callstack[0]) {
case 'auth' : $extension = 'authentication'; break;
case 'enrol' : $extension = 'enrolment'; break;
case 'portfolio': $extension = 'portfolio'; break;
default : $extension = null;
}
}
else if ($callstack[0] == 'mod') {
// This is a legacy MNet-1 client call to the "Custom" extension to execute
// arbitrary methods. Assume they're targeting a moodle system and use the call_moodle()
// method to invoke some code within Moodle.
$extension = 'custom';
$method = 'call_moodle';
}
if (!$extension || !$method) {
// unknown call
throw new xmlrpc_fault(7012, 'nosuchfunction');
}
// Get an instance of the extension
$extension_instance = mnet_extension::get($extension);
if (!is_object($extension_instance)) {
throw new xmlrpc_fault(704, 'nosuchservice');
}
// Check if they have access to this extension and this method
if (!$extension_instance->check_call_access(self::$remote_client, $method)) {
// access denied
throw new xmlrpc_fault(702, 'nosuchservice');
}
// Validate the method and params for this extension
if (!$extension_instance->validate_call($method, $params)) {
// invalid method or param count
throw new xmlrpc_fault(702, 'nosuchfunction');
}
// Everything checks out, execute this method on this extension via the dummy
// and get the XML-RPC encoded result
$xmlrpc_server = xmlrpc_server_create();
$xmlrpc_method = implode('/', $callstack);
xmlrpc_server_register_method($xmlrpc_server, $xmlrpc_method, array($this, 'dispatch_dummy'));
// we pass the actual callback through to the dummy via the $user_data param
$response = xmlrpc_server_call_method($xmlrpc_server, $payload, array($extension_instance, 'rpc_'.$method), array('encoding' => 'utf-8'));
// Encrypt and sign the XML response
$response = $this->prepare_response($response, $MNET->get_keypair_pem());
// output the response to the output stream
echo $response;
return;
}
/**
* Because PHP's XMLRPC calls with the convention of func($methodname, $params_array, $user_data),
* we use this dummy adapter to convert it to a normal call of func($param1, $param2, ...) and return
* the result. For this, the user data parameter is filled with a callback suitable for call_user_func_array()
*
* @param string $method the original xmlrpc method, eg. system/keyswap. Ignored
* @param array $params the parameters to pass through
* @param callback $callback the callback to actually call, from user data
* @return mixed whatever the actual method returns
*/
private function dispatch_dummy($method, $params, $callback) {
return call_user_func_array($callback, $params);
}
}
Was mache ich falsch? Das ganze läuft auf meiner Qnap, auf der auch der Historian läuft. PHP ist 5.2.14 mit