474 lines
14 KiB
PHP
474 lines
14 KiB
PHP
<?php
|
|
|
|
/*
|
|
* Configured URL patterns :
|
|
*
|
|
* Example :
|
|
*
|
|
* array (
|
|
* '|get/(?P<name>[a-zA-Z0-9]+)$|' => array (
|
|
* 'handler' => 'get',
|
|
* 'authenticated' => true,
|
|
* 'api_mode' => false,
|
|
* 'methods' => array('GET'),
|
|
* ),
|
|
* '|get/all$|' => => array (
|
|
* 'handler' => 'get_all',
|
|
* 'authenticated' => true,
|
|
* 'api_mode' => false,
|
|
* 'methods' => array('GET', 'POST'),
|
|
* ),
|
|
* )
|
|
*
|
|
*/
|
|
$url_patterns =array();
|
|
|
|
/**
|
|
* Add an URL pattern
|
|
*
|
|
* @param[in] $pattern string The URL pattern (required)
|
|
* @param[in] $handler callable The URL pattern handler (must be callable, required)
|
|
* @param[in] $authenticated boolean Permit to define if this URL is accessible only for authenticated users (optional, default: true)
|
|
* @param[in] $override boolean Allow override if a command already exists with the same name (optional, default: false)
|
|
* @param[in] $api_mode boolean Enable API mode (optional, default: false)
|
|
* @param[in] $methods array|null HTTP method (optional, default: array('GET', 'POST'))
|
|
**/
|
|
function add_url_handler($pattern, $handler=null, $authenticated=false, $override=true, $api_mode=false, $methods=null) {
|
|
if (is_null($methods))
|
|
$methods = array('GET', 'POST');
|
|
elseif (!is_array($methods))
|
|
$methods = array($methods);
|
|
global $url_patterns;
|
|
if (is_array($pattern)) {
|
|
if (is_null($handler))
|
|
foreach($pattern as $p => $h)
|
|
add_url_handler($p, $h, $authenticated, $override, $api_mode, $methods);
|
|
else
|
|
foreach($pattern as $p)
|
|
add_url_handler($p, $handler, $authenticated, $override, $api_mode, $methods);
|
|
}
|
|
else {
|
|
if (!isset($url_patterns[$pattern])) {
|
|
$url_patterns[$pattern] = array(
|
|
'handler' => $handler,
|
|
'authenticated' => $authenticated,
|
|
'api_mode' => $api_mode,
|
|
'methods' => $methods,
|
|
);
|
|
}
|
|
elseif ($override) {
|
|
logging('DEBUG', "URL : override pattern '$pattern' with handler '$handler' (old handler = '".$url_patterns[$pattern]."')");
|
|
$url_patterns[$pattern] = array(
|
|
'handler' => $handler,
|
|
'authenticated' => $authenticated,
|
|
'api_mode' => $api_mode,
|
|
'methods' => $methods,
|
|
);
|
|
}
|
|
else {
|
|
logging('DEBUG', "URL : pattern '$pattern' already defined : do not override.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Error 404 page
|
|
*/
|
|
|
|
/**
|
|
* Error 404 handler
|
|
*
|
|
* @param[in] $request UrlRequest|null The request (optional, default: null)
|
|
*
|
|
* @retval void
|
|
**/
|
|
function error_404($request=null) {
|
|
display_template('error_404.tpl', _("Whoops ! Page not found"));
|
|
exit();
|
|
}
|
|
|
|
$_404_url_handler = 'error_404';
|
|
function set_404_url_handler($handler=null) {
|
|
global $_404_url_handler;
|
|
$_404_url_handler = $handler;
|
|
}
|
|
|
|
|
|
/*
|
|
* Interprets the requested URL and return the corresponding UrlRequest object
|
|
*
|
|
* @param[in] $default_url string|null The default URL if current one does not
|
|
* match with any configured pattern.
|
|
*
|
|
* @retval UrlRequest The UrlRequest object corresponding to the the requested URL.
|
|
*/
|
|
function get_request($default_url=null) {
|
|
global $url_patterns, $_404_url_handler;
|
|
$current_url = get_current_url();
|
|
if ($current_url === false) {
|
|
logging('FATAL', _('Unable to determine the requested page. If the problem persists, please contact support.'));
|
|
exit();
|
|
}
|
|
if (!is_array($url_patterns)) {
|
|
logging('FATAL', 'URL : No URL patterns configured !');
|
|
exit();
|
|
}
|
|
|
|
logging('DEBUG', "URL : current url = '$current_url'");
|
|
logging('DEBUG', "URL : check current url with the following URL patterns :\n - ".implode("\n - ", array_keys($url_patterns)));
|
|
foreach ($url_patterns as $pattern => $handler_infos) {
|
|
$m = url_match($pattern, $current_url, $handler_infos['methods']);
|
|
if (is_array($m)) {
|
|
$request = new UrlRequest($current_url, $handler_infos, $m);
|
|
// Reset last redirect
|
|
if (isset($_SESSION['last_redirect']))
|
|
unset($_SESSION['last_redirect']);
|
|
logging('DEBUG', "URL : result :\n".varDump($request, 1));
|
|
return $request;
|
|
}
|
|
}
|
|
if ($default_url !== false) {
|
|
logging('DEBUG', "Current url match with no pattern. Redirect to default url ('$default_url')");
|
|
redirect($default_url);
|
|
exit();
|
|
}
|
|
// Error 404
|
|
$api_mode = (strpos($current_url, 'api/') === 0);
|
|
logging('DEBUG', "Current URL match with no pattern. Use error 404 handler (API mode=$api_mode).");
|
|
return new UrlRequest(
|
|
$current_url,
|
|
array(
|
|
'handler' => $_404_url_handler,
|
|
'authenticated' => false,
|
|
'api_mode' => $api_mode,
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check if the current requested URL match with a specific pattern
|
|
*
|
|
* @param[in] $pattern string The URL pattern
|
|
* @param[in] $current_url string|false The current URL (optional)
|
|
* @param[in] $methods array|null HTTP method (optional, default: no check)
|
|
*
|
|
* @retval array|false The URL info if pattern matched, false otherwise.
|
|
**/
|
|
function url_match($pattern, $current_url=false, $methods=null) {
|
|
if ($methods && !in_array($_SERVER['REQUEST_METHOD'], $methods))
|
|
return false;
|
|
if ($current_url === false) {
|
|
$current_url = get_current_url();
|
|
if (!$current_url) return False;
|
|
}
|
|
if (preg_match($pattern, $current_url, $m)) {
|
|
logging('DEBUG', "URL : Match found with pattern '$pattern' :\n\t".str_replace("\n", "\n\t", print_r($m, 1)));
|
|
return $m;
|
|
}
|
|
return False;
|
|
}
|
|
|
|
/*
|
|
* Retreive current requested URL and return it
|
|
*
|
|
* @retval string|false The current request URL or false if fail
|
|
**/
|
|
function get_current_url() {
|
|
if (isset($_REQUEST['go']))
|
|
return $_REQUEST['go'];
|
|
return detect_current_url();
|
|
}
|
|
|
|
/*
|
|
* Try to detect current requested URL and return it
|
|
*
|
|
* @retval string|false The current request URL or false if detection fail
|
|
**/
|
|
function detect_current_url() {
|
|
logging('DEBUG', "URL : request URI = '".$_SERVER['REQUEST_URI']."'");
|
|
|
|
$base = get_rewrite_base();
|
|
logging('DEBUG', "URL : rewrite base = '$base'");
|
|
|
|
if ($_SERVER['REQUEST_URI'] == $base)
|
|
return '';
|
|
|
|
if (substr($_SERVER['REQUEST_URI'], 0, strlen($base)) != $base) {
|
|
logging('ERROR', "URL : request URI (".$_SERVER['REQUEST_URI'].") does not start with rewrite base ($base)");
|
|
return False;
|
|
}
|
|
|
|
$current_url = substr($_SERVER['REQUEST_URI'], strlen($base));
|
|
|
|
// URL contain params ?
|
|
$params_start = strpos($current_url, '?');
|
|
if ($params_start !== false)
|
|
// Params detected, remove it
|
|
|
|
// No url / currrent url start by '?' ?
|
|
if ($params_start == 0)
|
|
return '';
|
|
else
|
|
return substr($current_url, 0, $params_start);
|
|
|
|
return $current_url;
|
|
}
|
|
|
|
/**
|
|
* Try to detect rewrite base from public root URL
|
|
*
|
|
* @retval string The detected rewrite base
|
|
**/
|
|
function get_rewrite_base() {
|
|
global $public_root_url;
|
|
if (preg_match('|^https?://[^/]+/(.*)$|', $public_root_url, $m))
|
|
return '/'.remove_trailing_slash($m[1]).'/';
|
|
elseif (preg_match('|^/(.*)$|', $public_root_url, $m))
|
|
return '/'.remove_trailing_slash($m[1]).'/';
|
|
return '/';
|
|
}
|
|
|
|
/**
|
|
* Trigger redirect to specified URL (or homepage if omited)
|
|
*
|
|
* @param[in] $go string|false The destination URL
|
|
*
|
|
* @retval void
|
|
**/
|
|
function redirect($go=false) {
|
|
global $public_root_url;
|
|
|
|
if ($go===false)
|
|
$go = "";
|
|
|
|
if (is_absolute_url($go))
|
|
$url = $go;
|
|
elseif (isset($public_root_url) && $public_root_url) {
|
|
// Check $public_root_url end
|
|
if (substr($public_root_url, -1)=='/') {
|
|
$public_root_url=substr($public_root_url, 0, -1);
|
|
}
|
|
$url="$public_root_url/$go";
|
|
}
|
|
else
|
|
$url="/$go";
|
|
|
|
// Prevent loop
|
|
if (isset($_SESSION['last_redirect']) && $_SESSION['last_redirect'] == $url)
|
|
logging('FATAL', _('Unable to determine the requested page (loop detected). If the problem persists, please contact support.'));
|
|
else
|
|
$_SESSION['last_redirect'] = $url;
|
|
|
|
logging('DEBUG',"redirect($go) => Redirect to : <$url>");
|
|
header("Location: $url");
|
|
exit();
|
|
}
|
|
|
|
/**
|
|
* Handle the current requested URL
|
|
*
|
|
* @param[in] $default_url string|null The default URL if current one does not
|
|
* match with any configured pattern.
|
|
*
|
|
* @retval void
|
|
**/
|
|
function handle_request($default_url=null) {
|
|
global $smarty, $api_mode;
|
|
|
|
$request = get_request($default_url);
|
|
|
|
if (!is_callable($request -> handler)) {
|
|
logging('ERROR', "URL handler function ".$request -> handler."() does not exists !");
|
|
logging('FATAL', _("This request cannot be processed."));
|
|
}
|
|
|
|
if ($request -> api_mode)
|
|
$api_mode = true;
|
|
if (isset($smarty) && $smarty)
|
|
$smarty -> assign('request', $request);
|
|
|
|
// Check authentication (if need)
|
|
if($request -> authenticated && function_exists('force_authentication'))
|
|
force_authentication();
|
|
|
|
try {
|
|
return call_user_func($request -> handler, $request);
|
|
}
|
|
catch (Exception $e) {
|
|
log_exception($e, "An exception occured running URL handler function ".$request -> handler."()");
|
|
logging('FATAL', _("This request could not be processed correctly."));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove trailing slash in specified URL
|
|
*
|
|
* @param[in] $url string The URL
|
|
*
|
|
* @retval string The specified URL without trailing slash
|
|
**/
|
|
function remove_trailing_slash($url) {
|
|
if ($url == '/')
|
|
return $url;
|
|
elseif (substr($url, -1) == '/')
|
|
return substr($url, 0, -1);
|
|
return $url;
|
|
}
|
|
|
|
/**
|
|
* Check an AJAX request and trigger a fatal error on fail
|
|
*
|
|
* Check if session key is present and valid and set AJAX
|
|
* mode.
|
|
*
|
|
* @param[in] $session_key string The current session key (optional)
|
|
*
|
|
* @retval void
|
|
**/
|
|
function check_ajax_request($session_key=null) {
|
|
global $ajax, $debug_ajax;
|
|
$ajax=true;
|
|
|
|
if (check_session_key($session_key))
|
|
fatal_error('Invalid request');
|
|
|
|
if ($debug_ajax)
|
|
logging('DEBUG',"Ajax Request : ".vardump($_REQUEST));
|
|
}
|
|
|
|
/**
|
|
* Get the public absolute URL
|
|
*
|
|
* @param[in] $relative_url string|null Relative URL to convert (Default: current URL)
|
|
*
|
|
* @retval string The public absolute URL
|
|
**/
|
|
function get_absolute_url($relative_url=null) {
|
|
global $public_root_url;
|
|
if (!is_string($relative_url))
|
|
$relative_url = get_current_url();
|
|
if ($public_root_url[0] == '/') {
|
|
logging('DEBUG', "URL :: get_absolute_url($relative_url): configured public root URL is relative ($public_root_url) => try to detect it from current request infos.");
|
|
$public_root_url = 'http'.(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'?'s':'').'://'.$_SERVER['HTTP_HOST'].$public_root_url;
|
|
logging('DEBUG', "URL :: get_absolute_url($relative_url): detected public_root_url: $public_root_url");
|
|
}
|
|
|
|
if (substr($relative_url, 0, 1) == '/')
|
|
$relative_url = substr($url, 1);
|
|
$url = remove_trailing_slash($public_root_url)."/$relative_url";
|
|
logging('DEBUG', "URL :: get_absolute_url($relative_url): result = $url");
|
|
return $url;
|
|
}
|
|
|
|
/**
|
|
* Check if specified URL is absolute
|
|
*
|
|
* @param[in] $url string The URL to check
|
|
*
|
|
* @retval boolean True if specified URL is absolute, False otherwise
|
|
**/
|
|
function is_absolute_url($url) {
|
|
return boolval(preg_match('#^https?://#', $url));
|
|
}
|
|
|
|
/*
|
|
* Add parameter in specified URL
|
|
*
|
|
* @param[in] &$url string The reference of the URL
|
|
* @param[in] $param string The parameter name
|
|
* @param[in] $value string The parameter value
|
|
* @param[in] $encode boolean Set if parameter value must be URL encoded (optional, default: true)
|
|
*
|
|
* @retval string|null The completed URL
|
|
*/
|
|
function add_url_parameter(&$url, $param, $value, $encode=true) {
|
|
if (strpos($url, '?') === false)
|
|
$url .= '?';
|
|
else
|
|
$url .= '&';
|
|
$url .= "$param=".($encode?urlencode($value):$value);
|
|
return $url;
|
|
}
|
|
|
|
/**
|
|
* URL request abstraction
|
|
*
|
|
* @author Benjamin Renard <brenard@easter-eggs.com>
|
|
*/
|
|
class UrlRequest {
|
|
|
|
// The URL requested handler
|
|
private $current_url = null;
|
|
|
|
// The URL requested handler
|
|
private $handler = null;
|
|
|
|
// Request need authentication ?
|
|
private $authenticated = true;
|
|
|
|
// API mode enabled ?
|
|
private $api_mode = false;
|
|
|
|
// Parameters detected on requested URL
|
|
private $url_params = array();
|
|
|
|
public function __construct($current_url, $handler_infos, $url_params=array()) {
|
|
$this -> current_url = $current_url;
|
|
$this -> handler = $handler_infos['handler'];
|
|
$this -> authenticated = (isset($handler_infos['authenticated'])?boolval($handler_infos['authenticated']):true);
|
|
$this -> api_mode = (isset($handler_infos['api_mode'])?boolval($handler_infos['api_mode']):false);
|
|
$this -> url_params = $url_params;
|
|
}
|
|
|
|
/**
|
|
* Get request info
|
|
*
|
|
* @param[in] $key string The name of the info
|
|
*
|
|
* @retval mixed The value
|
|
**/
|
|
public function __get($key) {
|
|
if ($key == 'current_url')
|
|
return $this -> current_url;
|
|
if ($key == 'handler')
|
|
return $this -> handler;
|
|
if ($key == 'authenticated')
|
|
return $this -> authenticated;
|
|
if ($key == 'api_mode')
|
|
return $this -> api_mode;
|
|
if ($key == 'referer')
|
|
return $this -> get_referer();
|
|
if ($key == 'http_method')
|
|
return $_SERVER['REQUEST_METHOD'];
|
|
if (array_key_exists($key, $this->url_params)) {
|
|
return urldecode($this->url_params[$key]);
|
|
}
|
|
// Unknown key, log warning
|
|
logging('WARNING', "__get($key): invalid property requested\n".get_debug_backtrace_context());
|
|
}
|
|
|
|
/**
|
|
* Check is request info is set
|
|
*
|
|
* @param[in] $key string The name of the info
|
|
*
|
|
* @retval boolval True is info is set, False otherwise
|
|
**/
|
|
public function __isset($key) {
|
|
if (in_array($key, array('current_url', 'handler', 'authenticated')))
|
|
return True;
|
|
return array_key_exists($key, $this->url_params);
|
|
}
|
|
|
|
/*
|
|
* Get request referer (if known)
|
|
*
|
|
* @retval string|null The request referer URL if known, null otherwise
|
|
*/
|
|
public function get_referer() {
|
|
if (isset($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER'])
|
|
return $_SERVER['HTTP_REFERER'];
|
|
return null;
|
|
}
|
|
|
|
}
|