259 lines
8.3 KiB
PHP
259 lines
8.3 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 2002-2017 Horde LLC (http://www.horde.org/)
|
|
*
|
|
* See the enclosed file COPYING for license information (LGPL). If you
|
|
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
|
|
*
|
|
* @author Jan Schneider <jan@horde.org>
|
|
* @package Rpc
|
|
*/
|
|
|
|
/**
|
|
* The Horde_Rpc class provides a set of server and client methods for RPC
|
|
* communication.
|
|
*
|
|
* TODO:
|
|
* - Introspection documentation and method signatures.
|
|
*
|
|
* EXAMPLE:
|
|
* <code>
|
|
* $response = Horde_Rpc::request('xmlrpc',
|
|
* 'http://localhost:80/horde/rpc.php',
|
|
* 'contacts.search',
|
|
* $transport_client,
|
|
* array(array('jan'), array('localsql'),
|
|
* array('name', 'email')));
|
|
* </code>
|
|
*
|
|
* @author Jan Schneider <jan@horde.org>
|
|
* @package Rpc
|
|
*/
|
|
class Horde_Rpc
|
|
{
|
|
/**
|
|
* All driver-specific parameters.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_params = array();
|
|
|
|
/**
|
|
* Do we need an authenticated user?
|
|
*
|
|
* @var boolean
|
|
*/
|
|
protected $_requireAuthorization = true;
|
|
|
|
/**
|
|
* Whether we should exit if auth fails instead of requesting
|
|
* authorization credentials.
|
|
*
|
|
* @var boolean
|
|
*/
|
|
protected $_requestMissingAuthorization = true;
|
|
|
|
/**
|
|
* Request variables, cookies etc...
|
|
*
|
|
* @var Horde_Controller_Request_Http
|
|
*/
|
|
protected $_request;
|
|
|
|
/**
|
|
* Logging
|
|
*
|
|
* @var Horde_Log_Logger
|
|
*/
|
|
protected $_logger;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param Horde_Controller_Request_Http $request The request object.
|
|
* @param array $params A hash containing any additional configuration or
|
|
* connection parameters a subclass might need.
|
|
*/
|
|
public function __construct($request, $params = array())
|
|
{
|
|
// Create a stub if we don't have a useable logger.
|
|
if (isset($params['logger'])
|
|
&& is_callable(array($params['logger'], 'log'))) {
|
|
$this->_logger = $params['logger'];
|
|
unset($params['logger']);
|
|
} else {
|
|
$this->_logger = new Horde_Support_Stub;
|
|
}
|
|
|
|
$this->_params = $params;
|
|
$this->_request = $request;
|
|
|
|
if (isset($params['requireAuthorization'])) {
|
|
$this->_requireAuthorization = $params['requireAuthorization'];
|
|
}
|
|
if (isset($params['requestMissingAuthorization'])) {
|
|
$this->_requestMissingAuthorization = $params['requestMissingAuthorization'];
|
|
}
|
|
|
|
$this->_logger->debug('Horde_Rpc::__construct complete');
|
|
}
|
|
|
|
/**
|
|
* Check authentication. Different backends may handle
|
|
* authentication in different ways. The base class implementation
|
|
* checks for HTTP Authentication against the Horde auth setup.
|
|
*
|
|
* @return boolean Returns true if authentication is successful.
|
|
* Should send appropriate "not authorized" headers
|
|
* or other response codes/body if auth fails,
|
|
* and take care of exiting.
|
|
*/
|
|
public function authorize()
|
|
{
|
|
$this->_logger->debug('Horde_Rpc::authorize() starting');
|
|
if (!$this->_requireAuthorization) {
|
|
return true;
|
|
}
|
|
|
|
// @TODO: inject this
|
|
$auth = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Auth')->create();
|
|
$serverVars = $this->_request->getServerVars();
|
|
if (!empty($serverVars['PHP_AUTH_USER'])) {
|
|
$user = $serverVars['PHP_AUTH_USER'];
|
|
$pass = $serverVars['PHP_AUTH_PW'];
|
|
} elseif (!empty($serverVars['Authorization'])) {
|
|
$hash = str_replace('Basic ', '', $serverVars['Authorization']);
|
|
$hash = base64_decode($hash);
|
|
if (strpos($hash, ':') !== false) {
|
|
list($user, $pass) = explode(':', $hash, 2);
|
|
}
|
|
}
|
|
|
|
if (!isset($user)
|
|
|| !$auth->authenticate($user, array('password' => $pass))) {
|
|
if ($this->_requestMissingAuthorization) {
|
|
header('WWW-Authenticate: Basic realm="Horde RPC"');
|
|
}
|
|
header('HTTP/1.0 401 Unauthorized');
|
|
echo '401 Unauthorized';
|
|
exit;
|
|
}
|
|
|
|
$this->_logger->debug('Horde_Rpc::authorize() exiting');
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get the request body input. Different RPC backends can override
|
|
* this to return an open stream to php://stdin, for instance -
|
|
* whatever is easiest to handle in the getResponse() method.
|
|
*
|
|
* The base class implementation looks for $HTTP_RAW_POST_DATA and
|
|
* returns that if it's available; otherwise, it returns the
|
|
* contents of php://stdin.
|
|
*
|
|
* @return mixed The input - a string (default), a filehandle, etc.
|
|
*/
|
|
public function getInput()
|
|
{
|
|
if (isset($GLOBALS['HTTP_RAW_POST_DATA'])) {
|
|
return $GLOBALS['HTTP_RAW_POST_DATA'];
|
|
} else {
|
|
return implode("\r\n", file('php://input'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sends an RPC request to the server implementation and returns the
|
|
* result.
|
|
*
|
|
* @param string The raw request string.
|
|
*
|
|
* @return string The response from the server.
|
|
*/
|
|
public function getResponse($request)
|
|
{
|
|
return 'not implemented';
|
|
}
|
|
|
|
/**
|
|
* Returns the Content-Type of the response.
|
|
*
|
|
* @return string The MIME Content-Type of the RPC response.
|
|
*/
|
|
public function getResponseContentType()
|
|
{
|
|
return 'text/xml';
|
|
}
|
|
|
|
/**
|
|
* Send the output back to the client
|
|
*
|
|
* @param string $output The output to send back to the client. Can be
|
|
* overridden in classes if needed.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function sendOutput($output)
|
|
{
|
|
header('Content-Type: ' . $this->getResponseContentType());
|
|
header('Content-length: ' . strlen($output));
|
|
header('Accept-Charset: UTF-8');
|
|
echo $output;
|
|
}
|
|
|
|
/**
|
|
* Builds an RPC request and sends it to the RPC server.
|
|
*
|
|
* This statically called method is actually the RPC client.
|
|
*
|
|
* @param string $driver The protocol driver to use. Currently
|
|
* 'soap', 'xmlrpc' and 'jsonrpc' are
|
|
* available.
|
|
* @param string|Horde_Url $url The path to the RPC server on the called
|
|
* host.
|
|
* @param string $method The method to call.
|
|
* @param mixed $client An appropriate request client for the type
|
|
* of request. (Horde_Http_Request, SoapClient
|
|
* etc..)
|
|
* @param array $params A hash containing any necessary parameters
|
|
* for the method call.
|
|
*
|
|
* @return mixed The returned result from the method
|
|
* @throws Horde_Rpc_Exception
|
|
*/
|
|
public static function request($driver, $url, $method, $client,
|
|
$params = null)
|
|
{
|
|
$driver = Horde_String::ucfirst(basename($driver));
|
|
$class = 'Horde_Rpc_' . $driver;
|
|
if (class_exists($class)) {
|
|
return call_user_func(array($class, 'request'), $url, $method, $client, $params);
|
|
} else {
|
|
throw new Horde_Rpc_Exception('Class definition of ' . $class . ' not found.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attempts to return a concrete RPC server instance based on
|
|
* $driver.
|
|
*
|
|
* @param mixed $driver The type of concrete Horde_Rpc subclass to return.
|
|
* @param array $params A hash containing any additional configuration or
|
|
* connection parameters a subclass might need.
|
|
*
|
|
* @return Horde_Rpc The newly created concrete Horde_Rpc server instance,
|
|
* or an exception if there is an error.
|
|
*/
|
|
public static function factory($driver, $request, $params = null)
|
|
{
|
|
$driver = basename($driver);
|
|
$class = 'Horde_Rpc_' . $driver;
|
|
if (class_exists($class)) {
|
|
return new $class($request, $params);
|
|
} else {
|
|
throw new Horde_Rpc_Exception('Class definition of ' . $class . ' not found.');
|
|
}
|
|
}
|
|
}
|