Files
server/usr/share/psa-pear/pear/php/Horde/Rpc/ActiveSync.php
2026-01-07 20:52:11 +01:00

253 lines
8.7 KiB
PHP

<?php
/**
* Copyright 2009-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 Michael J Rubinsky <mrubinsk@horde.org>
*
* @category Horde
* @package Rpc
*/
class Horde_Rpc_ActiveSync extends Horde_Rpc
{
/**
* Holds the request's GET variables
*
* @var array
*/
protected $_get = array();
/**
* The ActiveSync server object
*
* @var Horde_ActiveSync
*/
protected $_server;
/**
* Content type header to send in response.
*
* @var string
*/
protected $_contentType = 'application/vnd.ms-sync.wbxml';
/**
* Constructor.
*
* @param Horde_Controller_Request_Http The request object.
*
* @param array $params A hash containing configuration parameters:
* - server: (Horde_ActiveSync) The ActiveSync server object.
* DEFAULT: none, REQUIRED
*/
public function __construct(Horde_Controller_Request_Http $request, array $params = array())
{
parent::__construct($request, $params);
// Use the server's getGetVars() method since they might be transmitted
// as base64 encoded binary data.
$serverVars = $request->getServerVars();
$this->_get = $params['server']->getGetVars();
if ($request->getMethod() == 'POST' &&
(((empty($this->_get['Cmd']) || empty($this->_get['DeviceId']) ||
empty($this->_get['DeviceType'])) && empty($serverVars['QUERY_STRING'])) &&
stripos($serverVars['REQUEST_URI'], 'autodiscover/autodiscover') === false)) {
$this->_logger->err('Missing required parameters.');
throw new Horde_Rpc_Exception('Your device requested the ActiveSync URL wihtout required parameters.');
}
$this->_server = $params['server'];
}
/**
* Returns the Content-Type of the response.
*
* @return string The MIME Content-Type of the RPC response.
*/
public function getResponseContentType()
{
return $this->_contentType;
}
/**
* Horde_ActiveSync will read the input stream directly, do not access
* it here.
*
* @see Horde_Rpc#getInput()
*/
public function getInput()
{
return null;
}
/**
* Sends an RPC request to the server and returns the result.
*
* @param string $request PHP input stream (ignored).
*/
public function getResponse($request)
{
ob_start(null, 1048576);
$serverVars = $this->_request->getServerVars();
switch ($serverVars['REQUEST_METHOD']) {
case 'OPTIONS':
case 'GET':
if ($serverVars['REQUEST_METHOD'] == 'GET' &&
$this->_get['Cmd'] != 'OPTIONS' &&
stripos($serverVars['REQUEST_URI'], 'autodiscover/autodiscover') === false) {
$this->_logger->debug('Accessing ActiveSync endpoint from browser or missing required data.');
throw new Horde_Rpc_Exception(
Horde_Rpc_Translation::t('Trying to access the ActiveSync endpoint from a browser. Not Supported.'));
}
if (stripos($serverVars['REQUEST_URI'], 'autodiscover/autodiscover') !== false) {
try {
$result = $this->_server->handleRequest('Autodiscover', null);
if (!$result) {
$this->_logger->err('Unknown error during Autodiscover.');
throw new Horde_Exception('Unknown Error');
}
$this->_contentType = $result;
} catch (Horde_Exception_AuthenticationFailure $e) {
$this->_sendAuthenticationFailedHeaders($e);
exit;
} catch (Horde_Exception $e) {
$this->_handleError($e);
}
break;
}
$this->_logger->debug('Horde_Rpc_ActiveSync::getResponse() starting for OPTIONS');
try {
if (!$this->_server->handleRequest('Options', null)) {
throw new Horde_Exception('Unknown Error');
}
} catch (Horde_Exception_AuthenticationFailure $e) {
$this->_sendAuthenticationFailedHeaders($e);
exit;
} catch (Horde_Exception $e) {
$this->_handleError($e);
}
break;
case 'POST':
// Autodiscover Request
if (stripos($serverVars['REQUEST_URI'], 'autodiscover/autodiscover.xml') !== false) {
$this->_get['Cmd'] = 'Autodiscover';
$this->_get['DeviceId'] = null;
}
$this->_logger->debug('Horde_Rpc_ActiveSync::getResponse() starting for ' . $this->_get['Cmd']);
try {
$ret = $this->_server->handleRequest($this->_get['Cmd'], $this->_get['DeviceId']);
if ($ret === false) {
throw new Horde_Rpc_Exception(sprintf(
'Received FALSE while handling %s command.', $this->_get['Cmd']));
} elseif ($ret !== true) {
$this->_contentType = $ret;
}
} catch (Horde_ActiveSync_Exception_InvalidRequest $e) {
$this->_logger->err(sprintf(
'Returning HTTP 400 while handling %s command. Error is: %s',
$this->_get['Cmd'], $e->getMessage()));
$this->_handleError($e);
header('HTTP/1.1 400 Invalid Request');
exit;
} catch (Horde_Exception_AuthenticationFailure $e) {
$this->_sendAuthenticationFailedHeaders($e);
exit;
} catch (Horde_Exception $e) {
$this->_logger->err(sprintf(
'Returning HTTP 500 while handling %s command. Error is: %s',
$this->_get['Cmd'],
$e->getMessage()));
$this->_handleError($e);
header('HTTP/1.1 500');
exit;
}
break;
}
}
/**
* Override the authorize method and always return true. The ActiveSync
* server classes handle authentication directly since we need complete
* control over what responses are sent.
*
* @return boolean
*/
public function authorize()
{
return true;
}
/**
*
* @see Horde_Rpc#sendOutput($output)
*/
public function sendOutput($output)
{
// Unfortunately, even though we can stream the data to the client
// with a chunked encoding, using chunked encoding also breaks the
// progress bar on the PDA. So we de-chunk here and just output a
// content-length header and send it as a 'normal' packet. If the output
// packet exceeds 1MB (see ob_start) then it will be sent as a chunked
// packet anyway because PHP will have to flush the buffer.
$len = ob_get_length();
$data = ob_get_contents();
ob_end_clean();
if (!headers_sent()) {
header('Content-Length: ' . $len);
header('Content-Type: ' . $this->_contentType);
flush();
echo $data;
} else {
flush();
sleep(2);
$this->_logger->debug('Output ' . $len . ' Bytes of data found in content buffer since output started');
echo $data;
}
}
/**
* Output exception information to the logger.
*
* @param Exception $e The exception
*
* @throws Horde_Rpc_Exception $e
*/
protected function _handleError($e)
{
$trace = $e->getTraceAsString();
$m = $e->getMessage();
$buffer = ob_get_clean();
$this->_logger->err('Error in communicating with ActiveSync server: ' . $m);
$this->_logger->err($trace);
$this->_logger->err('Buffer contents: ' . $buffer);
}
/**
* Send 401 Unauthorized headers.
*
* @param Horde_Exception_AuthenticationFailure
*/
protected function _sendAuthenticationFailedHeaders($e)
{
switch ($e->getCode()) {
case Horde_ActiveSync_Status::SYNC_NOT_ALLOWED:
case Horde_ActiveSync_Status::DEVICE_BLOCKED_FOR_USER:
$this->_logger->notice('Sending HTTP 403 Forbidden header response.');
header('HTTP/1.1 403 Forbidden');
break;
default:
$this->_logger->notice('Sending HTTP 401 Unauthorized header response.');
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Basic realm="Horde ActiveSync"');
}
}
}