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; 1845a874f4SNicouse ComboStrap\Router; 1945a874f4SNicouse ComboStrap\RouterRedirection; 2045a874f4SNicouse ComboStrap\RouterRedirectionBuilder; 21c3437056SNickeauuse ComboStrap\Site; 2204fd306cSNickeauuse ComboStrap\SiteConfig; 23c3437056SNickeauuse ComboStrap\Sqlite; 2404fd306cSNickeauuse ComboStrap\Web\Url; 2511d09b86Sgerardnicouse ComboStrap\Web\UrlEndpoint; 2654743e42Sgerardnicouse ComboStrap\Web\UrlRewrite; 27c3437056SNickeau 2804fd306cSNickeaurequire_once(__DIR__ . '/../vendor/autoload.php'); 29c3437056SNickeau 30c3437056SNickeau/** 31c3437056SNickeau * Class action_plugin_combo_url 32c3437056SNickeau * 33c3437056SNickeau * The actual URL manager 34c3437056SNickeau * 35c3437056SNickeau * 36c3437056SNickeau */ 37c3437056SNickeauclass action_plugin_combo_router extends DokuWiki_Action_Plugin 38c3437056SNickeau{ 39c3437056SNickeau 40c3437056SNickeau /** 41c3437056SNickeau * @deprecated 42c3437056SNickeau */ 43c3437056SNickeau const URL_MANAGER_ENABLE_CONF = "enableUrlManager"; 44c3437056SNickeau const ROUTER_ENABLE_CONF = "enableRouter"; 45c3437056SNickeau 46c3437056SNickeau 47c3437056SNickeau // Where the target id value comes from 48c3437056SNickeau 49c3437056SNickeau 50c3437056SNickeau // The constant parameters 51c3437056SNickeau 52c3437056SNickeau /** @var string - a name used in log and other places */ 53c3437056SNickeau const NAME = 'Url Manager'; 54c3437056SNickeau const CANONICAL = 'router'; 55c3437056SNickeau const PAGE_404 = "<html lang=\"en\"><body></body></html>"; 56c3437056SNickeau const REFRESH_HEADER_NAME = "Refresh"; 57c3437056SNickeau const REFRESH_HEADER_PREFIX = self::REFRESH_HEADER_NAME . ': 0;url='; 5804fd306cSNickeau const LOCATION_HEADER_PREFIX = HttpResponse::LOCATION_HEADER_NAME . ": "; 59c3437056SNickeau public const URL_MANAGER_NAME = "Router"; 60c3437056SNickeau 61c3437056SNickeau 62c3437056SNickeau function __construct() 63c3437056SNickeau { 64c3437056SNickeau // enable direct access to language strings 65c3437056SNickeau // ie $this->lang 66c3437056SNickeau $this->setupLocale(); 67c3437056SNickeau 68c3437056SNickeau } 69c3437056SNickeau 70c3437056SNickeau /** 7104fd306cSNickeau * @param string $refreshHeader 72c3437056SNickeau * @return false|string 73c3437056SNickeau */ 7404fd306cSNickeau public static function getUrlFromRefresh(string $refreshHeader) 75c3437056SNickeau { 76c3437056SNickeau return substr($refreshHeader, strlen(action_plugin_combo_router::REFRESH_HEADER_PREFIX)); 77c3437056SNickeau } 78c3437056SNickeau 79c3437056SNickeau public static function getUrlFromLocation($refreshHeader) 80c3437056SNickeau { 81c3437056SNickeau return substr($refreshHeader, strlen(action_plugin_combo_router::LOCATION_HEADER_PREFIX)); 82c3437056SNickeau } 83c3437056SNickeau 84c3437056SNickeau 85c3437056SNickeau /** 86c3437056SNickeau * Determine if the request should be banned based on the id 87c3437056SNickeau * 88c3437056SNickeau * @param string $id 89c3437056SNickeau * @return bool 90c3437056SNickeau * 91c3437056SNickeau * See also {@link https://perishablepress.com/7g-firewall/#features} 92c3437056SNickeau * for blocking rules on http request data such as: 93c3437056SNickeau * * query_string 94c3437056SNickeau * * user_agent, 95c3437056SNickeau * * remote host 96c3437056SNickeau */ 97c3437056SNickeau public static function isShadowBanned(string $id): bool 98c3437056SNickeau { 99c3437056SNickeau /** 100c3437056SNickeau * ie 101c3437056SNickeau * wp-json:api:flutter_woo:config_file 102c3437056SNickeau * wp-content:plugins:wpdiscuz:themes:default:style-rtl.css 103c3437056SNickeau * wp-admin 104c3437056SNickeau * 2020:wp-includes:wlwmanifest.xml 105c3437056SNickeau * wp-content:start 106c3437056SNickeau * wp-admin:css:start 107c3437056SNickeau * sito:wp-includes:wlwmanifest.xml 108c3437056SNickeau * site:wp-includes:wlwmanifest.xml 109c3437056SNickeau * cms:wp-includes:wlwmanifest.xml 110c3437056SNickeau * test:wp-includes:wlwmanifest.xml 111c3437056SNickeau * media:wp-includes:wlwmanifest.xml 112c3437056SNickeau * wp2:wp-includes:wlwmanifest.xml 113c3437056SNickeau * 2019:wp-includes:wlwmanifest.xml 114c3437056SNickeau * shop:wp-includes:wlwmanifest.xml 115c3437056SNickeau * wp1:wp-includes:wlwmanifest.xml 116c3437056SNickeau * news:wp-includes:wlwmanifest.xml 117c3437056SNickeau * 2018:wp-includes:wlwmanifest.xml 118c3437056SNickeau */ 119c3437056SNickeau if (strpos($id, 'wp-') !== false) { 120c3437056SNickeau return true; 121c3437056SNickeau } 122c3437056SNickeau 123c3437056SNickeau /** 124c3437056SNickeau * 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 125c3437056SNickeau * db:oracle:999999.9:union:all:select_null:from_dual 126c3437056SNickeau * db:oracle:999999.9:union:all:select_null:from_dual_and_0_0 127c3437056SNickeau */ 128c3437056SNickeau if (preg_match('/_chr_|_0_0/', $id) === 1) { 129c3437056SNickeau return true; 130c3437056SNickeau } 131c3437056SNickeau 132c3437056SNickeau 133c3437056SNickeau /** 134c3437056SNickeau * ie 135c3437056SNickeau * git:objects: 136c3437056SNickeau * git:refs:heads:stable 137c3437056SNickeau * git:logs:refs:heads:main 138c3437056SNickeau * git:logs:refs:heads:stable 139c3437056SNickeau * git:hooks:pre-push.sample 140c3437056SNickeau * git:hooks:pre-receive.sample 141c3437056SNickeau */ 142c3437056SNickeau if (strpos($id, "git:") === 0) { 143c3437056SNickeau return true; 144c3437056SNickeau } 145c3437056SNickeau 146c3437056SNickeau return false; 147c3437056SNickeau 148c3437056SNickeau } 149c3437056SNickeau 150c3437056SNickeau /** 151c3437056SNickeau * @param string $id 152c3437056SNickeau * @return bool 153c3437056SNickeau * well-known:traffic-advice = https://github.com/buettner/private-prefetch-proxy/blob/main/traffic-advice.md 154c3437056SNickeau * .well-known/security.txt, id=well-known:security.txt = https://securitytxt.org/ 155c3437056SNickeau * well-known:dnt-policy.txt 156c3437056SNickeau */ 157c3437056SNickeau public static function isWellKnownFile(string $id): bool 158c3437056SNickeau { 159c3437056SNickeau return strpos($id, "well-known") === 0; 160c3437056SNickeau } 161c3437056SNickeau 162c3437056SNickeau 163c3437056SNickeau function register(Doku_Event_Handler $controller) 164c3437056SNickeau { 165c3437056SNickeau 16604fd306cSNickeau if (SiteConfig::getConfValue(self::ROUTER_ENABLE_CONF, 1)) { 16704fd306cSNickeau 168c3437056SNickeau /** 169c3437056SNickeau * This will call the function {@link action_plugin_combo_router::_router()} 170c3437056SNickeau * The event is not DOKUWIKI_STARTED because this is not the first one 171c3437056SNickeau * 172c3437056SNickeau * https://www.dokuwiki.org/devel:event:init_lang_load 173c3437056SNickeau */ 174c3437056SNickeau $controller->register_hook('DOKUWIKI_STARTED', 17504fd306cSNickeau 'BEFORE', 176c3437056SNickeau $this, 177c3437056SNickeau 'router', 178c3437056SNickeau array()); 179c3437056SNickeau 180c3437056SNickeau /** 1815187326aSNico * Bot Ban functionality 182c3437056SNickeau * 1835187326aSNico * Because we make a redirection to the home page, we need to check 1845187326aSNico * if the home is readable, for that, the AUTH plugin needs to be initialized 1855187326aSNico * That's why we wait 1865187326aSNico * https://www.dokuwiki.org/devel:event:dokuwiki_init_done 1875187326aSNico * 1885187326aSNico * and we can't use 189c3437056SNickeau * https://www.dokuwiki.org/devel:event:init_lang_load 1905187326aSNico * because there is no auth setup in {@link auth_aclcheck_cb()} 1915187326aSNico * and the the line `if (!$auth instanceof AuthPlugin) return AUTH_NONE;` return none; 192c3437056SNickeau */ 1935187326aSNico $controller->register_hook('DOKUWIKI_INIT_DONE', 'BEFORE', $this, 'ban', array()); 194c3437056SNickeau 195c3437056SNickeau } 196c3437056SNickeau 197c3437056SNickeau 198c3437056SNickeau } 199c3437056SNickeau 200c3437056SNickeau /** 201c3437056SNickeau * 202c3437056SNickeau * We have created a spacial ban function that is 203c3437056SNickeau * called before the first function 204c3437056SNickeau * {@link action_plugin_combo_metalang::load_lang()} 205c3437056SNickeau * to spare CPU. 206c3437056SNickeau * 207c3437056SNickeau * @param $event 208c3437056SNickeau * @throws Exception 209c3437056SNickeau */ 210c3437056SNickeau function ban(&$event) 211c3437056SNickeau { 212c3437056SNickeau 21345a874f4SNico $id = Router::getOriginalIdFromRequest(); 21406ecf9e7Sgerardnico if ($id === null) { 21506ecf9e7Sgerardnico return; 21606ecf9e7Sgerardnico } 21704fd306cSNickeau $page = MarkupPath::createMarkupFromId($id); 2185187326aSNico if (FileSystems::exists($page)) { 2195187326aSNico return; 2205187326aSNico } 2215187326aSNico 222c3437056SNickeau // Well known 223c3437056SNickeau if (self::isWellKnownFile($id)) { 22445a874f4SNico $redirection = RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_WELL_KNOWN) 22545a874f4SNico ->setType(RouterRedirection::REDIRECT_NOTFOUND_METHOD) 22645a874f4SNico ->build(); 22745a874f4SNico $this->logRedirection($redirection); 22804fd306cSNickeau ExecutionContext::getActualOrCreateFromEnv() 22904fd306cSNickeau ->response() 23004fd306cSNickeau ->setStatus(HttpResponseStatus::NOT_FOUND) 23104fd306cSNickeau ->end(); 232c3437056SNickeau return; 233c3437056SNickeau } 234c3437056SNickeau 235c3437056SNickeau // Shadow banned 236c3437056SNickeau if (self::isShadowBanned($id)) { 23745a874f4SNico $webSiteHomePage = MarkupPath::createMarkupFromId(Site::getIndexPageName()); 23845a874f4SNico $redirection = RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_SHADOW_BANNED) 23945a874f4SNico ->setType(RouterRedirection::REDIRECT_TRANSPARENT_METHOD) 24045a874f4SNico ->setTargetMarkupPath($webSiteHomePage) 24145a874f4SNico ->build(); 24245a874f4SNico $this->executeTransparentRedirect($redirection); 243c3437056SNickeau } 2445187326aSNico 245c3437056SNickeau } 246c3437056SNickeau 247c3437056SNickeau /** 248c3437056SNickeau * @param $event Doku_Event 249c3437056SNickeau * @param $param 250c3437056SNickeau * @return void 251c3437056SNickeau */ 25245a874f4SNico function router(Doku_Event &$event, $param) 253c3437056SNickeau { 254c3437056SNickeau 25504fd306cSNickeau /** 25604fd306cSNickeau * Just the {@link ExecutionContext::SHOW_ACTION} 25704fd306cSNickeau * may be redirected 25804fd306cSNickeau */ 25904fd306cSNickeau $executionContext = ExecutionContext::getActualOrCreateFromEnv(); 26004fd306cSNickeau if ($executionContext->getExecutingAction() !== ExecutionContext::SHOW_ACTION) { 26104fd306cSNickeau return; 26204fd306cSNickeau } 263c3437056SNickeau 26445a874f4SNico 26545a874f4SNico /** 266*ecf8d738SNico * If the ID is a permalink, it is already the real id 267*ecf8d738SNico * Why? because unfortunately, DOKUWIKI_STARTED is not the first event 268*ecf8d738SNico * {@link action_plugin_combo_lang::load_lang()} may have already 269*ecf8d738SNico * transformed a permalink into a real dokuwiki id 27045a874f4SNico */ 271*ecf8d738SNico global $ID; 272*ecf8d738SNico $page = MarkupPath::createMarkupFromId($ID); 27345a874f4SNico if (FileSystems::exists($page)) { 27445a874f4SNico return; 27545a874f4SNico } 27645a874f4SNico 27745a874f4SNico /** 27845a874f4SNico * Doku Rewrite is not supported 27945a874f4SNico */ 28054743e42Sgerardnico $urlRewrite = Site::getUrlRewrite(); 28154743e42Sgerardnico if ($urlRewrite == UrlRewrite::VALUE_DOKU_REWRITE) { 28254743e42Sgerardnico UrlRewrite::sendErrorMessage(); 28354743e42Sgerardnico return; 28454743e42Sgerardnico } 285c3437056SNickeau 286c3437056SNickeau /** 28745a874f4SNico * Try to find a redirection 288c3437056SNickeau */ 28945a874f4SNico $router = new Router(); 29004fd306cSNickeau try { 29145a874f4SNico $redirection = $router->getRedirection(); 29204fd306cSNickeau } catch (ExceptionSqliteNotAvailable $e) { 29345a874f4SNico // no Sql Lite 294c3437056SNickeau return; 295b1aef534SNico } catch (ExceptionNotFound $e) { 29645a874f4SNico // no redirection 297c3437056SNickeau return; 29845a874f4SNico } catch (Exception $e) { 29945a874f4SNico // Error 30045a874f4SNico LogUtility::error("An unexpected error has occurred while trying to get a redirection", LogUtility::SUPPORT_CANONICAL, $e); 301c3437056SNickeau return; 302c3437056SNickeau } 303c3437056SNickeau 304c3437056SNickeau 305c3437056SNickeau /** 30645a874f4SNico * Special Mode where the redirection is just a change of ACT 307c3437056SNickeau */ 30845a874f4SNico if ($redirection->getOrigin() === Router::GO_TO_EDIT_MODE) { 309c3437056SNickeau global $ACT; 310c3437056SNickeau $ACT = 'edit'; 31145a874f4SNico return; 31245a874f4SNico } 31345a874f4SNico 31445a874f4SNico /** 31545a874f4SNico * Other redirections 31645a874f4SNico */ 31745a874f4SNico switch ($redirection->getType()) { 31845a874f4SNico case RouterRedirection::REDIRECT_TRANSPARENT_METHOD: 31945a874f4SNico try { 32045a874f4SNico $this->executeTransparentRedirect($redirection); 32145a874f4SNico } catch (ExceptionCompile $e) { 32245a874f4SNico LogUtility::error("Internal Error: A transparent redirect errors has occurred", LogUtility::SUPPORT_CANONICAL, $e); 32345a874f4SNico } 32445a874f4SNico return; 32545a874f4SNico default: 32645a874f4SNico try { 32745a874f4SNico $this->executeHttpRedirect($redirection); 32845a874f4SNico } catch (ExceptionCompile $e) { 32945a874f4SNico LogUtility::error("Internal Error: A http redirect errors has occurred", LogUtility::SUPPORT_CANONICAL, $e); 33045a874f4SNico } 33145a874f4SNico } 33245a874f4SNico 333c3437056SNickeau 334c3437056SNickeau } 335c3437056SNickeau 336c3437056SNickeau 337c3437056SNickeau /** 338c3437056SNickeau * Redirect to an internal page ie: 339c3437056SNickeau * * on the same domain 340c3437056SNickeau * * no HTTP redirect 341c3437056SNickeau * * id rewrite 342*ecf8d738SNico * It happens when we use the id in the URL 34345a874f4SNico * @param RouterRedirection $redirection - target page id 34445a874f4SNico * @return void - return true if the user has the permission and that the redirect was done 34545a874f4SNico * @throws ExceptionCompile 346c3437056SNickeau */ 347c3437056SNickeau private 34845a874f4SNico function executeTransparentRedirect(RouterRedirection $redirection): void 349c3437056SNickeau { 35045a874f4SNico $markupPath = $redirection->getTargetMarkupPath(); 35145a874f4SNico if ($markupPath === null) { 35245a874f4SNico throw new ExceptionCompile("A transparent redirect should have a wiki path. Origin {$redirection->getOrigin()}"); 353c3437056SNickeau } 35445a874f4SNico $targetPageId = $redirection->getTargetMarkupPath()->toAbsoluteId(); 355c3437056SNickeau 356c3437056SNickeau // If the user does not have the right to see the target page 357c3437056SNickeau // don't do anything 358c3437056SNickeau if (!(Identity::isReader($targetPageId))) { 35945a874f4SNico return; 360c3437056SNickeau } 361c3437056SNickeau 362c3437056SNickeau // Change the id 363c3437056SNickeau global $ID; 364c3437056SNickeau global $INFO; 365c3437056SNickeau $sourceId = $ID; 366c3437056SNickeau $ID = $targetPageId; 36704fd306cSNickeau if (isset($_REQUEST["id"])) { 36804fd306cSNickeau $_REQUEST["id"] = $targetPageId; 36904fd306cSNickeau } 37004fd306cSNickeau if (isset($_GET["id"])) { 37104fd306cSNickeau $_GET["id"] = $targetPageId; 37204fd306cSNickeau } 3734cadd4f8SNickeau 374c3437056SNickeau /** 3754cadd4f8SNickeau * Refresh the $INFO data 3764cadd4f8SNickeau * 3774cadd4f8SNickeau * the info attributes are used elsewhere 3784cadd4f8SNickeau * 'id': for the sidebar 3794cadd4f8SNickeau * 'exist' : for the meta robot = noindex,follow, see {@link tpl_metaheaders()} 3804cadd4f8SNickeau * 'rev' : for the edit button to be sure that the page is still the same 381c3437056SNickeau */ 3824cadd4f8SNickeau $INFO = pageinfo(); 383c3437056SNickeau 384c3437056SNickeau /** 385c3437056SNickeau * Not compatible with 386c3437056SNickeau * https://www.dokuwiki.org/config:send404 is enabled 387c3437056SNickeau * 388c3437056SNickeau * This check happens before that dokuwiki is started 389c3437056SNickeau * and send an header in doku.php 390c3437056SNickeau * 391c3437056SNickeau * We send a warning 392c3437056SNickeau */ 393c3437056SNickeau global $conf; 39445a874f4SNico if ($conf['send404']) { 395c3437056SNickeau 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); 396c3437056SNickeau } 397c3437056SNickeau 398c3437056SNickeau // Redirection 39945a874f4SNico $this->logRedirection($redirection); 400c3437056SNickeau 401c3437056SNickeau } 402c3437056SNickeau 403c3437056SNickeau 404c3437056SNickeau /** 405c3437056SNickeau * The general HTTP Redirect method to an internal page 406c3437056SNickeau * where the redirection method decide which type of redirection 40745a874f4SNico * @throws ExceptionCompile - if any error 408c3437056SNickeau */ 409c3437056SNickeau private 41045a874f4SNico function executeHttpRedirect(RouterRedirection $redirection): void 411c3437056SNickeau { 412c3437056SNickeau 413c3437056SNickeau 414c3437056SNickeau // Log the redirections 41545a874f4SNico $this->logRedirection($redirection); 416c3437056SNickeau 417c3437056SNickeau 41845a874f4SNico $targetUrl = $redirection->getTargetUrl(); 41904fd306cSNickeau 42045a874f4SNico if ($targetUrl !== null) { 421c3437056SNickeau 422c3437056SNickeau // defend against HTTP Response Splitting 423c3437056SNickeau // https://owasp.org/www-community/attacks/HTTP_Response_Splitting 42445a874f4SNico $targetUrl = stripctl($targetUrl->toAbsoluteUrlString()); 42545a874f4SNico 426c3437056SNickeau 427c3437056SNickeau } else { 428c3437056SNickeau 42945a874f4SNico global $ID; 430c3437056SNickeau 431c3437056SNickeau // if this is search engine redirect 43245a874f4SNico $url = UrlEndpoint::createDokuUrl(); 43345a874f4SNico switch ($redirection->getOrigin()) { 43445a874f4SNico case RouterRedirection::TARGET_ORIGIN_SEARCH_ENGINE: 43545a874f4SNico { 436c3437056SNickeau $replacementPart = array(':', '_', '-'); 437c3437056SNickeau $query = str_replace($replacementPart, ' ', $ID); 43811d09b86Sgerardnico $url->setQueryParameter(ExecutionContext::DO_ATTRIBUTE, ExecutionContext::SEARCH_ACTION); 43911d09b86Sgerardnico $url->setQueryParameter("q", $query); 44045a874f4SNico $url->setQueryParameter(DokuwikiId::DOKUWIKI_ID_ATTRIBUTE, $ID); 44145a874f4SNico break; 44245a874f4SNico } 44345a874f4SNico default: 44445a874f4SNico 44545a874f4SNico $markupPath = $redirection->getTargetMarkupPath(); 44645a874f4SNico if ($markupPath == null) { 44745a874f4SNico // should not happen (Both may be null but only on edit mode) 44845a874f4SNico throw new ExceptionCompile("Internal Error When executing a http redirect, the URL or the wiki page should not be null"); 44945a874f4SNico } 45045a874f4SNico $url->setQueryParameter(DokuwikiId::DOKUWIKI_ID_ATTRIBUTE, $markupPath->toAbsoluteId()); 45145a874f4SNico 45245a874f4SNico 453c3437056SNickeau } 454c3437056SNickeau 4555b0932efSgerardnico /** 4565b0932efSgerardnico * Doing a permanent redirect with a added query string 4575b0932efSgerardnico * create a new page url on the search engine 4585b0932efSgerardnico * 4595b0932efSgerardnico * ie 4605b0932efSgerardnico * http://host/page 4615b0932efSgerardnico * is not the same 4625b0932efSgerardnico * than 4635b0932efSgerardnico * http://host/page?whatever 4645b0932efSgerardnico * 4655b0932efSgerardnico * We can't pass query string otherwise, we get 46649b8fb24Sgerardnico * the SEO warning / error 4675b0932efSgerardnico * `Alternative page with proper canonical tag` 46811d09b86Sgerardnico * 46911d09b86Sgerardnico * Use HTTP X header for debug 4705b0932efSgerardnico */ 47145a874f4SNico if ($redirection->getType() !== RouterRedirection::REDIRECT_PERMANENT_METHOD) { 47211d09b86Sgerardnico $url->setQueryParameter(action_plugin_combo_routermessage::ORIGIN_PAGE, $ID); 47345a874f4SNico $url->setQueryParameter(action_plugin_combo_routermessage::ORIGIN_TYPE, $redirection->getOrigin()); 4745b0932efSgerardnico } 4755b0932efSgerardnico 47645a874f4SNico 47711d09b86Sgerardnico $targetUrl = $url->toAbsoluteUrlString(); 478c3437056SNickeau 47945a874f4SNico 48045a874f4SNico } 48145a874f4SNico 48245a874f4SNico 48345a874f4SNico /** 48445a874f4SNico * Check that we are not redirecting to the same URL 48545a874f4SNico * to avoid the TOO_MANY_REDIRECT error 48645a874f4SNico */ 48745a874f4SNico $requestURL = Url::createFromString($_SERVER['REQUEST_URI'])->toAbsoluteUrlString(); 48845a874f4SNico if ($requestURL === $targetUrl) { 48945a874f4SNico throw new ExceptionCompile("A redirection should not redirect to the requested URL. Redirection Origin: {$redirection->getOrigin()}, Redirection URL:{$targetUrl} "); 490c3437056SNickeau } 491c3437056SNickeau 492c3437056SNickeau /** 493c3437056SNickeau * The dokuwiki function {@link send_redirect()} 494c3437056SNickeau * set the `Location header` and in php, the header function 495c3437056SNickeau * in this case change the status code to 302 Arghhhh. 496c3437056SNickeau * The code below is adapted from this function {@link send_redirect()} 497c3437056SNickeau */ 498c3437056SNickeau global $MSG; // are there any undisplayed messages? keep them in session for display 499c3437056SNickeau if (isset($MSG) && count($MSG) && !defined('NOSESSION')) { 500c3437056SNickeau //reopen session, store data and close session again 501c3437056SNickeau @session_start(); 502c3437056SNickeau $_SESSION[DOKU_COOKIE]['msg'] = $MSG; 503c3437056SNickeau } 504c3437056SNickeau session_write_close(); // always close the session 505c3437056SNickeau 50645a874f4SNico switch ($redirection->getType()) { 5075b0932efSgerardnico 50845a874f4SNico case RouterRedirection::REDIRECT_PERMANENT_METHOD: 50904fd306cSNickeau ExecutionContext::getActualOrCreateFromEnv() 51004fd306cSNickeau ->response() 51104fd306cSNickeau ->setStatus(HttpResponseStatus::PERMANENT_REDIRECT) 512c3437056SNickeau ->addHeader(self::LOCATION_HEADER_PREFIX . $targetUrl) 51304fd306cSNickeau ->end(); 51445a874f4SNico return; 5155b0932efSgerardnico 51645a874f4SNico case RouterRedirection::REDIRECT_NOTFOUND_METHOD: 5175b0932efSgerardnico 518c3437056SNickeau // Empty 404 body to not get the standard 404 page of the browser 519c3437056SNickeau // but a blank page to avoid a sort of FOUC. 520c3437056SNickeau // ie the user see a page briefly 52104fd306cSNickeau ExecutionContext::getActualOrCreateFromEnv() 52204fd306cSNickeau ->response() 52304fd306cSNickeau ->setStatus(HttpResponseStatus::NOT_FOUND) 524c3437056SNickeau ->addHeader(self::REFRESH_HEADER_PREFIX . $targetUrl) 52504fd306cSNickeau ->setBody(self::PAGE_404, Mime::getHtml()) 52604fd306cSNickeau ->end(); 52745a874f4SNico return; 528c3437056SNickeau 529c3437056SNickeau default: 53045a874f4SNico throw new ExceptionCompile("The type ({$redirection->getType()}) is not an http redirection"); 531c3437056SNickeau 532c3437056SNickeau } 533c3437056SNickeau 534c3437056SNickeau 535c3437056SNickeau } 536c3437056SNickeau 537c3437056SNickeau 538c3437056SNickeau /** 539c3437056SNickeau * 540c3437056SNickeau * * For a conf file, it will update the Redirection Action Data as Referrer, Count Of Redirection, Redirection Date 541c3437056SNickeau * * For a SQlite database, it will add a row into the log 542c3437056SNickeau * 543c3437056SNickeau * @param string $sourcePageId 544c3437056SNickeau * @param $targetPageId 545c3437056SNickeau * @param $algorithmic 546c3437056SNickeau * @param $method - http or rewrite 547c3437056SNickeau */ 54845a874f4SNico function logRedirection(RouterRedirection $redirection) 549c3437056SNickeau { 55045a874f4SNico global $ID; 551c3437056SNickeau 552c3437056SNickeau $row = array( 553c3437056SNickeau "TIMESTAMP" => date("c"), 55445a874f4SNico "SOURCE" => $ID, 55545a874f4SNico "TARGET" => $redirection->getTargetAsString(), 55670bbd7f1Sgerardnico "REFERRER" => $_SERVER['HTTP_REFERER'] ?? null, 55745a874f4SNico "TYPE" => $redirection->getOrigin(), 55845a874f4SNico "METHOD" => $redirection->getType() 559c3437056SNickeau ); 56045a874f4SNico try { 561c3437056SNickeau $request = Sqlite::createOrGetBackendSqlite() 562c3437056SNickeau ->createRequest() 563c3437056SNickeau ->setTableRow('redirections_log', $row); 56445a874f4SNico } catch (ExceptionSqliteNotAvailable $e) { 56545a874f4SNico return; 56645a874f4SNico } 567c3437056SNickeau try { 568c3437056SNickeau $request 569c3437056SNickeau ->execute(); 57004fd306cSNickeau } catch (ExceptionCompile $e) { 571c3437056SNickeau LogUtility::msg("Redirection Log Insert Error. {$e->getMessage()}"); 572c3437056SNickeau } finally { 573c3437056SNickeau $request->close(); 574c3437056SNickeau } 575c3437056SNickeau 576c3437056SNickeau 577c3437056SNickeau } 578c3437056SNickeau 579c3437056SNickeau 580c3437056SNickeau} 581