1c3437056SNickeau<?php 2c3437056SNickeau 3c3437056SNickeau 411d09b86Sgerardnicouse ComboStrap\DokuwikiId; 504fd306cSNickeauuse ComboStrap\ExceptionBadArgument; 604fd306cSNickeauuse ComboStrap\ExceptionBadSyntax; 704fd306cSNickeauuse ComboStrap\ExceptionCompile; 8b1aef534SNicouse ComboStrap\ExceptionNotFound; 904fd306cSNickeauuse ComboStrap\ExceptionSqliteNotAvailable; 1004fd306cSNickeauuse ComboStrap\ExecutionContext; 1104fd306cSNickeauuse ComboStrap\FileSystems; 12c3437056SNickeauuse ComboStrap\HttpResponse; 1304fd306cSNickeauuse ComboStrap\HttpResponseStatus; 14c3437056SNickeauuse ComboStrap\Identity; 15c3437056SNickeauuse ComboStrap\LogUtility; 1604fd306cSNickeauuse ComboStrap\MarkupPath; 17c3437056SNickeauuse ComboStrap\Mime; 18c3437056SNickeauuse ComboStrap\PageRules; 19*45a874f4SNicouse ComboStrap\Router; 20*45a874f4SNicouse ComboStrap\RouterRedirection; 21*45a874f4SNicouse ComboStrap\RouterRedirectionBuilder; 22c3437056SNickeauuse ComboStrap\Site; 2304fd306cSNickeauuse ComboStrap\SiteConfig; 24c3437056SNickeauuse ComboStrap\Sqlite; 2504fd306cSNickeauuse ComboStrap\Web\Url; 2611d09b86Sgerardnicouse ComboStrap\Web\UrlEndpoint; 2754743e42Sgerardnicouse ComboStrap\Web\UrlRewrite; 28c3437056SNickeau 2904fd306cSNickeaurequire_once(__DIR__ . '/../vendor/autoload.php'); 30c3437056SNickeau 31c3437056SNickeau/** 32c3437056SNickeau * Class action_plugin_combo_url 33c3437056SNickeau * 34c3437056SNickeau * The actual URL manager 35c3437056SNickeau * 36c3437056SNickeau * 37c3437056SNickeau */ 38c3437056SNickeauclass action_plugin_combo_router extends DokuWiki_Action_Plugin 39c3437056SNickeau{ 40c3437056SNickeau 41c3437056SNickeau /** 42c3437056SNickeau * @deprecated 43c3437056SNickeau */ 44c3437056SNickeau const URL_MANAGER_ENABLE_CONF = "enableUrlManager"; 45c3437056SNickeau const ROUTER_ENABLE_CONF = "enableRouter"; 46c3437056SNickeau 47c3437056SNickeau 48c3437056SNickeau // Where the target id value comes from 49c3437056SNickeau 50c3437056SNickeau 51c3437056SNickeau // The constant parameters 52c3437056SNickeau 53c3437056SNickeau /** @var string - a name used in log and other places */ 54c3437056SNickeau const NAME = 'Url Manager'; 55c3437056SNickeau const CANONICAL = 'router'; 56c3437056SNickeau const PAGE_404 = "<html lang=\"en\"><body></body></html>"; 57c3437056SNickeau const REFRESH_HEADER_NAME = "Refresh"; 58c3437056SNickeau const REFRESH_HEADER_PREFIX = self::REFRESH_HEADER_NAME . ': 0;url='; 5904fd306cSNickeau const LOCATION_HEADER_PREFIX = HttpResponse::LOCATION_HEADER_NAME . ": "; 60c3437056SNickeau public const URL_MANAGER_NAME = "Router"; 61c3437056SNickeau 62c3437056SNickeau 63c3437056SNickeau /** 64c3437056SNickeau * @var PageRules 65c3437056SNickeau */ 66c3437056SNickeau private $pageRules; 67c3437056SNickeau 68c3437056SNickeau 69c3437056SNickeau function __construct() 70c3437056SNickeau { 71c3437056SNickeau // enable direct access to language strings 72c3437056SNickeau // ie $this->lang 73c3437056SNickeau $this->setupLocale(); 74c3437056SNickeau 75c3437056SNickeau } 76c3437056SNickeau 77c3437056SNickeau /** 7804fd306cSNickeau * @param string $refreshHeader 79c3437056SNickeau * @return false|string 80c3437056SNickeau */ 8104fd306cSNickeau public static function getUrlFromRefresh(string $refreshHeader) 82c3437056SNickeau { 83c3437056SNickeau return substr($refreshHeader, strlen(action_plugin_combo_router::REFRESH_HEADER_PREFIX)); 84c3437056SNickeau } 85c3437056SNickeau 86c3437056SNickeau public static function getUrlFromLocation($refreshHeader) 87c3437056SNickeau { 88c3437056SNickeau return substr($refreshHeader, strlen(action_plugin_combo_router::LOCATION_HEADER_PREFIX)); 89c3437056SNickeau } 90c3437056SNickeau 91c3437056SNickeau 92c3437056SNickeau /** 93c3437056SNickeau * Determine if the request should be banned based on the id 94c3437056SNickeau * 95c3437056SNickeau * @param string $id 96c3437056SNickeau * @return bool 97c3437056SNickeau * 98c3437056SNickeau * See also {@link https://perishablepress.com/7g-firewall/#features} 99c3437056SNickeau * for blocking rules on http request data such as: 100c3437056SNickeau * * query_string 101c3437056SNickeau * * user_agent, 102c3437056SNickeau * * remote host 103c3437056SNickeau */ 104c3437056SNickeau public static function isShadowBanned(string $id): bool 105c3437056SNickeau { 106c3437056SNickeau /** 107c3437056SNickeau * ie 108c3437056SNickeau * wp-json:api:flutter_woo:config_file 109c3437056SNickeau * wp-content:plugins:wpdiscuz:themes:default:style-rtl.css 110c3437056SNickeau * wp-admin 111c3437056SNickeau * 2020:wp-includes:wlwmanifest.xml 112c3437056SNickeau * wp-content:start 113c3437056SNickeau * wp-admin:css:start 114c3437056SNickeau * sito:wp-includes:wlwmanifest.xml 115c3437056SNickeau * site:wp-includes:wlwmanifest.xml 116c3437056SNickeau * cms:wp-includes:wlwmanifest.xml 117c3437056SNickeau * test:wp-includes:wlwmanifest.xml 118c3437056SNickeau * media:wp-includes:wlwmanifest.xml 119c3437056SNickeau * wp2:wp-includes:wlwmanifest.xml 120c3437056SNickeau * 2019:wp-includes:wlwmanifest.xml 121c3437056SNickeau * shop:wp-includes:wlwmanifest.xml 122c3437056SNickeau * wp1:wp-includes:wlwmanifest.xml 123c3437056SNickeau * news:wp-includes:wlwmanifest.xml 124c3437056SNickeau * 2018:wp-includes:wlwmanifest.xml 125c3437056SNickeau */ 126c3437056SNickeau if (strpos($id, 'wp-') !== false) { 127c3437056SNickeau return true; 128c3437056SNickeau } 129c3437056SNickeau 130c3437056SNickeau /** 131c3437056SNickeau * db:oracle:long_or_1_utl_inaddr.get_host_address_chr_33_chr_126_chr_33_chr_65_chr_66_chr_67_chr_49_chr_52_chr_53_chr_90_chr_81_chr_54_chr_50_chr_68_chr_87_chr_81_chr_65_chr_70_chr_80_chr_79_chr_73_chr_89_chr_67_chr_70_chr_68_chr_33_chr_126_chr_33 132c3437056SNickeau * db:oracle:999999.9:union:all:select_null:from_dual 133c3437056SNickeau * db:oracle:999999.9:union:all:select_null:from_dual_and_0_0 134c3437056SNickeau */ 135c3437056SNickeau if (preg_match('/_chr_|_0_0/', $id) === 1) { 136c3437056SNickeau return true; 137c3437056SNickeau } 138c3437056SNickeau 139c3437056SNickeau 140c3437056SNickeau /** 141c3437056SNickeau * ie 142c3437056SNickeau * git:objects: 143c3437056SNickeau * git:refs:heads:stable 144c3437056SNickeau * git:logs:refs:heads:main 145c3437056SNickeau * git:logs:refs:heads:stable 146c3437056SNickeau * git:hooks:pre-push.sample 147c3437056SNickeau * git:hooks:pre-receive.sample 148c3437056SNickeau */ 149c3437056SNickeau if (strpos($id, "git:") === 0) { 150c3437056SNickeau return true; 151c3437056SNickeau } 152c3437056SNickeau 153c3437056SNickeau return false; 154c3437056SNickeau 155c3437056SNickeau } 156c3437056SNickeau 157c3437056SNickeau /** 158c3437056SNickeau * @param string $id 159c3437056SNickeau * @return bool 160c3437056SNickeau * well-known:traffic-advice = https://github.com/buettner/private-prefetch-proxy/blob/main/traffic-advice.md 161c3437056SNickeau * .well-known/security.txt, id=well-known:security.txt = https://securitytxt.org/ 162c3437056SNickeau * well-known:dnt-policy.txt 163c3437056SNickeau */ 164c3437056SNickeau public static function isWellKnownFile(string $id): bool 165c3437056SNickeau { 166c3437056SNickeau return strpos($id, "well-known") === 0; 167c3437056SNickeau } 168c3437056SNickeau 169c3437056SNickeau 170c3437056SNickeau function register(Doku_Event_Handler $controller) 171c3437056SNickeau { 172c3437056SNickeau 17304fd306cSNickeau if (SiteConfig::getConfValue(self::ROUTER_ENABLE_CONF, 1)) { 17404fd306cSNickeau 175c3437056SNickeau /** 176c3437056SNickeau * This will call the function {@link action_plugin_combo_router::_router()} 177c3437056SNickeau * The event is not DOKUWIKI_STARTED because this is not the first one 178c3437056SNickeau * 179c3437056SNickeau * https://www.dokuwiki.org/devel:event:init_lang_load 180c3437056SNickeau */ 181c3437056SNickeau $controller->register_hook('DOKUWIKI_STARTED', 18204fd306cSNickeau 'BEFORE', 183c3437056SNickeau $this, 184c3437056SNickeau 'router', 185c3437056SNickeau array()); 186c3437056SNickeau 187c3437056SNickeau /** 1885187326aSNico * Bot Ban functionality 189c3437056SNickeau * 1905187326aSNico * Because we make a redirection to the home page, we need to check 1915187326aSNico * if the home is readable, for that, the AUTH plugin needs to be initialized 1925187326aSNico * That's why we wait 1935187326aSNico * https://www.dokuwiki.org/devel:event:dokuwiki_init_done 1945187326aSNico * 1955187326aSNico * and we can't use 196c3437056SNickeau * https://www.dokuwiki.org/devel:event:init_lang_load 1975187326aSNico * because there is no auth setup in {@link auth_aclcheck_cb()} 1985187326aSNico * and the the line `if (!$auth instanceof AuthPlugin) return AUTH_NONE;` return none; 199c3437056SNickeau */ 2005187326aSNico $controller->register_hook('DOKUWIKI_INIT_DONE', 'BEFORE', $this, 'ban', array()); 201c3437056SNickeau 202c3437056SNickeau } 203c3437056SNickeau 204c3437056SNickeau 205c3437056SNickeau } 206c3437056SNickeau 207c3437056SNickeau /** 208c3437056SNickeau * 209c3437056SNickeau * We have created a spacial ban function that is 210c3437056SNickeau * called before the first function 211c3437056SNickeau * {@link action_plugin_combo_metalang::load_lang()} 212c3437056SNickeau * to spare CPU. 213c3437056SNickeau * 214c3437056SNickeau * @param $event 215c3437056SNickeau * @throws Exception 216c3437056SNickeau */ 217c3437056SNickeau function ban(&$event) 218c3437056SNickeau { 219c3437056SNickeau 220*45a874f4SNico $id = Router::getOriginalIdFromRequest(); 22106ecf9e7Sgerardnico if ($id === null) { 22206ecf9e7Sgerardnico return; 22306ecf9e7Sgerardnico } 22404fd306cSNickeau $page = MarkupPath::createMarkupFromId($id); 2255187326aSNico if (FileSystems::exists($page)) { 2265187326aSNico return; 2275187326aSNico } 2285187326aSNico 229c3437056SNickeau // Well known 230c3437056SNickeau if (self::isWellKnownFile($id)) { 231*45a874f4SNico $redirection = RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_WELL_KNOWN) 232*45a874f4SNico ->setType(RouterRedirection::REDIRECT_NOTFOUND_METHOD) 233*45a874f4SNico ->build(); 234*45a874f4SNico $this->logRedirection($redirection); 23504fd306cSNickeau ExecutionContext::getActualOrCreateFromEnv() 23604fd306cSNickeau ->response() 23704fd306cSNickeau ->setStatus(HttpResponseStatus::NOT_FOUND) 23804fd306cSNickeau ->end(); 239c3437056SNickeau return; 240c3437056SNickeau } 241c3437056SNickeau 242c3437056SNickeau // Shadow banned 243c3437056SNickeau if (self::isShadowBanned($id)) { 244*45a874f4SNico $webSiteHomePage = MarkupPath::createMarkupFromId(Site::getIndexPageName()); 245*45a874f4SNico $redirection = RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_SHADOW_BANNED) 246*45a874f4SNico ->setType(RouterRedirection::REDIRECT_TRANSPARENT_METHOD) 247*45a874f4SNico ->setTargetMarkupPath($webSiteHomePage) 248*45a874f4SNico ->build(); 249*45a874f4SNico $this->executeTransparentRedirect($redirection); 250c3437056SNickeau } 2515187326aSNico 252c3437056SNickeau } 253c3437056SNickeau 254c3437056SNickeau /** 255c3437056SNickeau * @param $event Doku_Event 256c3437056SNickeau * @param $param 257c3437056SNickeau * @return void 258c3437056SNickeau */ 259*45a874f4SNico function router(Doku_Event &$event, $param) 260c3437056SNickeau { 261c3437056SNickeau 26204fd306cSNickeau /** 26304fd306cSNickeau * Just the {@link ExecutionContext::SHOW_ACTION} 26404fd306cSNickeau * may be redirected 26504fd306cSNickeau */ 26604fd306cSNickeau $executionContext = ExecutionContext::getActualOrCreateFromEnv(); 26704fd306cSNickeau if ($executionContext->getExecutingAction() !== ExecutionContext::SHOW_ACTION) { 26804fd306cSNickeau return; 26904fd306cSNickeau } 270c3437056SNickeau 271*45a874f4SNico 272*45a874f4SNico /** 273*45a874f4SNico * Redirect only if the page is not found 274*45a874f4SNico */ 275*45a874f4SNico $id = Router::getOriginalIdFromRequest(); 276*45a874f4SNico if ($id === null) { 277*45a874f4SNico return; 278*45a874f4SNico } 279*45a874f4SNico $page = MarkupPath::createMarkupFromId($id); 280*45a874f4SNico if (FileSystems::exists($page)) { 281*45a874f4SNico return; 282*45a874f4SNico } 283*45a874f4SNico 284*45a874f4SNico 285*45a874f4SNico /** 286*45a874f4SNico * Doku Rewrite is not supported 287*45a874f4SNico */ 28854743e42Sgerardnico $urlRewrite = Site::getUrlRewrite(); 28954743e42Sgerardnico if ($urlRewrite == UrlRewrite::VALUE_DOKU_REWRITE) { 29054743e42Sgerardnico UrlRewrite::sendErrorMessage(); 29154743e42Sgerardnico return; 29254743e42Sgerardnico } 293c3437056SNickeau 294c3437056SNickeau /** 295*45a874f4SNico * Try to find a redirection 296c3437056SNickeau */ 297*45a874f4SNico $router = new Router(); 29804fd306cSNickeau try { 299*45a874f4SNico $redirection = $router->getRedirection(); 30004fd306cSNickeau } catch (ExceptionSqliteNotAvailable $e) { 301*45a874f4SNico // no Sql Lite 302c3437056SNickeau return; 303b1aef534SNico } catch (ExceptionNotFound $e) { 304*45a874f4SNico // no redirection 305c3437056SNickeau return; 306*45a874f4SNico } catch (Exception $e) { 307*45a874f4SNico // Error 308*45a874f4SNico LogUtility::error("An unexpected error has occurred while trying to get a redirection", LogUtility::SUPPORT_CANONICAL, $e); 309c3437056SNickeau return; 310c3437056SNickeau } 311c3437056SNickeau 312c3437056SNickeau 313c3437056SNickeau /** 314*45a874f4SNico * Special Mode where the redirection is just a change of ACT 315c3437056SNickeau */ 316*45a874f4SNico if ($redirection->getOrigin() === Router::GO_TO_EDIT_MODE) { 317c3437056SNickeau global $ACT; 318c3437056SNickeau $ACT = 'edit'; 319*45a874f4SNico return; 320*45a874f4SNico } 321*45a874f4SNico 322*45a874f4SNico /** 323*45a874f4SNico * Other redirections 324*45a874f4SNico */ 325*45a874f4SNico switch ($redirection->getType()) { 326*45a874f4SNico case RouterRedirection::REDIRECT_TRANSPARENT_METHOD: 327*45a874f4SNico try { 328*45a874f4SNico $this->executeTransparentRedirect($redirection); 329*45a874f4SNico } catch (ExceptionCompile $e) { 330*45a874f4SNico LogUtility::error("Internal Error: A transparent redirect errors has occurred", LogUtility::SUPPORT_CANONICAL, $e); 331*45a874f4SNico } 332*45a874f4SNico return; 333*45a874f4SNico default: 334*45a874f4SNico try { 335*45a874f4SNico $this->executeHttpRedirect($redirection); 336*45a874f4SNico } catch (ExceptionCompile $e) { 337*45a874f4SNico LogUtility::error("Internal Error: A http redirect errors has occurred", LogUtility::SUPPORT_CANONICAL, $e); 338*45a874f4SNico } 339*45a874f4SNico } 340*45a874f4SNico 341c3437056SNickeau 342c3437056SNickeau } 343c3437056SNickeau 344c3437056SNickeau 345c3437056SNickeau /** 346c3437056SNickeau * Redirect to an internal page ie: 347c3437056SNickeau * * on the same domain 348c3437056SNickeau * * no HTTP redirect 349c3437056SNickeau * * id rewrite 350*45a874f4SNico * @param RouterRedirection $redirection - target page id 351*45a874f4SNico * @return void - return true if the user has the permission and that the redirect was done 352*45a874f4SNico * @throws ExceptionCompile 353c3437056SNickeau */ 354c3437056SNickeau private 355*45a874f4SNico function executeTransparentRedirect(RouterRedirection $redirection): void 356c3437056SNickeau { 357*45a874f4SNico $markupPath = $redirection->getTargetMarkupPath(); 358*45a874f4SNico if ($markupPath === null) { 359*45a874f4SNico throw new ExceptionCompile("A transparent redirect should have a wiki path. Origin {$redirection->getOrigin()}"); 360c3437056SNickeau } 361*45a874f4SNico $targetPageId = $redirection->getTargetMarkupPath()->toAbsoluteId(); 362c3437056SNickeau 363c3437056SNickeau // If the user does not have the right to see the target page 364c3437056SNickeau // don't do anything 365c3437056SNickeau if (!(Identity::isReader($targetPageId))) { 366*45a874f4SNico return; 367c3437056SNickeau } 368c3437056SNickeau 369c3437056SNickeau // Change the id 370c3437056SNickeau global $ID; 371c3437056SNickeau global $INFO; 372c3437056SNickeau $sourceId = $ID; 373c3437056SNickeau $ID = $targetPageId; 37404fd306cSNickeau if (isset($_REQUEST["id"])) { 37504fd306cSNickeau $_REQUEST["id"] = $targetPageId; 37604fd306cSNickeau } 37704fd306cSNickeau if (isset($_GET["id"])) { 37804fd306cSNickeau $_GET["id"] = $targetPageId; 37904fd306cSNickeau } 3804cadd4f8SNickeau 381c3437056SNickeau /** 3824cadd4f8SNickeau * Refresh the $INFO data 3834cadd4f8SNickeau * 3844cadd4f8SNickeau * the info attributes are used elsewhere 3854cadd4f8SNickeau * 'id': for the sidebar 3864cadd4f8SNickeau * 'exist' : for the meta robot = noindex,follow, see {@link tpl_metaheaders()} 3874cadd4f8SNickeau * 'rev' : for the edit button to be sure that the page is still the same 388c3437056SNickeau */ 3894cadd4f8SNickeau $INFO = pageinfo(); 390c3437056SNickeau 391c3437056SNickeau /** 392c3437056SNickeau * Not compatible with 393c3437056SNickeau * https://www.dokuwiki.org/config:send404 is enabled 394c3437056SNickeau * 395c3437056SNickeau * This check happens before that dokuwiki is started 396c3437056SNickeau * and send an header in doku.php 397c3437056SNickeau * 398c3437056SNickeau * We send a warning 399c3437056SNickeau */ 400c3437056SNickeau global $conf; 401*45a874f4SNico if ($conf['send404']) { 402c3437056SNickeau LogUtility::msg("The <a href=\"https://www.dokuwiki.org/config:send404\">dokuwiki send404 configuration</a> is on and should be disabled when using the url manager", LogUtility::LVL_MSG_ERROR, self::CANONICAL); 403c3437056SNickeau } 404c3437056SNickeau 405c3437056SNickeau // Redirection 406*45a874f4SNico $this->logRedirection($redirection); 407c3437056SNickeau 408c3437056SNickeau } 409c3437056SNickeau 410c3437056SNickeau 411c3437056SNickeau /** 412c3437056SNickeau * The general HTTP Redirect method to an internal page 413c3437056SNickeau * where the redirection method decide which type of redirection 414*45a874f4SNico * @throws ExceptionCompile - if any error 415c3437056SNickeau */ 416c3437056SNickeau private 417*45a874f4SNico function executeHttpRedirect(RouterRedirection $redirection): void 418c3437056SNickeau { 419c3437056SNickeau 420c3437056SNickeau 421c3437056SNickeau // Log the redirections 422*45a874f4SNico $this->logRedirection($redirection); 423c3437056SNickeau 424c3437056SNickeau 425*45a874f4SNico $targetUrl = $redirection->getTargetUrl(); 42604fd306cSNickeau 427*45a874f4SNico if ($targetUrl !== null) { 428c3437056SNickeau 429c3437056SNickeau // defend against HTTP Response Splitting 430c3437056SNickeau // https://owasp.org/www-community/attacks/HTTP_Response_Splitting 431*45a874f4SNico $targetUrl = stripctl($targetUrl->toAbsoluteUrlString()); 432*45a874f4SNico 433c3437056SNickeau 434c3437056SNickeau } else { 435c3437056SNickeau 436*45a874f4SNico global $ID; 437c3437056SNickeau 438c3437056SNickeau // if this is search engine redirect 439*45a874f4SNico $url = UrlEndpoint::createDokuUrl(); 440*45a874f4SNico switch ($redirection->getOrigin()) { 441*45a874f4SNico case RouterRedirection::TARGET_ORIGIN_SEARCH_ENGINE: 442*45a874f4SNico { 443c3437056SNickeau $replacementPart = array(':', '_', '-'); 444c3437056SNickeau $query = str_replace($replacementPart, ' ', $ID); 44511d09b86Sgerardnico $url->setQueryParameter(ExecutionContext::DO_ATTRIBUTE, ExecutionContext::SEARCH_ACTION); 44611d09b86Sgerardnico $url->setQueryParameter("q", $query); 447*45a874f4SNico $url->setQueryParameter(DokuwikiId::DOKUWIKI_ID_ATTRIBUTE, $ID); 448*45a874f4SNico break; 449*45a874f4SNico } 450*45a874f4SNico default: 451*45a874f4SNico 452*45a874f4SNico $markupPath = $redirection->getTargetMarkupPath(); 453*45a874f4SNico if ($markupPath == null) { 454*45a874f4SNico // should not happen (Both may be null but only on edit mode) 455*45a874f4SNico throw new ExceptionCompile("Internal Error When executing a http redirect, the URL or the wiki page should not be null"); 456*45a874f4SNico } 457*45a874f4SNico $url->setQueryParameter(DokuwikiId::DOKUWIKI_ID_ATTRIBUTE, $markupPath->toAbsoluteId()); 458*45a874f4SNico 459*45a874f4SNico 460c3437056SNickeau } 461c3437056SNickeau 4625b0932efSgerardnico /** 4635b0932efSgerardnico * Doing a permanent redirect with a added query string 4645b0932efSgerardnico * create a new page url on the search engine 4655b0932efSgerardnico * 4665b0932efSgerardnico * ie 4675b0932efSgerardnico * http://host/page 4685b0932efSgerardnico * is not the same 4695b0932efSgerardnico * than 4705b0932efSgerardnico * http://host/page?whatever 4715b0932efSgerardnico * 4725b0932efSgerardnico * We can't pass query string otherwise, we get 47349b8fb24Sgerardnico * the SEO warning / error 4745b0932efSgerardnico * `Alternative page with proper canonical tag` 47511d09b86Sgerardnico * 47611d09b86Sgerardnico * Use HTTP X header for debug 4775b0932efSgerardnico */ 478*45a874f4SNico if ($redirection->getType() !== RouterRedirection::REDIRECT_PERMANENT_METHOD) { 47911d09b86Sgerardnico $url->setQueryParameter(action_plugin_combo_routermessage::ORIGIN_PAGE, $ID); 480*45a874f4SNico $url->setQueryParameter(action_plugin_combo_routermessage::ORIGIN_TYPE, $redirection->getOrigin()); 4815b0932efSgerardnico } 4825b0932efSgerardnico 483*45a874f4SNico 48411d09b86Sgerardnico $targetUrl = $url->toAbsoluteUrlString(); 485c3437056SNickeau 486*45a874f4SNico 487*45a874f4SNico } 488*45a874f4SNico 489*45a874f4SNico 490*45a874f4SNico /** 491*45a874f4SNico * Check that we are not redirecting to the same URL 492*45a874f4SNico * to avoid the TOO_MANY_REDIRECT error 493*45a874f4SNico */ 494*45a874f4SNico $requestURL = Url::createFromString($_SERVER['REQUEST_URI'])->toAbsoluteUrlString(); 495*45a874f4SNico if ($requestURL === $targetUrl) { 496*45a874f4SNico throw new ExceptionCompile("A redirection should not redirect to the requested URL. Redirection Origin: {$redirection->getOrigin()}, Redirection URL:{$targetUrl} "); 497c3437056SNickeau } 498c3437056SNickeau 499c3437056SNickeau /** 500c3437056SNickeau * The dokuwiki function {@link send_redirect()} 501c3437056SNickeau * set the `Location header` and in php, the header function 502c3437056SNickeau * in this case change the status code to 302 Arghhhh. 503c3437056SNickeau * The code below is adapted from this function {@link send_redirect()} 504c3437056SNickeau */ 505c3437056SNickeau global $MSG; // are there any undisplayed messages? keep them in session for display 506c3437056SNickeau if (isset($MSG) && count($MSG) && !defined('NOSESSION')) { 507c3437056SNickeau //reopen session, store data and close session again 508c3437056SNickeau @session_start(); 509c3437056SNickeau $_SESSION[DOKU_COOKIE]['msg'] = $MSG; 510c3437056SNickeau } 511c3437056SNickeau session_write_close(); // always close the session 512c3437056SNickeau 513*45a874f4SNico switch ($redirection->getType()) { 5145b0932efSgerardnico 515*45a874f4SNico case RouterRedirection::REDIRECT_PERMANENT_METHOD: 51604fd306cSNickeau ExecutionContext::getActualOrCreateFromEnv() 51704fd306cSNickeau ->response() 51804fd306cSNickeau ->setStatus(HttpResponseStatus::PERMANENT_REDIRECT) 519c3437056SNickeau ->addHeader(self::LOCATION_HEADER_PREFIX . $targetUrl) 52004fd306cSNickeau ->end(); 521*45a874f4SNico return; 5225b0932efSgerardnico 523*45a874f4SNico case RouterRedirection::REDIRECT_NOTFOUND_METHOD: 5245b0932efSgerardnico 525c3437056SNickeau // Empty 404 body to not get the standard 404 page of the browser 526c3437056SNickeau // but a blank page to avoid a sort of FOUC. 527c3437056SNickeau // ie the user see a page briefly 52804fd306cSNickeau ExecutionContext::getActualOrCreateFromEnv() 52904fd306cSNickeau ->response() 53004fd306cSNickeau ->setStatus(HttpResponseStatus::NOT_FOUND) 531c3437056SNickeau ->addHeader(self::REFRESH_HEADER_PREFIX . $targetUrl) 53204fd306cSNickeau ->setBody(self::PAGE_404, Mime::getHtml()) 53304fd306cSNickeau ->end(); 534*45a874f4SNico return; 535c3437056SNickeau 536c3437056SNickeau default: 537*45a874f4SNico throw new ExceptionCompile("The type ({$redirection->getType()}) is not an http redirection"); 538c3437056SNickeau 539c3437056SNickeau } 540c3437056SNickeau 541c3437056SNickeau 542c3437056SNickeau } 543c3437056SNickeau 544c3437056SNickeau 545c3437056SNickeau /** 546c3437056SNickeau * 547c3437056SNickeau * * For a conf file, it will update the Redirection Action Data as Referrer, Count Of Redirection, Redirection Date 548c3437056SNickeau * * For a SQlite database, it will add a row into the log 549c3437056SNickeau * 550c3437056SNickeau * @param string $sourcePageId 551c3437056SNickeau * @param $targetPageId 552c3437056SNickeau * @param $algorithmic 553c3437056SNickeau * @param $method - http or rewrite 554c3437056SNickeau */ 555*45a874f4SNico function logRedirection(RouterRedirection $redirection) 556c3437056SNickeau { 557*45a874f4SNico global $ID; 558c3437056SNickeau 559c3437056SNickeau $row = array( 560c3437056SNickeau "TIMESTAMP" => date("c"), 561*45a874f4SNico "SOURCE" => $ID, 562*45a874f4SNico "TARGET" => $redirection->getTargetAsString(), 56370bbd7f1Sgerardnico "REFERRER" => $_SERVER['HTTP_REFERER'] ?? null, 564*45a874f4SNico "TYPE" => $redirection->getOrigin(), 565*45a874f4SNico "METHOD" => $redirection->getType() 566c3437056SNickeau ); 567*45a874f4SNico try { 568c3437056SNickeau $request = Sqlite::createOrGetBackendSqlite() 569c3437056SNickeau ->createRequest() 570c3437056SNickeau ->setTableRow('redirections_log', $row); 571*45a874f4SNico } catch (ExceptionSqliteNotAvailable $e) { 572*45a874f4SNico return; 573*45a874f4SNico } 574c3437056SNickeau try { 575c3437056SNickeau $request 576c3437056SNickeau ->execute(); 57704fd306cSNickeau } catch (ExceptionCompile $e) { 578c3437056SNickeau LogUtility::msg("Redirection Log Insert Error. {$e->getMessage()}"); 579c3437056SNickeau } finally { 580c3437056SNickeau $request->close(); 581c3437056SNickeau } 582c3437056SNickeau 583c3437056SNickeau 584c3437056SNickeau } 585c3437056SNickeau 586c3437056SNickeau 587c3437056SNickeau} 588