Commit 4d6b3ddb by Qiang Xue

request wip

parent 1536a682
......@@ -9,6 +9,7 @@
namespace yii\web;
use Yii;
use yii\base\DictionaryIterator;
/**
......@@ -22,19 +23,24 @@ use yii\base\DictionaryIterator;
class CookieCollection extends \yii\base\Object implements \IteratorAggregate, \ArrayAccess, \Countable
{
/**
* @var boolean whether to enable cookie validation. By setting this property to true,
* if a cookie is tampered on the client side, it will be ignored when received on the server side.
*/
public $enableValidation = true;
/**
* @var Cookie[] the cookies in this collection (indexed by the cookie names)
*/
private $_cookies = array();
/**
* Constructor.
* @param Cookie[] $cookies the initial cookies in the collection.
* @param array $config name-value pairs that will be used to initialize the object properties
*/
public function __construct($cookies = array(), $config = array())
public function __construct($config = array())
{
$this->_cookies = $cookies;
parent::__construct($config);
$this->_cookies = $this->loadCookies();
}
/**
......@@ -86,7 +92,7 @@ class CookieCollection extends \yii\base\Object implements \IteratorAggregate, \
* @return mixed the value of the named cookie.
* @see get()
*/
public function getValue($name, $defaultValue)
public function getValue($name, $defaultValue = null)
{
return isset($this->_cookies[$name]) ? $this->_cookies[$name]->value : $defaultValue;
}
......@@ -102,7 +108,13 @@ class CookieCollection extends \yii\base\Object implements \IteratorAggregate, \
$c = $this->_cookies[$cookie->name];
setcookie($c->name, '', 0, $c->path, $c->domain, $c->secure, $c->httpOnly);
}
setcookie($cookie->name, $cookie->value, $cookie->expire, $cookie->path, $cookie->domain, $cookie->secure, $cookie->httpOnly);
$value = $cookie->value;
if ($this->enableValidation) {
$value = Yii::$app->getSecurityManager()->hashData(serialize($value));
}
setcookie($cookie->name, $value, $cookie->expire, $cookie->path, $cookie->domain, $cookie->secure, $cookie->httpOnly);
$this->_cookies[$cookie->name] = $cookie;
}
......@@ -192,4 +204,33 @@ class CookieCollection extends \yii\base\Object implements \IteratorAggregate, \
{
$this->remove($name);
}
/**
* Returns the current cookies in terms of [[Cookie]] objects.
* @return Cookie[] list of current cookies
*/
protected function loadCookies()
{
$cookies = array();
if ($this->enableValidation) {
$sm = \Yii::$app->getSecurityManager();
foreach ($_COOKIE as $name => $value) {
if (is_string($value) && ($value = $sm->validateData($value)) !== false) {
$cookies[$name] = new Cookie(array(
'name' => $name,
'value' => @unserialize($value),
));
}
}
} else {
foreach ($_COOKIE as $name => $value) {
$cookies[$name] = new Cookie(array(
'name' => $name,
'value' => $value,
));
}
}
return $cookies;
}
}
......@@ -178,15 +178,29 @@ class Request extends \yii\base\Request
} else {
$this->_restParams = array();
if (function_exists('mb_parse_str')) {
mb_parse_str(file_get_contents('php://input'), $this->_restParams);
mb_parse_str($this->getRawBody(), $this->_restParams);
} else {
parse_str(file_get_contents('php://input'), $this->_restParams);
parse_str($this->getRawBody(), $this->_restParams);
}
}
}
return $this->_restParams;
}
private $_rawBody;
/**
* Returns the raw HTTP request body.
* @return string the request body
*/
public function getRawBody()
{
if ($this->_rawBody === null) {
$this->_rawBody = file_get_contents('php://input');
}
return $this->_rawBody;
}
/**
* Sets the RESTful parameters.
* @param array $values the RESTful parameters (name-value pairs)
......@@ -382,6 +396,11 @@ class Request extends \yii\base\Request
return $this->_pathInfo;
}
/**
* Sets the path info of the current request.
* This method is mainly provided for testing purpose.
* @param string $value the path info of the current request
*/
public function setPathInfo($value)
{
$this->_pathInfo = trim($value, '/');
......@@ -403,7 +422,22 @@ class Request extends \yii\base\Request
$pathInfo = substr($pathInfo, 0, $pos);
}
$pathInfo = $this->decodeUrl($pathInfo);
$pathInfo = urldecode($pathInfo);
// try to encode in UTF8 if not so
// http://w3.org/International/questions/qa-forms-utf-8.html
if (!preg_match('%^(?:
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*$%xs', $pathInfo)) {
$pathInfo = utf8_encode($pathInfo);
}
$scriptUrl = $this->getScriptUrl();
$baseUrl = $this->getBaseUrl();
......@@ -414,42 +448,13 @@ class Request extends \yii\base\Request
} elseif (strpos($_SERVER['PHP_SELF'], $scriptUrl) === 0) {
$pathInfo = substr($_SERVER['PHP_SELF'], strlen($scriptUrl));
} else {
return false;
throw new InvalidConfigException('Unable to determine the path info of the current request.');
}
return trim($pathInfo, '/');
}
/**
* Decodes the given URL.
* This method is an improved variant of the native urldecode() function. It will properly encode
* UTF-8 characters which may be returned by urldecode().
* @param string $url encoded URL
* @return string decoded URL
*/
public function decodeUrl($url)
{
$url = urldecode($url);
// is it UTF-8?
// http://w3.org/International/questions/qa-forms-utf-8.html
if (preg_match('%^(?:
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*$%xs', $url)) {
return $url;
} else {
return utf8_encode($url);
}
}
/**
* Returns the currently requested URL.
* This is a shortcut to the concatenation of [[hostInfo]] and [[requestUri]].
* @return string the currently requested URL.
......@@ -714,101 +719,11 @@ class Request extends \yii\base\Request
public function getCookies()
{
if ($this->_cookies === null) {
$this->_cookies = new CookieCollection($this->loadCookies());
}
return $this->_cookies;
}
/**
* Returns the current cookies in terms of [[Cookie]] objects.
* @return Cookie[] list of current cookies
*/
protected function loadCookies()
{
$cookies = array();
if ($this->enableCookieValidation) {
$sm = Yii::app()->getSecurityManager();
foreach ($_COOKIE as $name => $value) {
if (is_string($value) && ($value = $sm->validateData($value)) !== false) {
$cookies[$name] = new CHttpCookie($name, @unserialize($value));
}
}
} else {
foreach ($_COOKIE as $name => $value) {
$cookies[$name] = new Cookie(array(
'name' => $name,
'value' => $value,
$this->_cookies = new CookieCollection(array(
'enableValidation' => $this->enableCookieValidation,
));
}
}
return $cookies;
}
private $_csrfToken;
/**
* Returns the random token used to perform CSRF validation.
* The token will be read from cookie first. If not found, a new token
* will be generated.
* @return string the random token for CSRF validation.
* @see enableCsrfValidation
*/
public function getCsrfToken()
{
if ($this->_csrfToken === null) {
$cookie = $this->getCookies()->itemAt($this->csrfTokenName);
if (!$cookie || ($this->_csrfToken = $cookie->value) == null) {
$cookie = $this->createCsrfCookie();
$this->_csrfToken = $cookie->value;
$this->getCookies()->add($cookie->name, $cookie);
}
}
return $this->_csrfToken;
}
/**
* Creates a cookie with a randomly generated CSRF token.
* Initial values specified in {@link csrfCookie} will be applied
* to the generated cookie.
* @return CHttpCookie the generated cookie
* @see enableCsrfValidation
*/
protected function createCsrfCookie()
{
$cookie = new CHttpCookie($this->csrfTokenName, sha1(uniqid(mt_rand(), true)));
if (is_array($this->csrfCookie)) {
foreach ($this->csrfCookie as $name => $value) {
$cookie->$name = $value;
}
}
return $cookie;
}
/**
* Performs the CSRF validation.
* This is the event handler responding to {@link CApplication::onBeginRequest}.
* The default implementation will compare the CSRF token obtained
* from a cookie and from a POST field. If they are different, a CSRF attack is detected.
* @param CEvent $event event parameter
* @throws CHttpException if the validation fails
*/
public function validateCsrfToken($event)
{
if ($this->getIsPostRequest()) {
// only validate POST requests
$cookies = $this->getCookies();
if ($cookies->contains($this->csrfTokenName) && isset($_POST[$this->csrfTokenName])) {
$tokenFromCookie = $cookies->itemAt($this->csrfTokenName)->value;
$tokenFromPost = $_POST[$this->csrfTokenName];
$valid = $tokenFromCookie === $tokenFromPost;
} else {
$valid = false;
}
if (!$valid) {
throw new CHttpException(400, Yii::t('yii|The CSRF token could not be verified.'));
}
}
return $this->_cookies;
}
}
......@@ -82,6 +82,11 @@ class Response extends \yii\base\Response
* If this option is disabled by the web server, when this method is called a download configuration dialog
* will open but the downloaded file will have 0 bytes.
*
* <b>Known issues</b>:
* There is a Bug with Internet Explorer 6, 7 and 8 when X-SENDFILE is used over an SSL connection, it will show
* an error message like this: "Internet Explorer was not able to open this Internet site. The requested site is either unavailable or cannot be found.".
* You can work around this problem by removing the <code>Pragma</code>-header.
*
* <b>Example</b>:
* <pre>
* <?php
......@@ -102,65 +107,57 @@ class Response extends \yii\base\Response
* <li>forceDownload: specifies whether the file will be downloaded or shown inline, defaults to true. (Since version 1.1.9.)</li>
* <li>addHeaders: an array of additional http headers in header-value pairs (available since version 1.1.10)</li>
* </ul>
* @todo
*/
public function xSendFile($filePath, $options = array())
public function xSendFile($filePath, $options=array())
{
if (!isset($options['forceDownload']) || $options['forceDownload']) {
$disposition = 'attachment';
} else {
$disposition = 'inline';
}
if(!isset($options['forceDownload']) || $options['forceDownload'])
$disposition='attachment';
else
$disposition='inline';
if (!isset($options['saveName'])) {
$options['saveName'] = basename($filePath);
}
if(!isset($options['saveName']))
$options['saveName']=basename($filePath);
if (!isset($options['mimeType'])) {
if (($options['mimeType'] = CFileHelper::getMimeTypeByExtension($filePath)) === null) {
$options['mimeType'] = 'text/plain';
}
if(!isset($options['mimeType']))
{
if(($options['mimeType']=CFileHelper::getMimeTypeByExtension($filePath))===null)
$options['mimeType']='text/plain';
}
if (!isset($options['xHeader'])) {
$options['xHeader'] = 'X-Sendfile';
}
if(!isset($options['xHeader']))
$options['xHeader']='X-Sendfile';
if ($options['mimeType'] !== null) {
header('Content-type: ' . $options['mimeType']);
}
header('Content-Disposition: ' . $disposition . '; filename="' . $options['saveName'] . '"');
if (isset($options['addHeaders'])) {
foreach ($options['addHeaders'] as $header => $value) {
header($header . ': ' . $value);
}
if($options['mimeType'] !== null)
header('Content-type: '.$options['mimeType']);
header('Content-Disposition: '.$disposition.'; filename="'.$options['saveName'].'"');
if(isset($options['addHeaders']))
{
foreach($options['addHeaders'] as $header=>$value)
header($header.': '.$value);
}
header(trim($options['xHeader']) . ': ' . $filePath);
header(trim($options['xHeader']).': '.$filePath);
if (!isset($options['terminate']) || $options['terminate']) {
if(!isset($options['terminate']) || $options['terminate'])
Yii::app()->end();
}
}
/**
* Redirects the browser to the specified URL.
* @param string $url URL to be redirected to. If the URL is a relative one, the base URL of
* the application will be inserted at the beginning.
* @param string $url URL to be redirected to. Note that when URL is not
* absolute (not starting with "/") it will be relative to current request URL.
* @param boolean $terminate whether to terminate the current application
* @param integer $statusCode the HTTP status code. Defaults to 302. See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html}
* for details about HTTP status code.
*/
public function redirect($url, $terminate = true, $statusCode = 302)
public function redirect($url,$terminate=true,$statusCode=302)
{
if (strpos($url, '/') === 0) {
$url = $this->getHostInfo() . $url;
}
header('Location: ' . $url, true, $statusCode);
if ($terminate) {
if(strpos($url,'/')===0 && strpos($url,'//')!==0)
$url=$this->getHostInfo().$url;
header('Location: '.$url, true, $statusCode);
if($terminate)
Yii::app()->end();
}
}
/**
* Returns the cookie collection.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment