PageRenderTime 40ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/typo3conf/ext/realurl/class.tx_realurl.php

https://github.com/moodley/fdummy
PHP | 2787 lines | 1614 code | 289 blank | 884 comment | 400 complexity | b9f1b668f5260e817bfc22b904be50ce MD5 | raw file
  1. <?php
  2. /***************************************************************
  3. * Copyright notice
  4. *
  5. * (c) 2004 Kasper Skaarhoj (kasper@typo3.com)
  6. * (c) 2005-2010 Dmitry Dulepov (dmitry@typo3.org)
  7. * All rights reserved
  8. *
  9. * This script is part of the Typo3 project. The Typo3 project is
  10. * free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 2 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * The GNU General Public License can be found at
  16. * http://www.gnu.org/copyleft/gpl.html.
  17. * A copy is found in the textfile GPL.txt and important notices to the license
  18. * from the author is found in LICENSE.txt distributed with these scripts.
  19. *
  20. *
  21. * This script is distributed in the hope that it will be useful,
  22. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24. * GNU General Public License for more details.
  25. *
  26. * This copyright notice MUST APPEAR in all copies of the script!
  27. ***************************************************************/
  28. /**
  29. * Class for creating and parsing Speaking Urls
  30. *
  31. * $Id: class.tx_realurl.php 63839 2012-06-25 11:38:02Z dmitry $
  32. *
  33. * @author Kasper Skaarhoj <kasper@typo3.com>
  34. * @author Dmitry Dulepov <dmitry@typo3.org>
  35. */
  36. /**
  37. * [CLASS/FUNCTION INDEX of SCRIPT]
  38. *
  39. *
  40. *
  41. * 107: class tx_realurl
  42. *
  43. * SECTION: Translate parameters to a Speaking URL (t3lib_tstemplate::linkData)
  44. * 152: function tx_realurl()
  45. * 170: function encodeSpURL(&$params, $ref)
  46. * 241: function encodeSpURL_doEncode($inputQuery, $cHashCache = FALSE, $origUrl = '')
  47. * 324: function encodeSpURL_pathFromId(&$paramKeyValues, &$pathParts)
  48. * 353: function encodeSpURL_gettingPostVarSets(&$paramKeyValues, &$pathParts, $postVarSetCfg)
  49. * 390: function encodeSpURL_fileName(&$paramKeyValues)
  50. * 412: function encodeSpURL_setSequence($varSetCfg, &$paramKeyValues, &$pathParts)
  51. * 516: function encodeSpURL_setSingle($keyWord, $keyValues, &$paramKeyValues, &$pathParts)
  52. * 550: function encodeSpURL_encodeCache($urlToEncode, $internalExtras, $setEncodedURL = '')
  53. * 617: function encodeSpURL_cHashCache($newUrl, &$paramKeyValues)
  54. *
  55. * SECTION: Translate a Speaking URL to parameters (tslib_fe)
  56. * 669: function decodeSpURL($params, $ref)
  57. * 759: function decodeSpURL_checkRedirects($speakingURIpath)
  58. * 801: function decodeSpURL_doDecode($speakingURIpath, $cHashCache = FALSE)
  59. * 877: function decodeSpURL_createQueryStringParam($paramArr, $prependString = '')
  60. * 900: function decodeSpURL_createQueryString(&$getVars)
  61. * 925: function decodeSpURL_idFromPath(&$pathParts)
  62. * 966: function decodeSpURL_settingPreVars(&$pathParts, $config)
  63. * 989: function decodeSpURL_settingPostVarSets(&$pathParts, $postVarSetCfg)
  64. * 1054: function decodeSpURL_fixBrackets(&$arr)
  65. * 1080: function decodeSpURL_fileName($fileName)
  66. * 1108: function decodeSpURL_getSequence(&$pathParts, $setupArr)
  67. * 1201: function decodeSpURL_getSingle($keyValues)
  68. * 1217: function decodeSpURL_throw404($msg)
  69. * 1251: function decodeSpURL_jumpAdmin()
  70. * 1275: function decodeSpURL_jumpAdmin_goBackend($pageId)
  71. * 1290: function decodeSpURL_decodeCache($speakingURIpath, $cachedInfo = '')
  72. * 1354: function decodeSpURL_cHashCache($speakingURIpath)
  73. *
  74. * SECTION: Alias-ID look up functions
  75. * 1385: function lookUpTranslation($cfg, $value, $aliasToUid = FALSE)
  76. * 1495: function lookUp_uniqAliasToId($cfg, $aliasValue, $onlyNonExpired = FALSE)
  77. * 1522: function lookUp_idToUniqAlias($cfg, $idValue, $lang, $aliasValue = '')
  78. * 1550: function lookUp_newAlias($cfg, $newAliasValue, $idValue, $lang)
  79. * 1620: function lookUp_cleanAlias($cfg, $newAliasValue)
  80. *
  81. * SECTION: General helper functions (both decode/encode)
  82. * 1665: function setConfig()
  83. * 1699: function getPostVarSetConfig($page_id, $mainCat = 'postVarSets')
  84. * 1721: function pageAliasToID($alias)
  85. * 1744: function rawurlencodeParam($str)
  86. * 1759: function checkCondition($setup, $prevVal, $value)
  87. * 1777: function isBEUserLoggedIn()
  88. *
  89. * SECTION: External Hooks
  90. * 1794: function clearPageCacheMgm($params, $ref)
  91. * 1809: function isString(&$str, $paramName)
  92. * 1834: function findRootPageId($host = '')
  93. *
  94. * TOTAL FUNCTIONS: 41
  95. * (This index is automatically created/updated by the extension "extdeveval")
  96. *
  97. */
  98. /**
  99. * Class for creating and parsing Speaking Urls
  100. * This class interfaces with hooks in TYPO3 inside tslib_fe (for parsing speaking URLs to GET parameters) and in t3lib_tstemplate (for parsing GET parameters into a speaking URL)
  101. *
  102. * @author Kasper Skaarhoj <kasper@typo3.com>
  103. * @author Dmitry Dulepov <dmitry@typo3.org>
  104. * @package TYPO3
  105. * @subpackage tx_realurl
  106. */
  107. class tx_realurl {
  108. // External, static:
  109. var $NA = '-'; // Substitute value for "blank" values
  110. var $maxLookUpLgd = 100; // Max. length of look-up strings. Just a "brake"
  111. var $prefixEnablingSpURL = 'index.php'; // Only work Speaking URL on URLs starting with "index.php"
  112. var $decodeCacheTTL = 1; // TTL for decode cache, default is 1 day.
  113. var $encodeCacheTTL = 1; // TTL for encode cache, default is 1 day.
  114. // Internal:
  115. /** @var tslib_fe */
  116. var $pObj; // tslib_fe / GLOBALS['TSFE'] (for ->decodeSpURL())
  117. var $extConf; // Configuration for extension, from $TYPO3_CONF_VARS['EXTCONF']['realurl']
  118. var $adminJumpSet = FALSE; // Is set true (->encodeSpURL) if AdminJump is active in some way. Is set false again when captured first time!
  119. var $fe_user_prefix_set = FALSE; // Is set true (->encodeSpURL) if there is a frontend user logged in
  120. var $filePart; // Contains the filename when a Speaking URL is decoded.
  121. var $dirParts; // All directory parts of the string
  122. var $orig_paramKeyValues = array(); // Contains the index of GETvars that the URL had when the encoding began.
  123. var $appendedSlash = false; // Set true if slash is appended
  124. var $encodePageId = 0; // Set with the page id during encoding. for internal use only.
  125. var $speakingURIpath_procValue = ''; // For decoding, the path we are processing.
  126. var $disableDecodeCache = FALSE; // If set internally, decode caching is disabled. Used when a 303 header is set in tx_realurl_advanced.
  127. var $decode_editInBackend = FALSE; // If set (in adminjump function) then we will redirect to edit the found page id in the backend.
  128. var $encodeError = FALSE; // If set true encoding failed , probably because the url was outside of root line - and the input url is returned directly.
  129. var $host = ''; // Current host name. Set in setConfig()
  130. /**
  131. * Additional values to use when creating chash cache. This works, for
  132. * example, when using _DOMAINS and cHash for links that do not really
  133. * need a cHash.
  134. *
  135. * @var array
  136. */
  137. protected $additionalParametersForChash;
  138. /**
  139. * Actual host name (configuration key) for the current request. This can
  140. * be different from the $this->host if there are host aliases.
  141. *
  142. * @var string
  143. */
  144. protected $hostConfigured = '';
  145. var $multidomain = false;
  146. var $urlPrepend = array();
  147. var $useMySQLExtendedSyntax = false;
  148. /**
  149. * Holds a uid of the detected language during decoding to limit search of
  150. * titles only to this language. Valid values are:
  151. * -1 - no language detected
  152. * 0 - default language (only if really detected!)
  153. * >0 - a language uid taken from preVars or _DOMAINS (corresponds to uid in sys_languages table)
  154. *
  155. * @var int
  156. */
  157. protected $detectedLanguage = -1;
  158. /**
  159. * Inidicates wwether devLog is enabled
  160. *
  161. * @var true
  162. */
  163. protected $enableDevLog = false;
  164. /**
  165. * Contains a request id. This is to simplify identification of a single
  166. * request when the site is accessed concurently
  167. *
  168. * @var string
  169. */
  170. protected $devLogId;
  171. /**
  172. * Mime type that can be set according to the file extension (decoding only).
  173. *
  174. * @var string
  175. */
  176. protected $mimeType = null;
  177. var $enableStrictMode = false;
  178. var $enableChashDebug = false;
  179. /**
  180. * If non-empty, corresponding URL query parameter will be ignored in preVars
  181. * (note: preVars only!). This is necessary for _DOMAINS feature. This value
  182. * is set to empty in adjustConfigurationByHostEncode().
  183. *
  184. * @see tx_realurl::adjustConfigurationByHostEncode()
  185. * @see tx_realurl::encodeSpURL_doEncode()
  186. * @var string
  187. */
  188. protected $ignoreGETvar;
  189. /**
  190. * Contains URL parameters that were merged into URL. This is necessary
  191. * if cHash has to be recalculated due to bypassed parameters. Used during
  192. * encoding only.
  193. *
  194. * @var array
  195. * @see http://bugs.typo3.org/view.php?id=11219
  196. */
  197. protected $cHashParameters;
  198. /**
  199. * Indicates wether cHash should be rebuilt for the URL. Used during
  200. * encoding only.
  201. *
  202. * @var boolean
  203. * @see http://bugs.typo3.org/view.php?id=11219
  204. */
  205. protected $rebuildCHash;
  206. /************************************
  207. *
  208. * Translate parameters to a Speaking URL (t3lib_tstemplate::linkData)
  209. *
  210. ************************************/
  211. /**
  212. * Creates an instance of this class
  213. *
  214. * @return void
  215. */
  216. public function __construct() {
  217. if (!t3lib_extMgm::isLoaded('dbal') && strpos(get_resource_type($GLOBALS['TYPO3_DB']->link), 'mysql link') !== false) {
  218. $res = $GLOBALS['TYPO3_DB']->sql_query('SELECT @@VERSION');
  219. $rec = $GLOBALS['TYPO3_DB']->sql_fetch_row($res);
  220. $GLOBALS['TYPO3_DB']->sql_free_result($res);
  221. $this->useMySQLExtendedSyntax = version_compare($rec[0], '4.1.0', '>');
  222. }
  223. $sysconf = (array)unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['realurl']);
  224. $this->enableStrictMode = (boolean)$sysconf['enableStrictMode'];
  225. $this->enableChashUrlDebug = (boolean)$sysconf['enableChashUrlDebug'];
  226. $this->initDevLog($sysconf);
  227. }
  228. /**
  229. * Initializes devLog support
  230. *
  231. * @param array $sysconf
  232. * @return void
  233. */
  234. protected function initDevLog(array $sysconf) {
  235. $this->enableDevLog = (boolean)$sysconf['enableDevLog'];
  236. if ($this->enableDevLog) {
  237. $this->devLogId = (isset($_SERVER['UNIQUE_ID']) ? $_SERVER['UNIQUE_ID'] : uniqid(''));
  238. }
  239. }
  240. /**
  241. * Translates a URL with query string (GET parameters) into Speaking URL.
  242. * Called from t3lib_tstemplate::linkData
  243. *
  244. * @param array Array of parameters from t3lib_tstemplate::linkData - the function creating all links inside TYPO3
  245. * @return void
  246. */
  247. public function encodeSpURL(&$params) {
  248. $this->devLog('Entering encodeSpURL for ' . $params['LD']['totalURL']);
  249. if ($this->isInWorkspace()) {
  250. $this->devLog('Workspace detected. Not doing anything!');
  251. return;
  252. }
  253. if (!$params['TCEmainHook']) {
  254. // Return directly, if simulateStaticDocuments is set:
  255. if ($GLOBALS['TSFE']->config['config']['simulateStaticDocuments']) {
  256. $GLOBALS['TT']->setTSlogMessage('SimulateStaticDocuments is enabled. RealURL disables itself.', 2);
  257. return;
  258. }
  259. // Return directly, if realurl is not enabled:
  260. if (!$GLOBALS['TSFE']->config['config']['tx_realurl_enable']) {
  261. $GLOBALS['TT']->setTSlogMessage('RealURL is not enabled in TS setup. Finished.');
  262. return;
  263. }
  264. }
  265. // Checking prefix:
  266. $prefix = $GLOBALS['TSFE']->absRefPrefix . $this->prefixEnablingSpURL;
  267. if (substr($params['LD']['totalURL'], 0, strlen($prefix)) != $prefix) {
  268. return;
  269. }
  270. $this->devLog('Starting URL encode');
  271. // Initializing config / request URL:
  272. $this->setConfig();
  273. $adjustedConfiguration = $this->adjustConfigurationByHost('encode', $params);
  274. $this->adjustRootPageId();
  275. $internalExtras = array();
  276. // Init "Admin Jump"; If frontend edit was enabled by the current URL of the page, set it again in the generated URL (and disable caching!)
  277. if (!$params['TCEmainHook']) {
  278. if ($GLOBALS['TSFE']->applicationData['tx_realurl']['adminJumpActive']) {
  279. $GLOBALS['TSFE']->set_no_cache();
  280. $this->adminJumpSet = TRUE;
  281. $internalExtras['adminJump'] = 1;
  282. }
  283. // If there is a frontend user logged in, set fe_user_prefix
  284. if (is_array($GLOBALS['TSFE']->fe_user->user)) {
  285. $this->fe_user_prefix_set = TRUE;
  286. $internalExtras['feLogin'] = 1;
  287. }
  288. }
  289. // Parse current URL into main parts:
  290. $uParts = parse_url($params['LD']['totalURL']);
  291. // Look in memory cache first
  292. $urlData = $this->hostConfigured . ' | ' . $uParts['query'];
  293. $newUrl = $this->encodeSpURL_encodeCache($urlData, $internalExtras);
  294. if (!$newUrl) {
  295. // Encode URL
  296. $newUrl = $this->encodeSpURL_doEncode($uParts['query'], $this->extConf['init']['enableCHashCache'], $params['LD']['totalURL']);
  297. // Set new URL in cache
  298. $this->encodeSpURL_encodeCache($urlData, $internalExtras, $newUrl);
  299. }
  300. unset($urlData);
  301. // Adding any anchor there might be:
  302. if ($uParts['fragment']) {
  303. $newUrl .= '#' . $uParts['fragment'];
  304. }
  305. // Reapply config.absRefPrefix if necessary
  306. if ((!isset($this->extConf['init']['reapplyAbsRefPrefix']) || $this->extConf['init']['reapplyAbsRefPrefix']) && $GLOBALS['TSFE']->absRefPrefix) {
  307. // Prevent // in case of absRefPrefix ending with / and emptyUrlReturnValue=/
  308. if (substr($GLOBALS['TSFE']->absRefPrefix, -1, 1) == '/' && substr($newUrl, 0, 1) == '/') {
  309. $newUrl = substr($newUrl, 1);
  310. }
  311. $newUrl = $GLOBALS['TSFE']->absRefPrefix . $newUrl;
  312. }
  313. // Set prepending of URL (e.g. hostname) which will be processed by typoLink_PostProc hook in tslib_content:
  314. if (isset($adjustedConfiguration['urlPrepend']) && !isset($this->urlPrepend[$newUrl])) {
  315. $urlPrepend = $adjustedConfiguration['urlPrepend'];
  316. if (substr($urlPrepend, -1) == '/') {
  317. $urlPrepend = substr($urlPrepend, 0, -1);
  318. }
  319. $this->urlPrepend[$newUrl] = $urlPrepend;
  320. }
  321. // Call hooks
  322. if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl']['encodeSpURL_postProc'])) {
  323. foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl']['encodeSpURL_postProc'] as $userFunc) {
  324. $hookParams = array(
  325. 'pObj' => &$this,
  326. 'params' => $params,
  327. 'URL' => &$newUrl,
  328. );
  329. t3lib_div::callUserFunction($userFunc, $hookParams, $this);
  330. }
  331. }
  332. // Setting the encoded URL in the LD key of the params array - that value is passed by reference and thus returned to the linkData function!
  333. $params['LD']['totalURL'] = $newUrl;
  334. }
  335. /**
  336. * Prepends URL generated by RealURL by something (e.g. a host).
  337. * This method gets called by the typoLink_PostProc hook in tslib_content:
  338. *
  339. * @param array $parameters: Array of parameters from typoLink_PostProc hook in tslib_content
  340. * @param object $cObj: Reference to the calling tslib_content instance
  341. * @return void
  342. */
  343. public function encodeSpURL_urlPrepend(&$parameters, &$pObj) {
  344. if (isset($parameters['finalTagParts']['url'])) {
  345. // We must check for absolute URLs here because typolink can force
  346. // absolute URLs for pages with restricted access. It prepends
  347. // current host always. See http://bugs.typo3.org/view.php?id=18200
  348. $testUrl = $parameters['finalTagParts']['url'];
  349. if (preg_match('/^https?:\/\/[^\/]+\//', $testUrl)) {
  350. $testUrl = preg_replace('/https?:\/\/[^\/]+(.+)$/', '\1', $testUrl);
  351. }
  352. if (isset($this->urlPrepend[$testUrl])) {
  353. $urlKey = $url = $testUrl;
  354. // Remove absRefPrefix if necessary
  355. $absRefPrefixLength = strlen($GLOBALS['TSFE']->absRefPrefix);
  356. if ($absRefPrefixLength != 0 && substr($url, 0, $absRefPrefixLength) == $GLOBALS['TSFE']->absRefPrefix) {
  357. $url = substr($url, $absRefPrefixLength);
  358. }
  359. $url = $this->urlPrepend[$urlKey] . ($url{0} != '/' ? '/' : '') . $url;
  360. unset($this->urlPrepend[$testUrl]);
  361. // Adjust the URL:
  362. $parameters['finalTag'] = str_replace(
  363. '"' . htmlspecialchars($parameters['finalTagParts']['url']) . '"',
  364. '"' . htmlspecialchars($url) . '"',
  365. $parameters['finalTag']
  366. );
  367. $parameters['finalTagParts']['url'] = $url;
  368. $pObj->lastTypoLinkUrl = $url;
  369. }
  370. }
  371. }
  372. /**
  373. * Transforms a query string into a speaking URL according to the configuration in ->extConf
  374. *
  375. * @param string Input query string
  376. * @param boolean If set, the cHashCache table is used for "&cHash"
  377. * @param string Original URL
  378. * @return string Output Speaking URL (with as many GET parameters encoded into the URL as possible).
  379. * @see encodeSpURL()
  380. */
  381. protected function encodeSpURL_doEncode($inputQuery, $cHashCache = FALSE, $origUrl = '') {
  382. $this->cHashParameters = array();
  383. $this->rebuildCHash = false;
  384. // Extract all GET parameters into an ARRAY:
  385. $paramKeyValues = array();
  386. $GETparams = explode('&', $inputQuery);
  387. foreach ($GETparams as $paramAndValue) {
  388. list($p, $v) = explode('=', $paramAndValue, 2);
  389. $p = rawurldecode($p);
  390. if ($p != '') {
  391. $paramKeyValues[$p] = rawurldecode($v);
  392. }
  393. }
  394. $this->orig_paramKeyValues = $paramKeyValues;
  395. // Init array in which to collect the "directories" of the URL:
  396. $pathParts = array();
  397. // Pre-vars:
  398. $this->encodeSpURL_setSequence($this->extConf['preVars'], $paramKeyValues, $pathParts);
  399. // Create path from ID value:
  400. $page_id = $this->encodePageId = $paramKeyValues['id'];
  401. $this->encodeError = FALSE;
  402. $this->encodeSpURL_pathFromId($paramKeyValues, $pathParts);
  403. if ($this->encodeError) {
  404. return $origUrl;
  405. }
  406. // Fixed Post-vars:
  407. $fixedPostVarSetCfg = $this->getPostVarSetConfig($page_id, 'fixedPostVars');
  408. if (is_array($fixedPostVarSetCfg)) {
  409. $this->encodeSpURL_setSequence($fixedPostVarSetCfg, $paramKeyValues, $pathParts);
  410. }
  411. // Post var sets:
  412. $postVarSetCfg = $this->getPostVarSetConfig($page_id);
  413. $this->encodeSpURL_gettingPostVarSets($paramKeyValues, $pathParts, $postVarSetCfg);
  414. // Compile Speaking URL path
  415. $pathParts = $this->cleanUpPathParts($pathParts);
  416. // Add filename, if any:
  417. $newUrl = $this->createURLWithFileName($paramKeyValues, $pathParts);
  418. // Fix empty URLs
  419. $newUrl = $this->fixEmptyUrl($newUrl);
  420. // Clear ignored var
  421. if (isset($paramKeyValues[$this->ignoreGETvar])) {
  422. unset($paramKeyValues[$this->ignoreGETvar]);
  423. }
  424. // Store cHash cache:
  425. if ($cHashCache) {
  426. $this->encodeSpURL_cHashCache($newUrl, $paramKeyValues);
  427. }
  428. // Manage remaining GET parameters:
  429. if (count($paramKeyValues)) {
  430. $q = array();
  431. foreach ($paramKeyValues as $k => $v) {
  432. $q[] = $this->rawurlencodeParam($k) . '=' . rawurlencode($v);
  433. }
  434. $newUrl .= '?' . implode('&', $q);
  435. }
  436. // Memory clean up
  437. unset($this->cHashParameters);
  438. // Return new, Speaking URL encoded URL:
  439. return $newUrl;
  440. }
  441. /**
  442. * Creating the TYPO3 Page path into $pathParts from the "id" value in $paramKeyValues
  443. *
  444. * @param array Current URLs GETvar => value pairs in array, being translated into pathParts: Here we take out "id" GET var.
  445. * @param array Numerical array of path-parts, continously being filled. Here, the "page path" is being added by which-ever method is preferred. Passed by reference.
  446. * @return void Unsetting "id" from $paramKeyValues / Setting page path in $pathParts
  447. * @see encodeSpURL_doEncode()
  448. */
  449. protected function encodeSpURL_pathFromId(&$paramKeyValues, &$pathParts) {
  450. // Return immediately if no GET vars remain to be translated:
  451. if (!count($paramKeyValues)) {
  452. return;
  453. }
  454. // Creating page path:
  455. switch ((string)$this->extConf['pagePath']['type']) {
  456. case 'user':
  457. $params = array('paramKeyValues' => &$paramKeyValues, 'pathParts' => &$pathParts, 'pObj' => &$this, 'conf' => $this->extConf['pagePath'], 'mode' => 'encode');
  458. t3lib_div::callUserFunction($this->extConf['pagePath']['userFunc'], $params, $this);
  459. break;
  460. default: // Default: Just passing through the ID/alias of the page:
  461. $pathParts[] = rawurlencode($paramKeyValues['id']);
  462. unset($paramKeyValues['id']);
  463. break;
  464. }
  465. }
  466. /**
  467. * Traversing setup for variables AFTER the page path.
  468. *
  469. * @param array Current URLs GETvar => value pairs in array, being translated into pathParts, continously shortend. Passed by reference.
  470. * @param array Numerical array of path-parts, continously being filled. Passed by reference.
  471. * @param array $postVarSetCfg config
  472. * @return void Removing values from $paramKeyValues / Setting values in $pathParts
  473. * @see encodeSpURL_doEncode(), decodeSpURL_settingPostVarSets()
  474. */
  475. protected function encodeSpURL_gettingPostVarSets(&$paramKeyValues, &$pathParts, $postVarSetCfg) {
  476. // Traverse setup for postVarSets. If any of those matches
  477. if (is_array($postVarSetCfg)) {
  478. foreach ($postVarSetCfg as $keyWord => $cfg) {
  479. switch ((string)$cfg['type']) {
  480. case 'admin':
  481. if ($this->adminJumpSet) {
  482. $pathParts[] = rawurlencode($keyWord);
  483. $this->adminJumpSet = FALSE; // ... this makes sure that any subsequent "admin-jump" activation is set...
  484. }
  485. break;
  486. case 'single':
  487. $this->encodeSpURL_setSingle($keyWord, $cfg['keyValues'], $paramKeyValues, $pathParts);
  488. break;
  489. default:
  490. unset($cfg['type']); // Just to make sure it is NOT set.
  491. foreach ($cfg as $Gcfg) {
  492. if (isset($paramKeyValues[$Gcfg['GETvar']])) {
  493. $pathParts[] = rawurlencode($keyWord);
  494. $pathPartsSize = count($pathParts);
  495. $cHashParameters = $this->cHashParameters;
  496. $this->encodeSpURL_setSequence($cfg, $paramKeyValues, $pathParts);
  497. // If (1) nothing was added or (2) only empty segments added, remove this part completely
  498. if (count($pathParts) == $pathPartsSize) {
  499. array_pop($pathParts);
  500. }
  501. else {
  502. $dropSegment = true;
  503. for ($i = $pathPartsSize; $i < count($pathParts); $i++) {
  504. if ($pathParts[$i] != '') {
  505. $dropSegment = false;
  506. break;
  507. }
  508. }
  509. if ($dropSegment) {
  510. $pathParts = array_slice($pathParts, 0, $pathPartsSize - 1);
  511. // Nothing goes to cHash from this part.
  512. $this->cHashParameters = $cHashParameters;
  513. }
  514. }
  515. break;
  516. }
  517. }
  518. break;
  519. }
  520. }
  521. }
  522. }
  523. /**
  524. * Setting a filename if any filename is configured to match remaining variables.
  525. *
  526. * @param array Current URLs GETvar => value pairs in array, being translated into pathParts, continously shortend. Passed by reference.
  527. * @return string Returns the filename to prepend, if any
  528. * @see encodeSpURL_doEncode(), decodeSpURL_fileName()
  529. */
  530. protected function encodeSpURL_fileName(array &$paramKeyValues) {
  531. // Look if any filename matches the remaining variables:
  532. if (is_array($this->extConf['fileName']['index'])) {
  533. foreach ($this->extConf['fileName']['index'] as $keyWord => $cfg) {
  534. $pathParts = array();
  535. if ($this->encodeSpURL_setSingle($keyWord, $cfg['keyValues'], $paramKeyValues, $pathParts)) {
  536. return $keyWord != '_DEFAULT' ? $keyWord : '';
  537. }
  538. }
  539. }
  540. return '';
  541. }
  542. /**
  543. * Traverses a set of GETvars configured (array of segments)
  544. *
  545. * @param array Array of segment-configurations.
  546. * @param array Current URLs GETvar => value pairs in array, being translated into pathParts, continously shortend. Passed by reference.
  547. * @param array Numerical array of path-parts, continously being filled. Passed by reference.
  548. * @return void Removing values from $paramKeyValues / Setting values in $pathParts
  549. * @see encodeSpURL_doEncode(), encodeSpURL_gettingPostVarSets(), decodeSpURL_getSequence()
  550. */
  551. protected function encodeSpURL_setSequence($varSetCfg, &$paramKeyValues, &$pathParts) {
  552. // Traverse array of segments configuration
  553. $prevVal = '';
  554. if (is_array($varSetCfg)) {
  555. foreach ($varSetCfg as $setup) {
  556. switch ($setup['type']) {
  557. case 'action':
  558. $pathPartVal = '';
  559. // Look for admin jump:
  560. if ($this->adminJumpSet) {
  561. foreach ($setup['index'] as $pKey => $pCfg) {
  562. if ((string)$pCfg['type'] == 'admin') {
  563. $pathPartVal = $pKey;
  564. $this->adminJumpSet = FALSE;
  565. break;
  566. }
  567. }
  568. }
  569. // Look for frontend user login:
  570. if ($this->fe_user_prefix_set) {
  571. foreach ($setup['index'] as $pKey => $pCfg) {
  572. if ((string)$pCfg['type'] == 'feLogin') {
  573. $pathPartVal = $pKey;
  574. $this->fe_user_prefix_set = FALSE;
  575. break;
  576. }
  577. }
  578. }
  579. // If either pathPartVal has been set OR if _DEFAULT type is not bypass, set a value:
  580. if (strlen($pathPartVal) || $setup['index']['_DEFAULT']['type'] != 'bypass') {
  581. // If admin jump did not set $pathPartVal, look for first pass-through (no "type" set):
  582. if (!strlen($pathPartVal)) {
  583. foreach ($setup['index'] as $pKey => $pCfg) {
  584. if (!strlen($pCfg['type'])) {
  585. $pathPartVal = $pKey;
  586. break;
  587. }
  588. }
  589. }
  590. // Setting part of path:
  591. $pathParts[] = rawurlencode(strlen($pathPartVal) ? $pathPartVal : $this->NA);
  592. }
  593. break;
  594. default:
  595. if (!is_array($setup['cond']) || $this->checkCondition($setup['cond'], $prevVal)) {
  596. // Looking if the GET var is found in parameter index
  597. $GETvar = $setup['GETvar'];
  598. if ($GETvar == $this->ignoreGETvar) {
  599. // Do not do anything with this var!
  600. continue;
  601. }
  602. $parameterSet = isset($paramKeyValues[$GETvar]);
  603. $GETvarVal = $parameterSet ? $paramKeyValues[$GETvar] : '';
  604. // Set reverse map:
  605. $revMap = is_array($setup['valueMap']) ? array_flip($setup['valueMap']) : array();
  606. if (isset($revMap[$GETvarVal])) {
  607. $prevVal = $GETvarVal;
  608. $pathParts[] = rawurlencode($revMap[$GETvarVal]);
  609. $this->cHashParameters[$GETvar] = $GETvarVal;
  610. } elseif ($setup['noMatch'] == 'bypass') {
  611. // If no match in reverse value map and "bypass" is set, remove the parameter from the URL
  612. // Must rebuild cHash because we remove a parameter!
  613. $this->rebuildCHash |= $parameterSet;
  614. } elseif ($setup['noMatch'] == 'null') {
  615. // If no match and "null" is set, then set "dummy" value
  616. // Set "dummy" value (?)
  617. $prevVal = '';
  618. $pathParts[] = '';
  619. $this->rebuildCHash |= $parameterSet;
  620. } elseif ($setup['userFunc']) {
  621. $params = array(
  622. 'pObj' => &$this,
  623. 'value' => $GETvarVal,
  624. 'decodeAlias' => false,
  625. 'pathParts' => &$pathParts
  626. );
  627. $prevVal = $GETvarVal;
  628. $GETvarVal = t3lib_div::callUserFunction($setup['userFunc'], $params, $this);
  629. $pathParts[] = rawurlencode($GETvarVal);
  630. $this->cHashParameters[$GETvar] = $prevVal;
  631. } elseif (is_array($setup['lookUpTable'])) {
  632. $prevVal = $GETvarVal;
  633. $GETvarVal = $this->lookUpTranslation($setup['lookUpTable'], $GETvarVal);
  634. $pathParts[] = rawurlencode($GETvarVal);
  635. $this->cHashParameters[$GETvar] = $prevVal;
  636. } elseif (isset($setup['valueDefault'])) {
  637. $prevVal = $setup['valueDefault'];
  638. $pathParts[] = rawurlencode($setup['valueDefault']);
  639. $this->cHashParameters[$GETvar] = $setup['valueMap'][$setup['valueDefault']];
  640. $this->rebuildCHash |= !$parameterSet;
  641. } else {
  642. $prevVal = $GETvarVal;
  643. $pathParts[] = rawurlencode($GETvarVal);
  644. $this->cHashParameters[$GETvar] = $prevVal;
  645. $this->rebuildCHash |= !$parameterSet;
  646. }
  647. // Finally, unset GET var so it doesn't get processed once more:
  648. unset($paramKeyValues[$setup['GETvar']]);
  649. }
  650. break;
  651. }
  652. }
  653. }
  654. }
  655. /**
  656. * Traversing an array of GETvar => value pairs and checking if both variable names AND values are matching any found in $paramKeyValues; If so, the keyword representing those values is set and the GEtvars are unset from $paramkeyValues array
  657. *
  658. * @param string Keyword to set as a representation of the GETvars configured.
  659. * @param array Array of GETvar => values which content in $paramKeyvalues must match exactly in order to be substituted with the keyword, $keyWord
  660. * @param array Current URLs GETvar => value pairs in array, being translated into pathParts, continously shortend. Passed by reference.
  661. * @param array Numerical array of path-parts, continously being filled. Passed by reference.
  662. * @return boolean Return true, if any value from $paramKeyValues was removed.
  663. * @see encodeSpURL_fileName(), encodeSpURL_gettingPostVarSets(), decodeSpURL_getSingle()
  664. */
  665. protected function encodeSpURL_setSingle($keyWord, $keyValues, &$paramKeyValues, &$pathParts) {
  666. if (is_array($keyValues)) {
  667. $allSet = TRUE;
  668. // Check if all GETvars configured are found in $paramKeyValues:
  669. foreach ($keyValues as $getVar => $value) {
  670. if (!isset($paramKeyValues[$getVar]) || strcmp($paramKeyValues[$getVar], $value)) {
  671. $allSet = FALSE;
  672. break;
  673. }
  674. }
  675. // If all is set, unset the GETvars and set the value.
  676. if ($allSet) {
  677. $pathParts[] = rawurlencode($keyWord);
  678. foreach ($keyValues as $getVar => $value) {
  679. $this->cHashParameters[$getVar] = $value;
  680. unset($paramKeyValues[$getVar]);
  681. }
  682. return TRUE;
  683. }
  684. }
  685. return FALSE;
  686. }
  687. /**
  688. * Setting / Getting encoded URL to/from cache (memory cache, but could be extended to database cache)
  689. *
  690. * @param string Host + the original URL with GET parameters - identifying the cached version to find
  691. * @param array Array with extra data to include in encoding. This is flags if adminJump url or feLogin flags are set since these are NOT a part of the URL to encode and therefore are needed for the hash to be true.
  692. * @param string If set, this URL will be cached as the encoded version of $urlToEncode. Otherwise the function will look for and return the cached version of $urlToEncode
  693. * @return mixed If $setEncodedURL is true, this will be STORED as the cached version and the function returns false, otherwise the cached version is returned (string).
  694. * @see encodeSpURL()
  695. */
  696. protected function encodeSpURL_encodeCache($urlData, $internalExtras, $setEncodedURL = '') {
  697. // Create hash string:
  698. $hash = md5($urlData . '///' . serialize($internalExtras));
  699. if (!$setEncodedURL) { // Asking for cached encoded URL:
  700. // First, check memory, otherwise ask database:
  701. if (!isset($GLOBALS['TSFE']->applicationData['tx_realurl']['_CACHE'][$hash]) && $this->extConf['init']['enableUrlEncodeCache']) {
  702. $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('content', 'tx_realurl_urlencodecache',
  703. 'url_hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($hash, 'tx_realurl_urlencodecache') .
  704. ' AND tstamp>' . strtotime('midnight', time() - 24 * 3600 * $this->encodeCacheTTL));
  705. if (false != ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
  706. $GLOBALS['TSFE']->applicationData['tx_realurl']['_CACHE'][$hash] = $row['content'];
  707. }
  708. $GLOBALS['TYPO3_DB']->sql_free_result($res);
  709. }
  710. return $GLOBALS['TSFE']->applicationData['tx_realurl']['_CACHE'][$hash];
  711. }
  712. else { // Setting encoded URL in cache:
  713. // No caching if FE editing is enabled!
  714. if (!$this->isBEUserLoggedIn()) {
  715. $GLOBALS['TSFE']->applicationData['tx_realurl']['_CACHE'][$hash] = $setEncodedURL;
  716. // If the page id is NOT an integer, it's an alias we have to look up:
  717. if (!self::testInt($this->encodePageId)) {
  718. $this->encodePageId = $this->pageAliasToID($this->encodePageId);
  719. }
  720. if ($this->extConf['init']['enableUrlEncodeCache'] && $this->canCachePageURL($this->encodePageId)) {
  721. $insertFields = array(
  722. 'url_hash' => $hash,
  723. 'origparams' => $urlData,
  724. 'internalExtras' => count($internalExtras) ? serialize($internalExtras) : '',
  725. 'content' => $setEncodedURL,
  726. 'page_id' => $this->encodePageId,
  727. 'tstamp' => time()
  728. );
  729. if ($this->useMySQLExtendedSyntax) {
  730. $query = $GLOBALS['TYPO3_DB']->INSERTquery('tx_realurl_urlencodecache', $insertFields);
  731. $query .= ' ON DUPLICATE KEY UPDATE tstamp=' . $insertFields['tstamp'];
  732. $GLOBALS['TYPO3_DB']->sql_query($query);
  733. } else {
  734. $GLOBALS['TYPO3_DB']->sql_query('START TRANSACTION');
  735. $GLOBALS['TYPO3_DB']->exec_DELETEquery('tx_realurl_urlencodecache', 'url_hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($hash, 'tx_realurl_urlencodecache'));
  736. $GLOBALS['TYPO3_DB']->exec_INSERTquery('tx_realurl_urlencodecache', $insertFields);
  737. $GLOBALS['TYPO3_DB']->sql_query('COMMIT');
  738. }
  739. }
  740. }
  741. }
  742. return '';
  743. }
  744. /**
  745. * Will store a record in a cachetable holding the value of the "cHash" parameter in a link, if any.
  746. * Background:
  747. * The "cHash" parameter is a hash over the values in the Query String of a URL and it "authenticates" the URL to the frontend so we can safely cache page content with that parameter combination.
  748. * Technically, there is no problem with the "cHash" parameter - it is like any other parameter something we could encode with Speaking URLs. The problem is: a cHash string is not "speaking" (and never will be!)
  749. * So; the only option we are left with if we want to remove the "?cHash=...:" remains in URLs and at the same time do not want to include it in the virtual path is; store it in the database!
  750. * This is what this function does: Stores a record in the database which relates the cHash value to a hash id of the URL. This is done ONLY if the "cHash" parameter is the only one left which would make the URL non-speaking. Otherwise it is left behind.
  751. * Obviously, this whole thing only works if there is a function in the decode part which will look up the cHash again and include it in the GET parameters resolved from the Speaking URL - but there is of course...
  752. *
  753. * @param string Speaking URL path (being hashed to an integer and cHash value related to this.)
  754. * @param array Params array, passed by reference. If "cHash" is the only value left it will be put in the cache table and the value is unset in the array.
  755. * @return void
  756. * @see decodeSpURL_cHashCache()
  757. */
  758. protected function encodeSpURL_cHashCache($newUrl, &$paramKeyValues) {
  759. // If "cHash" is the ONLY parameter left...
  760. // (if there are others our problem is that the cHash probably covers those
  761. // as well and if we include the cHash anyways we might get duplicates for
  762. // the same speaking URL in the cache table!)
  763. if (isset($paramKeyValues['cHash'])) {
  764. if ($this->rebuildCHash) {
  765. $cHashParameters = array_merge($this->cHashParameters, $paramKeyValues);
  766. unset($cHashParameters['cHash']);
  767. $cHashParameters = t3lib_div::cHashParams(t3lib_div::implodeArrayForUrl('', $cHashParameters));
  768. unset($cHashParameters['']);
  769. if (count($cHashParameters) == 1) {
  770. // No cHash needed.
  771. unset($paramKeyValues['cHash']);
  772. }
  773. elseif (count($cHashParameters) > 1) {
  774. if (method_exists('t3lib_div', 'calculateCHash')) {
  775. $paramKeyValues['cHash'] = t3lib_div::calculateCHash($cHashParameters);
  776. }
  777. else {
  778. $paramKeyValues['cHash'] = t3lib_div::shortMD5(serialize($cHashParameters));
  779. }
  780. }
  781. unset($cHashParameters);
  782. }
  783. if (count($paramKeyValues) == 1) {
  784. $stringForHash = $newUrl;
  785. if (count($this->additionalParametersForChash)) {
  786. $stringForHash .= '|' . serialize($this->additionalParametersForChash);
  787. }
  788. $spUrlHash = md5($stringForHash);
  789. $spUrlHashQuoted = $GLOBALS['TYPO3_DB']->fullQuoteStr($spUrlHash, 'tx_realurl_chashcache');
  790. // first, look if a cHash is already there for this SpURL
  791. list($row) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('chash_string',
  792. 'tx_realurl_chashcache', 'spurl_hash=' . $spUrlHashQuoted);
  793. if (!is_array($row)) {
  794. // Nothing found, insert to the cache
  795. $data = array(
  796. 'spurl_hash' => $spUrlHash,
  797. 'spurl_string' => $this->enableChashUrlDebug ? $stringForHash : null,
  798. 'chash_string' => $paramKeyValues['cHash']
  799. );
  800. $GLOBALS['TYPO3_DB']->exec_INSERTquery('tx_realurl_chashcache', $data);
  801. }
  802. else {
  803. // If one found, check if it is different, and if so update:
  804. if ($row['chash_string'] != $paramKeyValues['cHash']) {
  805. // If that chash_string is different from the one we want to
  806. // insert, that might be a bug or mean that encryptionKey was
  807. // changed so cHash values will be different now
  808. // In any case we will just silently update the value:
  809. $data = array(
  810. 'chash_string' => $paramKeyValues['cHash']
  811. );
  812. $GLOBALS['TYPO3_DB']->exec_UPDATEquery('tx_realurl_chashcache',
  813. 'spurl_hash=' . $spUrlHashQuoted, $data);
  814. }
  815. }
  816. // Unset "cHash" (and array should now be empty!)
  817. unset($paramKeyValues['cHash']);
  818. }
  819. }
  820. }
  821. /************************************
  822. *
  823. * Translate a Speaking URL to parameters (tslib_fe)
  824. *
  825. ************************************/
  826. /**
  827. * Parse speaking URL and translate it to parameters understood by TYPO3
  828. * Function is called from tslib_fe
  829. * The overall format of a speaking URL is these five parts [TYPO3_SITE_URL] / [pre-var] / [page-identification] / [post-vars] / [file.ext]
  830. * - "TYPO3_SITE_URL" is fixed value from the environment,
  831. * - "pre-var" is any number of segments separated by "/" mapping to GETvars AND with a known lenght,
  832. * - "page-identification" identifies the page id in TYPO3 possibly with multiple segments separated by "/" BUT with an UNKNOWN length,
  833. * - "post-vars" is sets of segments offering the same features as "pre-var"
  834. * - "file.ext" is any filename that might apply
  835. *
  836. * @param array Params for hook
  837. * @return void Setting internal variables.
  838. */
  839. public function decodeSpURL($params) {
  840. $this->devLog('Entering decodeSpURL');
  841. // Setting parent object reference (which is $GLOBALS['TSFE'])
  842. $this->pObj = &$params['pObj'];
  843. // Initializing config / request URL:
  844. $this->setConfig();
  845. $this->adjustConfigurationByHost('decode');
  846. $this->adjustRootPageId();
  847. // If there has been a redirect (basically; we arrived here otherwise than via "index.php" in the URL) this can happend either due to a CGI-script or because of reWrite rule. Earlier we used $GLOBALS['HTTP_SERVER_VARS']['REDIRECT_URL'] to check but...
  848. if ($this->pObj->siteScript && substr($this->pObj->siteScript, 0, 9) != 'index.php' && substr($this->pObj->siteScript, 0, 1) != '?') {
  849. // Getting the path which is above the current site url:
  850. // For instance "first/second/third/index.html?&param1=value1&param2=value2"
  851. // should be the result of the URL
  852. // "http://localhost/typo3/dev/dummy_1/first/second/third/index.html?&param1=value1&param2=value2"
  853. // Note: sometimes in fcgi installations it is absolute, so we have to make it
  854. // relative to work properly.
  855. $speakingURIpath = $this->pObj->siteScript{0} == '/' ? substr($this->pObj->siteScript, 1) : $this->pObj->siteScript;
  856. // Call hooks
  857. if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl']['decodeSpURL_preProc'])) {
  858. foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl']['decodeSpURL_preProc'] as $userFunc) {
  859. $hookParams = array(
  860. 'pObj' => &$this,
  861. 'params' => $params,
  862. 'URL' => &$speakingURIpath,
  863. );
  864. t3lib_div::callUserFunction($userFunc, $hookParams, $this);
  865. }
  866. }
  867. // Append missing slash if configured for:
  868. if ($this->extConf['init']['appendMissingSlash']) {
  869. $regexp = '~^([^\?]*[^/])(\?.*)?$~';
  870. if (substr($speakingURIpath, -1, 1) == '?') {
  871. $speakingURIpath = substr($speakingURIpath, 0, -1);
  872. }
  873. if (preg_match($regexp, $speakingURIpath)) { // Only process if a slash is missing:
  874. $options = t3lib_div::trimExplode(',', $this->extConf['init']['appendMissingSlash'], true);
  875. if (in_array('ifNotFile', $options)) {
  876. if (!preg_match('/\/[^\/\?]+\.[^\/]+(\?.*)?$/', '/' . $speakingURIpath)) {
  877. $speakingURIpath = preg_replace($regexp, '\1/\2', $speakingURIpath);
  878. $this->appendedSlash = true;
  879. }
  880. }
  881. else {
  882. $speakingURIpath = preg_replace($regexp, '\1/\2', $speakingURIpath);
  883. $this->appendedSlash = true;
  884. }
  885. if ($this->appendedSlash && count($options) > 0) {
  886. foreach ($options as $option) {
  887. $matches = array();
  888. if (preg_match('/^redirect(\[(30[1237])\])?$/', $option, $matches)) {
  889. $code = count($matches) > 1 ? $matches[2] : 301;
  890. $status = 'HTTP/1.0 ' . $code . ' TYPO3 RealURL redirect';
  891. // Check path segment to be relative for the current site.
  892. // parse_url() does not work with relative URLs, so we use it to test
  893. if (!@parse_url($speakingURIpath, PHP_URL_HOST)) {
  894. @ob_end_clean();
  895. header($status);
  896. header('Location: ' . t3lib_div::locationHeaderUrl($speakingURIpath));
  897. exit;
  898. }
  899. }
  900. }
  901. }
  902. }
  903. }
  904. // If the URL is a single script like "123.1.html" it might be an "old" simulateStaticDocument request. If this is the case and support for this is configured, do NOT try and resolve it as a Speaking URL
  905. $fI = t3lib_div::split_fileref($speakingURIpath);
  906. if (!self::testInt($this->pObj->id) && $fI['path'] == '' && $this->extConf['fileName']['defaultToHTMLsuffixOnPrev'] && $this->extConf['init']['respectSimulateStaticURLs']) {
  907. // If page ID does not exist yet and page is on the root level and both
  908. // respectSimulateStaticURLs and defaultToHTMLsuffixOnPrev are set, than
  909. // ignore respectSimulateStaticURLs and attempt to resolve page id.
  910. // See http://bugs.typo3.org/view.php?id=1530
  911. $GLOBALS['TT']->setTSlogMessage('decodeSpURL: ignoring respectSimulateStaticURLs due defaultToHTMLsuffixOnPrev for the root level page!)', 2);
  912. $this->extConf['init']['respectSimulateStaticURLs'] = false;
  913. }
  914. if (!$this->extConf['init']['respectSimulateStaticURLs'] || $fI['path']) {
  915. $this->devLog('RealURL powered decoding (TM) starting!');
  916. // Parse path:
  917. $uParts = @parse_url($speakingURIpath);
  918. if (!is_array($uParts)) {
  919. $this->decodeSpURL_throw404('Current URL is invalid');
  920. }
  921. $speakingURIpath = $this->speakingURIpath_procValue = $uParts['path'];
  922. // Redirecting if needed (exits if so).
  923. $this->decodeSpURL_checkRedirects($speakingURIpath);
  924. // Looking for cached information:
  925. $cachedInfo = $this->decodeSpURL_decodeCache($speakingURIpath);
  926. // If no cached info was found, create it:
  927. if (!is_array($cachedInfo)) {
  928. // Decode URL:
  929. $cachedInfo = $this->decodeSpURL_doDecode($speakingURIpath, $this->extConf['init']['enableCHashCache']);
  930. // Storing cached information:
  931. $this->decodeSpURL_decodeCache($speakingURIpath, $cachedInfo);
  932. }
  933. // Re-create QUERY_STRING from Get vars for use with typoLink()
  934. $_SERVER['QUERY_STRING'] = $this->decodeSpURL_createQueryString($cachedInfo['GET_VARS']);
  935. // Jump-admin if configured:
  936. $this->decodeSpURL_jumpAdmin_goBackend($cachedInfo['id']);
  937. // Setting info in TSFE:
  938. $this->pObj->mergingWithGetVars($cachedInfo['GET_VARS']);
  939. $this->pObj->id = $cachedInfo['id'];
  940. if ($this->mimeType) {
  941. header('Content-type: ' . $this->mimeType);
  942. $this->mimeType = null;
  943. }
  944. }
  945. }
  946. }
  947. /**
  948. * Look for redirect configuration.
  949. * If the input path is found as key in $this->extConf['redirects'] this method redirects to the URL found as value
  950. *
  951. * @param string Path from SpeakingURL.
  952. * @return void
  953. * @see decodeSpURL_doDecode()
  954. */
  955. protected function decodeSpURL_checkRedirects($speakingURIpath) {
  956. $speakingURIpath = strtolower(trim($speakingURIpath));
  957. if (isset($this->extConf['redirects'][$speakingURIpath])) {
  958. $url = $this->extConf['redirects'][$speakingURIpath];
  959. if (preg_match('/^30[1237];/', $url)) {
  960. $redirectCode = intval(substr($url, 0, 3));
  961. $url = substr($url, 4);
  962. header('HTTP/1.0 ' . $redirectCode . ' Redirect');
  963. }
  964. header('Location: ' . t3lib_div::locationHeaderUrl($url));
  965. exit();
  966. }
  967. // Regex redirects:
  968. if (is_array($this->extConf['redirects_regex'])) {
  969. foreach ($this->extConf['redirects_regex'] as $regex => $substString) {
  970. if (preg_match('/' . $regex . '/', $speakingURIpath)) {
  971. $url = @preg_replace('/' . $regex . '/', $substString, $speakingURIpath);
  972. if ($url) {
  973. if (preg_match('/^30[1237];/', $url)) {
  974. $redirectCode = intval(substr($url, 0, 3));
  975. header('HTTP/1.0 ' . $redirectCode . ' Redirect');
  976. $url = substr($url, 4);
  977. }
  978. header('Location: ' . t3lib_div::locationHeaderUrl($url));
  979. exit();
  980. }
  981. }
  982. }
  983. }
  984. // DB defined redirects:
  985. $hash = t3lib_div::md5int($speakingURIpath);
  986. $url = $GLOBALS['TYPO3_DB']->fullQuoteStr($speakingURIpath, 'tx_realurl_redirects');
  987. $domainId = $this->getCurrentDomainId();
  988. list($redirectRow) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
  989. 'destination,has_moved,domain_limit', 'tx_realurl_redirects',
  990. 'url_hash=' . $hash . ' AND url=' . $url . ' AND domain_limit IN (0,' . $domainId . ')',
  991. '', 'domain_limit DESC');
  992. if (is_array($redirectRow)) {
  993. // Update statistics
  994. $fields_values = array(
  995. 'counter' => 'counter+1',
  996. 'tstamp' => time(),
  997. 'last_referer' => t3lib_div::getIndpEnv('HTTP_REFERER')
  998. );
  999. $GLOBALS['TYPO3_DB']->exec_UPDATEquery('tx_realurl_redirects',
  1000. 'url_hash=' . $hash . ' AND url=' . $url . ' AND domain_limit=' . $redirectRow['domain_limit'],
  1001. $fields_values, array('counter'));
  1002. // Redirect
  1003. if ($redirectRow['has_moved']) {
  1004. header('HTTP/1.1 301 Moved Permanently');
  1005. }
  1006. header('Location: ' . t3lib_div::locationHeaderUrl($redirectRow['destination']));
  1007. exit();
  1008. }
  1009. }
  1010. /**
  1011. * Obtains current domain id from sys_domain.
  1012. *
  1013. * @return int
  1014. */
  1015. protected function getCurrentDomainId() {
  1016. list($row) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid',
  1017. 'sys_domain',
  1018. 'domainName=' . $GLOBALS['TYPO3_DB']->fullQuoteStr(t3lib_div::getIndpEnv('HTTP_HOST'), 'sys_domain') .
  1019. ' AND redirectTo=\'\''
  1020. );
  1021. $result = (is_array($row) ? intval($row['uid']) : 0);
  1022. return $result;
  1023. }
  1024. /**
  1025. * Decodes a speaking URL path into an array of GET parameters and a page id.
  1026. *
  1027. * @param string Speaking URL path (after the "root" path of the website!) but without query parameters
  1028. * @param boolean If cHash caching is enabled or not.
  1029. * @return array Array with id and GET parameters.
  1030. * @see decodeSpURL()
  1031. */
  1032. protected function decodeSpURL_doDecode($speakingURIpath, $cHashCache = FALSE) {
  1033. // Cached info:
  1034. $cachedInfo = array();
  1035. // Convert URL to segments
  1036. $pathParts = explode('/', $speakingURIpath);
  1037. array_walk($pathParts, create_function('&$value', '$value = rawurldecode($value);'));
  1038. // Strip/process file name or extension first
  1039. $file_GET_VARS = $this->decodeSpURL_decodeFileName($pathParts);
  1040. // Setting original dir-parts:
  1041. $this->dirParts = $pathParts;
  1042. // Setting "preVars":
  1043. $pre_GET_VARS = $this->decodeSpURL_settingPreVars($pathParts, $this->extConf['preVars']);
  1044. if (isset($this->extConf['pagePath']['languageGetVar'])) {
  1045. $languageGetVar = $this->extConf['pagePath']['languageGetVar'];
  1046. if (isset($pre_GET_VARS[$languageGetVar]) && self::testInt($pre_GET_VARS[$languageGetVar])) {
  1047. // Language from URL
  1048. $this->detectedLanguage = $pre_GET_VARS[$languageGetVar];
  1049. }
  1050. elseif (isset($_GET[$languageGetVar]) && self::testInt($_GET[$languageGetVar])) {
  1051. // This is for _DOMAINS feature
  1052. $this->detectedLanguage = $_GET[$languageGetVar];
  1053. }
  1054. }
  1055. // Setting page id:
  1056. list($cachedInfo['id'], $id_GET_VARS, $cachedInfo['rootpage_id']) = $this->decodeSpURL_idFromPath($pathParts);
  1057. // Fixed Post-vars:
  1058. $fixedPostVarSetCfg = $this->getPostVarSetConfig($cachedInfo['id'], 'fixedPostVars');
  1059. $fixedPost_GET_VARS = $this->decodeSpURL_settingPreVars($pathParts, $fixedPostVarSetCfg);
  1060. // Setting "postVarSets":
  1061. $postVarSetCfg = $this->getPostVarSetConfig($cachedInfo['id']);
  1062. $post_GET_VARS = $this->decodeSpURL_settingPostVarSets($pathParts, $postVarSetCfg, $cachedInfo['id']);
  1063. // Looking for remaining parts:
  1064. if (count($pathParts)) {
  1065. $this->decodeSpURL_throw404('"' . $speakingURIpath . '" could not be found, closest page matching is ' . substr(implode('/', $this->dirParts), 0, -strlen(implode('/', $pathParts))) . '');
  1066. }
  1067. // Merge Get vars together:
  1068. $cachedInfo['GET_VARS'] = array();
  1069. if (is_array($pre_GET_VARS))
  1070. $cachedInfo['GET_VARS'] = t3lib_div::array_merge_recursive_overrule($cachedInfo['GET_VARS'], $pre_GET_VARS);
  1071. if (is_array($id_GET_VARS))
  1072. $cachedInfo['GET_VARS'] = t3lib_div::array_merge_recursive_overrule($cachedInfo['GET_VARS'], $id_GET_VARS);
  1073. if (is_array($fixedPost_GET_VARS))
  1074. $cachedInfo['GET_VARS'] = t3lib_div::array_merge_recursive_overrule($cachedInfo['GET_VARS'], $fixedPost_GET_VARS);
  1075. if (is_array($post_GET_VARS))
  1076. $cachedInfo['GET_VARS'] = t3lib_div::array_merge_recursive_overrule($cachedInfo['GET_VARS'], $post_GET_VARS);
  1077. if (is_array($file_GET_VARS))
  1078. $cachedInfo['GET_VARS'] = t3lib_div::array_merge_recursive_overrule($cachedInfo['GET_VARS'], $file_GET_VARS);
  1079. // cHash handling:
  1080. if ($cHashCache) {
  1081. $cHash_value = $this->decodeSpURL_cHashCache($speakingURIpath);
  1082. if ($cHash_value) {
  1083. $cachedInfo['GET_VARS']['cHash'] = $cHash_value;
  1084. }
  1085. }
  1086. // Return information found:
  1087. return $cachedInfo;
  1088. }
  1089. /**
  1090. * Generates a parameter string from an array recursively
  1091. *
  1092. * @param array Array to generate strings from
  1093. * @param string path to prepend to every parameter
  1094. * @return array Array with parameter strings
  1095. */
  1096. protected function decodeSpURL_createQueryStringParam($paramArr, $prependString = '') {
  1097. if (!is_array($paramArr)) {
  1098. return array($prependString . '=' . $paramArr);
  1099. }
  1100. if (count($paramArr) == 0) {
  1101. return array();
  1102. }
  1103. $paramList = array();
  1104. foreach ($paramArr as $var => $value) {
  1105. $paramList = array_merge($paramList, $this->decodeSpURL_createQueryStringParam($value, $prependString . '[' . $var . ']'));
  1106. }
  1107. return $paramList;
  1108. }
  1109. /**
  1110. * Re-creates QUERY_STRING for use with typoLink()
  1111. *
  1112. * @param array List of Get vars
  1113. * @return string QUERY_STRING value
  1114. */
  1115. protected function decodeSpURL_createQueryString(&$getVars) {
  1116. if (!is_array($getVars) || count($getVars) == 0) {
  1117. return $_SERVER['QUERY_STRING'];
  1118. }
  1119. $parameters = array();
  1120. foreach ($getVars as $var => $value) {
  1121. $parameters = array_merge($parameters, $this->decodeSpURL_createQueryStringParam($value, $var));
  1122. }
  1123. // If cHash is provided in the query string, replace it in $getVars
  1124. $cHash_override = t3lib_div::_GET('cHash');
  1125. if ($cHash_override) {
  1126. $getVars['cHash'] = $cHash_override;
  1127. }
  1128. $queryString = t3lib_div::getIndpEnv('QUERY_STRING');
  1129. if ($queryString) {
  1130. array_push($parameters, $queryString);
  1131. }
  1132. return implode('&', $parameters);
  1133. }
  1134. /**
  1135. * Extracts the page ID from URL.
  1136. *
  1137. * @param array Parts of path. NOTICE: Passed by reference.
  1138. * @return array array(Page ID, GETvars array if any eg. MP)
  1139. * @see decodeSpURL_doDecode()
  1140. */
  1141. protected function decodeSpURL_idFromPath(&$pathParts) {
  1142. // Creating page path:
  1143. switch ((string)$this->extConf['pagePath']['type']) {
  1144. case 'user':
  1145. $params = array('pathParts' => &$pathParts, 'pObj' => &$this, 'conf' => $this->extConf['pagePath'], 'mode' => 'decode');
  1146. $result = t3lib_div::callUserFunction($this->extConf['pagePath']['userFunc'], $params, $this);
  1147. break;
  1148. default: // Default: Just passing through the ID/alias of the page:
  1149. $value = array_shift($pathParts);
  1150. $result = array($value);
  1151. break;
  1152. }
  1153. if (count($result) < 2) {
  1154. $result[1] = null;
  1155. $result[2] = $this->extConf['pagePath']['rootpage_id'];
  1156. } else if (count($result) < 3) {
  1157. $result[2] = $this->extConf['pagePath']['rootpage_id'];
  1158. }
  1159. return $result;
  1160. }
  1161. /**
  1162. * Analysing the path BEFORE the page identification part of the URL
  1163. *
  1164. * @param array The path splitted by "/". NOTICE: Passed by reference and shortend for each time a segment is matching configuration
  1165. * @param array Configuration
  1166. * @return array GET-vars resulting from the analysis
  1167. * @see decodeSpURL_doDecode()
  1168. */
  1169. protected function decodeSpURL_settingPreVars(&$pathParts, $config) {
  1170. if (is_array($config)) {
  1171. // Pulling vars of the pathParts
  1172. $GET_string = $this->decodeSpURL_getSequence($pathParts, $config);
  1173. // If a get string is created, then:
  1174. if ($GET_string) {
  1175. $GET_VARS = false;
  1176. parse_str($GET_string, $GET_VARS);
  1177. return $GET_VARS;
  1178. }
  1179. }
  1180. return null;
  1181. }
  1182. /**
  1183. * Analysing the path AFTER the page identification part of the URL
  1184. *
  1185. * @param array The path splitted by "/". NOTICE: Passed by reference and shortend for each time a segment is matching configuration
  1186. * @param array $postVarSetCfg config
  1187. * @return array GET-vars resulting from the analysis
  1188. * @see decodeSpURL_doDecode(), encodeSpURL_gettingPostVarSets()
  1189. */
  1190. protected function decodeSpURL_settingPostVarSets(&$pathParts, $postVarSetCfg, $pid) {
  1191. if (is_array($postVarSetCfg)) {
  1192. $GET_string = '';
  1193. // Getting first value, the key (and keep stripping of sets of segments until the end is reached!)
  1194. while (false != ($key = array_shift($pathParts))) {
  1195. $key = rawurldecode($key);
  1196. if (is_array($postVarSetCfg[$key])) {
  1197. switch ((string)$postVarSetCfg[$key]['type']) {
  1198. case 'admin':
  1199. $this->decodeSpURL_jumpAdmin();
  1200. break;
  1201. case 'single':
  1202. $GET_string .= $this->decodeSpURL_getSingle($postVarSetCfg[$key]['keyValues']);
  1203. break;
  1204. default:
  1205. unset($postVarSetCfg[$key]['type']); // Just to make sure it is not set!
  1206. $GET_string .= $this->decodeSpURL_getSequence($pathParts, $postVarSetCfg[$key]);
  1207. break;
  1208. }
  1209. } elseif ($this->extConf['init']['postVarSet_failureMode'] == 'redirect_goodUpperDir') {
  1210. // Add the element just taken off. What is left now will be the post-parts that were not mapped to anything.
  1211. array_unshift($pathParts, $key);
  1212. $originalDirs = $this->dirParts;
  1213. // Popping of pages of original dirs (as many as are remaining in $pathParts)
  1214. while (count($pathParts)) {
  1215. array_pop($pathParts);
  1216. array_pop($originalDirs);
  1217. }
  1218. // If a file part was detected, add that:
  1219. $this->appendFilePart($originalDirs);
  1220. // Implode URL and redirect:
  1221. $redirectUrl = implode('/', $originalDirs);
  1222. header('HTTP/1.1 301 Moved Permanently');
  1223. header('Location: ' . t3lib_div::locationHeaderUrl($redirectUrl));
  1224. exit();
  1225. } elseif ($this->extConf['init']['postVarSet_failureMode'] == 'ignore') {
  1226. // Add the element just taken off. What is left now will be the post-parts that were not mapped to anything.
  1227. array_unshift($pathParts, $key);
  1228. break;
  1229. } else {
  1230. $this->decodeSpURL_throw404('Segment "' . $key . '" was not a keyword for a postVarSet as expected on page with id=' . $pid . '.');
  1231. }
  1232. }
  1233. // If a get string is created, then:
  1234. if ($GET_string) {
  1235. $GET_VARS = false;
  1236. parse_str($GET_string, $GET_VARS);
  1237. $this->decodeSpURL_fixMagicQuotes($GET_VARS);
  1238. $this->decodeSpURL_fixBrackets($GET_VARS);
  1239. return $GET_VARS;
  1240. }
  1241. }
  1242. return null;
  1243. }
  1244. /**
  1245. * Fix for the magic_quotes_gpc. See http://bugs.typo3.org/view.php?id=18133
  1246. *
  1247. * @param mixed $array
  1248. * @return void
  1249. */
  1250. protected function decodeSpURL_fixMagicQuotes(&$array) {
  1251. if (get_magic_quotes_gpc() && is_array($array)) {
  1252. t3lib_div::stripSlashesOnArray($array);
  1253. }
  1254. }
  1255. /**
  1256. * Fixes a problem with parse_url that returns `a[b[c]` instead of `a[b[c]]` when parsing `a%5Bb%5Bc%5D%5D`
  1257. *
  1258. * @param mixed $arr
  1259. * @return void
  1260. * @see decodeSpURL_settingPostVarSets()
  1261. */
  1262. protected function decodeSpURL_fixBrackets(&$arr) {
  1263. $bad_keys = array();
  1264. foreach ($arr as $k => $v) {
  1265. if (is_array($v)) {
  1266. $this->decodeSpURL_fixBrackets($arr[$k]);
  1267. } else {
  1268. if (strchr($k, '[') && !strchr($k, ']')) {
  1269. $bad_keys[] = $k;
  1270. }
  1271. }
  1272. }
  1273. if (count($bad_keys) > 0) {
  1274. foreach ($bad_keys as $key) {
  1275. $arr[$key . ']'] = $arr[$key];
  1276. unset($arr[$key]);
  1277. }
  1278. }
  1279. }
  1280. /**
  1281. * Decodes the file name and adjusts file parts accordingly
  1282. *
  1283. * @param array $pathParts Path parts of the URLs (can be modified)
  1284. * @return array GET varaibles from the file name or empty array
  1285. */
  1286. protected function decodeSpURL_decodeFileName(array &$pathParts) {
  1287. $getVars = array();
  1288. $fileName = rawurldecode(array_pop($pathParts));
  1289. $fileParts = t3lib_div::revExplode('.', $fileName, 2);
  1290. if (count($fileParts) == 2 && !$fileParts[1]) {
  1291. $this->decodeSpURL_throw404('File "' . $fileName . '" was not found (2)!');
  1292. }
  1293. list($segment, $extension) = $fileParts;
  1294. if ($extension) {
  1295. $getVars = array();
  1296. if (!$this->decodeSpURL_decodeFileName_lookupInIndex($fileName, $segment, $extension, $pathParts, $getVars)) {
  1297. if (!$this->decodeSpURL_decodeFileName_checkHtmlSuffix($fileName, $segment, $extension, $pathParts)) {
  1298. $this->decodeSpURL_throw404('File "' . $fileName . '" was not found (1)!');
  1299. }
  1300. }
  1301. }
  1302. return $getVars;
  1303. }
  1304. /**
  1305. * Checks if the suffix matches to the configured one.
  1306. *
  1307. * @param string $fileName
  1308. * @param string $segment
  1309. * @param string $extension
  1310. * @param array $pathPartsCopy
  1311. * @see tx_realurl::decodeSpURL_decodeFileName()
  1312. */
  1313. protected function decodeSpURL_decodeFileName_checkHtmlSuffix($fileName, $segment, $extension, array &$pathParts) {
  1314. $handled = false;
  1315. if (isset($this->extConf['fileName']['defaultToHTMLsuffixOnPrev']) && $this->extConf['fileName']['defaultToHTMLsuffixOnPrev']) {
  1316. $suffix = $this->extConf['fileName']['defaultToHTMLsuffixOnPrev'];
  1317. $suffix = (!$this->isString($suffix, 'defaultToHTMLsuffixOnPrev') ? '.html' : $suffix);
  1318. if ($suffix == '.' . $extension) {
  1319. $pathParts[] = rawurlencode($segment);
  1320. $this->filePart = '.' . $extension;
  1321. $handled = true;
  1322. }
  1323. }
  1324. if (!$handled && isset($this->extConf['fileName']['acceptHTMLsuffix']) && $this->extConf['fileName']['acceptHTMLsuffix']) {
  1325. $suffix = $this->extConf['fileName']['acceptHTMLsuffix'];
  1326. $suffix = (!$this->isString($suffix, 'acceptHTMLsuffix') ? '.html' : $suffix);
  1327. if (substr($fileName, -strlen($suffix)) == $suffix) {
  1328. $pathParts[] = rawurlencode($segment);
  1329. $this->filePart = $suffix;
  1330. $handled = true;
  1331. }
  1332. }
  1333. if (!$handled) {
  1334. $this->decodeSpURL_throw404('File "' . $fileName . '" was not found (2)!');
  1335. }
  1336. return $handled;
  1337. }
  1338. /**
  1339. * Looks up the file name or the extension in the index.
  1340. *
  1341. * @param string $fileName
  1342. * @param string $segment
  1343. * @param string $extension
  1344. * @param array $pathPartsCopy Path parts (can be modified)
  1345. * @return array GET variables (can be enpty in case if there is a default file name)
  1346. * @see tx_realurl::decodeSpURL_decodeFileName()
  1347. */
  1348. protected function decodeSpURL_decodeFileName_lookupInIndex($fileName, $segment, $extension, array &$pathPartsCopy, array &$getVars) {
  1349. $handled = false;
  1350. $keyValues = '';
  1351. if (is_array($this->extConf['fileName']['index'])) {
  1352. foreach ($this->extConf['fileName']['index'] as $key => $config) {
  1353. // Note: strict comparison because the following is true in PHP: 0 == 'whatever'
  1354. if ($key === $fileName) {
  1355. $keyValues = $config['keyValues'];
  1356. $this->filePart = $fileName;
  1357. if (isset($config['mimetype'])) {
  1358. $this->mimeType = $config['mimetype'];
  1359. }
  1360. $handled = true;
  1361. break;
  1362. }
  1363. elseif ($key === '.' . $extension) {
  1364. $keyValues = $config['keyValues'];
  1365. $pathPartsCopy[] = urlencode($segment);
  1366. $this->filePart = '.' . $extension;
  1367. if (isset($config['mimetype'])) {
  1368. $this->mimeType = $config['mimetype'];
  1369. }
  1370. $handled = true;
  1371. break;
  1372. }
  1373. }
  1374. }
  1375. // Must decode key values if set
  1376. if ($keyValues) {
  1377. $getString = $this->decodeSpURL_getSingle($keyValues);
  1378. parse_str($getString, $getVars);
  1379. }
  1380. return $handled;
  1381. }
  1382. /**
  1383. * Pulling variables of the path parts
  1384. *
  1385. * @param array Parts of path. NOTICE: Passed by reference.
  1386. * @param array Setup array for segments in set.
  1387. * @return string GET parameter string
  1388. * @see decodeSpURL_settingPreVars(), decodeSpURL_settingPostVarSets()
  1389. */
  1390. protected function decodeSpURL_getSequence(&$pathParts, $setupArr) {
  1391. $GET_string = '';
  1392. $prevVal = '';
  1393. foreach ($setupArr as $setup) {
  1394. if (count($pathParts) == 0) {
  1395. // If we are here, it means we are at the end of the URL.
  1396. // Since some items still remain in the $setupArr, it means
  1397. // we stripped empty segments at the end of the URL on encoding.
  1398. // Reconstruct them or cHash check will fail in TSFE.
  1399. // Related to bugs #15906, #18477.
  1400. if (!$setup['optional'] && $setup['noMatch'] != 'bypass') {
  1401. if (!isset($_GET[$setup['GETvar']]) && (!is_array($setup['cond']) || $this->checkCondition($setup['cond'], $prevVal))) {
  1402. $GET_string .= '&' . rawurlencode($setup['GETvar']) . '=';
  1403. $prevVal = '';
  1404. }
  1405. }
  1406. }
  1407. else {
  1408. // Get value and remove from path parts:
  1409. $value = $origValue = array_shift($pathParts);
  1410. $value = rawurldecode($value);
  1411. switch ($setup['type']) {
  1412. case 'action':
  1413. // Find index key:
  1414. $idx = isset($setup['index'][$value]) ? $value : '_DEFAULT';
  1415. // Look up type:
  1416. switch ((string)$setup['index'][$idx]['type']) {
  1417. case 'redirect':
  1418. $url = (string)$setup['index'][$idx]['url'];
  1419. $url = str_replace('###INDEX###', rawurlencode($value), $url);
  1420. $this->appendFilePart($pathParts);
  1421. $remainPath = implode('/', $pathParts);
  1422. if ($this->appendedSlash) {
  1423. $remainPath = substr($remainPath, 0, -1);
  1424. }
  1425. $url = str_replace('###REMAIN_PATH###', rawurlencode(rawurldecode($remainPath)), $url);
  1426. header('Location: ' . t3lib_div::locationHeaderUrl($url));
  1427. exit();
  1428. break;
  1429. case 'admin':
  1430. $this->decodeSpURL_jumpAdmin();
  1431. break;
  1432. case 'notfound':
  1433. $this->decodeSpURL_throw404('A required value from "' . @implode(',', @array_keys($setup['match'])) . '" of path was not matching "' . $value . '" which was actually found.');
  1434. break;
  1435. case 'bypass':
  1436. array_unshift($pathParts, $origValue);
  1437. break;
  1438. case 'feLogin':
  1439. // Do nothing.
  1440. break;
  1441. }
  1442. break;
  1443. default:
  1444. if (!is_array($setup['cond']) || $this->checkCondition($setup['cond'], $prevVal)) {
  1445. // Map value if applicable:
  1446. if (isset($setup['valueMap'][$value])) {
  1447. $value = $setup['valueMap'][$value];
  1448. } elseif ($setup['noMatch'] == 'bypass') {
  1449. // If no match and "bypass" is set, then return the value to $pathParts and break
  1450. array_unshift($pathParts, $origValue);
  1451. break;
  1452. } elseif ($setup['noMatch'] == 'null') { // If no match and "null" is set, then break (without setting any value!)
  1453. break;
  1454. } elseif ($setup['userFunc']) {
  1455. $params = array(
  1456. 'decodeAlias' => true,
  1457. 'origValue' => $origValue,
  1458. 'pathParts' => &$pathParts,
  1459. 'pObj' => &$this,
  1460. 'value' => $value,
  1461. );
  1462. $value = t3lib_div::callUserFunction($setup['userFunc'], $params, $this);
  1463. } elseif (is_array($setup['lookUpTable'])) {
  1464. $temp = $value;
  1465. $value = $this->lookUpTranslation($setup['lookUpTable'], $value, TRUE);
  1466. if ($setup['lookUpTable']['enable404forInvalidAlias'] && !self::testInt($value) && !strcmp($value, $temp)) {
  1467. $this->decodeSpURL_throw404('Couldn\'t map alias "' . $value . '" to an ID');
  1468. }
  1469. } elseif (isset($setup['valueDefault'])) { // If no matching value and a default value is given, set that:
  1470. $value = $setup['valueDefault'];
  1471. }
  1472. // Set previous value:
  1473. $prevVal = $value;
  1474. // Add to GET string:
  1475. if ($setup['GETvar'] && strlen($value)) { // Checking length of value; normally a *blank* parameter is not found in the URL! And if we don't do this we may disturb "cHash" calculations!
  1476. if (isset($this->extConf['init']['emptySegmentValue']) && $this->extConf['init']['emptySegmentValue'] === $value) {
  1477. $value = '';
  1478. }
  1479. $GET_string .= '&' . rawurlencode($setup['GETvar']) . '=' . rawurlencode($value);
  1480. }
  1481. } else {
  1482. array_unshift($pathParts, $origValue);
  1483. break;
  1484. }
  1485. break;
  1486. }
  1487. }
  1488. }
  1489. return $GET_string;
  1490. }
  1491. /**
  1492. * Traverses incoming array of GET-var => value pairs and implodes that to a string of GET parameters
  1493. *
  1494. * @param array Parameters
  1495. * @return string GET parameters
  1496. * @see decodeSpURL_fileName(), decodeSpURL_settingPostVarSets(), encodeSpURL_setSingle()
  1497. */
  1498. protected function decodeSpURL_getSingle($keyValues) {
  1499. $GET_string = '';
  1500. if (is_array($keyValues)) {
  1501. foreach ($keyValues as $kkey => $vval) {
  1502. $GET_string .= '&' . rawurlencode($kkey) . '=' . rawurlencode($vval);
  1503. }
  1504. }
  1505. return $GET_string;
  1506. }
  1507. /**
  1508. * Throws a 404 message.
  1509. *
  1510. * @param string Message string
  1511. * @return void
  1512. */
  1513. public function decodeSpURL_throw404($msg) {
  1514. // Log error:
  1515. if (!$this->extConf['init']['disableErrorLog']) {
  1516. $hash = t3lib_div::md5int($this->speakingURIpath_procValue);
  1517. $rootpage_id = intval($this->extConf['pagePath']['rootpage_id']);
  1518. $cond = 'url_hash=' . intval($hash) . ' AND rootpage_id=' . $rootpage_id;
  1519. $fields_values = array('url_hash' => $hash, 'url' => $this->speakingURIpath_procValue, 'error' => $msg, 'counter' => 1, 'tstamp' => time(), 'cr_date' => time(), 'rootpage_id' => $rootpage_id, 'last_referer' => t3lib_div::getIndpEnv('HTTP_REFERER'));
  1520. if ($this->useMySQLExtendedSyntax) {
  1521. $query = $GLOBALS['TYPO3_DB']->INSERTquery('tx_realurl_errorlog', $fields_values);
  1522. $query .= ' ON DUPLICATE KEY UPDATE ' . 'error=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($msg, 'tx_realurl_errorlog') . ',' . 'counter=counter+1,' . 'tstamp=' . $fields_values['tstamp'] . ',' . 'last_referer=' . $GLOBALS['TYPO3_DB']->fullQuoteStr(t3lib_div::getIndpEnv('HTTP_REFERER'), 'tx_realurl_errorlog');
  1523. $GLOBALS['TYPO3_DB']->sql_query($query);
  1524. } else {
  1525. $GLOBALS['TYPO3_DB']->sql_query('START TRANSACTION');
  1526. list($error_row) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('counter', 'tx_realurl_errorlog', $cond);
  1527. if (count($error_row)) {
  1528. $fields_values = array('error' => $msg, 'counter' => $error_row['counter'] + 1, 'tstamp' => time(), 'last_referer' => t3lib_div::getIndpEnv('HTTP_REFERER'));
  1529. $GLOBALS['TYPO3_DB']->exec_UPDATEquery('tx_realurl_errorlog', $cond, $fields_values);
  1530. } else {
  1531. $GLOBALS['TYPO3_DB']->exec_INSERTquery('tx_realurl_errorlog', $fields_values);
  1532. }
  1533. $GLOBALS['TYPO3_DB']->sql_query('COMMIT');
  1534. }
  1535. }
  1536. // Call handler
  1537. $this->pObj->pageNotFoundAndExit($msg);
  1538. }
  1539. /**
  1540. * This function either a) jumps to the Backend Login page with redirect URL to current page (that is if no BE-login is currently found) or b) it enables edit icons on the page
  1541. *
  1542. * @return void
  1543. */
  1544. protected function decodeSpURL_jumpAdmin() {
  1545. if ($this->pObj->beUserLogin && is_object($GLOBALS['BE_USER'])) {
  1546. if ($this->extConf['init']['adminJumpToBackend']) {
  1547. $this->decode_editInBackend = TRUE;
  1548. } elseif ($GLOBALS['BE_USER']->extAdmEnabled) {
  1549. $GLOBALS['TSFE']->displayFieldEditIcons = 1;
  1550. $GLOBALS['BE_USER']->uc['TSFE_adminConfig']['edit_editNoPopup'] = 1;
  1551. $GLOBALS['TSFE']->applicationData['tx_realurl']['adminJumpActive'] = 1;
  1552. $GLOBALS['TSFE']->set_no_cache();
  1553. }
  1554. } else {
  1555. $adminUrl = t3lib_div::getIndpEnv('TYPO3_SITE_URL') . TYPO3_mainDir . 'index.php?redirect_url=' . rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'));
  1556. header('Location: ' . t3lib_div::locationHeaderUrl($adminUrl));
  1557. exit();
  1558. }
  1559. }
  1560. /**
  1561. * Will exit after redirect to backend (with "&edit=...") if $this->decode_editInBackend is set
  1562. *
  1563. * @param integer Page id.
  1564. * @return void
  1565. */
  1566. protected function decodeSpURL_jumpAdmin_goBackend($pageId) {
  1567. if ($this->decode_editInBackend) {
  1568. $editUrl = t3lib_div::getIndpEnv('TYPO3_SITE_URL') . TYPO3_mainDir . 'alt_main.php?edit=' . intval($pageId);
  1569. header('Location: ' . t3lib_div::locationHeaderUrl($editUrl));
  1570. exit();
  1571. }
  1572. }
  1573. /**
  1574. * Manages caching of URLs to be decoded.
  1575. *
  1576. * @param string Speaking URL path to be decoded
  1577. * @param array Optional; If supplied array then this array is stored as the cached information for the input $speakingURIpath. If this argument is not set the method tries to look up such an array associated with input speakingURIpath
  1578. * @return mixed Returns array with cached information related to $speakingURIpath (unless $cachedInfo is an array in which case it is stored back to database).
  1579. */
  1580. protected function decodeSpURL_decodeCache($speakingURIpath, $cachedInfo = '') {
  1581. if ($this->extConf['init']['enableUrlDecodeCache'] && !$this->disableDecodeCache) {
  1582. // Create hash string:
  1583. if (is_array($cachedInfo)) { // STORE cachedInfo
  1584. if (!$this->isBEUserLoggedIn() && $this->canCachePageURL($cachedInfo['id'])) {
  1585. $rootpage_id = intval($cachedInfo['rootpage_id']);
  1586. $hash = md5($speakingURIpath . $rootpage_id);
  1587. $insertFields = array('url_hash' => $hash, 'spurl' => $speakingURIpath, 'content' => serialize($cachedInfo), 'page_id' => $cachedInfo['id'], 'rootpage_id' => $rootpage_id, 'tstamp' => time());
  1588. if ($this->useMySQLExtendedSyntax) {
  1589. $query = $GLOBALS['TYPO3_DB']->INSERTquery('tx_realurl_urldecodecache', $insertFields);
  1590. $query .= ' ON DUPLICATE KEY UPDATE tstamp=' . $insertFields['tstamp'];
  1591. $GLOBALS['TYPO3_DB']->sql_query($query);
  1592. } else {
  1593. $GLOBALS['TYPO3_DB']->sql_query('START TRANSACTION');
  1594. $GLOBALS['TYPO3_DB']->exec_DELETEquery('tx_realurl_urldecodecache', 'url_hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($hash, 'tx_realurl_urldecodecache'));
  1595. $GLOBALS['TYPO3_DB']->exec_INSERTquery('tx_realurl_urldecodecache', $insertFields);
  1596. $GLOBALS['TYPO3_DB']->sql_query('COMMIT');
  1597. }
  1598. }
  1599. }
  1600. else {
  1601. // GET cachedInfo.
  1602. $rootpage_id = intval($this->extConf['pagePath']['rootpage_id']);
  1603. $hash = md5($speakingURIpath . $rootpage_id);
  1604. $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('content', 'tx_realurl_urldecodecache',
  1605. 'url_hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($hash, 'tx_realurl_urldecodecache') .
  1606. ' AND ' .
  1607. //No need for root page id if we use full md5!
  1608. //'rootpage_id='.intval($rootpage_id) . ' AND ' .
  1609. 'tstamp>' . strtotime('midnight', time() - 24 * 3600 * $this->decodeCacheTTL));
  1610. $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
  1611. $GLOBALS['TYPO3_DB']->sql_free_result($res);
  1612. if ($row) {
  1613. return unserialize($row['content']);
  1614. }
  1615. }
  1616. }
  1617. return null;
  1618. }
  1619. /**
  1620. * Get "cHash" GET var from database. See explanation in encodeSpURL_cHashCache()
  1621. *
  1622. * @param string Speaking URL path (virtual path)
  1623. * @return string cHash value, if any.
  1624. * @see encodeSpURL_cHashCache()
  1625. */
  1626. protected function decodeSpURL_cHashCache($speakingURIpath) {
  1627. // Look up cHash for this spURL
  1628. // Apart from returning the right cHash value it can also:
  1629. // - Return no value (eg. if table has been cleared) even if there should
  1630. // be one! In this scenario it will look to the outside as if all
  1631. // parameters except cHash has been set.
  1632. // - Return a WRONG value (eg. if URLs has been changed internally, there
  1633. // are dublets etc.). In this scenario the cHash value should not match
  1634. // the calculated one in the tslib_fe and the usual error of that problem
  1635. // be issued (whatever that is). This scenario could even mean that a
  1636. // cHash value is returned even if no cHash value applies at all.
  1637. // Bottomline is: the realurl extension makes it more likely that a wrong
  1638. // cHash value is passed to the frontend but as such it doesn't do anything
  1639. // which a fabricated URL couldn't contain.
  1640. // I still don't know how to handle wrong cHash values in this table. It
  1641. // will seldomly be a problem (when parameters are changed manually mostly)
  1642. // but when it is, we have no standard procedure to clean it up. Of course
  1643. // clearing it will mean it is built up again - but also that tons of URLs
  1644. // will not work reliably!
  1645. $stringForHash = $speakingURIpath;
  1646. if (count($this->additionalParametersForChash)) {
  1647. $stringForHash .= '|' . serialize($this->additionalParametersForChash);
  1648. }
  1649. list($row) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('chash_string',
  1650. 'tx_realurl_chashcache', 'spurl_hash=' .
  1651. $GLOBALS['TYPO3_DB']->fullQuoteStr(md5($stringForHash),
  1652. 'tx_realurl_chashcache'));
  1653. if (!is_array($row) && $stringForHash != $speakingURIpath) {
  1654. // Use a more generic query if specific fails. This can happen when
  1655. // using _DOMAINS and the variable is set to 'bypass'.
  1656. $stringForHash = $speakingURIpath;
  1657. list($row) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('chash_string',
  1658. 'tx_realurl_chashcache', 'spurl_hash=' .
  1659. $GLOBALS['TYPO3_DB']->fullQuoteStr(md5($stringForHash),
  1660. 'tx_realurl_chashcache'));
  1661. }
  1662. return is_array($row) ? $row['chash_string'] : false;
  1663. }
  1664. /*******************************
  1665. *
  1666. * Alias-ID look up functions
  1667. *
  1668. ******************************/
  1669. /**
  1670. * Doing database lookup between "alias values" and "id numbers". Translation is bi-directional.
  1671. *
  1672. * @param array Configuration of look-up table, field names etc.
  1673. * @param string Value to match field in database to.
  1674. * @param boolean If TRUE, the input $value is an alias-string that needs translation to an ID integer. FALSE (default) means the reverse direction
  1675. * @return string Result value of lookup. If no value was found the $value is returned.
  1676. */
  1677. protected function lookUpTranslation($cfg, $value, $aliasToUid = FALSE) {
  1678. // Assemble list of fields to look up. This includes localization related fields:
  1679. $langEnabled = FALSE;
  1680. $fieldList = array();
  1681. if ($cfg['languageGetVar'] && $cfg['transOrigPointerField'] && $cfg['languageField']) {
  1682. $fieldList[] = 'uid';
  1683. $fieldList[] = $cfg['transOrigPointerField'];
  1684. $fieldList[] = $cfg['languageField'];
  1685. $langEnabled = TRUE;
  1686. }
  1687. // Translate an alias string to an ID:
  1688. if ($aliasToUid) {
  1689. // First, test if there is an entry in cache for the alias:
  1690. if ($cfg['useUniqueCache'] && $returnId = $this->lookUp_uniqAliasToId($cfg, $value)) {
  1691. return $returnId;
  1692. }
  1693. else { // If no cached entry, look it up directly in the table:
  1694. $fieldList[] = $cfg['id_field'];
  1695. $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(implode(',', $fieldList), $cfg['table'],
  1696. $cfg['alias_field'] . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($value, $cfg['table']) .
  1697. ' ' . $cfg['addWhereClause']);
  1698. $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
  1699. $GLOBALS['TYPO3_DB']->sql_free_result($res);
  1700. if ($row) {
  1701. $returnId = $row[$cfg['id_field']];
  1702. // If localization is enabled, check if this record is a localized version and if so, find uid of the original version.
  1703. if ($langEnabled && $row[$cfg['languageField']] > 0) {
  1704. $returnId = $row[$cfg['transOrigPointerField']];
  1705. }
  1706. // Return the id:
  1707. return $returnId;
  1708. }
  1709. }
  1710. } else { // Translate an ID to alias string
  1711. // Define the language for the alias:
  1712. $lang = intval($this->orig_paramKeyValues[$cfg['languageGetVar']]);
  1713. if (t3lib_div::inList($cfg['languageExceptionUids'], $lang)) { // Might be excepted (like you should for CJK cases which does not translate to ASCII equivalents)
  1714. $lang = 0;
  1715. }
  1716. // First, test if there is an entry in cache for the id:
  1717. if ($cfg['useUniqueCache'] && !$cfg['autoUpdate'] && $returnAlias = $this->lookUp_idToUniqAlias($cfg, $value, $lang)) {
  1718. return $returnAlias;
  1719. } else { // If no cached entry, look up alias directly in the table (and possibly store cache value)
  1720. $fieldList[] = $cfg['alias_field'];
  1721. $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(implode(',', $fieldList), $cfg['table'],
  1722. $cfg['id_field'] . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($value, $cfg['table']) .
  1723. ' ' . $cfg['addWhereClause']);
  1724. $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
  1725. $GLOBALS['TYPO3_DB']->sql_free_result($res);
  1726. if ($row) {
  1727. // Looking for localized version of that:
  1728. if ($langEnabled && $lang) {
  1729. // If the lang value is there, look for a localized version of record:
  1730. $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($cfg['alias_field'], $cfg['table'],
  1731. $cfg['transOrigPointerField'] . '=' . intval($row['uid']) . '
  1732. AND ' . $cfg['languageField'] . '=' . intval($lang) . '
  1733. ' . $cfg['addWhereClause']);
  1734. $lrow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
  1735. $GLOBALS['TYPO3_DB']->sql_free_result($res);
  1736. if ($lrow) {
  1737. $row = $lrow;
  1738. }
  1739. }
  1740. $mLength = $cfg['maxLength'] ? $cfg['maxLength'] : $this->maxLookUpLgd;
  1741. if ($cfg['useUniqueCache']) { // If cache is to be used, store the alias in the cache:
  1742. $aliasBaseValue = $row[$cfg['alias_field']];
  1743. return $this->lookUp_newAlias($cfg, substr($aliasBaseValue, 0, $mLength), $value, $lang);
  1744. } else { // If no cache for alias, then just return whatever value is appropriate:
  1745. if (strlen($row[$cfg['alias_field']]) <= $mLength) {
  1746. return $row[$cfg['alias_field']];
  1747. } else {
  1748. return $value;
  1749. }
  1750. }
  1751. }
  1752. }
  1753. }
  1754. // In case no value was found in translation we return the incoming value. It may be argued that this is not a good idea but generally this can be avoided by using the "useUniqueCache" principle which will ensure unique translation both ways.
  1755. return $value;
  1756. }
  1757. /**
  1758. * Looks up an ID value (integer) in lookup-table based on input alias value.
  1759. * (The lookup table for id<->alias is meant to contain UNIQUE alias strings for id integers)
  1760. * In the lookup table 'tx_realurl_uniqalias' the field "value_alias" should be unique (per combination of field_alias+field_id+tablename)! However the "value_id" field doesn't have to; that is a feature which allows more aliases to point to the same id. The alias selected for converting id to alias will be the first inserted at the moment. This might be more intelligent in the future, having an order column which can be controlled from the backend for instance!
  1761. *
  1762. * @param array Configuration array
  1763. * @param string Alias value to convert to ID
  1764. * @param boolean <code>true</code> if only non-expiring record should be looked up
  1765. * @return integer ID integer. If none is found: false
  1766. * @see lookUpTranslation(), lookUp_idToUniqAlias()
  1767. */
  1768. protected function lookUp_uniqAliasToId($cfg, $aliasValue, $onlyNonExpired = FALSE) {
  1769. // Look up the ID based on input alias value:
  1770. list($row) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('value_id', 'tx_realurl_uniqalias',
  1771. 'value_alias=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($aliasValue, 'tx_realurl_uniqalias') .
  1772. ' AND field_alias=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($cfg['alias_field'], 'tx_realurl_uniqalias') .
  1773. ' AND field_id=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($cfg['id_field'], 'tx_realurl_uniqalias') .
  1774. ' AND tablename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($cfg['table'], 'tx_realurl_uniqalias') .
  1775. ' AND ' . ($onlyNonExpired ? 'expire=0' : '(expire=0 OR expire>' . time() . ')'));
  1776. return (is_array($row) ? $row['value_id'] : false);
  1777. }
  1778. /**
  1779. * Looks up a alias string in lookup-table based on input ID value (integer)
  1780. * (The lookup table for id<->alias is meant to contain UNIQUE alias strings for id integers)
  1781. *
  1782. * @param array Configuration array
  1783. * @param string ID value to convert to alias value
  1784. * @param integer sys_language_uid to use for lookup
  1785. * @param string Optional alias value to limit search to
  1786. * @return string Alias string. If none is found: false
  1787. * @see lookUpTranslation(), lookUp_uniqAliasToId()
  1788. */
  1789. protected function lookUp_idToUniqAlias($cfg, $idValue, $lang, $aliasValue = '') {
  1790. // Look for an alias based on ID:
  1791. list($row) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('value_alias', 'tx_realurl_uniqalias',
  1792. 'value_id=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($idValue, 'tx_realurl_uniqalias') .
  1793. ' AND field_alias=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($cfg['alias_field'], 'tx_realurl_uniqalias') .
  1794. ' AND field_id=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($cfg['id_field'], 'tx_realurl_uniqalias') .
  1795. ' AND tablename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($cfg['table'], 'tx_realurl_uniqalias') .
  1796. ' AND lang=' . intval($lang) .
  1797. ' AND expire=0' .
  1798. ($aliasValue ? ' AND value_alias=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($aliasValue, 'tx_realurl_uniqalias') : ''),
  1799. '', '', '1');
  1800. if (is_array($row)) {
  1801. return $row['value_alias'];
  1802. }
  1803. return null;
  1804. }
  1805. /**
  1806. * Creates a new alias<->id relation in database lookup table.
  1807. *
  1808. * WARNING! This function is internal to RealURL. It is made public for
  1809. * backwards compatibility but its behavior and parameters may change as
  1810. * necessary for RealURL. No guaranties at all!
  1811. *
  1812. * @param array Configuration array of lookup table
  1813. * @param string Preferred new alias (final alias might be different if duplicates were found in the cache)
  1814. * @param integer ID associated with alias
  1815. * @param integer sys_language_uid to store with record
  1816. * @return string Final alias string
  1817. * @see lookUpTranslation()
  1818. * @internal
  1819. */
  1820. protected function lookUp_newAlias($cfg, $newAliasValue, $idValue, $lang) {
  1821. // Clean preferred alias
  1822. $newAliasValue = $this->lookUp_cleanAlias($cfg, $newAliasValue);
  1823. // If autoupdate is true we might be here even if an alias exists. Therefore we check if that alias is the $newAliasValue and if so, we return that instead of making a new, unique one.
  1824. if ($cfg['autoUpdate'] && $this->lookUp_idToUniqAlias($cfg, $idValue, $lang, $newAliasValue)) {
  1825. return $newAliasValue;
  1826. }
  1827. // Now, go create a unique alias:
  1828. $uniqueAlias = '';
  1829. $counter = 0;
  1830. $maxTry = 100;
  1831. $test_newAliasValue = $newAliasValue;
  1832. while ($counter < $maxTry) {
  1833. // If the test-alias did NOT exist, it must be unique and we break out:
  1834. $foundId = $this->lookUp_uniqAliasToId($cfg, $test_newAliasValue, true);
  1835. if (!$foundId || $foundId == $idValue) {
  1836. $uniqueAlias = $test_newAliasValue;
  1837. break;
  1838. }
  1839. // Otherwise, increment counter and test again...
  1840. $counter++;
  1841. $test_newAliasValue = $newAliasValue . '-' . $counter;
  1842. }
  1843. // if no unique alias was found in the process above, just suffix a hash string and assume that is unique...
  1844. if (!$uniqueAlias) {
  1845. $uniqueAlias = $newAliasValue .= '-' . t3lib_div::shortMD5(microtime());
  1846. }
  1847. // Insert the new id<->alias relation:
  1848. $insertArray = array('tstamp' => time(), 'tablename' => $cfg['table'], 'field_alias' => $cfg['alias_field'], 'field_id' => $cfg['id_field'], 'value_alias' => $uniqueAlias, 'value_id' => $idValue, 'lang' => $lang);
  1849. // Checking that this alias hasn't been stored since we looked last time:
  1850. $returnAlias = $this->lookUp_idToUniqAlias($cfg, $idValue, $lang, $uniqueAlias);
  1851. if ($returnAlias) {
  1852. // If we are here it is because another process managed to create this alias in the time between we looked the first time and now when we want to put it in database.
  1853. $uniqueAlias = $returnAlias;
  1854. }
  1855. else {
  1856. // Expire all other aliases:
  1857. // Look for an alias based on ID:
  1858. $GLOBALS['TYPO3_DB']->exec_UPDATEquery('tx_realurl_uniqalias', 'value_id=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($idValue, 'tx_realurl_uniqalias') . '
  1859. AND field_alias=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($cfg['alias_field'], 'tx_realurl_uniqalias') . '
  1860. AND field_id=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($cfg['id_field'], 'tx_realurl_uniqalias') . '
  1861. AND tablename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($cfg['table'], 'tx_realurl_uniqalias') . '
  1862. AND lang=' . intval($lang) . '
  1863. AND expire=0', array('expire' => time() + 24 * 3600 * ($cfg['expireDays'] ? $cfg['expireDays'] : 60)));
  1864. // Store new alias:
  1865. $GLOBALS['TYPO3_DB']->exec_INSERTquery('tx_realurl_uniqalias', $insertArray);
  1866. }
  1867. // Return new unique alias:
  1868. return $uniqueAlias;
  1869. }
  1870. /**
  1871. * Clean up the alias
  1872. * (Almost the same function as encodeTitle() in class.tx_realurl_advanced.php)
  1873. *
  1874. * @param array Configuration array
  1875. * @param string Alias value to clean up
  1876. * @return string New alias value
  1877. * @see lookUpTranslation()
  1878. */
  1879. public function lookUp_cleanAlias($cfg, $newAliasValue) {
  1880. // Fetch character set:
  1881. $charset = $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'] ? $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'] : $GLOBALS['TSFE']->defaultCharSet;
  1882. $processedTitle = $newAliasValue;
  1883. // Convert to lowercase:
  1884. if ($cfg['useUniqueCache_conf']['strtolower']) {
  1885. $processedTitle = $GLOBALS['TSFE']->csConvObj->conv_case($charset, $processedTitle, 'toLower');
  1886. }
  1887. // Convert some special tokens to the space character:
  1888. $space = $cfg['useUniqueCache_conf']['spaceCharacter'] ? substr($cfg['useUniqueCache_conf']['spaceCharacter'], 0, 1) : '_';
  1889. $processedTitle = strtr($processedTitle, ' -+_', $space . $space . $space . $space); // convert spaces
  1890. // Convert extended letters to ascii equivalents:
  1891. $processedTitle = $GLOBALS['TSFE']->csConvObj->specCharsToASCII($charset, $processedTitle);
  1892. // Strip the rest
  1893. if ($this->extConf['init']['enableAllUnicodeLetters']) {
  1894. // Warning: slow!!!
  1895. $processedTitle = preg_replace('/[^\p{L}0-9\\' . $space . ']/u', '', $processedTitle);
  1896. }
  1897. else {
  1898. $processedTitle = preg_replace('/[^a-zA-Z0-9\\' . $space . ']/', '', $processedTitle);
  1899. }
  1900. $processedTitle = preg_replace('/\\' . $space . '{2,}/', $space, $processedTitle); // Convert multiple 'spaces' to a single one
  1901. $processedTitle = trim($processedTitle, $space);
  1902. if ($cfg['useUniqueCache_conf']['encodeTitle_userProc']) {
  1903. $encodingConfiguration = array('strtolower' => $cfg['useUniqueCache_conf']['strtolower'], 'spaceCharacter' => $cfg['useUniqueCache_conf']['spaceCharacter']);
  1904. $params = array('pObj' => &$this, 'title' => $newAliasValue, 'processedTitle' => $processedTitle, 'encodingConfiguration' => $encodingConfiguration);
  1905. $processedTitle = t3lib_div::callUserFunction($cfg['useUniqueCache_conf']['encodeTitle_userProc'], $params, $this);
  1906. }
  1907. // Return value:
  1908. return $processedTitle;
  1909. }
  1910. /*******************************
  1911. *
  1912. * General helper functions (both decode/encode)
  1913. *
  1914. ******************************/
  1915. /**
  1916. * Sets configuration in $this->extConf, taking host domain into account
  1917. *
  1918. * @return void
  1919. * @see encodeSpURL(), decodeSpURL()
  1920. */
  1921. protected function setConfig() {
  1922. // Finding host-name / IP, always in lowercase:
  1923. $this->hostConfigured = $this->host = $this->getHost();
  1924. $_realurl_conf = (array)@unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['realurl']);
  1925. // Autoconfiguration
  1926. if ($_realurl_conf['enableAutoConf'] && !isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl']) && !@include_once (PATH_site . TX_REALURL_AUTOCONF_FILE) && !isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl'])) {
  1927. require_once (t3lib_extMgm::extPath('realurl', 'class.tx_realurl_autoconfgen.php'));
  1928. $_realurl_gen = t3lib_div::makeInstance('tx_realurl_autoconfgen');
  1929. $_realurl_gen->generateConfiguration();
  1930. unset($_realurl_gen);
  1931. @include_once(PATH_site . TX_REALURL_AUTOCONF_FILE);
  1932. }
  1933. $extConf = &$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl'];
  1934. $this->multidomain = $this->isMultidomain();
  1935. // First pass, finding configuration OR pointer string:
  1936. if (isset($extConf[$this->host])) {
  1937. $this->extConf = $extConf[$this->host];
  1938. // If it turned out to be a string pointer, then look up the real config:
  1939. while (!is_null($this->extConf) && is_string($this->extConf)) {
  1940. $this->hostConfigured = $this->extConf;
  1941. $this->extConf = $extConf[$this->extConf];
  1942. }
  1943. if (!is_array($this->extConf)) {
  1944. $this->extConf = $extConf['_DEFAULT'];
  1945. $this->hostConfigured = '_DEFAULT';
  1946. if ($this->multidomain && isset($this->extConf['pagePath']['rootpage_id'])) {
  1947. // This can't be right!
  1948. unset($this->extConf['pagePath']['rootpage_id']);
  1949. }
  1950. }
  1951. }
  1952. else {
  1953. if ($this->enableStrictMode && $this->multidomain) {
  1954. $this->pObj->pageNotFoundAndExit('RealURL strict mode error: ' .
  1955. 'multidomain configuration detected and domain \'' . $this->host .
  1956. '\' is not configured for RealURL. Please, fix your RealURL configuration!');
  1957. }
  1958. $this->extConf = (array)$extConf['_DEFAULT'];
  1959. $this->hostConfigured = '_DEFAULT';
  1960. if ($this->multidomain && isset($this->extConf['pagePath']['rootpage_id'])) {
  1961. // This can't be right!
  1962. unset($this->extConf['pagePath']['rootpage_id']);
  1963. }
  1964. }
  1965. }
  1966. /**
  1967. * Determines the current host. Sometimes it is not possible to determine
  1968. * that from the environment, so the hook is used to get the host from the
  1969. * third-party scripts.
  1970. *
  1971. * @return string
  1972. */
  1973. protected function getHost() {
  1974. $host = strtolower((string)t3lib_div::getIndpEnv('HTTP_HOST'));
  1975. if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl']['getHost'])) {
  1976. foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl']['getHost'] as $userFunc) {
  1977. $hookParams = array(
  1978. 'host' => $host,
  1979. );
  1980. $newHost = t3lib_div::callUserFunction($userFunc, $hookParams, $this);
  1981. if (!empty($newHost) && is_string($newHost)) {
  1982. $host = $newHost;
  1983. }
  1984. }
  1985. }
  1986. return $host;
  1987. }
  1988. /**
  1989. * Returns configuration for a postVarSet (default) based on input page id
  1990. *
  1991. * @param integer Page id
  1992. * @param string Main key in realurl configuration array. Default is "postVarSets" but could be "fixedPostVars"
  1993. * @return array Configuration array
  1994. * @see decodeSpURL_doDecode()
  1995. * @internal
  1996. */
  1997. public function getPostVarSetConfig($page_id, $mainCat = 'postVarSets') {
  1998. // If the page id is NOT an integer, it's an alias we have to look up:
  1999. if (!self::testInt($page_id)) {
  2000. $page_id = $this->pageAliasToID($page_id);
  2001. }
  2002. // Checking if the value is not an array but a pointer to another key:
  2003. if (isset($this->extConf[$mainCat][$page_id]) && !is_array($this->extConf[$mainCat][$page_id])) {
  2004. $page_id = $this->extConf[$mainCat][$page_id];
  2005. }
  2006. $cfg = is_array($this->extConf[$mainCat][$page_id]) ? $this->extConf[$mainCat][$page_id] :
  2007. (is_array($this->extConf[$mainCat]['_DEFAULT']) ? $this->extConf[$mainCat]['_DEFAULT'] : array());
  2008. return $cfg;
  2009. }
  2010. /**
  2011. * Page alias-to-id translation including memory caching.
  2012. *
  2013. * @param string Page Alias string
  2014. * @return integer Page id, zero if none was found.
  2015. */
  2016. protected function pageAliasToID($alias) {
  2017. // Look in memory cache first, and if not there, look it up:
  2018. if (!isset($GLOBALS['TSFE']->applicationData['tx_realurl']['_CACHE_aliases'][$alias])) {
  2019. $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', 'pages',
  2020. 'alias=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($alias, 'pages') .
  2021. ' AND pages.deleted=0');
  2022. $pageRec = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
  2023. $GLOBALS['TYPO3_DB']->sql_free_result($res);
  2024. $GLOBALS['TSFE']->applicationData['tx_realurl']['_CACHE_aliases'][$alias] = intval($pageRec['uid']);
  2025. }
  2026. // Return ID:
  2027. return $GLOBALS['TSFE']->applicationData['tx_realurl']['_CACHE_aliases'][$alias];
  2028. }
  2029. /**
  2030. * Rawurlencodes the input string; used for GET parameter names of variables that were NOT SpURL encoded. Offers the possibility of NOT encoding them...
  2031. *
  2032. * @param string Input string
  2033. * @return string Output string
  2034. * @see encodeSpURL()
  2035. */
  2036. protected function rawurlencodeParam($str) {
  2037. if (!$this->extConf['init']['doNotRawUrlEncodeParameterNames']) {
  2038. return rawurlencode($str);
  2039. } else
  2040. return $str;
  2041. }
  2042. /**
  2043. * Checks condition for varSets
  2044. *
  2045. * @param array Configuration for condition
  2046. * @param string Previous value in sequence of GET vars. The value is the "system" value; In other words: The *real* id, not the alias for a value.
  2047. * @return boolean TRUE if proceed is ok, otherwise false.
  2048. * @see encodeSpURL_setSequence(), decodeSpURL_getSequence()
  2049. */
  2050. protected function checkCondition($setup, $prevVal) {
  2051. $return = true;
  2052. // Check previous value:
  2053. if (isset($setup['prevValueInList'])) {
  2054. if (!t3lib_div::inList($setup['prevValueInList'], $prevVal))
  2055. $return = false;
  2056. }
  2057. return $return;
  2058. }
  2059. /**
  2060. * Checks if BE user is logged in.
  2061. *
  2062. * @return boolean <code>true</code> if BE user is logged in
  2063. * @internal
  2064. */
  2065. public function isBEUserLoggedIn() {
  2066. return $this->pObj->beUserLogin;
  2067. }
  2068. /**
  2069. * Adjusts the configuration used for RealURL processing, depending on a specific domain disposal.
  2070. *
  2071. * @param string $type: Calling type of realurl (encode|decode)
  2072. * @param array $params: Parameters delivered to RealURL (e.g. from t3lib_TStemplate->linkData hook)
  2073. * @return mixed Information required for further processing or false if something went wrong
  2074. */
  2075. protected function adjustConfigurationByHost($type, $params = null) {
  2076. $result = false;
  2077. $this->additionalParametersForChash = array();
  2078. if (isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl']['_DOMAINS'])) {
  2079. $configuration = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl']['_DOMAINS'];
  2080. if (is_array($configuration)) {
  2081. if ($type == 'encode' && isset($configuration['encode'])) {
  2082. $result = $this->adjustConfigurationByHostEncode($configuration['encode'], $params);
  2083. } elseif ($type == 'decode' && isset($configuration['decode'])) {
  2084. $result = $this->adjustConfigurationByHostDecode($configuration['decode']);
  2085. }
  2086. }
  2087. }
  2088. return $result;
  2089. }
  2090. /**
  2091. * Adjusts the configuration used for RealURL path encoding, depending on a specific domain disposal.
  2092. *
  2093. * @param array $configuration: Configuration required to determine hosts while path encoding
  2094. * @param array $params: Parameters delivered to RealURL by t3lib_TStemplate->linkData hook
  2095. * @return mixed Information required for further processing or false if something went wrong
  2096. */
  2097. protected function adjustConfigurationByHostEncode($configuration, $params) {
  2098. $this->ignoreGETvar = '';
  2099. if (is_array($params) && isset($params['LD']['totalURL']) && is_array($configuration)) {
  2100. $urlParts = parse_url($params['LD']['totalURL']);
  2101. $urlParams = array();
  2102. parse_str($urlParts['query'], $urlParams);
  2103. foreach ($configuration as $disposal) {
  2104. if (isset($disposal['rootpage_id']) && $disposal['rootpage_id'] != $this->extConf['pagePath']['rootpage_id']) {
  2105. continue;
  2106. }
  2107. if (isset($disposal['GETvar']) && isset($disposal['value'])) {
  2108. $GETvar = $disposal['GETvar'];
  2109. $currentValue = t3lib_div::_GET($GETvar);
  2110. $expectedValue = (isset($urlParams[$GETvar]) ? $urlParams[$GETvar] : false);
  2111. if ($expectedValue !== false && $disposal['value'] == $expectedValue) {
  2112. if (!isset($disposal['ifDifferentToCurrent']) || $disposal['value'] != $currentValue) {
  2113. if (isset($disposal['useConfiguration'])) {
  2114. $this->ignoreGETvar = $GETvar;
  2115. $this->setConfigurationByReference($disposal['useConfiguration']);
  2116. }
  2117. $this->additionalParametersForChash[$GETvar] = $this->testInt($urlParams[$GETvar]) ? intval($urlParams[$GETvar]) : $urlParams[$GETvar];
  2118. return $disposal;
  2119. }
  2120. else {
  2121. $this->ignoreGETvar = $GETvar;
  2122. break;
  2123. }
  2124. }
  2125. }
  2126. }
  2127. }
  2128. return false;
  2129. }
  2130. /**
  2131. * Adjusts the configuration used for RealURL path decoding, depending on a specific domain disposal.
  2132. *
  2133. * @param array $configuration: Configuration required to determine hosts while path decoding
  2134. * @return void
  2135. */
  2136. protected function adjustConfigurationByHostDecode($configuration) {
  2137. if (is_array($configuration)) {
  2138. $host = strtolower(t3lib_div::getIndpEnv('TYPO3_HOST_ONLY'));
  2139. $hostConfiguration = false;
  2140. if (isset($configuration[$host])) {
  2141. $hostConfiguration = $configuration[$host];
  2142. } else {
  2143. $keys = array_keys($configuration);
  2144. foreach ($keys as $regexp) {
  2145. if (preg_match('/^\/[^\/]+\/$/', $regexp) && preg_match($regexp, $host)) {
  2146. $hostConfiguration = $configuration[$regexp];
  2147. break;
  2148. }
  2149. }
  2150. }
  2151. if (is_array($hostConfiguration)) {
  2152. if (isset($hostConfiguration['GETvars']) && is_array($hostConfiguration['GETvars'])) {
  2153. foreach ($hostConfiguration['GETvars'] as $key => $value) {
  2154. if (empty($_GET[$key])) {
  2155. $_GET[$key] = $value;
  2156. $this->additionalParametersForChash[$key] = $this->testInt($value) ? $value : $value;
  2157. }
  2158. }
  2159. if (isset($hostConfiguration['useConfiguration'])) {
  2160. $this->setConfigurationByReference($hostConfiguration['useConfiguration']);
  2161. }
  2162. }
  2163. }
  2164. }
  2165. }
  2166. /**
  2167. * Sets the configuration to an existing configuration part by a reference.
  2168. *
  2169. * @param string $useConfiguration: Reference to another existing configuration part which shall be used
  2170. * @return boolean Whether the action was successful
  2171. */
  2172. protected function setConfigurationByReference($useConfiguration) {
  2173. $extConf = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl'];
  2174. $resolved = array();
  2175. $result = false;
  2176. // Resolve configurations references and avoid endless loops:
  2177. while (is_string($useConfiguration) && isset($extConf[$useConfiguration]) && !in_array($useConfiguration, $resolved)) {
  2178. $resolved[] = $useConfiguration;
  2179. $useConfiguration = $extConf[$useConfiguration];
  2180. }
  2181. // Adjust the configuration:
  2182. if (is_array($useConfiguration)) {
  2183. $this->extConf = $useConfiguration;
  2184. $result = true;
  2185. }
  2186. return $result;
  2187. }
  2188. /**********************************
  2189. *
  2190. * External Hooks
  2191. *
  2192. **********************************/
  2193. /**
  2194. * Hook function for clearing page cache
  2195. *
  2196. * @param array Params for hook
  2197. * @return void
  2198. */
  2199. public function clearPageCacheMgm($params) {
  2200. $pageIdArray = $params['table'] == 'pages' ? array(intval($params['uid'])) : $params['pageIdArray'];
  2201. if (is_array($pageIdArray) && count($pageIdArray) > 0) {
  2202. $pageIdList = implode(',', $GLOBALS['TYPO3_DB']->cleanIntArray($pageIdArray));
  2203. $GLOBALS['TYPO3_DB']->exec_DELETEquery('tx_realurl_urlencodecache', 'page_id IN (' . $pageIdList . ')');
  2204. $GLOBALS['TYPO3_DB']->exec_DELETEquery('tx_realurl_urldecodecache', 'page_id IN (' . $pageIdList . ')');
  2205. $GLOBALS['TYPO3_DB']->exec_DELETEquery('tx_realurl_pathcache', 'page_id IN (' . $pageIdList . ') AND expire>0 AND expire<=' . time());
  2206. }
  2207. }
  2208. /**
  2209. * Checks for wrong boolean values (like <code>'1'</code> or </code>'true'</code> instead of <code>1</code> and <code>true</code>.
  2210. *
  2211. * @param mixed $str Parameter to check
  2212. * @param string $paramName Parameter name (for logging)
  2213. * @return <code>true</code> if string (and not bad boolean)
  2214. */
  2215. protected function isString(&$str, $paramName) {
  2216. if (!is_string($str)) {
  2217. return false;
  2218. }
  2219. if (preg_match('/^(1|0|true|false)$/i', $str)) {
  2220. $logMessage = sprintf('Wrong boolean value for parameter "%s": "%s". It is a string, not a boolean!', $paramName, $str);
  2221. $this->devLog($logMessage);
  2222. $GLOBALS['TT']->setTSlogMessage($logMessage, 2);
  2223. if ($str == intval($str)) {
  2224. $str = intval($str);
  2225. } else {
  2226. $str = (strtolower($str) == 'true');
  2227. }
  2228. return false;
  2229. }
  2230. return true;
  2231. }
  2232. /**
  2233. * Attempts to find root page ID for the current host. Processes redirectes as well.
  2234. *
  2235. * @return mixed Found root page UID or false if not found
  2236. * @internal
  2237. */
  2238. public function findRootPageId() {
  2239. $rootpage_id = false; $host = $this->host;
  2240. if (!$this->enableStrictMode) {
  2241. // Search by host
  2242. do {
  2243. $domain = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('pid,redirectTo,domainName', 'sys_domain',
  2244. 'domainName=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($host, 'sys_domain') .
  2245. ' AND hidden=0');
  2246. if (count($domain) > 0) {
  2247. if (!$domain[0]['redirectTo']) {
  2248. $rootpage_id = intval($domain[0]['pid']);
  2249. $this->devLog('Found rootpage_id by domain lookup', array('domain' => $domain[0]['domainName'], 'rootpage_id' => $rootpage_id));
  2250. break;
  2251. }
  2252. else {
  2253. $parts = @parse_url($domain[0]['redirectTo']);
  2254. $host = $parts['host'];
  2255. }
  2256. }
  2257. } while (count($domain) > 0);
  2258. // If root page id is not found, try other ways. We can do it only
  2259. // and only if there are no multiple domains. Otherwise we would
  2260. // get a lot of wrong page ids from old root pages, etc.
  2261. if (!$rootpage_id && !$this->multidomain) {
  2262. // Try by TS template
  2263. $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('pid',
  2264. 'sys_template', 'root=1 AND hidden=0 AND deleted=0');
  2265. if (count($rows) == 1) {
  2266. $rootpage_id = $rows[0]['pid'];
  2267. $this->devLog('Found rootpage_id by searching sys_template', array('rootpage_id' => $rootpage_id));
  2268. }
  2269. }
  2270. }
  2271. return $rootpage_id;
  2272. }
  2273. /**
  2274. * Checks if TYPO3 runs in the multidomain environment with different page ids
  2275. *
  2276. * @return boolean
  2277. */
  2278. protected function isMultidomain() {
  2279. static $multidomain = null;
  2280. if ($multidomain === null) {
  2281. list($row) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('COUNT(distinct pid) AS t',
  2282. 'sys_domain', 'redirectTo=\'\' AND hidden=0');
  2283. $multidomain = ($row['t'] > 1);
  2284. }
  2285. return $multidomain;
  2286. }
  2287. /**
  2288. * Checks if rootpage_id is set and if not, sets it
  2289. *
  2290. * @return void
  2291. */
  2292. protected function adjustRootPageId() {
  2293. if (!$this->extConf['pagePath']['rootpage_id']) {
  2294. if ($this->enableStrictMode) {
  2295. $this->pObj->pageNotFoundAndExit('RealURL strict mode error: ' .
  2296. 'multidomain configuration without rootpage_id. ' .
  2297. 'Please, fix your RealURL configuration!');
  2298. }
  2299. $GLOBALS['TT']->setTSlogMessage('RealURL warning: rootpage_id was not configured!');
  2300. $this->extConf['pagePath']['rootpage_id'] = $this->findRootPageId();
  2301. if ($this->multidomain && !$this->extConf['pagePath']['rootpage_id']) {
  2302. $this->pObj->pageNotFoundAndExit('RealURL error: ' .
  2303. 'unable to determine rootpage_id for the current domain.');
  2304. }
  2305. }
  2306. }
  2307. /**
  2308. * Cleans up empty path segments
  2309. *
  2310. * @param array $pathParts
  2311. * @return array
  2312. */
  2313. protected function cleanUpPathParts(array $pathParts) {
  2314. // Remove trailing empty segments
  2315. for ($index = count($pathParts) - 1; $index >= 0 && $pathParts[$index] == ''; $index--) {
  2316. unset($pathParts[$index]);
  2317. }
  2318. if (isset($this->extConf['init']['emptySegmentValue'])) {
  2319. $emptyValue = rawurlencode($this->extConf['init']['emptySegmentValue']);
  2320. // Set empty value
  2321. for ($index = count($pathParts) - 1; $index >= 0; $index--) {
  2322. if ($pathParts[$index] == '') {
  2323. $pathParts[$index] = $emptyValue;
  2324. }
  2325. }
  2326. }
  2327. return $pathParts;
  2328. }
  2329. /**
  2330. * Creates a new URL and appends a file name to the url if necessary
  2331. *
  2332. * @param array $paramKeyValues
  2333. * @param array $pathParts
  2334. * @return string
  2335. */
  2336. protected function createURLWithFileName(array &$paramKeyValues, array $pathParts) {
  2337. $url = implode('/', $pathParts);
  2338. $paramKeyValuesCopy = $paramKeyValues;
  2339. $fileName = rawurlencode($this->encodeSpURL_fileName($paramKeyValues));
  2340. if ($fileName{0} == '.') {
  2341. // Only extension
  2342. if ($url == '') {
  2343. // Home page. We can't append just extension here. So we pass
  2344. // parameters as is to the home page.
  2345. $paramKeyValues = $paramKeyValuesCopy;
  2346. }
  2347. else {
  2348. $url .= $fileName;
  2349. }
  2350. }
  2351. elseif ($fileName) {
  2352. // File name includes extension
  2353. $url .= '/' . $fileName;
  2354. }
  2355. elseif ($url != '') {
  2356. $suffix = $this->extConf['fileName']['defaultToHTMLsuffixOnPrev'];
  2357. if ($suffix) {
  2358. if (!$this->isString($suffix, 'defaultToHTMLsuffixOnPrev')) {
  2359. $suffix = '.html';
  2360. }
  2361. $url .= $suffix;
  2362. }
  2363. else {
  2364. $url .= '/';
  2365. }
  2366. }
  2367. return $url;
  2368. }
  2369. /**
  2370. * Fixes empty URL
  2371. */
  2372. protected function fixEmptyUrl($newUrl) {
  2373. if (!strlen($newUrl)) {
  2374. if (is_bool($this->extConf['init']['emptyUrlReturnValue']) && $this->extConf['init']['emptyUrlReturnValue']) {
  2375. $newUrl = ($GLOBALS['TSFE']->config['config']['absRefPrefix'] ? $GLOBALS['TSFE']->config['config']['absRefPrefix'] : $GLOBALS['TSFE']->baseUrl);
  2376. } else {
  2377. $newUrl = '' . $this->extConf['init']['emptyUrlReturnValue'];
  2378. }
  2379. }
  2380. return $newUrl;
  2381. }
  2382. /**
  2383. * Checks if system runs in non-live workspace
  2384. *
  2385. * @return boolean
  2386. */
  2387. protected function isInWorkspace() {
  2388. $result = false;
  2389. if ($GLOBALS['TSFE']->beUserLogin) {
  2390. $result = ($GLOBALS['BE_USER']->workspace != 0);
  2391. }
  2392. return $result;
  2393. }
  2394. /**
  2395. * Outputs a devLog message
  2396. *
  2397. * @param string $message
  2398. * @param int $severity
  2399. * @param mixed $dataVar
  2400. * @return void
  2401. * @internal
  2402. */
  2403. public function devLog($message, $dataVar = false, $severity = 0) {
  2404. if ($this->enableDevLog) {
  2405. t3lib_div::devLog('[' . $this->devLogId . '] ' . $message, 'realurl', $severity, $dataVar);
  2406. }
  2407. }
  2408. /**
  2409. * Sets encoding result to error
  2410. *
  2411. * @return void
  2412. * @internal
  2413. */
  2414. public function setEncodeError() {
  2415. $this->encodeError = true;
  2416. }
  2417. /**
  2418. * Obtains a copy of configuration
  2419. *
  2420. * @return array
  2421. * @internal
  2422. */
  2423. public function getConfiguration() {
  2424. return $this->extConf;
  2425. }
  2426. /**
  2427. * Appends the file part to path segments.
  2428. *
  2429. * @param array $segments
  2430. * @internal
  2431. */
  2432. public function appendFilePart(array &$segments) {
  2433. if ($this->filePart) {
  2434. if ($this->filePart{0} == '.') {
  2435. $segmentCount = count($segments);
  2436. if ($segmentCount > 0) {
  2437. $segments[$segmentCount - 1] .= urlencode($this->filePart);
  2438. }
  2439. }
  2440. else {
  2441. $segments[] = urlencode($this->filePart);
  2442. }
  2443. }
  2444. }
  2445. /**
  2446. * Determines if this page can be cached with RealURL encode or decode cache
  2447. *
  2448. * @param int $pageId
  2449. * @return boolean
  2450. */
  2451. protected function canCachePageURL($pageId) {
  2452. list($pageRecord) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('tx_realurl_nocache',
  2453. 'pages', 'uid=' . intval($pageId));
  2454. return is_array($pageRecord) ? !$pageRecord['tx_realurl_nocache'] : false;
  2455. }
  2456. /**
  2457. * Returns the detected language (decoding only). Language is detected
  2458. * from preVars or _DOMAINS feature.
  2459. *
  2460. * @return int
  2461. */
  2462. public function getDetectedLanguage() {
  2463. return intval($this->detectedLanguage);
  2464. }
  2465. /**
  2466. * Tests if the value represents an integer number.
  2467. *
  2468. * @param mixed $value
  2469. * @return bool
  2470. */
  2471. static public function testInt($value) {
  2472. static $useOldGoodTestInt = null;
  2473. if (is_null($useOldGoodTestInt)) {
  2474. $useOldGoodTestInt = !class_exists('t3lib_utility_Math');
  2475. }
  2476. if ($useOldGoodTestInt) {
  2477. $result = t3lib_div::testInt($value);
  2478. }
  2479. else {
  2480. $result = t3lib_utility_Math::canBeInterpretedAsInteger($value);
  2481. }
  2482. return $result;
  2483. }
  2484. }
  2485. if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/realurl/class.tx_realurl.php']) {
  2486. include_once ($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/realurl/class.tx_realurl.php']);
  2487. }
  2488. ?>