randomToken('md5');
// Store a file in the session save path (safer than /tmp, as open_basedir might limit that)
file_put_contents($this->pathForToken($token), $token);
return $token;
}
/**
* Validate a token
*
* @param string $token
* @return boolean True if the token is valid
*/
protected function checkToken($token)
{
if (!$token) {
return false;
}
$file = $this->pathForToken($token);
$content = null;
if (file_exists($file)) {
$content = file_get_contents($file);
unlink($file);
}
return $content == $token;
}
/**
* Create a new ParameterConfirmationToken
*
* @param string $parameterName Name of the querystring parameter to check
* @param HTTPRequest $request
*/
public function __construct($parameterName, HTTPRequest $request)
{
// Store the parameter name
$this->parameterName = $parameterName;
$this->request = $request;
// Store the parameter value
$this->parameter = $request->getVar($parameterName);
$this->parameterBackURL = $this->backURLToken($request);
// If the token provided is valid, mark it as such
$token = $request->getVar($parameterName.'token');
if ($this->checkToken($token)) {
$this->token = $token;
}
}
/**
* Check if this token exists in the BackURL
*
* @param HTTPRequest $request
* @return string Value of token in backurl, or null if not in backurl
*/
protected function backURLToken(HTTPRequest $request)
{
$backURL = $request->getVar('BackURL');
if (!strstr($backURL, '?')) {
return null;
}
// Filter backURL if it contains the given request parameter
list(,$query) = explode('?', $backURL);
parse_str($query, $queryArgs);
$name = $this->getName();
if (isset($queryArgs[$name])) {
return $queryArgs[$name];
}
return null;
}
/**
* Get the name of this token
*
* @return string
*/
public function getName()
{
return $this->parameterName;
}
/**
* Is the parameter requested?
* ?parameter and ?parameter=1 are both considered requested
*
* @return bool
*/
public function parameterProvided()
{
return $this->parameter !== null;
}
/**
* Is the parmeter requested in a BackURL param?
*
* @return bool
*/
public function existsInReferer()
{
return $this->parameterBackURL !== null;
}
/**
* Is the necessary token provided for this parameter?
* A value must be provided for the token
*
* @return bool
*/
public function tokenProvided()
{
return !empty($this->token);
}
/**
* Is this parameter requested without a valid token?
*
* @return bool True if the parameter is given without a valid token
*/
public function reloadRequired()
{
return $this->parameterProvided() && !$this->tokenProvided();
}
/**
* Check if this token is provided either in the backurl, or directly,
* but without a token
*
* @return bool
*/
public function reloadRequiredIfError()
{
// Don't reload if token exists
return $this->reloadRequired() || $this->existsInReferer();
}
/**
* Suppress the current parameter by unsetting it from $_GET
*/
public function suppress()
{
$this->request->offsetUnset($this->parameterName);
}
/**
* Determine the querystring parameters to include
*
* @param bool $includeToken Include the token value as well?
* @return array List of querystring parameters with name and token parameters
*/
public function params($includeToken = true)
{
$params = array(
$this->parameterName => $this->parameter,
);
if ($includeToken) {
$params[$this->parameterName . 'token'] = $this->genToken();
}
return $params;
}
/**
* Get redirect url, excluding querystring
*
* @return string
*/
protected function currentURL()
{
return Controller::join_links(
BASE_URL ?: '/',
$this->request->getURL(false)
);
}
/**
* Get redirection URL
*
* @return string
*/
protected function redirectURL()
{
// If url is encoded via BackURL, defer to home page (prevent redirect to form action)
if ($this->existsInReferer() && !$this->parameterProvided()) {
$url = BASE_URL ?: '/';
$params = $this->params();
} else {
$url = $this->currentURL();
$params = array_merge($this->request->getVars(), $this->params());
}
// Merge get params with current url
return Controller::join_links($url, '?' . http_build_query($params));
}
/**
* Forces a reload of the request with the token included
*
* @return HTTPResponse
*/
public function reloadWithToken()
{
$location = $this->redirectURL();
$locationJS = Convert::raw2js($location);
$locationATT = Convert::raw2att($location);
$body = <<location.href='$locationJS';
You are being redirected. If you are not redirected soon, click here to continue the flush
HTML;
// Build response
$result = new HTTPResponse($body);
$result->redirect($location);
return $result;
}
/**
* Given a list of token names, suppress all tokens that have not been validated, and
* return the non-validated token with the highest priority
*
* @param array $keys List of token keys in ascending priority (low to high)
* @param HTTPRequest $request
* @return ParameterConfirmationToken The token container for the unvalidated $key given with the highest priority
*/
public static function prepare_tokens($keys, HTTPRequest $request)
{
$target = null;
foreach ($keys as $key) {
$token = new ParameterConfirmationToken($key, $request);
// Validate this token
if ($token->reloadRequired() || $token->reloadRequiredIfError()) {
$token->suppress();
$target = $token;
}
}
return $target;
}
}