[CLEANUP] Cleanup phpDoc in ext:frontend
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Controller / TypoScriptFrontendController.php
1 <?php
2 namespace TYPO3\CMS\Frontend\Controller;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Error\Http\PageNotFoundException;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Core\Utility\HttpUtility;
20 use TYPO3\CMS\Frontend\Page\PageRepository;
21
22 /**
23 * Class for the built TypoScript based frontend. Instantiated in
24 * index_ts.php script as the global object TSFE.
25 *
26 * Main frontend class, instantiated in the index_ts.php script as the global
27 * object TSFE.
28 *
29 * This class has a lot of functions and internal variable which are used from
30 * index_ts.php.
31 *
32 * The class is instantiated as $GLOBALS['TSFE'] in index_ts.php.
33 *
34 * The use of this class should be inspired by the order of function calls as
35 * found in index_ts.php.
36 *
37 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
38 */
39 class TypoScriptFrontendController {
40
41 /**
42 * The page id (int)
43 * @var string
44 */
45 public $id = '';
46
47 /**
48 * The type (read-only)
49 * @var int
50 */
51 public $type = '';
52
53 /**
54 * The submitted cHash
55 * @var string
56 */
57 public $cHash = '';
58
59 /**
60 * Page will not be cached. Write only TRUE. Never clear value (some other
61 * code might have reasons to set it TRUE).
62 * @var bool
63 */
64 public $no_cache = FALSE;
65
66 /**
67 * The rootLine (all the way to tree root, not only the current site!)
68 * @var array
69 */
70 public $rootLine = '';
71
72 /**
73 * The pagerecord
74 * @var array
75 */
76 public $page = '';
77
78 /**
79 * This will normally point to the same value as id, but can be changed to
80 * point to another page from which content will then be displayed instead.
81 * @var int
82 */
83 public $contentPid = 0;
84
85 /**
86 * Gets set when we are processing a page of type mounpoint with enabled overlay in getPageAndRootline()
87 * Used later in checkPageForMountpointRedirect() to determine the final target URL where the user
88 * should be redirected to.
89 *
90 * @var array|NULL
91 */
92 protected $originalMountPointPage = NULL;
93
94 /**
95 * Gets set when we are processing a page of type shortcut in the early stages
96 * opf init.php when we do not know about languages yet, used later in init.php
97 * to determine the correct shortcut in case a translation changes the shortcut
98 * target
99 * @var array|NULL
100 */
101 protected $originalShortcutPage = NULL;
102
103 /**
104 * sys_page-object, pagefunctions
105 *
106 * @var PageRepository
107 */
108 public $sys_page = '';
109
110 /**
111 * @var string
112 */
113 public $jumpurl = '';
114
115 /**
116 * Is set to 1 if a pageNotFound handler could have been called.
117 * @var int
118 */
119 public $pageNotFound = 0;
120
121 /**
122 * Domain start page
123 * @var int
124 */
125 public $domainStartPage = 0;
126
127 /**
128 * Array containing a history of why a requested page was not accessible.
129 * @var array
130 */
131 public $pageAccessFailureHistory = array();
132
133 /**
134 * @var string
135 */
136 public $MP = '';
137
138 /**
139 * @var string
140 */
141 public $RDCT = '';
142
143 /**
144 * This can be set from applications as a way to tag cached versions of a page
145 * and later perform some external cache management, like clearing only a part
146 * of the cache of a page...
147 * @var int
148 */
149 public $page_cache_reg1 = 0;
150
151 /**
152 * Contains the value of the current script path that activated the frontend.
153 * Typically "index.php" but by rewrite rules it could be something else! Used
154 * for Speaking Urls / Simulate Static Documents.
155 * @var string
156 */
157 public $siteScript = '';
158
159 /**
160 * The frontend user
161 *
162 * @var \TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication
163 */
164 public $fe_user = '';
165
166 /**
167 * Global flag indicating that a frontend user is logged in. This is set only if
168 * a user really IS logged in. The group-list may show other groups (like added
169 * by IP filter or so) even though there is no user.
170 * @var bool
171 */
172 public $loginUser = FALSE;
173
174 /**
175 * (RO=readonly) The group list, sorted numerically. Group '0,-1' is the default
176 * group, but other groups may be added by other means than a user being logged
177 * in though...
178 * @var string
179 */
180 public $gr_list = '';
181
182 /**
183 * Flag that indicates if a backend user is logged in!
184 * @var bool
185 */
186 public $beUserLogin = FALSE;
187
188 /**
189 * Integer, that indicates which workspace is being previewed.
190 * @var int
191 */
192 public $workspacePreview = 0;
193
194 /**
195 * Shows whether logins are allowed in branch
196 * @var bool
197 */
198 public $loginAllowedInBranch = TRUE;
199
200 /**
201 * Shows specific mode (all or groups)
202 * @var string
203 */
204 public $loginAllowedInBranch_mode = '';
205
206 /**
207 * Set to backend user ID to initialize when keyword-based preview is used
208 * @var int
209 */
210 public $ADMCMD_preview_BEUSER_uid = 0;
211
212 /**
213 * Flag indication that preview is active. This is based on the login of a
214 * backend user and whether the backend user has read access to the current
215 * page. A value of 1 means ordinary preview, 2 means preview of a non-live
216 * workspace
217 * @var int
218 */
219 public $fePreview = 0;
220
221 /**
222 * Flag indicating that hidden pages should be shown, selected and so on. This
223 * goes for almost all selection of pages!
224 * @var bool
225 */
226 public $showHiddenPage = FALSE;
227
228 /**
229 * Flag indicating that hidden records should be shown. This includes
230 * sys_template, pages_language_overlay and even fe_groups in addition to all
231 * other regular content. So in effect, this includes everything except pages.
232 * @var bool
233 */
234 public $showHiddenRecords = FALSE;
235
236 /**
237 * Value that contains the simulated usergroup if any
238 * @var int
239 */
240 public $simUserGroup = 0;
241
242 /**
243 * Copy of $GLOBALS['TYPO3_CONF_VARS']
244 *
245 * @var array
246 */
247 public $TYPO3_CONF_VARS = array();
248
249 /**
250 * "CONFIG" object from TypoScript. Array generated based on the TypoScript
251 * configuration of the current page. Saved with the cached pages.
252 * @var array
253 */
254 public $config = '';
255
256 /**
257 * The TypoScript template object. Used to parse the TypoScript template
258 *
259 * @var \TYPO3\CMS\Core\TypoScript\TemplateService
260 */
261 public $tmpl = NULL;
262
263 /**
264 * Is set to the time-to-live time of cached pages. If FALSE, default is
265 * 60*60*24, which is 24 hours.
266 * @var bool|int
267 */
268 public $cacheTimeOutDefault = FALSE;
269
270 /**
271 * Set internally if cached content is fetched from the database
272 * @var bool
273 * @internal
274 */
275 public $cacheContentFlag = FALSE;
276
277 /**
278 * Set to the expire time of cached content
279 * @var int
280 */
281 public $cacheExpires = 0;
282
283 /**
284 * Set if cache headers allowing caching are sent.
285 * @var bool
286 */
287 public $isClientCachable = FALSE;
288
289 /**
290 * Used by template fetching system. This array is an identification of
291 * the template. If $this->all is empty it's because the template-data is not
292 * cached, which it must be.
293 * @var array
294 */
295 public $all = array();
296
297 /**
298 * Toplevel - objArrayName, eg 'page'
299 * @var string
300 */
301 public $sPre = '';
302
303 /**
304 * TypoScript configuration of the page-object pointed to by sPre.
305 * $this->tmpl->setup[$this->sPre.'.']
306 * @var array
307 */
308 public $pSetup = '';
309
310 /**
311 * This hash is unique to the template, the $this->id and $this->type vars and
312 * the gr_list (list of groups). Used to get and later store the cached data
313 * @var string
314 */
315 public $newHash = '';
316
317 /**
318 * If config.ftu (Frontend Track User) is set in TypoScript for the current
319 * page, the string value of this var is substituted in the rendered source-code
320 * with the string, '&ftu=[token...]' which enables GET-method usertracking as
321 * opposed to cookie based
322 * @var string
323 */
324 public $getMethodUrlIdToken = '';
325
326 /**
327 * This flag is set before inclusion of pagegen.php IF no_cache is set. If this
328 * flag is set after the inclusion of pagegen.php, no_cache is forced to be set.
329 * This is done in order to make sure that php-code from pagegen does not falsely
330 * clear the no_cache flag.
331 * @var bool
332 */
333 public $no_cacheBeforePageGen = FALSE;
334
335 /**
336 * This flag indicates if temporary content went into the cache during
337 * page-generation.
338 * @var mixed
339 */
340 public $tempContent = FALSE;
341
342 /**
343 * Passed to TypoScript template class and tells it to force template rendering
344 * @var bool
345 */
346 public $forceTemplateParsing = FALSE;
347
348 /**
349 * The array which cHash_calc is based on, see ->makeCacheHash().
350 * @var array
351 */
352 public $cHash_array = array();
353
354 /**
355 * May be set to the pagesTSconfig
356 * @var array
357 */
358 public $pagesTSconfig = '';
359
360 /**
361 * Eg. insert JS-functions in this array ($additionalHeaderData) to include them
362 * once. Use associative keys.
363 *
364 * Keys in use:
365 *
366 * JSFormValidate: <script type="text/javascript" src="'.$GLOBALS["TSFE"]->absRefPrefix.'typo3/sysext/frontend/Resources/Public/JavaScript/jsfunc.validateform.js"></script>
367 * JSincludeFormupdate: <script type="text/javascript" src="typo3/js/jsfunc.updateform.js"></script>
368 * JSMenuCode, JSMenuCode_menu: JavaScript for the JavaScript menu
369 * JSCode: reserved
370 *
371 * used to accumulate additional HTML-code for the header-section,
372 * <head>...</head>. Insert either associative keys (like
373 * additionalHeaderData['myStyleSheet'], see reserved keys above) or num-keys
374 * (like additionalHeaderData[] = '...')
375 *
376 * @var array
377 */
378 public $additionalHeaderData = array();
379
380 /**
381 * Used to accumulate additional HTML-code for the footer-section of the template
382 * @var array
383 */
384 public $additionalFooterData = array();
385
386 /**
387 * Used to accumulate additional JavaScript-code. Works like
388 * additionalHeaderData. Reserved keys at 'openPic' and 'mouseOver'
389 *
390 * @var array
391 */
392 public $additionalJavaScript = array();
393
394 /**
395 * Used to accumulate additional Style code. Works like additionalHeaderData.
396 *
397 * @var array
398 */
399 public $additionalCSS = array();
400
401 /**
402 * You can add JavaScript functions to each entry in these arrays. Please see
403 * how this is done in the GMENU_LAYERS script. The point is that many
404 * applications on a page can set handlers for onload, onmouseover and onmouseup
405 *
406 * @var array
407 * @deprecated since TYPO3 CMS 7, will be removed in CMS 8
408 */
409 public $JSeventFuncCalls = array(
410 'onmousemove' => array(),
411 'onmouseup' => array(),
412 'onkeydown' => array(),
413 'onkeyup' => array(),
414 'onkeypress' => array(),
415 'onload' => array(),
416 'onunload' => array()
417 );
418
419 /**
420 * Used to accumulate DHTML-layers.
421 * @var string
422 */
423 public $divSection = '';
424
425 /**
426 * Default bodytag, if nothing else is set. This can be overridden by
427 * applications like TemplaVoila.
428 * @var string
429 */
430 public $defaultBodyTag = '<body>';
431
432 /**
433 * Debug flag, may output special debug html-code.
434 * @var string
435 */
436 public $debug = '';
437
438 /**
439 * Default internal target
440 * @var string
441 */
442 public $intTarget = '';
443
444 /**
445 * Default external target
446 * @var string
447 */
448 public $extTarget = '';
449
450 /**
451 * Default file link target
452 * @var string
453 */
454 public $fileTarget = '';
455
456 /**
457 * Keys are page ids and values are default &MP (mount point) values to set
458 * when using the linking features...)
459 * @var array
460 */
461 public $MP_defaults = array();
462
463 /**
464 * If set, typolink() function encrypts email addresses. Is set in pagegen-class.
465 * @var string|int
466 */
467 public $spamProtectEmailAddresses = 0;
468
469 /**
470 * Absolute Reference prefix
471 * @var string
472 */
473 public $absRefPrefix = '';
474
475 /**
476 * Factor for form-field widths compensation
477 * @var string
478 */
479 public $compensateFieldWidth = '';
480
481 /**
482 * Lock file path
483 * @var string
484 */
485 public $lockFilePath = '';
486
487 /**
488 * <A>-tag parameters
489 * @var string
490 */
491 public $ATagParams = '';
492
493 /**
494 * Search word regex, calculated if there has been search-words send. This is
495 * used to mark up the found search words on a page when jumped to from a link
496 * in a search-result.
497 * @var string
498 */
499 public $sWordRegEx = '';
500
501 /**
502 * Is set to the incoming array sword_list in case of a page-view jumped to from
503 * a search-result.
504 * @var string
505 */
506 public $sWordList = '';
507
508 /**
509 * A string prepared for insertion in all links on the page as url-parameters.
510 * Based on configuration in TypoScript where you defined which GET_VARS you
511 * would like to pass on.
512 * @var string
513 */
514 public $linkVars = '';
515
516 /**
517 * A string set with a comma list of additional GET vars which should NOT be
518 * included in the cHash calculation. These vars should otherwise be detected
519 * and involved in caching, eg. through a condition in TypoScript.
520 * @var string
521 */
522 public $excludeCHashVars = '';
523
524 /**
525 * If set, edit icons are rendered aside content records. Must be set only if
526 * the ->beUserLogin flag is set and set_no_cache() must be called as well.
527 * @var string
528 */
529 public $displayEditIcons = '';
530
531 /**
532 * If set, edit icons are rendered aside individual fields of content. Must be
533 * set only if the ->beUserLogin flag is set and set_no_cache() must be called as
534 * well.
535 * @var string
536 */
537 public $displayFieldEditIcons = '';
538
539 /**
540 * Site language, 0 (zero) is default, int+ is uid pointing to a sys_language
541 * record. Should reflect which language menus, templates etc is displayed in
542 * (master language) - but not necessarily the content which could be falling
543 * back to default (see sys_language_content)
544 * @var int
545 */
546 public $sys_language_uid = 0;
547
548 /**
549 * Site language mode for content fall back.
550 * @var string
551 */
552 public $sys_language_mode = '';
553
554 /**
555 * Site content selection uid (can be different from sys_language_uid if content
556 * is to be selected from a fall-back language. Depends on sys_language_mode)
557 * @var int
558 */
559 public $sys_language_content = 0;
560
561 /**
562 * Site content overlay flag; If set - and sys_language_content is > 0 - ,
563 * records selected will try to look for a translation pointing to their uid. (If
564 * configured in [ctrl][languageField] / [ctrl][transOrigP...]
565 * @var int
566 */
567 public $sys_language_contentOL = 0;
568
569 /**
570 * Is set to the iso code of the sys_language_content if that is properly defined
571 * by the sys_language record representing the sys_language_uid. (Requires the
572 * extension "static_info_tables")
573 * @var string
574 */
575 public $sys_language_isocode = '';
576
577 /**
578 * 'Global' Storage for various applications. Keys should be 'tx_'.extKey for
579 * extensions.
580 * @var array
581 */
582 public $applicationData = array();
583
584 /**
585 * @var array
586 */
587 public $register = array();
588
589 /**
590 * Stack used for storing array and retrieving register arrays (see
591 * LOAD_REGISTER and RESTORE_REGISTER)
592 * @var array
593 */
594 public $registerStack = array();
595
596 /**
597 * Checking that the function is not called eternally. This is done by
598 * interrupting at a depth of 50
599 * @var int
600 */
601 public $cObjectDepthCounter = 50;
602
603 /**
604 * Used by cObj->RECORDS and cObj->CONTENT to ensure the a records is NOT
605 * rendered twice through it!
606 * @var array
607 */
608 public $recordRegister = array();
609
610 /**
611 * This is set to the [table]:[uid] of the latest record rendered. Note that
612 * class ContentObjectRenderer has an equal value, but that is pointing to the
613 * record delivered in the $data-array of the ContentObjectRenderer instance, if
614 * the cObjects CONTENT or RECORD created that instance
615 * @var string
616 */
617 public $currentRecord = '';
618
619 /**
620 * Used by class \TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject
621 * to keep track of access-keys.
622 * @var array
623 */
624 public $accessKey = array();
625
626 /**
627 * Numerical array where image filenames are added if they are referenced in the
628 * rendered document. This includes only TYPO3 generated/inserted images.
629 * @var array
630 */
631 public $imagesOnPage = array();
632
633 /**
634 * Is set in ContentObjectRenderer->cImage() function to the info-array of the
635 * most recent rendered image. The information is used in
636 * ContentObjectRenderer->IMGTEXT
637 * @var array
638 */
639 public $lastImageInfo = array();
640
641 /**
642 * Used to generate page-unique keys. Point is that uniqid() functions is very
643 * slow, so a unikey key is made based on this, see function uniqueHash()
644 * @var int
645 */
646 public $uniqueCounter = 0;
647
648 /**
649 * @var string
650 */
651 public $uniqueString = '';
652
653 /**
654 * This value will be used as the title for the page in the indexer (if
655 * indexing happens)
656 * @var string
657 */
658 public $indexedDocTitle = '';
659
660 /**
661 * Alternative page title (normally the title of the page record). Can be set
662 * from applications you make.
663 * @var string
664 */
665 public $altPageTitle = '';
666
667 /**
668 * The base URL set for the page header.
669 * @var string
670 */
671 public $baseUrl = '';
672
673 /**
674 * The proper anchor prefix needed when using speaking urls. (only set if
675 * baseUrl is set)
676 * @var string
677 */
678 public $anchorPrefix = '';
679
680 /**
681 * IDs we already rendered for this page (to make sure they are unique)
682 * @var array
683 */
684 private $usedUniqueIds = array();
685
686 /**
687 * Page content render object
688 *
689 * @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
690 */
691 public $cObj = '';
692
693 /**
694 * All page content is accumulated in this variable. See pagegen.php
695 * @var string
696 */
697 public $content = '';
698
699 /**
700 * Set to the browser: net / msie if 4+ browsers
701 * @var string
702 */
703 public $clientInfo = '';
704
705 /**
706 * @var int
707 */
708 public $scriptParseTime = 0;
709
710 /**
711 * Character set (charset) conversion object:
712 * charset conversion class. May be used by any application.
713 *
714 * @var \TYPO3\CMS\Core\Charset\CharsetConverter
715 */
716 public $csConvObj;
717
718 /**
719 * The default charset used in the frontend if nothing else is set.
720 * @var string
721 */
722 public $defaultCharSet = 'utf-8';
723
724 /**
725 * Internal charset of the frontend during rendering. (Default: UTF-8)
726 * @var string
727 */
728 public $renderCharset = '';
729
730 /**
731 * Output charset of the websites content. This is the charset found in the
732 * header, meta tag etc. If different from $renderCharset a conversion
733 * happens before output to browser. Defaults to ->renderCharset if not set.
734 * @var string
735 */
736 public $metaCharset = '';
737
738 /**
739 * Assumed charset of locale strings.
740 * @var string
741 */
742 public $localeCharset = '';
743
744 /**
745 * Set to the system language key (used on the site)
746 * @var string
747 */
748 public $lang = '';
749
750 /**
751 * @var array
752 */
753 public $LL_labels_cache = array();
754
755 /**
756 * @var array
757 */
758 public $LL_files_cache = array();
759
760 /**
761 * List of language dependencies for actual language. This is used for local
762 * variants of a language that depend on their "main" language, like Brazilian,
763 * Portuguese or Canadian French.
764 *
765 * @var array
766 */
767 protected $languageDependencies = array();
768
769 /**
770 * Locking object for accessing "cache_pagesection"
771 *
772 * @var \TYPO3\CMS\Core\Locking\Locker
773 */
774 public $pagesection_lockObj;
775
776 /**
777 * Locking object for accessing "cache_pages"
778 *
779 * @var \TYPO3\CMS\Core\Locking\Locker
780 */
781 public $pages_lockObj;
782
783 /**
784 * @var \TYPO3\CMS\Core\Page\PageRenderer
785 */
786 protected $pageRenderer;
787
788 /**
789 * The page cache object, use this to save pages to the cache and to
790 * retrieve them again
791 *
792 * @var \TYPO3\CMS\Core\Cache\Backend\AbstractBackend
793 */
794 protected $pageCache;
795
796 /**
797 * @var array
798 */
799 protected $pageCacheTags = array();
800
801 /**
802 * @var \TYPO3\CMS\Frontend\Page\CacheHashCalculator The cHash Service class used for cHash related functionality
803 */
804 protected $cacheHash;
805
806 /**
807 * Runtime cache of domains per processed page ids.
808 *
809 * @var array
810 */
811 protected $domainDataCache = array();
812
813 /**
814 * Class constructor
815 * Takes a number of GET/POST input variable as arguments and stores them internally.
816 * The processing of these variables goes on later in this class.
817 * Also sets internal clientInfo array (browser information) and a unique string (->uniqueString) for this script instance; A md5 hash of the microtime()
818 *
819 * @param array $TYPO3_CONF_VARS The global $TYPO3_CONF_VARS array. Will be set internally in ->TYPO3_CONF_VARS
820 * @param mixed $id The value of \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('id')
821 * @param int $type The value of \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('type')
822 * @param bool|string $no_cache The value of \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('no_cache'), evaluated to 1/0
823 * @param string $cHash The value of \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('cHash')
824 * @param string $jumpurl The value of \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('jumpurl')
825 * @param string $MP The value of \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('MP')
826 * @param string $RDCT The value of \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('RDCT')
827 * @see index_ts.php
828 */
829 public function __construct($TYPO3_CONF_VARS, $id, $type, $no_cache = '', $cHash = '', $jumpurl = '', $MP = '', $RDCT = '') {
830 // Setting some variables:
831 $this->TYPO3_CONF_VARS = $TYPO3_CONF_VARS;
832 $this->id = $id;
833 $this->type = $type;
834 if ($no_cache) {
835 if ($this->TYPO3_CONF_VARS['FE']['disableNoCacheParameter']) {
836 $warning = '&no_cache=1 has been ignored because $TYPO3_CONF_VARS[\'FE\'][\'disableNoCacheParameter\'] is set!';
837 $GLOBALS['TT']->setTSlogMessage($warning, 2);
838 } else {
839 $warning = '&no_cache=1 has been supplied, so caching is disabled! URL: "' . GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL') . '"';
840 $this->disableCache();
841 }
842 GeneralUtility::sysLog($warning, 'cms', GeneralUtility::SYSLOG_SEVERITY_WARNING);
843 }
844 $this->cHash = $cHash;
845 $this->jumpurl = $jumpurl;
846 $this->MP = $this->TYPO3_CONF_VARS['FE']['enable_mount_pids'] ? (string)$MP : '';
847 $this->RDCT = $RDCT;
848 $this->clientInfo = GeneralUtility::clientInfo();
849 $this->uniqueString = md5(microtime());
850 $this->csConvObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Charset\\CharsetConverter');
851 // Call post processing function for constructor:
852 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['tslib_fe-PostProc'])) {
853 $_params = array('pObj' => &$this);
854 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['tslib_fe-PostProc'] as $_funcRef) {
855 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
856 }
857 }
858 $this->cacheHash = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\CacheHashCalculator');
859 $this->initCaches();
860 }
861
862 /**
863 * Connect to SQL database. May exit after outputting an error message
864 * or some JavaScript redirecting to the install tool.
865 *
866 * @throws \RuntimeException
867 * @throws \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException
868 * @return void
869 */
870 public function connectToDB() {
871 try {
872 $GLOBALS['TYPO3_DB']->connectDB();
873 } catch (\RuntimeException $exception) {
874 switch ($exception->getCode()) {
875 case 1270853883:
876 // Cannot connect to current database
877 $message = 'Cannot connect to the configured database "' . TYPO3_db . '"';
878 if ($this->checkPageUnavailableHandler()) {
879 $this->pageUnavailableAndExit($message);
880 } else {
881 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
882 throw new \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException($message, 1301648782);
883 }
884 break;
885 case 1270853884:
886 // Username / password not accepted
887 $message = 'The current username, password or host was not accepted when' . ' the connection to the database was attempted to be established!';
888 if ($this->checkPageUnavailableHandler()) {
889 $this->pageUnavailableAndExit($message);
890 } else {
891 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
892 throw new \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException('Database Error: ' . $message, 1301648945);
893 }
894 break;
895 default:
896 throw $exception;
897 }
898 }
899 // Call post processing function for DB connection:
900 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['connectToDB'])) {
901 $_params = array('pObj' => &$this);
902 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['connectToDB'] as $_funcRef) {
903 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
904 }
905 }
906 }
907
908 /**
909 * Looks up the value of $this->RDCT in the database and if it is
910 * found to be associated with a redirect URL then the redirection
911 * is carried out with a 'Location:' header
912 * May exit after sending a location-header.
913 *
914 * @return void
915 */
916 public function sendRedirect() {
917 if ($this->RDCT) {
918 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('params', 'cache_md5params', 'md5hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($this->RDCT, 'cache_md5params'));
919 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
920 $this->updateMD5paramsRecord($this->RDCT);
921 header('Location: ' . $row['params']);
922 die;
923 }
924 }
925 }
926
927 /**
928 * Gets instance of PageRenderer
929 *
930 * @return \TYPO3\CMS\Core\Page\PageRenderer
931 */
932 public function getPageRenderer() {
933 if (!isset($this->pageRenderer)) {
934 $this->pageRenderer = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Page\\PageRenderer');
935 $this->pageRenderer->setTemplateFile('EXT:frontend/Resources/Private/Templates/MainPage.html');
936 $this->pageRenderer->setBackPath(TYPO3_mainDir);
937 }
938 return $this->pageRenderer;
939 }
940
941 /**
942 * This is needed for USER_INT processing
943 *
944 * @param \TYPO3\CMS\Core\Page\PageRenderer $pageRenderer
945 */
946 protected function setPageRenderer(\TYPO3\CMS\Core\Page\PageRenderer $pageRenderer) {
947 $this->pageRenderer = $pageRenderer;
948 }
949
950 /********************************************
951 *
952 * Initializing, resolving page id
953 *
954 ********************************************/
955 /**
956 * Initializes the caching system.
957 *
958 * @return void
959 */
960 protected function initCaches() {
961 $this->pageCache = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Cache\\CacheManager')->getCache('cache_pages');
962 }
963
964 /**
965 * Initializes the front-end login user.
966 *
967 * @return void
968 */
969 public function initFEuser() {
970 $this->fe_user = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Authentication\\FrontendUserAuthentication');
971 $this->fe_user->lockIP = $this->TYPO3_CONF_VARS['FE']['lockIP'];
972 $this->fe_user->checkPid = $this->TYPO3_CONF_VARS['FE']['checkFeUserPid'];
973 $this->fe_user->lifetime = (int)$this->TYPO3_CONF_VARS['FE']['lifetime'];
974 // List of pid's acceptable
975 $pid = GeneralUtility::_GP('pid');
976 $this->fe_user->checkPid_value = $pid ? $GLOBALS['TYPO3_DB']->cleanIntList($pid) : 0;
977 // Check if a session is transferred:
978 if (GeneralUtility::_GP('FE_SESSION_KEY')) {
979 $fe_sParts = explode('-', GeneralUtility::_GP('FE_SESSION_KEY'));
980 // If the session key hash check is OK:
981 if (md5(($fe_sParts[0] . '/' . $this->TYPO3_CONF_VARS['SYS']['encryptionKey'])) === (string)$fe_sParts[1]) {
982 $cookieName = \TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication::getCookieName();
983 $_COOKIE[$cookieName] = $fe_sParts[0];
984 if (isset($_SERVER['HTTP_COOKIE'])) {
985 // See http://forge.typo3.org/issues/27740
986 $_SERVER['HTTP_COOKIE'] .= ';' . $cookieName . '=' . $fe_sParts[0];
987 }
988 $this->fe_user->forceSetCookie = 1;
989 unset($cookieName);
990 }
991 }
992 $this->fe_user->start();
993 $this->fe_user->unpack_uc('');
994 // Gets session data
995 $this->fe_user->fetchSessionData();
996 $recs = GeneralUtility::_GP('recs');
997 // If any record registration is submitted, register the record.
998 if (is_array($recs)) {
999 $this->fe_user->record_registration($recs, $this->TYPO3_CONF_VARS['FE']['maxSessionDataSize']);
1000 }
1001 // Call hook for possible manipulation of frontend user object
1002 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['initFEuser'])) {
1003 $_params = array('pObj' => &$this);
1004 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['initFEuser'] as $_funcRef) {
1005 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1006 }
1007 }
1008 // For every 60 seconds the is_online timestamp is updated.
1009 if (is_array($this->fe_user->user) && $this->fe_user->user['uid'] && $this->fe_user->user['is_online'] < $GLOBALS['EXEC_TIME'] - 60) {
1010 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('fe_users', 'uid=' . (int)$this->fe_user->user['uid'], array('is_online' => $GLOBALS['EXEC_TIME']));
1011 }
1012 }
1013
1014 /**
1015 * Initializes the front-end user groups.
1016 * Sets ->loginUser and ->gr_list based on front-end user status.
1017 *
1018 * @return void
1019 */
1020 public function initUserGroups() {
1021 // This affects the hidden-flag selecting the fe_groups for the user!
1022 $this->fe_user->showHiddenRecords = $this->showHiddenRecords;
1023 // no matter if we have an active user we try to fetch matching groups which can be set without an user (simulation for instance!)
1024 $this->fe_user->fetchGroupData();
1025 if (is_array($this->fe_user->user) && count($this->fe_user->groupData['uid'])) {
1026 // global flag!
1027 $this->loginUser = TRUE;
1028 // group -2 is not an existing group, but denotes a 'default' group when a user IS logged in. This is used to let elements be shown for all logged in users!
1029 $this->gr_list = '0,-2';
1030 $gr_array = $this->fe_user->groupData['uid'];
1031 } else {
1032 $this->loginUser = FALSE;
1033 // group -1 is not an existing group, but denotes a 'default' group when not logged in. This is used to let elements be hidden, when a user is logged in!
1034 $this->gr_list = '0,-1';
1035 if ($this->loginAllowedInBranch) {
1036 // For cases where logins are not banned from a branch usergroups can be set based on IP masks so we should add the usergroups uids.
1037 $gr_array = $this->fe_user->groupData['uid'];
1038 } else {
1039 // Set to blank since we will NOT risk any groups being set when no logins are allowed!
1040 $gr_array = array();
1041 }
1042 }
1043 // Clean up.
1044 // Make unique...
1045 $gr_array = array_unique($gr_array);
1046 // sort
1047 sort($gr_array);
1048 if (count($gr_array) && !$this->loginAllowedInBranch_mode) {
1049 $this->gr_list .= ',' . implode(',', $gr_array);
1050 }
1051 if ($this->fe_user->writeDevLog) {
1052 GeneralUtility::devLog('Valid usergroups for TSFE: ' . $this->gr_list, 'TYPO3\\CMS\\Frontend\\Controller\\TypoScriptFrontendController');
1053 }
1054 }
1055
1056 /**
1057 * Checking if a user is logged in or a group constellation different from "0,-1"
1058 *
1059 * @return bool TRUE if either a login user is found (array fe_user->user) OR if the gr_list is set to something else than '0,-1' (could be done even without a user being logged in!)
1060 */
1061 public function isUserOrGroupSet() {
1062 return is_array($this->fe_user->user) || $this->gr_list !== '0,-1';
1063 }
1064
1065 /**
1066 * Provides ways to bypass the '?id=[xxx]&type=[xx]' format, using either PATH_INFO or virtual HTML-documents (using Apache mod_rewrite)
1067 *
1068 * Two options:
1069 * 1) Use PATH_INFO (also Apache) to extract id and type from that var. Does not require any special modules compiled with apache. (less typical)
1070 * 2) Using hook which enables features like those provided from "realurl" extension (AKA "Speaking URLs")
1071 *
1072 * @return void
1073 */
1074 public function checkAlternativeIdMethods() {
1075 $this->siteScript = GeneralUtility::getIndpEnv('TYPO3_SITE_SCRIPT');
1076 // Call post processing function for custom URL methods.
1077 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkAlternativeIdMethods-PostProc'])) {
1078 $_params = array('pObj' => &$this);
1079 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkAlternativeIdMethods-PostProc'] as $_funcRef) {
1080 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1081 }
1082 }
1083 }
1084
1085 /**
1086 * Clears the preview-flags, sets sim_exec_time to current time.
1087 * Hidden pages must be hidden as default, $GLOBALS['SIM_EXEC_TIME'] is set to $GLOBALS['EXEC_TIME']
1088 * in bootstrap initializeGlobalTimeVariables(). Alter it by adding or subtracting seconds.
1089 *
1090 * @return void
1091 */
1092 public function clear_preview() {
1093 $this->showHiddenPage = FALSE;
1094 $this->showHiddenRecords = FALSE;
1095 $GLOBALS['SIM_EXEC_TIME'] = $GLOBALS['EXEC_TIME'];
1096 $GLOBALS['SIM_ACCESS_TIME'] = $GLOBALS['ACCESS_TIME'];
1097 $this->fePreview = 0;
1098 }
1099
1100 /**
1101 * Checks if a backend user is logged in
1102 *
1103 * @return bool whether a backend user is logged in
1104 */
1105 public function isBackendUserLoggedIn() {
1106 return (bool)$this->beUserLogin;
1107 }
1108
1109 /**
1110 * Creates the backend user object and returns it.
1111 *
1112 * @return \TYPO3\CMS\Backend\FrontendBackendUserAuthentication the backend user object
1113 */
1114 public function initializeBackendUser() {
1115 // PRE BE_USER HOOK
1116 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/index_ts.php']['preBeUser'])) {
1117 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/index_ts.php']['preBeUser'] as $_funcRef) {
1118 $_params = array();
1119 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1120 }
1121 }
1122 /** @var $BE_USER \TYPO3\CMS\Backend\FrontendBackendUserAuthentication */
1123 $BE_USER = NULL;
1124 // If the backend cookie is set,
1125 // we proceed and check if a backend user is logged in.
1126 if ($_COOKIE[\TYPO3\CMS\Core\Authentication\BackendUserAuthentication::getCookieName()]) {
1127 $GLOBALS['TYPO3_MISC']['microtime_BE_USER_start'] = microtime(TRUE);
1128 $GLOBALS['TT']->push('Back End user initialized', '');
1129 // TODO: validate the comment below: is this necessary? if so,
1130 // formfield_status should be set to "" in \TYPO3\CMS\Backend\FrontendBackendUserAuthentication
1131 // which is a subclass of \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1132 // ----
1133 // the value this->formfield_status is set to empty in order to
1134 // disable login-attempts to the backend account through this script
1135 // New backend user object
1136 $BE_USER = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\FrontendBackendUserAuthentication');
1137 $BE_USER->OS = TYPO3_OS;
1138 $BE_USER->lockIP = $this->TYPO3_CONF_VARS['BE']['lockIP'];
1139 // Object is initialized
1140 $BE_USER->start();
1141 $BE_USER->unpack_uc('');
1142 if (!empty($BE_USER->user['uid'])) {
1143 $BE_USER->fetchGroupData();
1144 $this->beUserLogin = TRUE;
1145 }
1146 // Unset the user initialization.
1147 if (!$BE_USER->checkLockToIP() || !$BE_USER->checkBackendAccessSettingsFromInitPhp() || empty($BE_USER->user['uid'])) {
1148 $BE_USER = NULL;
1149 $this->beUserLogin = FALSE;
1150 $_SESSION['TYPO3-TT-start'] = FALSE;
1151 }
1152 $GLOBALS['TT']->pull();
1153 $GLOBALS['TYPO3_MISC']['microtime_BE_USER_end'] = microtime(TRUE);
1154 }
1155 // POST BE_USER HOOK
1156 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/index_ts.php']['postBeUser'])) {
1157 $_params = array(
1158 'BE_USER' => &$BE_USER
1159 );
1160 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/index_ts.php']['postBeUser'] as $_funcRef) {
1161 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1162 }
1163 }
1164 return $BE_USER;
1165 }
1166
1167 /**
1168 * Determines the id and evaluates any preview settings
1169 * Basically this function is about determining whether a backend user is logged in, if he has read access to the page and if he's previewing the page. That all determines which id to show and how to initialize the id.
1170 *
1171 * @return void
1172 */
1173 public function determineId() {
1174 // Call pre processing function for id determination
1175 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PreProcessing'])) {
1176 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PreProcessing'] as $functionReference) {
1177 $parameters = array('parentObject' => $this);
1178 GeneralUtility::callUserFunction($functionReference, $parameters, $this);
1179 }
1180 }
1181 // Getting ARG-v values if some
1182 $this->setIDfromArgV();
1183 // If there is a Backend login we are going to check for any preview settings:
1184 $GLOBALS['TT']->push('beUserLogin', '');
1185 $originalFrontendUser = NULL;
1186 if ($this->beUserLogin || $this->doWorkspacePreview()) {
1187 // Backend user preview features:
1188 if ($this->beUserLogin && $GLOBALS['BE_USER']->adminPanel instanceof \TYPO3\CMS\Frontend\View\AdminPanelView) {
1189 $this->fePreview = (bool)$GLOBALS['BE_USER']->adminPanel->extGetFeAdminValue('preview');
1190 // If admin panel preview is enabled...
1191 if ($this->fePreview) {
1192 if ($this->fe_user->user) {
1193 $originalFrontendUser = $this->fe_user->user;
1194 }
1195 $this->showHiddenPage = (bool)$GLOBALS['BE_USER']->adminPanel->extGetFeAdminValue('preview', 'showHiddenPages');
1196 $this->showHiddenRecords = (bool)$GLOBALS['BE_USER']->adminPanel->extGetFeAdminValue('preview', 'showHiddenRecords');
1197 // Simulate date
1198 $simTime = $GLOBALS['BE_USER']->adminPanel->extGetFeAdminValue('preview', 'simulateDate');
1199 if ($simTime) {
1200 $GLOBALS['SIM_EXEC_TIME'] = $simTime;
1201 $GLOBALS['SIM_ACCESS_TIME'] = $simTime - $simTime % 60;
1202 }
1203 // simulate user
1204 $simUserGroup = $GLOBALS['BE_USER']->adminPanel->extGetFeAdminValue('preview', 'simulateUserGroup');
1205 $this->simUserGroup = $simUserGroup;
1206 if ($simUserGroup) {
1207 if ($this->fe_user->user) {
1208 $this->fe_user->user[$this->fe_user->usergroup_column] = $simUserGroup;
1209 } else {
1210 $this->fe_user->user = array(
1211 $this->fe_user->usergroup_column => $simUserGroup
1212 );
1213 }
1214 }
1215 if (!$simUserGroup && !$simTime && !$this->showHiddenPage && !$this->showHiddenRecords) {
1216 $this->fePreview = 0;
1217 }
1218 }
1219 }
1220 if ($this->id) {
1221 if ($this->determineIdIsHiddenPage()) {
1222 // The preview flag is set only if the current page turns out to actually be hidden!
1223 $this->fePreview = 1;
1224 $this->showHiddenPage = TRUE;
1225 }
1226 // For Live workspace: Check root line for proper connection to tree root (done because of possible preview of page / branch versions)
1227 if (!$this->fePreview && $this->whichWorkspace() === 0) {
1228 // Initialize the page-select functions to check rootline:
1229 $temp_sys_page = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\PageRepository');
1230 $temp_sys_page->init($this->showHiddenPage);
1231 // If root line contained NO records and ->error_getRootLine_failPid tells us that it was because of a pid=-1 (indicating a "version" record)...:
1232 if (!count($temp_sys_page->getRootLine($this->id, $this->MP)) && $temp_sys_page->error_getRootLine_failPid == -1) {
1233 // Setting versioningPreview flag and try again:
1234 $temp_sys_page->versioningPreview = TRUE;
1235 if (count($temp_sys_page->getRootLine($this->id, $this->MP))) {
1236 // Finally, we got a root line (meaning that it WAS due to versioning preview of a page somewhere) and we set the fePreview flag which in itself will allow sys_page class to display previews of versionized records.
1237 $this->fePreview = 1;
1238 }
1239 }
1240 }
1241 }
1242 // The preview flag will be set if a backend user is in an offline workspace
1243 if (($GLOBALS['BE_USER']->user['workspace_preview'] || GeneralUtility::_GP('ADMCMD_view') || $this->doWorkspacePreview()) && ($this->whichWorkspace() === -1 || $this->whichWorkspace() > 0)) {
1244 // Will show special preview message.
1245 $this->fePreview = 2;
1246 }
1247 // If the front-end is showing a preview, caching MUST be disabled.
1248 if ($this->fePreview) {
1249 $this->disableCache();
1250 }
1251 }
1252 $GLOBALS['TT']->pull();
1253 // Now, get the id, validate access etc:
1254 $this->fetch_the_id();
1255 // Check if backend user has read access to this page. If not, recalculate the id.
1256 if ($this->beUserLogin && $this->fePreview) {
1257 if (!$GLOBALS['BE_USER']->doesUserHaveAccess($this->page, 1)) {
1258 // Resetting
1259 $this->clear_preview();
1260 $this->fe_user->user = $originalFrontendUser;
1261 // Fetching the id again, now with the preview settings reset.
1262 $this->fetch_the_id();
1263 }
1264 }
1265 // Checks if user logins are blocked for a certain branch and if so, will unset user login and re-fetch ID.
1266 $this->loginAllowedInBranch = $this->checkIfLoginAllowedInBranch();
1267 // Logins are not allowed:
1268 if (!$this->loginAllowedInBranch) {
1269 // Only if there is a login will we run this...
1270 if ($this->isUserOrGroupSet()) {
1271 if ($this->loginAllowedInBranch_mode == 'all') {
1272 // Clear out user and group:
1273 unset($this->fe_user->user);
1274 $this->gr_list = '0,-1';
1275 } else {
1276 $this->gr_list = '0,-2';
1277 }
1278 // Fetching the id again, now with the preview settings reset.
1279 $this->fetch_the_id();
1280 }
1281 }
1282 // Final cleaning.
1283 // Make sure it's an integer
1284 $this->id = ($this->contentPid = (int)$this->id);
1285 // Make sure it's an integer
1286 $this->type = (int)$this->type;
1287 // Call post processing function for id determination:
1288 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PostProc'])) {
1289 $_params = array('pObj' => &$this);
1290 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PostProc'] as $_funcRef) {
1291 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1292 }
1293 }
1294 }
1295
1296 /**
1297 * Checks if the page is hidden in the active workspace.
1298 * If it is hidden, preview flags will be set.
1299 *
1300 * @return bool
1301 */
1302 protected function determineIdIsHiddenPage() {
1303 $field = \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($this->id) ? 'uid' : 'alias';
1304 $pageSelectCondition = $field . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($this->id, 'pages');
1305 $page = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('uid,hidden,starttime,endtime', 'pages', $pageSelectCondition . ' AND pid>=0 AND deleted=0');
1306 $workspace = $this->whichWorkspace();
1307 if ($workspace !== 0 && $workspace !== FALSE) {
1308 // Fetch overlay of page if in workspace and check if it is hidden
1309 $pageSelectObject = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\PageRepository');
1310 $pageSelectObject->versioningPreview = TRUE;
1311 $pageSelectObject->init(FALSE);
1312 $targetPage = $pageSelectObject->getWorkspaceVersionOfRecord($this->whichWorkspace(), 'pages', $page['uid']);
1313 $result = $targetPage === -1 || $targetPage === -2;
1314 } else {
1315 $result = is_array($page) && ($page['hidden'] || $page['starttime'] > $GLOBALS['SIM_EXEC_TIME'] || $page['endtime'] != 0 && $page['endtime'] <= $GLOBALS['SIM_EXEC_TIME']);
1316 }
1317 return $result;
1318 }
1319
1320 /**
1321 * Get The Page ID
1322 * This gets the id of the page, checks if the page is in the domain and if the page is accessible
1323 * Sets variables such as $this->sys_page, $this->loginUser, $this->gr_list, $this->id, $this->type, $this->domainStartPage
1324 *
1325 * @throws \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException
1326 * @return void
1327 * @access private
1328 */
1329 public function fetch_the_id() {
1330 $GLOBALS['TT']->push('fetch_the_id initialize/', '');
1331 // Initialize the page-select functions.
1332 $this->sys_page = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\PageRepository');
1333 $this->sys_page->versioningPreview = $this->fePreview === 2 || (int)$this->workspacePreview || (bool)GeneralUtility::_GP('ADMCMD_view');
1334 $this->sys_page->versioningWorkspaceId = $this->whichWorkspace();
1335 $this->sys_page->init($this->showHiddenPage);
1336 // Set the valid usergroups for FE
1337 $this->initUserGroups();
1338 // Sets sys_page where-clause
1339 $this->setSysPageWhereClause();
1340 // Splitting $this->id by a period (.).
1341 // First part is 'id' and second part (if exists) will overrule the &type param
1342 $idParts = explode('.', $this->id, 2);
1343 $this->id = $idParts[0];
1344 if (isset($idParts[1])) {
1345 $this->type = $idParts[1];
1346 }
1347
1348 // If $this->id is a string, it's an alias
1349 $this->checkAndSetAlias();
1350 // The id and type is set to the integer-value - just to be sure...
1351 $this->id = (int)$this->id;
1352 $this->type = (int)$this->type;
1353 $GLOBALS['TT']->pull();
1354 // We find the first page belonging to the current domain
1355 $GLOBALS['TT']->push('fetch_the_id domain/', '');
1356 // The page_id of the current domain
1357 $this->domainStartPage = $this->findDomainRecord($this->TYPO3_CONF_VARS['SYS']['recursiveDomainSearch']);
1358 if (!$this->id) {
1359 if ($this->domainStartPage) {
1360 // If the id was not previously set, set it to the id of the domain.
1361 $this->id = $this->domainStartPage;
1362 } else {
1363 // Find the first 'visible' page in that domain
1364 $theFirstPage = $this->sys_page->getFirstWebPage($this->id);
1365 if ($theFirstPage) {
1366 $this->id = $theFirstPage['uid'];
1367 } else {
1368 $message = 'No pages are found on the rootlevel!';
1369 if ($this->checkPageUnavailableHandler()) {
1370 $this->pageUnavailableAndExit($message);
1371 } else {
1372 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1373 throw new \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException($message, 1301648975);
1374 }
1375 }
1376 }
1377 }
1378 $GLOBALS['TT']->pull();
1379 $GLOBALS['TT']->push('fetch_the_id rootLine/', '');
1380 // We store the originally requested id
1381 $requestedId = $this->id;
1382 $this->getPageAndRootlineWithDomain($this->domainStartPage);
1383 $GLOBALS['TT']->pull();
1384 if ($this->pageNotFound && $this->TYPO3_CONF_VARS['FE']['pageNotFound_handling']) {
1385 $pNotFoundMsg = array(
1386 1 => 'ID was not an accessible page',
1387 2 => 'Subsection was found and not accessible',
1388 3 => 'ID was outside the domain',
1389 4 => 'The requested page alias does not exist'
1390 );
1391 $this->pageNotFoundAndExit($pNotFoundMsg[$this->pageNotFound]);
1392 }
1393 if ($this->page['url_scheme'] > 0) {
1394 $newUrl = '';
1395 $requestUrlScheme = parse_url(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'), PHP_URL_SCHEME);
1396 if ((int)$this->page['url_scheme'] === HttpUtility::SCHEME_HTTP && $requestUrlScheme == 'https') {
1397 $newUrl = 'http://' . substr(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'), 8);
1398 } elseif ((int)$this->page['url_scheme'] === HttpUtility::SCHEME_HTTPS && $requestUrlScheme == 'http') {
1399 $newUrl = 'https://' . substr(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'), 7);
1400 }
1401 if ($newUrl !== '') {
1402 if ($_SERVER['REQUEST_METHOD'] === 'POST') {
1403 $headerCode = HttpUtility::HTTP_STATUS_303;
1404 } else {
1405 $headerCode = HttpUtility::HTTP_STATUS_301;
1406 }
1407 HttpUtility::redirect($newUrl, $headerCode);
1408 }
1409 }
1410 // Set no_cache if set
1411 if ($this->page['no_cache']) {
1412 $this->set_no_cache('no_cache is set in page properties');
1413 }
1414 // Init SYS_LASTCHANGED
1415 $this->register['SYS_LASTCHANGED'] = (int)$this->page['tstamp'];
1416 if ($this->register['SYS_LASTCHANGED'] < (int)$this->page['SYS_LASTCHANGED']) {
1417 $this->register['SYS_LASTCHANGED'] = (int)$this->page['SYS_LASTCHANGED'];
1418 }
1419 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['fetchPageId-PostProcessing'])) {
1420 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['fetchPageId-PostProcessing'] as $functionReference) {
1421 $parameters = array('parentObject' => $this);
1422 GeneralUtility::callUserFunction($functionReference, $parameters, $this);
1423 }
1424 }
1425 }
1426
1427 /**
1428 * Gets the page and rootline arrays based on the id, $this->id
1429 *
1430 * If the id does not correspond to a proper page, the 'previous' valid page in the rootline is found
1431 * If the page is a shortcut (doktype=4), the ->id is loaded with that id
1432 *
1433 * Whether or not the ->id is changed to the shortcut id or the previous id in rootline (eg if a page is hidden), the ->page-array and ->rootline is found and must also be valid.
1434 *
1435 * Sets or manipulates internal variables such as: $this->id, $this->page, $this->rootLine, $this->MP, $this->pageNotFound
1436 *
1437 * @throws \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException
1438 * @throws PageNotFoundException
1439 * @return void
1440 * @access private
1441 */
1442 public function getPageAndRootline() {
1443 $this->page = $this->sys_page->getPage($this->id);
1444 if (!count($this->page)) {
1445 // If no page, we try to find the page before in the rootLine.
1446 // Page is 'not found' in case the id itself was not an accessible page. code 1
1447 $this->pageNotFound = 1;
1448 $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
1449 if (count($this->rootLine)) {
1450 $c = count($this->rootLine) - 1;
1451 while ($c > 0) {
1452 // Add to page access failure history:
1453 $this->pageAccessFailureHistory['direct_access'][] = $this->rootLine[$c];
1454 // Decrease to next page in rootline and check the access to that, if OK, set as page record and ID value.
1455 $c--;
1456 $this->id = $this->rootLine[$c]['uid'];
1457 $this->page = $this->sys_page->getPage($this->id);
1458 if (count($this->page)) {
1459 break;
1460 }
1461 }
1462 }
1463 // If still no page...
1464 if (!count($this->page)) {
1465 $message = 'The requested page does not exist!';
1466 if ($this->TYPO3_CONF_VARS['FE']['pageNotFound_handling']) {
1467 $this->pageNotFoundAndExit($message);
1468 } else {
1469 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1470 throw new PageNotFoundException($message, 1301648780);
1471 }
1472 }
1473 }
1474 // Spacer is not accessible in frontend
1475 if ($this->page['doktype'] == PageRepository::DOKTYPE_SPACER) {
1476 $message = 'The requested page does not exist!';
1477 if ($this->TYPO3_CONF_VARS['FE']['pageNotFound_handling']) {
1478 $this->pageNotFoundAndExit($message);
1479 } else {
1480 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1481 throw new PageNotFoundException($message, 1301648781);
1482 }
1483 }
1484 // Is the ID a link to another page??
1485 if ($this->page['doktype'] == PageRepository::DOKTYPE_SHORTCUT) {
1486 // We need to clear MP if the page is a shortcut. Reason is if the short cut goes to another page, then we LEAVE the rootline which the MP expects.
1487 $this->MP = '';
1488 // saving the page so that we can check later - when we know
1489 // about languages - whether we took the correct shortcut or
1490 // whether a translation of the page overwrites the shortcut
1491 // target and we need to follow the new target
1492 $this->originalShortcutPage = $this->page;
1493 $this->page = $this->getPageShortcut($this->page['shortcut'], $this->page['shortcut_mode'], $this->page['uid']);
1494 $this->id = $this->page['uid'];
1495 }
1496 // If the page is a mountpoint which should be overlaid with the contents of the mounted page,
1497 // it must never be accessible directly, but only in the mountpoint context. Therefore we change
1498 // the current ID and the user is redirected by checkPageForMountpointRedirect().
1499 if ($this->page['doktype'] === PageRepository::DOKTYPE_MOUNTPOINT && $this->page['mount_pid_ol']) {
1500 $this->originalMountPointPage = $this->page;
1501 $this->page = $this->sys_page->getPage($this->page['mount_pid']);
1502 if (empty($this->page)) {
1503 $message = 'This page (ID ' . $this->originalMountPointPage['uid'] . ') is of type "Mount point" and '
1504 . 'mounts a page which is not accessible (ID ' . $this->originalMountPointPage['mount_pid'] . ').';
1505 throw new PageNotFoundException($message, 1402043263);
1506 }
1507 $this->MP = $this->page['uid'] . '-' . $this->originalMountPointPage['uid'];
1508 $this->id = $this->page['uid'];
1509 }
1510 // Gets the rootLine
1511 $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
1512 // If not rootline we're off...
1513 if (!count($this->rootLine)) {
1514 $ws = $this->whichWorkspace();
1515 if ($this->sys_page->error_getRootLine_failPid == -1 && $ws) {
1516 $this->sys_page->versioningPreview = TRUE;
1517 $this->versioningWorkspaceId = $ws;
1518 $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
1519 }
1520 if (!count($this->rootLine)) {
1521 $message = 'The requested page didn\'t have a proper connection to the tree-root!';
1522 if ($this->checkPageUnavailableHandler()) {
1523 $this->pageUnavailableAndExit($message);
1524 } else {
1525 $rootline = '(' . $this->sys_page->error_getRootLine . ')';
1526 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1527 throw new \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException($message . '<br /><br />' . $rootline, 1301648167);
1528 }
1529 }
1530 $this->fePreview = 1;
1531 }
1532 // Checking for include section regarding the hidden/starttime/endtime/fe_user (that is access control of a whole subbranch!)
1533 if ($this->checkRootlineForIncludeSection()) {
1534 if (!count($this->rootLine)) {
1535 $message = 'The requested page was not accessible!';
1536 if ($this->checkPageUnavailableHandler()) {
1537 $this->pageUnavailableAndExit($message);
1538 } else {
1539 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1540 throw new \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException($message, 1301648234);
1541 }
1542 } else {
1543 $el = reset($this->rootLine);
1544 $this->id = $el['uid'];
1545 $this->page = $this->sys_page->getPage($this->id);
1546 $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
1547 }
1548 }
1549 }
1550
1551 /**
1552 * Get page shortcut; Finds the records pointed to by input value $SC (the shortcut value)
1553 *
1554 * @param int $SC The value of the "shortcut" field from the pages record
1555 * @param int $mode The shortcut mode: 1 will select first subpage, 2 a random subpage, 3 the parent page; default is the page pointed to by $SC
1556 * @param int $thisUid The current page UID of the page which is a shortcut
1557 * @param int $itera Safety feature which makes sure that the function is calling itself recursively max 20 times (since this function can find shortcuts to other shortcuts to other shortcuts...)
1558 * @param array $pageLog An array filled with previous page uids tested by the function - new page uids are evaluated against this to avoid going in circles.
1559 * @param bool $disableGroupCheck If true, the group check is disabled when fetching the target page (needed e.g. for menu generation)
1560 * @throws \RuntimeException
1561 * @throws PageNotFoundException
1562 * @return mixed Returns the page record of the page that the shortcut pointed to.
1563 * @access private
1564 * @see getPageAndRootline()
1565 */
1566 public function getPageShortcut($SC, $mode, $thisUid, $itera = 20, $pageLog = array(), $disableGroupCheck = FALSE) {
1567 $idArray = GeneralUtility::intExplode(',', $SC);
1568 // Find $page record depending on shortcut mode:
1569 switch ($mode) {
1570 case PageRepository::SHORTCUT_MODE_FIRST_SUBPAGE:
1571
1572 case PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE:
1573 $pageArray = $this->sys_page->getMenu($idArray[0] ? $idArray[0] : $thisUid, '*', 'sorting', 'AND pages.doktype<199 AND pages.doktype!=' . PageRepository::DOKTYPE_BE_USER_SECTION);
1574 $pO = 0;
1575 if ($mode == PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE && count($pageArray)) {
1576 $randval = (int)rand(0, count($pageArray) - 1);
1577 $pO = $randval;
1578 }
1579 $c = 0;
1580 foreach ($pageArray as $pV) {
1581 if ($c == $pO) {
1582 $page = $pV;
1583 break;
1584 }
1585 $c++;
1586 }
1587 if (count($page) == 0) {
1588 $message = 'This page (ID ' . $thisUid . ') is of type "Shortcut" and configured to redirect to a subpage. ' . 'However, this page has no accessible subpages.';
1589 throw new PageNotFoundException($message, 1301648328);
1590 }
1591 break;
1592 case PageRepository::SHORTCUT_MODE_PARENT_PAGE:
1593 $parent = $this->sys_page->getPage($thisUid, $disableGroupCheck);
1594 $page = $this->sys_page->getPage($parent['pid'], $disableGroupCheck);
1595 if (count($page) == 0) {
1596 $message = 'This page (ID ' . $thisUid . ') is of type "Shortcut" and configured to redirect to its parent page. ' . 'However, the parent page is not accessible.';
1597 throw new PageNotFoundException($message, 1301648358);
1598 }
1599 break;
1600 default:
1601 $page = $this->sys_page->getPage($idArray[0], $disableGroupCheck);
1602 if (count($page) == 0) {
1603 $message = 'This page (ID ' . $thisUid . ') is of type "Shortcut" and configured to redirect to a page, which is not accessible (ID ' . $idArray[0] . ').';
1604 throw new PageNotFoundException($message, 1301648404);
1605 }
1606 }
1607 // Check if short cut page was a shortcut itself, if so look up recursively:
1608 if ($page['doktype'] == PageRepository::DOKTYPE_SHORTCUT) {
1609 if (!in_array($page['uid'], $pageLog) && $itera > 0) {
1610 $pageLog[] = $page['uid'];
1611 $page = $this->getPageShortcut($page['shortcut'], $page['shortcut_mode'], $page['uid'], $itera - 1, $pageLog, $disableGroupCheck);
1612 } else {
1613 $pageLog[] = $page['uid'];
1614 $message = 'Page shortcuts were looping in uids ' . implode(',', $pageLog) . '...!';
1615 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1616 throw new \RuntimeException($message, 1294587212);
1617 }
1618 }
1619 // Return resulting page:
1620 return $page;
1621 }
1622
1623 /**
1624 * Checks the current rootline for defined sections.
1625 *
1626 * @return bool
1627 * @access private
1628 */
1629 public function checkRootlineForIncludeSection() {
1630 $c = count($this->rootLine);
1631 $removeTheRestFlag = 0;
1632 for ($a = 0; $a < $c; $a++) {
1633 if (!$this->checkPagerecordForIncludeSection($this->rootLine[$a])) {
1634 // Add to page access failure history:
1635 $this->pageAccessFailureHistory['sub_section'][] = $this->rootLine[$a];
1636 $removeTheRestFlag = 1;
1637 }
1638 if ($this->rootLine[$a]['doktype'] == PageRepository::DOKTYPE_BE_USER_SECTION) {
1639 // If there is a backend user logged in, check if he has read access to the page:
1640 if ($this->beUserLogin) {
1641 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', 'pages', 'uid=' . (int)$this->id . ' AND ' . $GLOBALS['BE_USER']->getPagePermsClause(1));
1642 // versionOL()?
1643 list($isPage) = $GLOBALS['TYPO3_DB']->sql_fetch_row($res);
1644 if (!$isPage) {
1645 // If there was no page selected, the user apparently did not have read access to the current PAGE (not position in rootline) and we set the remove-flag...
1646 $removeTheRestFlag = 1;
1647 }
1648 } else {
1649 // Dont go here, if there is no backend user logged in.
1650 $removeTheRestFlag = 1;
1651 }
1652 }
1653 if ($removeTheRestFlag) {
1654 // Page is 'not found' in case a subsection was found and not accessible, code 2
1655 $this->pageNotFound = 2;
1656 unset($this->rootLine[$a]);
1657 }
1658 }
1659 return $removeTheRestFlag;
1660 }
1661
1662 /**
1663 * Checks page record for enableFields
1664 * Returns TRUE if enableFields does not disable the page record.
1665 * Takes notice of the ->showHiddenPage flag and uses SIM_ACCESS_TIME for start/endtime evaluation
1666 *
1667 * @param array $row The page record to evaluate (needs fields: hidden, starttime, endtime, fe_group)
1668 * @param bool $bypassGroupCheck Bypass group-check
1669 * @return bool TRUE, if record is viewable.
1670 * @see TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectRenderer::getTreeList(), checkPagerecordForIncludeSection()
1671 */
1672 public function checkEnableFields($row, $bypassGroupCheck = FALSE) {
1673 if (isset($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_checkEnableFields']) && is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_checkEnableFields'])) {
1674 $_params = array('pObj' => $this, 'row' => &$row, 'bypassGroupCheck' => &$bypassGroupCheck);
1675 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_checkEnableFields'] as $_funcRef) {
1676 // Call hooks: If one returns FALSE, method execution is aborted with result "This record is not available"
1677 $return = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1678 if ($return === FALSE) {
1679 return FALSE;
1680 }
1681 }
1682 }
1683 if ((!$row['hidden'] || $this->showHiddenPage) && $row['starttime'] <= $GLOBALS['SIM_ACCESS_TIME'] && ($row['endtime'] == 0 || $row['endtime'] > $GLOBALS['SIM_ACCESS_TIME']) && ($bypassGroupCheck || $this->checkPageGroupAccess($row))) {
1684 return TRUE;
1685 }
1686 }
1687
1688 /**
1689 * Check group access against a page record
1690 *
1691 * @param array $row The page record to evaluate (needs field: fe_group)
1692 * @param mixed $groupList List of group id's (comma list or array). Default is $this->gr_list
1693 * @return bool TRUE, if group access is granted.
1694 * @access private
1695 */
1696 public function checkPageGroupAccess($row, $groupList = NULL) {
1697 if (is_null($groupList)) {
1698 $groupList = $this->gr_list;
1699 }
1700 if (!is_array($groupList)) {
1701 $groupList = explode(',', $groupList);
1702 }
1703 $pageGroupList = explode(',', $row['fe_group'] ?: 0);
1704 return count(array_intersect($groupList, $pageGroupList)) > 0;
1705 }
1706
1707 /**
1708 * Checks page record for include section
1709 *
1710 * @param array $row The page record to evaluate (needs fields: extendToSubpages + hidden, starttime, endtime, fe_group)
1711 * @return bool Returns TRUE if either extendToSubpages is not checked or if the enableFields does not disable the page record.
1712 * @access private
1713 * @see checkEnableFields(), TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectRenderer::getTreeList(), checkRootlineForIncludeSection()
1714 */
1715 public function checkPagerecordForIncludeSection($row) {
1716 return !$row['extendToSubpages'] || $this->checkEnableFields($row) ? 1 : 0;
1717 }
1718
1719 /**
1720 * Checks if logins are allowed in the current branch of the page tree. Traverses the full root line and returns TRUE if logins are OK, otherwise FALSE (and then the login user must be unset!)
1721 *
1722 * @return bool returns TRUE if logins are OK, otherwise FALSE (and then the login user must be unset!)
1723 */
1724 public function checkIfLoginAllowedInBranch() {
1725 // Initialize:
1726 $c = count($this->rootLine);
1727 $disable = FALSE;
1728 // Traverse root line from root and outwards:
1729 for ($a = 0; $a < $c; $a++) {
1730 // If a value is set for login state:
1731 if ($this->rootLine[$a]['fe_login_mode'] > 0) {
1732 // Determine state from value:
1733 if ((int)$this->rootLine[$a]['fe_login_mode'] === 1) {
1734 $disable = TRUE;
1735 $this->loginAllowedInBranch_mode = 'all';
1736 } elseif ((int)$this->rootLine[$a]['fe_login_mode'] === 3) {
1737 $disable = TRUE;
1738 $this->loginAllowedInBranch_mode = 'groups';
1739 } else {
1740 $disable = FALSE;
1741 }
1742 }
1743 }
1744 return !$disable;
1745 }
1746
1747 /**
1748 * Analysing $this->pageAccessFailureHistory into a summary array telling which features disabled display and on which pages and conditions. That data can be used inside a page-not-found handler
1749 *
1750 * @return array Summary of why page access was not allowed.
1751 */
1752 public function getPageAccessFailureReasons() {
1753 $output = array();
1754 $combinedRecords = array_merge(is_array($this->pageAccessFailureHistory['direct_access']) ? $this->pageAccessFailureHistory['direct_access'] : array(array('fe_group' => 0)), is_array($this->pageAccessFailureHistory['sub_section']) ? $this->pageAccessFailureHistory['sub_section'] : array());
1755 if (count($combinedRecords)) {
1756 foreach ($combinedRecords as $k => $pagerec) {
1757 // If $k=0 then it is the very first page the original ID was pointing at and that will get a full check of course
1758 // If $k>0 it is parent pages being tested. They are only significant for the access to the first page IF they had the extendToSubpages flag set, hence checked only then!
1759 if (!$k || $pagerec['extendToSubpages']) {
1760 if ($pagerec['hidden']) {
1761 $output['hidden'][$pagerec['uid']] = TRUE;
1762 }
1763 if ($pagerec['starttime'] > $GLOBALS['SIM_ACCESS_TIME']) {
1764 $output['starttime'][$pagerec['uid']] = $pagerec['starttime'];
1765 }
1766 if ($pagerec['endtime'] != 0 && $pagerec['endtime'] <= $GLOBALS['SIM_ACCESS_TIME']) {
1767 $output['endtime'][$pagerec['uid']] = $pagerec['endtime'];
1768 }
1769 if (!$this->checkPageGroupAccess($pagerec)) {
1770 $output['fe_group'][$pagerec['uid']] = $pagerec['fe_group'];
1771 }
1772 }
1773 }
1774 }
1775 return $output;
1776 }
1777
1778 /**
1779 * This checks if there are ARGV-parameters in the QUERY_STRING and if so, those are used for the id
1780 * $this->id must be 'FALSE' in order for any processing to happen in here
1781 * If an id/alias value is extracted from the QUERY_STRING it is set in $this->id
1782 *
1783 * @return void
1784 * @access private
1785 */
1786 public function setIDfromArgV() {
1787 if (!$this->id) {
1788 list($theAlias) = explode('&', GeneralUtility::getIndpEnv('QUERY_STRING'));
1789 $theAlias = trim($theAlias);
1790 $this->id = $theAlias != '' && strpos($theAlias, '=') === FALSE ? $theAlias : 0;
1791 }
1792 }
1793
1794 /**
1795 * Gets ->page and ->rootline information based on ->id. ->id may change during this operation.
1796 * If not inside domain, then default to first page in domain.
1797 *
1798 * @param int $domainStartPage Page uid of the page where the found domain record is (pid of the domain record)
1799 * @return void
1800 * @access private
1801 */
1802 public function getPageAndRootlineWithDomain($domainStartPage) {
1803 $this->getPageAndRootline();
1804 // Checks if the $domain-startpage is in the rootLine. This is necessary so that references to page-id's from other domains are not possible.
1805 if ($domainStartPage && is_array($this->rootLine)) {
1806 $idFound = 0;
1807 foreach ($this->rootLine as $key => $val) {
1808 if ($val['uid'] == $domainStartPage) {
1809 $idFound = 1;
1810 break;
1811 }
1812 }
1813 if (!$idFound) {
1814 // Page is 'not found' in case the id was outside the domain, code 3
1815 $this->pageNotFound = 3;
1816 $this->id = $domainStartPage;
1817 // re-get the page and rootline if the id was not found.
1818 $this->getPageAndRootline();
1819 }
1820 }
1821 }
1822
1823 /**
1824 * Sets sys_page where-clause
1825 *
1826 * @return void
1827 * @access private
1828 */
1829 public function setSysPageWhereClause() {
1830 $this->sys_page->where_hid_del .= ' AND pages.doktype<200';
1831 $this->sys_page->where_groupAccess = $this->sys_page->getMultipleGroupsWhereClause('pages.fe_group', 'pages');
1832 }
1833
1834 /**
1835 * Looking up a domain record based on HTTP_HOST
1836 *
1837 * @param bool $recursive If set, it looks "recursively" meaning that a domain like "123.456.typo3.com" would find a domain record like "typo3.com" if "123.456.typo3.com" or "456.typo3.com" did not exist.
1838 * @return int Returns the page id of the page where the domain record was found.
1839 * @access private
1840 */
1841 public function findDomainRecord($recursive = FALSE) {
1842 if ($recursive) {
1843 $host = explode('.', GeneralUtility::getIndpEnv('HTTP_HOST'));
1844 while (count($host)) {
1845 $pageUid = $this->sys_page->getDomainStartPage(implode('.', $host), GeneralUtility::getIndpEnv('SCRIPT_NAME'), GeneralUtility::getIndpEnv('REQUEST_URI'));
1846 if ($pageUid) {
1847 return $pageUid;
1848 } else {
1849 array_shift($host);
1850 }
1851 }
1852 return $pageUid;
1853 } else {
1854 return $this->sys_page->getDomainStartPage(GeneralUtility::getIndpEnv('HTTP_HOST'), GeneralUtility::getIndpEnv('SCRIPT_NAME'), GeneralUtility::getIndpEnv('REQUEST_URI'));
1855 }
1856 }
1857
1858 /**
1859 * Page unavailable handler for use in frontend plugins from extensions.
1860 *
1861 * @param string $reason Reason text
1862 * @param string $header HTTP header to send
1863 * @return void Function exits.
1864 */
1865 public function pageUnavailableAndExit($reason = '', $header = '') {
1866 $header = $header ?: $this->TYPO3_CONF_VARS['FE']['pageUnavailable_handling_statheader'];
1867 $this->pageUnavailableHandler($this->TYPO3_CONF_VARS['FE']['pageUnavailable_handling'], $header, $reason);
1868 die;
1869 }
1870
1871 /**
1872 * Page-not-found handler for use in frontend plugins from extensions.
1873 *
1874 * @param string $reason Reason text
1875 * @param string $header HTTP header to send
1876 * @return void Function exits.
1877 */
1878 public function pageNotFoundAndExit($reason = '', $header = '') {
1879 $header = $header ?: $this->TYPO3_CONF_VARS['FE']['pageNotFound_handling_statheader'];
1880 $this->pageNotFoundHandler($this->TYPO3_CONF_VARS['FE']['pageNotFound_handling'], $header, $reason);
1881 die;
1882 }
1883
1884 /**
1885 * Checks whether the pageUnavailableHandler should be used. To be used, pageUnavailable_handling must be set
1886 * and devIPMask must not match the current visitor's IP address.
1887 *
1888 * @return bool TRUE/FALSE whether the pageUnavailable_handler should be used.
1889 */
1890 public function checkPageUnavailableHandler() {
1891 if (
1892 $this->TYPO3_CONF_VARS['FE']['pageUnavailable_handling']
1893 && !GeneralUtility::cmpIP(
1894 GeneralUtility::getIndpEnv('REMOTE_ADDR'),
1895 $this->TYPO3_CONF_VARS['SYS']['devIPmask']
1896 )
1897 ) {
1898 $checkPageUnavailableHandler = TRUE;
1899 } else {
1900 $checkPageUnavailableHandler = FALSE;
1901 }
1902 return $checkPageUnavailableHandler;
1903 }
1904
1905 /**
1906 * Page unavailable handler. Acts a wrapper for the pageErrorHandler method.
1907 *
1908 * @param mixed $code Which type of handling; If a true PHP-boolean or TRUE then a \TYPO3\CMS\Core\Messaging\ErrorpageMessage is outputted. If integer an error message with that number is shown. Otherwise the $code value is expected to be a "Location:" header value.
1909 * @param string $header If set, this is passed directly to the PHP function, header()
1910 * @param string $reason If set, error messages will also mention this as the reason for the page-not-found.
1911 * @return void (The function exits!)
1912 */
1913 public function pageUnavailableHandler($code, $header, $reason) {
1914 $this->pageErrorHandler($code, $header, $reason);
1915 }
1916
1917 /**
1918 * Page not found handler. Acts a wrapper for the pageErrorHandler method.
1919 *
1920 * @param mixed $code Which type of handling; If a true PHP-boolean or TRUE then a \TYPO3\CMS\Core\Messaging\ErrorpageMessage is outputted. If integer an error message with that number is shown. Otherwise the $code value is expected to be a "Location:" header value.
1921 * @param string $header If set, this is passed directly to the PHP function, header()
1922 * @param string $reason If set, error messages will also mention this as the reason for the page-not-found.
1923 * @return void (The function exits!)
1924 */
1925 public function pageNotFoundHandler($code, $header = '', $reason = '') {
1926 $this->pageErrorHandler($code, $header, $reason);
1927 }
1928
1929 /**
1930 * Generic error page handler.
1931 * Exits.
1932 *
1933 * @param mixed $code Which type of handling; If a true PHP-boolean or TRUE then a \TYPO3\CMS\Core\Messaging\ErrorpageMessage is outputted. If integer an error message with that number is shown. Otherwise the $code value is expected to be a "Location:" header value.
1934 * @param string $header If set, this is passed directly to the PHP function, header()
1935 * @param string $reason If set, error messages will also mention this as the reason for the page-not-found.
1936 * @throws \RuntimeException
1937 * @return void (The function exits!)
1938 */
1939 public function pageErrorHandler($code, $header = '', $reason = '') {
1940 // Issue header in any case:
1941 if ($header) {
1942 $headerArr = preg_split('/\\r|\\n/', $header, -1, PREG_SPLIT_NO_EMPTY);
1943 foreach ($headerArr as $header) {
1944 header($header);
1945 }
1946 }
1947 // Create response:
1948 // Simply boolean; Just shows TYPO3 error page with reason:
1949 if (gettype($code) == 'boolean' || (string)$code === '1') {
1950 $title = 'Page Not Found';
1951 $message = 'The page did not exist or was inaccessible.' . ($reason ? ' Reason: ' . htmlspecialchars($reason) : '');
1952 $messagePage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\ErrorpageMessage', $message, $title);
1953 $messagePage->output();
1954 die;
1955 } elseif (GeneralUtility::isFirstPartOfStr($code, 'USER_FUNCTION:')) {
1956 $funcRef = trim(substr($code, 14));
1957 $params = array(
1958 'currentUrl' => GeneralUtility::getIndpEnv('REQUEST_URI'),
1959 'reasonText' => $reason,
1960 'pageAccessFailureReasons' => $this->getPageAccessFailureReasons()
1961 );
1962 echo GeneralUtility::callUserFunction($funcRef, $params, $this);
1963 } elseif (GeneralUtility::isFirstPartOfStr($code, 'READFILE:')) {
1964 $readFile = GeneralUtility::getFileAbsFileName(trim(substr($code, 9)));
1965 if (@is_file($readFile)) {
1966 echo str_replace(
1967 array(
1968 '###CURRENT_URL###',
1969 '###REASON###'
1970 ),
1971 array(
1972 GeneralUtility::getIndpEnv('REQUEST_URI'),
1973 htmlspecialchars($reason)
1974 ),
1975 GeneralUtility::getUrl($readFile)
1976 );
1977 } else {
1978 throw new \RuntimeException('Configuration Error: 404 page "' . $readFile . '" could not be found.', 1294587214);
1979 }
1980 } elseif (GeneralUtility::isFirstPartOfStr($code, 'REDIRECT:')) {
1981 HttpUtility::redirect(substr($code, 9));
1982 } elseif (strlen($code)) {
1983 // Check if URL is relative
1984 $url_parts = parse_url($code);
1985 if ($url_parts['host'] == '') {
1986 $url_parts['host'] = GeneralUtility::getIndpEnv('HTTP_HOST');
1987 if ($code[0] === '/') {
1988 $code = GeneralUtility::getIndpEnv('TYPO3_REQUEST_HOST') . $code;
1989 } else {
1990 $code = GeneralUtility::getIndpEnv('TYPO3_REQUEST_DIR') . $code;
1991 }
1992 $checkBaseTag = FALSE;
1993 } else {
1994 $checkBaseTag = TRUE;
1995 }
1996 // Check recursion
1997 if ($code == GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')) {
1998 if ($reason == '') {
1999 $reason = 'Page cannot be found.';
2000 }
2001 $reason .= LF . LF . 'Additionally, ' . $code . ' was not found while trying to retrieve the error document.';
2002 throw new \RuntimeException(nl2br(htmlspecialchars($reason)), 1294587215);
2003 }
2004 // Prepare headers
2005 $headerArr = array(
2006 'User-agent: ' . GeneralUtility::getIndpEnv('HTTP_USER_AGENT'),
2007 'Referer: ' . GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')
2008 );
2009 $res = GeneralUtility::getUrl($code, 1, $headerArr);
2010 // Header and content are separated by an empty line
2011 list($header, $content) = explode(CRLF . CRLF, $res, 2);
2012 $content .= CRLF;
2013 if (FALSE === $res) {
2014 // Last chance -- redirect
2015 HttpUtility::redirect($code);
2016 } else {
2017 // Forward these response headers to the client
2018 $forwardHeaders = array(
2019 'Content-Type:'
2020 );
2021 $headerArr = preg_split('/\\r|\\n/', $header, -1, PREG_SPLIT_NO_EMPTY);
2022 foreach ($headerArr as $header) {
2023 foreach ($forwardHeaders as $h) {
2024 if (preg_match('/^' . $h . '/', $header)) {
2025 header($header);
2026 }
2027 }
2028 }
2029 // Put <base> if necesary
2030 if ($checkBaseTag) {
2031 // If content already has <base> tag, we do not need to do anything
2032 if (FALSE === stristr($content, '<base ')) {
2033 // Generate href for base tag
2034 $base = $url_parts['scheme'] . '://';
2035 if ($url_parts['user'] != '') {
2036 $base .= $url_parts['user'];
2037 if ($url_parts['pass'] != '') {
2038 $base .= ':' . $url_parts['pass'];
2039 }
2040 $base .= '@';
2041 }
2042 $base .= $url_parts['host'];
2043 // Add path portion skipping possible file name
2044 $base .= preg_replace('/(.*\\/)[^\\/]*/', '${1}', $url_parts['path']);
2045 // Put it into content (generate also <head> if necessary)
2046 $replacement = LF . '<base href="' . htmlentities($base) . '" />' . LF;
2047 if (stristr($content, '<head>')) {
2048 $content = preg_replace('/(<head>)/i', '\\1' . $replacement, $content);
2049 } else {
2050 $content = preg_replace('/(<html[^>]*>)/i', '\\1<head>' . $replacement . '</head>', $content);
2051 }
2052 }
2053 }
2054 // Output the content
2055 echo $content;
2056 }
2057 } else {
2058 $title = 'Page Not Found';
2059 $message = $reason ? 'Reason: ' . htmlspecialchars($reason) : 'Page cannot be found.';
2060 $messagePage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\ErrorpageMessage', $message, $title);
2061 $messagePage->output();
2062 }
2063 die;
2064 }
2065
2066 /**
2067 * Fetches the integer page id for a page alias.
2068 * Looks if ->id is not an integer and if so it will search for a page alias and if found the page uid of that page is stored in $this->id
2069 *
2070 * @return void
2071 * @access private
2072 */
2073 public function checkAndSetAlias() {
2074 if ($this->id && !\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($this->id)) {
2075 $aid = $this->sys_page->getPageIdFromAlias($this->id);
2076 if ($aid) {
2077 $this->id = $aid;
2078 } else {
2079 $this->pageNotFound = 4;
2080 }
2081 }
2082 }
2083
2084 /**
2085 * Merging values into the global $_GET
2086 *
2087 * @param array $GET_VARS Array of key/value pairs that will be merged into the current GET-vars. (Non-escaped values)
2088 * @return void
2089 */
2090 public function mergingWithGetVars($GET_VARS) {
2091 if (is_array($GET_VARS)) {
2092 // Getting $_GET var, unescaped.
2093 $realGet = GeneralUtility::_GET();
2094 if (!is_array($realGet)) {
2095 $realGet = array();
2096 }
2097 // Merge new values on top:
2098 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($realGet, $GET_VARS);
2099 // Write values back to $_GET:
2100 GeneralUtility::_GETset($realGet);
2101 // Setting these specifically (like in the init-function):
2102 if (isset($GET_VARS['type'])) {
2103 $this->type = (int)$GET_VARS['type'];
2104 }
2105 if (isset($GET_VARS['cHash'])) {
2106 $this->cHash = $GET_VARS['cHash'];
2107 }
2108 if (isset($GET_VARS['jumpurl'])) {
2109 $this->jumpurl = $GET_VARS['jumpurl'];
2110 }
2111 if (isset($GET_VARS['MP'])) {
2112 $this->MP = $this->TYPO3_CONF_VARS['FE']['enable_mount_pids'] ? $GET_VARS['MP'] : '';
2113 }
2114 if (isset($GET_VARS['no_cache']) && $GET_VARS['no_cache']) {
2115 $this->set_no_cache('no_cache is requested via GET parameter');
2116 }
2117 }
2118 }
2119
2120 /********************************************
2121 *
2122 * Template and caching related functions.
2123 *
2124 *******************************************/
2125 /**
2126 * Calculates a hash string based on additional parameters in the url.
2127 *
2128 * Calculated hash is stored in $this->cHash_array.
2129 * This is used to cache pages with more parameters than just id and type.
2130 *
2131 * @return void
2132 * @see reqCHash()
2133 */
2134 public function makeCacheHash() {
2135 // No need to test anything if caching was already disabled.
2136 if ($this->no_cache && !$this->TYPO3_CONF_VARS['FE']['pageNotFoundOnCHashError']) {
2137 return;
2138 }
2139 $GET = GeneralUtility::_GET();
2140 if ($this->cHash && is_array($GET)) {
2141 $this->cHash_array = $this->cacheHash->getRelevantParameters(GeneralUtility::implodeArrayForUrl('', $GET));
2142 $cHash_calc = $this->cacheHash->calculateCacheHash($this->cHash_array);
2143 if ($cHash_calc != $this->cHash) {
2144 if ($this->TYPO3_CONF_VARS['FE']['pageNotFoundOnCHashError']) {
2145 $this->pageNotFoundAndExit('Request parameters could not be validated (&cHash comparison failed)');
2146 } else {
2147 $this->disableCache();
2148 $GLOBALS['TT']->setTSlogMessage('The incoming cHash "' . $this->cHash . '" and calculated cHash "' . $cHash_calc . '" did not match, so caching was disabled. The fieldlist used was "' . implode(',', array_keys($this->cHash_array)) . '"', 2);
2149 }
2150 }
2151 } elseif (is_array($GET)) {
2152 // No cHash is set, check if that is correct
2153 if ($this->cacheHash->doParametersRequireCacheHash(GeneralUtility::implodeArrayForUrl('', $GET))) {
2154 $this->reqCHash();
2155 }
2156 }
2157 }
2158
2159 /**
2160 * Will disable caching if the cHash value was not set.
2161 * This function should be called to check the _existence_ of "&cHash" whenever a plugin generating cachable output is using extra GET variables. If there _is_ a cHash value the validation of it automatically takes place in makeCacheHash() (see above)
2162 *
2163 * @return void
2164 * @see makeCacheHash(), \TYPO3\CMS\Frontend\Plugin\AbstractPlugin::pi_cHashCheck()
2165 */
2166 public function reqCHash() {
2167 if (!$this->cHash) {
2168 if ($this->TYPO3_CONF_VARS['FE']['pageNotFoundOnCHashError']) {
2169 if ($this->tempContent) {
2170 $this->clearPageCacheContent();
2171 }
2172 $this->pageNotFoundAndExit('Request parameters could not be validated (&cHash empty)');
2173 } else {
2174 $this->disableCache();
2175 $GLOBALS['TT']->setTSlogMessage('TSFE->reqCHash(): No &cHash parameter was sent for GET vars though required so caching is disabled', 2);
2176 }
2177 }
2178 }
2179
2180 /**
2181 * Initialize the TypoScript template parser
2182 *
2183 * @return void
2184 */
2185 public function initTemplate() {
2186 $this->tmpl = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\TypoScript\\TemplateService');
2187 $this->tmpl->setVerbose((bool)$this->beUserLogin);
2188 $this->tmpl->init();
2189 $this->tmpl->tt_track = (bool)$this->beUserLogin;
2190 }
2191
2192 /**
2193 * See if page is in cache and get it if so
2194 * Stores the page content in $this->content if something is found.
2195 *
2196 * @return void
2197 */
2198 public function getFromCache() {
2199 if (!$this->no_cache) {
2200 $cc = $this->tmpl->getCurrentPageData();
2201 if (!is_array($cc)) {
2202 $key = $this->id . '::' . $this->MP;
2203 // Returns TRUE if the lock is active now
2204 $isLocked = $this->acquirePageGenerationLock($this->pagesection_lockObj, $key);
2205 if (!$isLocked) {
2206 // Lock is no longer active, the data in "cache_pagesection" is now ready
2207 $cc = $this->tmpl->getCurrentPageData();
2208 if (is_array($cc)) {
2209 // Release the lock
2210 $this->releasePageGenerationLock($this->pagesection_lockObj);
2211 }
2212 }
2213 }
2214 if (is_array($cc)) {
2215 // BE CAREFUL to change the content of the cc-array. This array is serialized and an md5-hash based on this is used for caching the page.
2216 // If this hash is not the same in here in this section and after page-generation, then the page will not be properly cached!
2217 // This array is an identification of the template. If $this->all is empty it's because the template-data is not cached, which it must be.
2218 $cc = $this->tmpl->matching($cc);
2219 ksort($cc);
2220 $this->all = $cc;
2221 }
2222 unset($cc);
2223 }
2224 // clearing the content-variable, which will hold the pagecontent
2225 $this->content = '';
2226 // Unsetting the lowlevel config
2227 unset($this->config);
2228 $this->cacheContentFlag = FALSE;
2229 // Look for page in cache only if caching is not disabled and if a shift-reload is not sent to the server.
2230 if (!$this->no_cache && !$this->headerNoCache()) {
2231 $lockHash = $this->getLockHash();
2232 if ($this->all) {
2233 $this->newHash = $this->getHash();
2234 $GLOBALS['TT']->push('Cache Row', '');
2235 $row = $this->getFromCache_queryRow();
2236 if (!is_array($row)) {
2237 $isLocked = $this->acquirePageGenerationLock($this->pages_lockObj, $lockHash);
2238 if (!$isLocked) {
2239 // Lock is no longer active, the data in "cache_pages" is now ready
2240 $row = $this->getFromCache_queryRow();
2241 if (is_array($row)) {
2242 // Release the lock
2243 $this->releasePageGenerationLock($this->pages_lockObj);
2244 }
2245 }
2246 }
2247 if (is_array($row)) {
2248 // Release this lock
2249 $this->releasePageGenerationLock($this->pages_lockObj);
2250 // Call hook when a page is retrieved from cache:
2251 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['pageLoadedFromCache'])) {
2252 $_params = array('pObj' => &$this, 'cache_pages_row' => &$row);
2253 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['pageLoadedFromCache'] as $_funcRef) {
2254 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2255 }
2256 }
2257 // Fetches the lowlevel config stored with the cached data
2258 $this->config = $row['cache_data'];
2259 // Getting the content
2260 $this->content = $row['content'];
2261 // Flag for temp content
2262 $this->tempContent = $row['temp_content'];
2263 // Setting flag, so we know, that some cached content has been loaded
2264 $this->cacheContentFlag = TRUE;
2265 $this->cacheExpires = $row['expires'];
2266
2267 if (isset($this->config['config']['debug'])) {
2268 $debugCacheTime = (bool)$this->config['config']['debug'];
2269 } else {
2270 $debugCacheTime = !empty($this->TYPO3_CONF_VARS['FE']['debug']);
2271 }
2272 if ($debugCacheTime) {
2273 $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'];
2274 $timeFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
2275 $this->content .= LF . '<!-- Cached page generated ' . date(($dateFormat . ' ' . $timeFormat), $row['tstamp']) . '. Expires ' . Date(($dateFormat . ' ' . $timeFormat), $row['expires']) . ' -->';
2276 }
2277 }
2278 $GLOBALS['TT']->pull();
2279 } else {
2280 $this->acquirePageGenerationLock($this->pages_lockObj, $lockHash);
2281 }
2282 }
2283 }
2284
2285 /**
2286 * Returning the cached version of page with hash = newHash
2287 *
2288 * @return array Cached row, if any. Otherwise void.
2289 */
2290 public function getFromCache_queryRow() {
2291 $GLOBALS['TT']->push('Cache Query', '');
2292 $row = $this->pageCache->get($this->newHash);
2293 $GLOBALS['TT']->pull();
2294 return $row;
2295 }
2296
2297 /**
2298 * Detecting if shift-reload has been clicked
2299 * Will not be called if re-generation of page happens by other reasons (for instance that the page is not in cache yet!)
2300 * Also, a backend user MUST be logged in for the shift-reload to be detected due to DoS-attack-security reasons.
2301 *
2302 * @return bool If shift-reload in client browser has been clicked, disable getting cached page (and regenerate it).
2303 */
2304 public function headerNoCache() {
2305 $disableAcquireCacheData = FALSE;
2306 if ($this->beUserLogin) {
2307 if (strtolower($_SERVER['HTTP_CACHE_CONTROL']) === 'no-cache' || strtolower($_SERVER['HTTP_PRAGMA']) === 'no-cache') {
2308 $disableAcquireCacheData = TRUE;
2309 }
2310 }
2311 // Call hook for possible by-pass of requiring of page cache (for recaching purpose)
2312 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['headerNoCache'])) {
2313 $_params = array('pObj' => &$this, 'disableAcquireCacheData' => &$disableAcquireCacheData);
2314 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['headerNoCache'] as $_funcRef) {
2315 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2316 }
2317 }
2318 return $disableAcquireCacheData;
2319 }
2320
2321 /**
2322 * Calculates the cache-hash
2323 * This hash is unique to the template, the variables ->id, ->type, ->gr_list (list of groups), ->MP (Mount Points) and cHash array
2324 * Used to get and later store the cached data.
2325 *
2326 * @return string MD5 hash of serialized hash base from createHashBase()
2327 * @access private
2328 * @see getFromCache(), getLockHash()
2329 */
2330 public function getHash() {
2331 return md5($this->createHashBase(FALSE));
2332 }
2333
2334 /**
2335 * Calculates the lock-hash
2336 * This hash is unique to the above hash, except that it doesn't contain the template information in $this->all.
2337 *
2338 * @return string MD5 hash
2339 * @access private
2340 * @see getFromCache(), getHash()
2341 */
2342 public function getLockHash() {
2343 $lockHash = $this->createHashBase(TRUE);
2344 return md5($lockHash);
2345 }
2346
2347 /**
2348 * Calculates the cache-hash (or the lock-hash)
2349 * This hash is unique to the template,
2350 * the variables ->id, ->type, ->gr_list (list of groups),
2351 * ->MP (Mount Points) and cHash array
2352 * Used to get and later store the cached data.
2353 *
2354 * @param bool $createLockHashBase Whether to create the lock hash, which doesn't contain the "this->all" (the template information)
2355 * @return string the serialized hash base
2356 */
2357 protected function createHashBase($createLockHashBase = FALSE) {
2358 $hashParameters = array(
2359 'id' => (int)$this->id,
2360 'type' => (int)$this->type,
2361 'gr_list' => (string)$this->gr_list,
2362 'MP' => (string)$this->MP,
2363 'cHash' => $this->cHash_array,
2364 'domainStartPage' => $this->domainStartPage
2365 );
2366 // Include the template information if we shouldn't create a lock hash
2367 if (!$createLockHashBase) {
2368 $hashParameters['all'] = $this->all;
2369 }
2370 // Call hook to influence the hash calculation
2371 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['createHashBase'])) {
2372 $_params = array(
2373 'hashParameters' => &$hashParameters,
2374 'createLockHashBase' => $createLockHashBase
2375 );
2376 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['createHashBase'] as $_funcRef) {
2377 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2378 }
2379 }
2380 return serialize($hashParameters);
2381 }
2382
2383 /**
2384 * Checks if config-array exists already but if not, gets it
2385 *
2386 * @throws \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException
2387 * @return void
2388 */
2389 public function getConfigArray() {
2390 $setStatPageName = FALSE;
2391 // If config is not set by the cache (which would be a major mistake somewhere) OR if INTincScripts-include-scripts have been registered, then we must parse the template in order to get it
2392 if (!is_array($this->config) || is_array($this->config['INTincScript']) || $this->forceTemplateParsing) {
2393 $GLOBALS['TT']->push('Parse template', '');
2394 // Force parsing, if set?:
2395 $this->tmpl->forceTemplateParsing = $this->forceTemplateParsing;
2396 // Start parsing the TS template. Might return cached version.
2397 $this->tmpl->start($this->rootLine);
2398 $GLOBALS['TT']->pull();
2399 if ($this->tmpl->loaded) {
2400 $GLOBALS['TT']->push('Setting the config-array', '');
2401 // toplevel - objArrayName
2402 $this->sPre = $this->tmpl->setup['types.'][$this->type];
2403 $this->pSetup = $this->tmpl->setup[$this->sPre . '.'];
2404 if (!is_array($this->pSetup)) {
2405 $message = 'The page is not configured! [type=' . $this->type . '][' . $this->sPre . '].';
2406 if ($this->checkPageUnavailableHandler()) {
2407 $this->pageUnavailableAndExit($message);
2408 } else {
2409 $explanation = 'This means that there is no TypoScript object of type PAGE with typeNum=' . $this->type . ' configured.';
2410 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
2411 throw new \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException($message . ' ' . $explanation, 1294587217);
2412 }
2413 } else {
2414 $this->config['config'] = array();
2415 // Filling the config-array, first with the main "config." part
2416 if (is_array($this->tmpl->setup['config.'])) {
2417 $this->config['config'] = $this->tmpl->setup['config.'];
2418 }
2419 // override it with the page/type-specific "config."
2420 if (is_array($this->pSetup['config.'])) {
2421 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($this->config['config'], $this->pSetup['config.']);
2422 }
2423 if ($this->config['config']['typolinkEnableLinksAcrossDomains']) {
2424 $this->config['config']['typolinkCheckRootline'] = TRUE;
2425 }
2426 // Set default values for removeDefaultJS and inlineStyle2TempFile so CSS and JS are externalized if compatversion is higher than 4.0
2427 if (GeneralUtility::compat_version('4.0')) {
2428 if (!isset($this->config['config']['removeDefaultJS'])) {
2429 $this->config['config']['removeDefaultJS'] = 'external';
2430 }
2431 if (!isset($this->config['config']['inlineStyle2TempFile'])) {
2432 $this->config['config']['inlineStyle2TempFile'] = 1;
2433 }
2434 }
2435 if (!isset($this->config['config']['compressJs'])) {
2436 $this->config['config']['compressJs'] = 0;
2437 }
2438 // Processing for the config_array:
2439 $this->config['rootLine'] = $this->tmpl->rootLine;
2440 $this->config['mainScript'] = trim($this->config['config']['mainScript']) ?: 'index.php';
2441 // Class for render Header and Footer parts
2442 $template = '';
2443 if ($this->pSetup['pageHeaderFooterTemplateFile']) {
2444 $file = $this->tmpl->getFileName($this->pSetup['pageHeaderFooterTemplateFile']);
2445 if ($file) {
2446 $this->getPageRenderer()->setTemplateFile($file);
2447 }
2448 }
2449 }
2450 $GLOBALS['TT']->pull();
2451 } else {
2452 if ($this->checkPageUnavailableHandler()) {
2453 $this->pageUnavailableAndExit('No TypoScript template found!');
2454 } else {
2455 $message = 'No TypoScript template found!';
2456 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
2457 throw new \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException($message, 1294587218);
2458 }
2459 }
2460 }
2461
2462 // No cache
2463 // Set $this->no_cache TRUE if the config.no_cache value is set!
2464 if ($this->config['config']['no_cache']) {
2465 $this->set_no_cache('config.no_cache is set');
2466 }
2467 // Merge GET with defaultGetVars
2468 if (!empty($this->config['config']['defaultGetVars.'])) {
2469 $modifiedGetVars = GeneralUtility::removeDotsFromTS($this->config['config']['defaultGetVars.']);
2470 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($modifiedGetVars, GeneralUtility::_GET());
2471 GeneralUtility::_GETset($modifiedGetVars);
2472 }
2473 // Hook for postProcessing the configuration array
2474 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['configArrayPostProc'])) {
2475 $params = array('config' => &$this->config['config']);
2476 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['configArrayPostProc'] as $funcRef) {
2477 GeneralUtility::callUserFunction($funcRef, $params, $this);
2478 }
2479 }
2480 }
2481
2482 /********************************************
2483 *
2484 * Further initialization and data processing
2485 * (jumpurl/submission of forms)
2486 *
2487 *******************************************/
2488
2489 /**
2490 * Setting the language key that will be used by the current page.
2491 * In this function it should be checked, 1) that this language exists, 2) that a page_overlay_record exists, .. and if not the default language, 0 (zero), should be set.
2492 *
2493 * @return void
2494 * @access private
2495 */
2496 public function settingLanguage() {
2497 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_preProcess'])) {
2498 $_params = array();
2499 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_preProcess'] as $_funcRef) {
2500 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2501 }
2502 }
2503
2504 // Initialize charset settings etc.
2505 $this->initLLvars();
2506
2507 // Get values from TypoScript:
2508 $this->sys_language_uid = ($this->sys_language_content = (int)$this->config['config']['sys_language_uid']);
2509 list($this->sys_language_mode, $sys_language_content) = GeneralUtility::trimExplode(';', $this->config['config']['sys_language_mode']);
2510 $this->sys_language_contentOL = $this->config['config']['sys_language_overlay'];
2511 // If sys_language_uid is set to another language than default:
2512 if ($this->sys_language_uid > 0) {
2513 // check whether a shortcut is overwritten by a translated page
2514 // we can only do this now, as this is the place where we get
2515 // to know about translations
2516 $this->checkTranslatedShortcut();
2517 // Request the overlay record for the sys_language_uid:
2518 $olRec = $this->sys_page->getPageOverlay($this->id, $this->sys_language_uid);
2519 if (!count($olRec)) {
2520 // If no OL record exists and a foreign language is asked for...
2521 if ($this->sys_language_uid) {
2522 // If requested translation is not available:
2523 if (GeneralUtility::hideIfNotTranslated($this->page['l18n_cfg'])) {
2524 $this->pageNotFoundAndExit('Page is not available in the requested language.');
2525 } else {
2526 switch ((string)$this->sys_language_mode) {
2527 case 'strict':
2528 $this->pageNotFoundAndExit('Page is not available in the requested language (strict).');
2529 break;
2530 case 'content_fallback':
2531 $fallBackOrder = GeneralUtility::intExplode(',', $sys_language_content);
2532 foreach ($fallBackOrder as $orderValue) {
2533 if ((string)$orderValue === '0' || count($this->sys_page->getPageOverlay($this->id, $orderValue))) {
2534 $this->sys_language_content = $orderValue;
2535 // Setting content uid (but leaving the sys_language_uid)
2536 break;
2537 }
2538 }
2539 break;
2540 case 'ignore':
2541 $this->sys_language_content = $this->sys_language_uid;
2542 break;
2543 default:
2544 // Default is that everything defaults to the default language...
2545 $this->sys_language_uid = ($this->sys_language_content = 0);
2546 }
2547 }
2548 }
2549 } else {
2550 // Setting sys_language if an overlay record was found (which it is only if a language is used)
2551 $this->page = $this->sys_page->getPageOverlay($this->page, $this->sys_language_uid);
2552 }
2553 }
2554 // Setting sys_language_uid inside sys-page:
2555 $this->sys_page->sys_language_uid = $this->sys_language_uid;
2556 // If default translation is not available:
2557 if ((!$this->sys_language_uid || !$this->sys_language_content) && $this->page['l18n_cfg'] & 1) {
2558 $message = 'Page is not available in default language.';
2559 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
2560 $this->pageNotFoundAndExit($message);
2561 }
2562 $this->updateRootLinesWithTranslations();
2563 // Finding the ISO code:
2564 if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('static_info_tables') && $this->sys_language_content) {
2565 // using sys_language_content because the ISO code only (currently) affect content selection from FlexForms - which should follow "sys_language_content"
2566 // Set the fourth parameter to TRUE in the next two getRawRecord() calls to
2567 // avoid versioning overlay to be applied as it generates an SQL error
2568 $sys_language_row = $this->sys_page->getRawRecord('sys_language', $this->sys_language_content, 'static_lang_isocode', TRUE);
2569 if (is_array($sys_language_row) && $sys_language_row['static_lang_isocode']) {
2570 $stLrow = $this->sys_page->getRawRecord('static_languages', $sys_language_row['static_lang_isocode'], 'lg_iso_2', TRUE);
2571 $this->sys_language_isocode = $stLrow['lg_iso_2'];
2572 }
2573 }
2574 // Setting softMergeIfNotBlank:
2575 $table_fields = GeneralUtility::trimExplode(',', $this->config['config']['sys_language_softMergeIfNotBlank'], TRUE);
2576 foreach ($table_fields as $TF) {
2577 list($tN, $fN) = explode(':', $TF);
2578 $GLOBALS['TCA'][$tN]['columns'][$fN]['l10n_mode'] = 'mergeIfNotBlank';
2579 }
2580 // Setting softExclude:
2581 $table_fields = GeneralUtility::trimExplode(',', $this->config['config']['sys_language_softExclude'], TRUE);
2582 foreach ($table_fields as $TF) {
2583 list($tN, $fN) = explode(':', $TF);
2584 $GLOBALS['TCA'][$tN]['columns'][$fN]['l10n_mode'] = 'exclude';
2585 }
2586 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_postProcess'])) {
2587 $_params = array();
2588 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_postProcess'] as $_funcRef) {
2589 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2590 }
2591 }
2592 }
2593
2594 /**
2595 * Updating content of the two rootLines IF the language key is set!
2596 */
2597 protected function updateRootLinesWithTranslations() {
2598 if ($this->sys_language_uid) {
2599 $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
2600 $this->tmpl->updateRootlineData($this->rootLine);
2601 }
2602 }
2603
2604 /**
2605 * Setting locale for frontend rendering
2606 *
2607 * @return void
2608 */
2609 public function settingLocale() {
2610 // Setting locale
2611 if ($this->config['config']['locale_all']) {
2612 // There's a problem that PHP parses float values in scripts wrong if the
2613 // locale LC_NUMERIC is set to something with a comma as decimal point
2614 // Do we set all except LC_NUMERIC
2615 $locale = setlocale(LC_COLLATE, $this->config['config']['locale_all']);
2616 if ($locale) {
2617 // PHP fatals with uppercase I characters in method names with turkish locale LC_CTYPE
2618 // @see http://bugs.php.net/bug.php?id=35050
2619 if (substr($this->config['config']['locale_all'], 0, 2) != 'tr') {
2620 setlocale(LC_CTYPE, $this->config['config']['locale_all']);
2621 }
2622 setlocale(LC_MONETARY, $this->config['config']['locale_all']);
2623 setlocale(LC_TIME, $this->config['config']['locale_all']);
2624 $this->localeCharset = $this->csConvObj->get_locale_charset($this->config['config']['locale_all']);
2625 } else {
2626 $GLOBALS['TT']->setTSlogMessage('Locale "' . htmlspecialchars($this->config['config']['locale_all']) . '" not found.', 3);
2627 }
2628 }
2629 }
2630
2631 /**
2632 * Checks whether a translated shortcut page has a different shortcut
2633 * target than the original language page.
2634 * If that is the case, things get corrected to follow that alternative
2635 * shortcut
2636 *
2637 * @return void
2638 * @author Ingo Renner <ingo@typo3.org>
2639 */
2640 protected function checkTranslatedShortcut() {
2641 if (!is_null($this->originalShortcutPage)) {
2642 $originalShortcutPageOverlay = $this->sys_page->getPageOverlay($this->originalShortcutPage['uid'], $this->sys_language_uid);
2643 if (!empty($originalShortcutPageOverlay['shortcut']) && $originalShortcutPageOverlay['shortcut'] != $this->id) {
2644 // the translation of the original shortcut page has a different shortcut target!
2645 // set the correct page and id
2646 $shortcut = $this->getPageShortcut($originalShortcutPageOverlay['shortcut'], $originalShortcutPageOverlay['shortcut_mode'], $originalShortcutPageOverlay['uid']);
2647 $this->id = ($this->contentPid = $shortcut['uid']);
2648 $this->page = $this->sys_page->getPage($this->id);
2649 // Fix various effects on things like menus f.e.
2650 $this->fetch_the_id();
2651 $this->tmpl->rootLine = array_reverse($this->rootLine);
2652 }
2653 }
2654 }
2655
2656 /**
2657 * Handle data submission
2658 *
2659 * @return void
2660 */
2661 public function handleDataSubmission() {
2662 // Check Submission of data.
2663 // This is done at this point, because we need the config values
2664 switch ($this->checkDataSubmission()) {
2665 case 'email':
2666 $this->sendFormmail();
2667 break;
2668 }
2669 }
2670
2671 /**
2672 * Checks if any email-submissions
2673 *
2674 * @return string "email" if a formmail has been sent, "" if none.
2675 */
2676 protected function checkDataSubmission() {
2677 $ret = '';
2678 $formtype_mail = isset($_POST['formtype_mail']) || isset($_POST['formtype_mail_x']);
2679 if ($formtype_mail) {
2680 $refInfo = parse_url(GeneralUtility::getIndpEnv('HTTP_REFERER'));
2681 if (GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY') == $refInfo['host'] || $this->TYPO3_CONF_VARS['SYS']['doNotCheckReferer']) {
2682 if ($this->locDataCheck($_POST['locationData'])) {
2683 if ($formtype_mail) {
2684 $ret = 'email';
2685 }
2686 $GLOBALS['TT']->setTSlogMessage('"Check Data Submission": Return value: ' . $ret, 0);
2687 return $ret;
2688 }
2689 } else {
2690 $GLOBALS['TT']->setTSlogMessage('"Check Data Submission": HTTP_HOST and REFERER HOST did not match when processing submitted formdata!', 3);
2691 }
2692 }
2693 // Hook for processing data submission to extensions:
2694 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkDataSubmission'])) {
2695 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkDataSubmission'] as $_classRef) {
2696 $_procObj = GeneralUtility::getUserObj($_classRef);
2697 $_procObj->checkDataSubmission($this);
2698 }
2699 }
2700 return $ret;
2701 }
2702
2703 /**
2704 * Checks if a formmail submission can be sent as email
2705 *
2706 * @param string $locationData The input from $_POST['locationData']
2707 * @return void
2708 * @access private
2709 * @see checkDataSubmission()
2710 */
2711 public function locDataCheck($locationData) {
2712 $locData = explode(':', $locationData);
2713 if (!$locData[1] || $this->sys_page->checkRecord($locData[1], $locData[2], 1)) {
2714 // $locData[1] -check means that a record is checked only if the locationData has a value for a record else than the page.
2715 if (count($this->sys_page->getPage($locData[0]))) {
2716 return 1;
2717 } else {
2718 $GLOBALS['TT']->setTSlogMessage('LocationData Error: The page pointed to by location data (' . $locationData . ') was not accessible.', 2);
2719 }
2720 } else {
2721 $GLOBALS['TT']->setTSlogMessage('LocationData Error: Location data (' . $locationData . ') record pointed to was not accessible.', 2);
2722 }
2723 }
2724
2725 /**
2726 * Sends the emails from the formmail content object.
2727 *
2728 * @return void
2729 * @see checkDataSubmission()
2730 */
2731 protected function sendFormmail() {
2732 /** @var $formmail \TYPO3\CMS\Frontend\Controller\DataSubmissionController */
2733 $formmail = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Controller\\DataSubmissionController');
2734 $EMAIL_VARS = GeneralUtility::_POST();
2735 $locationData = $EMAIL_VARS['locationData'];
2736 unset($EMAIL_VARS['locationData']);
2737 unset($EMAIL_VARS['formtype_mail'], $EMAIL_VARS['formtype_mail_x'], $EMAIL_VARS['formtype_mail_y']);
2738 $integrityCheck = $this->TYPO3_CONF_VARS['FE']['strictFormmail'];
2739 if (!$this->TYPO3_CONF_VARS['FE']['secureFormmail']) {
2740 // Check recipient field:
2741 // These two fields are the ones which contain recipient addresses that can be misused to send mail from foreign servers.
2742 $encodedFields = explode(',', 'recipient, recipient_copy');
2743 foreach ($encodedFields as $fieldKey) {
2744 if (strlen($EMAIL_VARS[$fieldKey])) {
2745 // Decode...
2746 if ($res = $this->codeString($EMAIL_VARS[$fieldKey], TRUE)) {
2747 $EMAIL_VARS[$fieldKey] = $res;
2748 } elseif ($integrityCheck) {
2749 // Otherwise abort:
2750 $GLOBALS['TT']->setTSlogMessage('"Formmail" discovered a field (' . $fieldKey . ') which could not be decoded to a valid string. Sending formmail aborted due to security reasons!', 3);
2751 return FALSE;
2752 } else {
2753 $GLOBALS['TT']->setTSlogMessage('"Formmail" discovered a field (' . $fieldKey . ') which could not be decoded to a valid string. The security level accepts this, but you should consider a correct coding though!', 2);
2754 }
2755 }
2756 }
2757 } else {
2758 $locData = explode(':', $locationData);
2759 $record = $this->sys_page->checkRecord($locData[1], $locData[2], 1);
2760 $EMAIL_VARS['recipient'] = $record['subheader'];
2761 $EMAIL_VARS['recipient_copy'] = $this->extractRecipientCopy($record['bodytext']);
2762 }
2763 // Hook for preprocessing of the content for formmails:
2764 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['sendFormmail-PreProcClass'])) {
2765 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['sendFormmail-PreProcClass'] as $_classRef) {
2766 $_procObj = GeneralUtility::getUserObj($_classRef);
2767 $EMAIL_VARS = $_procObj->sendFormmail_preProcessVariables($EMAIL_VARS, $this);
2768 }
2769 }
2770 $formmail->start($EMAIL_VARS);
2771 $formmail->sendtheMail();
2772 $GLOBALS['TT']->setTSlogMessage('"Formmail" invoked, sending mail to ' . $EMAIL_VARS['recipient'], 0);
2773 }
2774
2775 /**
2776 * Extracts the value of recipient copy field from a formmail CE bodytext
2777 *
2778 * @param string $bodytext The content of the related bodytext field
2779 * @return string The value of the recipient_copy field, or an empty string
2780 */
2781 public function extractRecipientCopy($bodytext) {
2782 $recipient_copy = '';
2783 $fdef = array();
2784 //|recipient_copy=hidden|karsten@localhost.localdomain
2785 preg_match('/^[\\s]*\\|[\\s]*recipient_copy[\\s]*=[\\s]*hidden[\\s]*\\|(.*)$/m', $bodytext, $fdef);
2786 $recipient_copy = $fdef[1] ?: '';
2787 return $recipient_copy;
2788 }
2789
2790 /**
2791 * Sets the jumpurl for page type "External URL"
2792 *
2793 * @return void
2794 */
2795 public function setExternalJumpUrl() {
2796 if ($extUrl = $this->sys_page->getExtURL($this->page, $this->config['config']['disablePageExternalUrl'])) {
2797 $this->jumpurl = $extUrl;
2798 GeneralUtility::_GETset(GeneralUtility::hmac($this->jumpurl, 'jumpurl'), 'juHash');
2799 }
2800 }
2801
2802 /**
2803 * Check the jumpUrl referer if required
2804 *
2805 * @return void
2806 */
2807 public function checkJumpUrlReferer() {
2808 if (strlen(