PrivateBin/lib/request.php
El RIDO 3a92c940a9 implementing media type negotiation (based on language negotiation
logic) in cases both JSON and (X)HTML are being requested, resolving #68
2016-04-08 23:29:44 +02:00

260 lines
7.1 KiB
PHP

<?php
/**
* ZeroBin
*
* a zero-knowledge paste bin
*
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
* @version 0.22
*/
/**
* request
*
* parses request parameters and provides helper functions for routing
*/
class request
{
/**
* MIME type for JSON
*
* @const string
*/
const MIME_JSON = 'application/json';
/**
* MIME type for HTML
*
* @const string
*/
const MIME_HTML = 'text/html';
/**
* MIME type for XHTML
*
* @const string
*/
const MIME_XHTML = 'application/xhtml+xml';
/**
* Input stream to use for PUT parameter parsing.
*
* @access private
* @var string
*/
private static $_inputStream = 'php://input';
/**
* Operation to perform.
*
* @access private
* @var string
*/
private $_operation = 'view';
/**
* Request parameters.
*
* @access private
* @var array
*/
private $_params = array();
/**
* If we are in a JSON API context.
*
* @access private
* @var bool
*/
private $_isJsonApi = false;
/**
* Constructor.
*
* @access public
* @return void
*/
public function __construct()
{
// in case stupid admin has left magic_quotes enabled in php.ini (for PHP < 5.4)
if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc())
{
$_POST = array_map('filter::stripslashes_deep', $_POST);
$_GET = array_map('filter::stripslashes_deep', $_GET);
$_COOKIE = array_map('filter::stripslashes_deep', $_COOKIE);
}
// decide if we are in JSON API or HTML context
$this->_isJsonApi = $this->_detectJsonRequest();
// parse parameters, depending on request type
switch (array_key_exists('REQUEST_METHOD', $_SERVER) ? $_SERVER['REQUEST_METHOD'] : 'GET')
{
case 'DELETE':
case 'PUT':
parse_str(file_get_contents(self::$_inputStream), $this->_params);
break;
case 'POST':
$this->_params = $_POST;
break;
default:
$this->_params = $_GET;
}
if (
!array_key_exists('pasteid', $this->_params) &&
!array_key_exists('jsonld', $this->_params) &&
array_key_exists('QUERY_STRING', $_SERVER) &&
!empty($_SERVER['QUERY_STRING'])
)
{
$this->_params['pasteid'] = $_SERVER['QUERY_STRING'];
}
// prepare operation, depending on current parameters
if (
(array_key_exists('data', $this->_params) && !empty($this->_params['data'])) ||
(array_key_exists('attachment', $this->_params) && !empty($this->_params['attachment']))
)
{
$this->_operation = 'create';
}
elseif (array_key_exists('pasteid', $this->_params) && !empty($this->_params['pasteid']))
{
if (array_key_exists('deletetoken', $this->_params) && !empty($this->_params['deletetoken']))
{
$this->_operation = 'delete';
}
else
{
$this->_operation = 'read';
}
}
elseif (array_key_exists('jsonld', $this->_params) && !empty($this->_params['jsonld']))
{
$this->_operation = 'jsonld';
}
}
/**
* Get current operation.
*
* @access public
* @return string
*/
public function getOperation()
{
return $this->_operation;
}
/**
* Get a request parameter.
*
* @access public
* @param string $param
* @param string $default
* @return string
*/
public function getParam($param, $default = '')
{
return array_key_exists($param, $this->_params) ? $this->_params[$param] : $default;
}
/**
* If we are in a JSON API context.
*
* @access public
* @return bool
*/
public function isJsonApiCall()
{
return $this->_isJsonApi;
}
/**
* Override the default input stream source, used for unit testing.
*
* @param string $input
*/
public static function setInputStream($input)
{
self::$_inputStream = $input;
}
/**
* detect the clients supported media type and decide if its a JSON API call or not
*
* Adapted from: http://stackoverflow.com/questions/3770513/detect-browser-language-in-php#3771447
*
* @access private
* @return bool
*/
private function _detectJsonRequest()
{
$hasAcceptHeader = array_key_exists('HTTP_ACCEPT', $_SERVER);
$acceptHeader = $hasAcceptHeader ? $_SERVER['HTTP_ACCEPT'] : '';
// simple cases
if (
(array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) &&
$_SERVER['HTTP_X_REQUESTED_WITH'] == 'JSONHttpRequest') ||
($hasAcceptHeader &&
strpos($acceptHeader, self::MIME_JSON) !== false &&
strpos($acceptHeader, self::MIME_HTML) === false &&
strpos($acceptHeader, self::MIME_XHTML) === false)
)
{
return true;
}
// advanced case: media type negotiation
$mediaTypes = array();
if ($hasAcceptHeader)
{
$mediaTypeRanges = explode(',', trim($acceptHeader));
foreach ($mediaTypeRanges as $mediaTypeRange)
{
if (preg_match(
'#(\*/\*|[a-z\-]+/[a-z\-+*]+(?:\s*;\s*[^q]\S*)*)(?:\s*;\s*q\s*=\s*(0(?:\.\d{0,3})|1(?:\.0{0,3})))?#',
trim($mediaTypeRange), $match
))
{
if (!isset($match[2]))
{
$match[2] = '1.0';
}
else
{
$match[2] = (string) floatval($match[2]);
}
if (!isset($mediaTypes[$match[2]]))
{
$mediaTypes[$match[2]] = array();
}
$mediaTypes[$match[2]][] = strtolower($match[1]);
}
}
krsort($mediaTypes);
foreach ($mediaTypes as $acceptedQuality => $acceptedValues)
{
if ($acceptedQuality === 0.0) continue;
foreach ($acceptedValues as $acceptedValue)
{
if (
strpos($acceptedValue, self::MIME_HTML) === 0 ||
strpos($acceptedValue, self::MIME_XHTML) === 0
)
{
return false;
}
elseif (strpos($acceptedValue, self::MIME_JSON) === 0)
{
return true;
}
}
}
}
return false;
}
}