<?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; } }