mirror of
https://gitlab.easter-eggs.com/ee/ldapsaisie.git
synced 2024-11-16 15:33:02 +01:00
385 lines
13 KiB
PHP
385 lines
13 KiB
PHP
<?php
|
|
/*******************************************************************************
|
|
* Copyright (C) 2007 Easter-eggs
|
|
* https://ldapsaisie.org
|
|
*
|
|
* Author: See AUTHORS file in top-level directory.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License version 2
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
******************************************************************************/
|
|
|
|
LSsession :: loadLSclass('LSurlRequest');
|
|
LSsession :: loadLSclass('LSlog_staticLoggerClass');
|
|
|
|
/**
|
|
* URL Routing Manager for LdapSaisie
|
|
*
|
|
* @author Benjamin Renard <brenard@easter-eggs.com>
|
|
*/
|
|
class LSurl extends LSlog_staticLoggerClass {
|
|
|
|
/**
|
|
* Current request (defined at least current URL have been analyse by LSurl :: handle_request())
|
|
* @var LSurlRequest
|
|
*/
|
|
public static $request = null;
|
|
|
|
/**
|
|
* 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'),
|
|
* ),
|
|
* )
|
|
*
|
|
* @see self::add_handler()
|
|
* @var array
|
|
*/
|
|
private static $patterns = array();
|
|
|
|
/**
|
|
* Add an URL pattern
|
|
*
|
|
* @param string $pattern The URL pattern (required)
|
|
* @param callable $handler The URL pattern handler (must be callable, required)
|
|
* @param boolean $authenticated Permit to define if this URL is accessible only for authenticated users (optional, default: true)
|
|
* @param boolean $authenticated Allow override if a command already exists with the same name (optional, default: false)
|
|
* @param boolean $authenticated Enable API mode (optional, default: false)
|
|
* @param array<string>|string|null $methods HTTP method (optional, default: array('GET', 'POST'))
|
|
**/
|
|
public static function add_handler($pattern, $handler=null, $authenticated=true, $override=true, $api_mode=false, $methods=null) {
|
|
if (is_null($methods))
|
|
$methods = array('GET', 'POST');
|
|
else
|
|
$methods = ensureIsArray($methods);
|
|
if (is_array($pattern)) {
|
|
if (is_null($handler))
|
|
foreach($pattern as $p => $h)
|
|
self :: add_handler($p, $h, $authenticated, $override, $api_mode, $methods);
|
|
else
|
|
foreach($pattern as $p)
|
|
self :: add_handler($p, $handler, $authenticated, $override, $api_mode, $methods);
|
|
}
|
|
else {
|
|
if (!isset(self :: $patterns[$pattern])) {
|
|
self :: $patterns[$pattern] = array(
|
|
'handler' => $handler,
|
|
'authenticated' => $authenticated,
|
|
'api_mode' => $api_mode,
|
|
'methods' => $methods,
|
|
);
|
|
}
|
|
elseif ($override) {
|
|
self :: log_debug("URL : override pattern '$pattern' with handler '".format_callable($handler)."' (old handler = '".varDump(self :: $patterns[$pattern])."')");
|
|
self :: $patterns[$pattern] = array(
|
|
'handler' => $handler,
|
|
'authenticated' => $authenticated,
|
|
'api_mode' => $api_mode,
|
|
'methods' => $methods,
|
|
);
|
|
}
|
|
else {
|
|
self :: log_debug("URL : pattern '$pattern' already defined : do not override.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Interprets the requested URL and return the corresponding LSurlRequest object
|
|
*
|
|
* @param string|null $default_url The default URL if current one does not
|
|
* match with any configured pattern.
|
|
*
|
|
* @return LSurlRequest The LSurlRequest object corresponding to the the requested URL.
|
|
**/
|
|
private static function get_request($default_url=null) {
|
|
$current_url = self :: get_current_url();
|
|
if (is_null($current_url)) {
|
|
self :: log_fatal(_("Fail to determine the requested URL."));
|
|
exit();
|
|
}
|
|
if (!is_array(self :: $patterns)) {
|
|
self :: log_fatal(_("No URL patterns configured !"));
|
|
exit();
|
|
}
|
|
|
|
self :: log_debug("URL : current url = '$current_url'");
|
|
self :: log_debug("URL : check current url with the following URL patterns :\n - ".implode("\n - ", array_keys(self :: $patterns)));
|
|
foreach (self :: $patterns as $pattern => $handler_infos) {
|
|
$m = self :: url_match($pattern, $current_url, $handler_infos['methods']);
|
|
if (is_array($m)) {
|
|
$request = new LSurlRequest($current_url, $handler_infos, $m);
|
|
// Reset last redirect
|
|
if (isset($_SESSION['last_redirect']))
|
|
unset($_SESSION['last_redirect']);
|
|
self :: log_debug("URL : result :\n".varDump($request));
|
|
return $request;
|
|
}
|
|
}
|
|
if (!is_null($default_url)) {
|
|
self :: log_debug("Current URL match with no pattern. Redirect to default URL ('$default_url')");
|
|
self :: redirect($default_url);
|
|
exit();
|
|
}
|
|
// Error 404
|
|
$api_mode = (strpos($current_url, 'api/') === 0);
|
|
self :: log_debug("Current URL match with no pattern. Use error 404 handler (API mode=$api_mode).");
|
|
return new LSurlRequest(
|
|
$current_url,
|
|
array(
|
|
'handler' => array('LSurl', 'error_404'),
|
|
'authenticated' => false,
|
|
'api_mode' => $api_mode,
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check if the current requested URL match with a specific pattern
|
|
*
|
|
* @param string $pattern The URL pattern
|
|
* @param string|false $current_url The current URL (optional)
|
|
* @param array<string>|string|null $methods HTTP method (optional, default: no check)
|
|
*
|
|
* @return array|false The URL info if pattern matched, false otherwise.
|
|
**/
|
|
private static 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 = self :: get_current_url();
|
|
if (!$current_url) return False;
|
|
}
|
|
if (preg_match($pattern, $current_url, $m)) {
|
|
self :: log_debug("URL : Match found with pattern '$pattern' :\n\t".str_replace("\n", "\n\t", print_r($m, 1)));
|
|
return $m;
|
|
}
|
|
return False;
|
|
}
|
|
|
|
/**
|
|
* Get the public absolute URL
|
|
*
|
|
* @param string|null $relative_url Relative URL to convert (Default: current URL)
|
|
*
|
|
* @return string The public absolute URL
|
|
**/
|
|
public static function get_public_absolute_url($relative_url=null) {
|
|
if (!is_string($relative_url))
|
|
$relative_url = self :: get_current_url();
|
|
|
|
$public_root_url = LSconfig :: get('public_root_url', '/', 'string');
|
|
|
|
if ($public_root_url[0] == '/') {
|
|
self :: log_debug("LSurl :: get_public_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;
|
|
self :: log_debug("LSurl :: get_public_absolute_url($relative_url): detected public_root_url: $public_root_url");
|
|
}
|
|
|
|
$url = self :: remove_trailing_slash($public_root_url)."/$relative_url";
|
|
self :: log_debug("LSurl :: get_public_absolute_url($relative_url): result = $url");
|
|
return $url;
|
|
}
|
|
|
|
/**
|
|
* Trigger redirect to specified URL (or homepage if omited)
|
|
*
|
|
* @param string|false $go The destination URL
|
|
*
|
|
* @return void
|
|
**/
|
|
public static function redirect($go=false) {
|
|
$public_root_url = LSconfig :: get('public_root_url', '/', 'string');
|
|
if ($go===false)
|
|
$go = "";
|
|
|
|
if (preg_match('#^(https?:)?//#',$go)) {
|
|
$url = $go;
|
|
}
|
|
else {
|
|
// 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";
|
|
}
|
|
|
|
// Prevent loop
|
|
if (isset($_SESSION['last_redirect']) && $_SESSION['last_redirect'] == $url)
|
|
self :: log_fatal(_("Fail to determine the requested URL (loop detected)."));
|
|
else
|
|
$_SESSION['last_redirect'] = $url;
|
|
|
|
self :: log_debug("redirect($go) => Redirect to : <$url>");
|
|
header("Location: $url");
|
|
|
|
// Set & display template
|
|
LStemplate :: assign('url', $url);
|
|
LStemplate :: display('redirect.tpl');
|
|
exit();
|
|
}
|
|
|
|
/**
|
|
* Error 404 handler
|
|
*
|
|
* @param LSurlRequest|null $request The request (optional, default: null)
|
|
*
|
|
* @return void
|
|
**/
|
|
public static function error_404($request=null) {
|
|
http_response_code(404);
|
|
$error = _("The requested page was not found.");
|
|
if (LSsession :: getAjaxDisplay() || ($request && $request->api_mode)) {
|
|
LSerror :: addErrorCode(null, $error);
|
|
LSsession :: displayAjaxReturn();
|
|
}
|
|
else {
|
|
LStemplate :: assign('error', $error);
|
|
LSsession :: setTemplate('error.tpl');
|
|
LSsession :: displayTemplate();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle the current requested URL
|
|
*
|
|
* @param string|null $default_url The default URL if current one does not
|
|
* match with any configured pattern.
|
|
*
|
|
* @return void
|
|
**/
|
|
public static function handle_request($default_url=null) {
|
|
self :: $request = self :: get_request($default_url);
|
|
|
|
if (!is_callable(self :: $request -> handler)) {
|
|
self :: log_error(
|
|
"URL handler function ".self :: $request -> handler."() does not exists !"
|
|
);
|
|
self :: log_fatal(_("This request could not be handled."));
|
|
}
|
|
|
|
if (self :: $request -> api_mode) {
|
|
LSsession :: setApiMode();
|
|
}
|
|
else {
|
|
if (self :: $request -> ajax)
|
|
LSsession :: setAjaxDisplay();
|
|
if (class_exists('LStemplate'))
|
|
LStemplate :: assign('request', self :: $request);
|
|
}
|
|
|
|
// Check authentication (if need)
|
|
if(self :: $request -> authenticated && ! LSsession :: startLSsession()) {
|
|
LSsession :: displayTemplate();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
call_user_func(self :: $request -> handler, self :: $request);
|
|
}
|
|
catch (Exception $e) {
|
|
self :: log_exception(
|
|
$e, "An exception occured running URL handler function ".
|
|
format_callable(self :: $request -> handler)
|
|
);
|
|
self :: log_fatal(_("This request could not be processed correctly."));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helpers to retrieve current requested URL
|
|
*/
|
|
|
|
/*
|
|
* Try to detect current requested URL and return it
|
|
*
|
|
* @return string|false The current request URL or false if detection fail
|
|
**/
|
|
private static function get_current_url() {
|
|
self :: log_debug("URL : request URI = '".$_SERVER['REQUEST_URI']."'");
|
|
|
|
$base = self :: get_rewrite_base();
|
|
self :: log_debug("URL : rewrite base = '$base'");
|
|
|
|
if ($_SERVER['REQUEST_URI'] == $base)
|
|
return '';
|
|
|
|
if (substr($_SERVER['REQUEST_URI'], 0, strlen($base)) != $base) {
|
|
self :: log_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
|
|
*
|
|
* @return string The detected rewrite base
|
|
**/
|
|
private static function get_rewrite_base() {
|
|
$public_root_url = LSconfig :: get('public_root_url', '/', 'string');
|
|
$rewrite_base = '/';
|
|
if (preg_match('|^https?://[^/]+(/.*)$|', $public_root_url, $m))
|
|
$rewrite_base = $m[1];
|
|
elseif (preg_match('|^(/.+)$|', $public_root_url, $m))
|
|
$rewrite_base = $m[1];
|
|
if ($rewrite_base != '/')
|
|
return self :: remove_trailing_slash($rewrite_base).'/';
|
|
return $rewrite_base;
|
|
}
|
|
|
|
/**
|
|
* Remove trailing slash in specified URL
|
|
*
|
|
* @param string $url The URL
|
|
*
|
|
* @return string The specified URL without trailing slash
|
|
**/
|
|
private static function remove_trailing_slash($url) {
|
|
if ($url == '/')
|
|
return $url;
|
|
elseif (substr($url, -1) == '/')
|
|
return substr($url, 0, -1);
|
|
return $url;
|
|
}
|
|
|
|
}
|