TYPO3 CMS  TYPO3_7-6
GeneralUtility.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\Utility;
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 
23 
37 {
38  // Severity constants used by \TYPO3\CMS\Core\Utility\GeneralUtility::sysLog()
44 
47 
54  protected static $allowHostHeaderValue = false;
55 
62  protected static $singletonInstances = [];
63 
69  protected static $nonSingletonInstances = [];
70 
76  protected static $finalClassNameCache = [];
77 
83  protected static $applicationContext = null;
84 
90  protected static $idnaStringCache = [];
91 
97  protected static $idnaConverter = null;
98 
104  protected static $supportedCgiServerApis = [
105  'fpm-fcgi',
106  'cgi',
107  'isapi',
108  'cgi-fcgi',
109  'srv', // HHVM with fastcgi
110  ];
111 
115  protected static $indpEnvCache = [];
116 
117  /*************************
118  *
119  * GET/POST Variables
120  *
121  * Background:
122  * Input GET/POST variables in PHP may have their quotes escaped with "\" or not depending on configuration.
123  * TYPO3 has always converted quotes to BE escaped if the configuration told that they would not be so.
124  * But the clean solution is that quotes are never escaped and that is what the functions below offers.
125  * Eventually TYPO3 should provide this in the global space as well.
126  * In the transitional phase (or forever..?) we need to encourage EVERY to read and write GET/POST vars through the API functions below.
127  * This functionality was previously needed to normalize between magic quotes logic, which was removed from PHP 5.4,
128  * so these methods are still in use, but not tackle the slash problem anymore.
129  *
130  *************************/
139  public static function _GP($var)
140  {
141  if (empty($var)) {
142  return;
143  }
144  if (isset($_POST[$var])) {
145  $value = $_POST[$var];
146  } elseif (isset($_GET[$var])) {
147  $value = $_GET[$var];
148  } else {
149  $value = null;
150  }
151  // This is there for backwards-compatibility, in order to avoid NULL
152  if (isset($value) && !is_array($value)) {
153  $value = (string)$value;
154  }
155  return $value;
156  }
157 
164  public static function _GPmerged($parameter)
165  {
166  $postParameter = isset($_POST[$parameter]) && is_array($_POST[$parameter]) ? $_POST[$parameter] : [];
167  $getParameter = isset($_GET[$parameter]) && is_array($_GET[$parameter]) ? $_GET[$parameter] : [];
168  $mergedParameters = $getParameter;
169  ArrayUtility::mergeRecursiveWithOverrule($mergedParameters, $postParameter);
170  return $mergedParameters;
171  }
172 
182  public static function _GET($var = null)
183  {
184  $value = $var === null ? $_GET : (empty($var) ? null : $_GET[$var]);
185  // This is there for backwards-compatibility, in order to avoid NULL
186  if (isset($value) && !is_array($value)) {
187  $value = (string)$value;
188  }
189  return $value;
190  }
191 
200  public static function _POST($var = null)
201  {
202  $value = $var === null ? $_POST : (empty($var) ? null : $_POST[$var]);
203  // This is there for backwards-compatibility, in order to avoid NULL
204  if (isset($value) && !is_array($value)) {
205  $value = (string)$value;
206  }
207  return $value;
208  }
209 
217  public static function _GETset($inputGet, $key = '')
218  {
219  if ($key != '') {
220  if (strpos($key, '|') !== false) {
221  $pieces = explode('|', $key);
222  $newGet = [];
223  $pointer = &$newGet;
224  foreach ($pieces as $piece) {
225  $pointer = &$pointer[$piece];
226  }
227  $pointer = $inputGet;
228  $mergedGet = $_GET;
229  ArrayUtility::mergeRecursiveWithOverrule($mergedGet, $newGet);
230  $_GET = $mergedGet;
231  $GLOBALS['HTTP_GET_VARS'] = $mergedGet;
232  } else {
233  $_GET[$key] = $inputGet;
234  $GLOBALS['HTTP_GET_VARS'][$key] = $inputGet;
235  }
236  } elseif (is_array($inputGet)) {
237  $_GET = $inputGet;
238  $GLOBALS['HTTP_GET_VARS'] = $inputGet;
239  }
240  }
241 
251  public static function removeXSS($string)
252  {
253  return \RemoveXSS::process($string);
254  }
255 
256  /*************************
257  *
258  * IMAGE FUNCTIONS
259  *
260  *************************/
280  public static function gif_compress($theFile, $type)
281  {
282  static::logDeprecatedFunction();
283  $returnCode = GraphicalFunctions::gifCompress($theFile, $type);
284  return $returnCode;
285  }
286 
295  public static function png_to_gif_by_imagemagick($theFile)
296  {
297  static::logDeprecatedFunction();
298  $newFile = GraphicalFunctions::pngToGifByImagemagick($theFile);
299  return $newFile;
300  }
301 
311  public static function read_png_gif($theFile, $output_png = false)
312  {
313  static::logDeprecatedFunction();
314  $newFile = GraphicalFunctions::readPngGif($theFile, $output_png);
315  return $newFile;
316  }
317 
318  /*************************
319  *
320  * STRING FUNCTIONS
321  *
322  *************************/
331  public static function fixed_lgd_cs($string, $chars, $appendString = '...')
332  {
333  if (is_object($GLOBALS['LANG'])) {
334  return $GLOBALS['LANG']->csConvObj->crop($GLOBALS['LANG']->charSet, $string, $chars, $appendString);
335  } elseif (is_object($GLOBALS['TSFE']) && is_object($GLOBALS['TSFE']->csConvObj)) {
336  $charSet = $GLOBALS['TSFE']->renderCharset != '' ? $GLOBALS['TSFE']->renderCharset : $GLOBALS['TSFE']->defaultCharSet;
337  return $GLOBALS['TSFE']->csConvObj->crop($charSet, $string, $chars, $appendString);
338  } else {
339  // This case should not happen
340  $csConvObj = self::makeInstance(\TYPO3\CMS\Core\Charset\CharsetConverter::class);
341  return $csConvObj->crop('utf-8', $string, $chars, $appendString);
342  }
343  }
344 
353  public static function cmpIP($baseIP, $list)
354  {
355  $list = trim($list);
356  if ($list === '') {
357  return false;
358  } elseif ($list === '*') {
359  return true;
360  }
361  if (strpos($baseIP, ':') !== false && self::validIPv6($baseIP)) {
362  return self::cmpIPv6($baseIP, $list);
363  } else {
364  return self::cmpIPv4($baseIP, $list);
365  }
366  }
367 
375  public static function cmpIPv4($baseIP, $list)
376  {
377  $IPpartsReq = explode('.', $baseIP);
378  if (count($IPpartsReq) === 4) {
379  $values = self::trimExplode(',', $list, true);
380  foreach ($values as $test) {
381  $testList = explode('/', $test);
382  if (count($testList) === 2) {
383  list($test, $mask) = $testList;
384  } else {
385  $mask = false;
386  }
387  if ((int)$mask) {
388  // "192.168.3.0/24"
389  $lnet = ip2long($test);
390  $lip = ip2long($baseIP);
391  $binnet = str_pad(decbin($lnet), 32, '0', STR_PAD_LEFT);
392  $firstpart = substr($binnet, 0, $mask);
393  $binip = str_pad(decbin($lip), 32, '0', STR_PAD_LEFT);
394  $firstip = substr($binip, 0, $mask);
395  $yes = $firstpart === $firstip;
396  } else {
397  // "192.168.*.*"
398  $IPparts = explode('.', $test);
399  $yes = 1;
400  foreach ($IPparts as $index => $val) {
401  $val = trim($val);
402  if ($val !== '*' && $IPpartsReq[$index] !== $val) {
403  $yes = 0;
404  }
405  }
406  }
407  if ($yes) {
408  return true;
409  }
410  }
411  }
412  return false;
413  }
414 
422  public static function cmpIPv6($baseIP, $list)
423  {
424  // Policy default: Deny connection
425  $success = false;
426  $baseIP = self::normalizeIPv6($baseIP);
427  $values = self::trimExplode(',', $list, true);
428  foreach ($values as $test) {
429  $testList = explode('/', $test);
430  if (count($testList) === 2) {
431  list($test, $mask) = $testList;
432  } else {
433  $mask = false;
434  }
435  if (self::validIPv6($test)) {
436  $test = self::normalizeIPv6($test);
437  $maskInt = (int)$mask ?: 128;
438  // Special case; /0 is an allowed mask - equals a wildcard
439  if ($mask === '0') {
440  $success = true;
441  } elseif ($maskInt == 128) {
442  $success = $test === $baseIP;
443  } else {
444  $testBin = self::IPv6Hex2Bin($test);
445  $baseIPBin = self::IPv6Hex2Bin($baseIP);
446  $success = true;
447  // Modulo is 0 if this is a 8-bit-boundary
448  $maskIntModulo = $maskInt % 8;
449  $numFullCharactersUntilBoundary = (int)($maskInt / 8);
450  if (substr($testBin, 0, $numFullCharactersUntilBoundary) !== substr($baseIPBin, 0, $numFullCharactersUntilBoundary)) {
451  $success = false;
452  } elseif ($maskIntModulo > 0) {
453  // If not an 8-bit-boundary, check bits of last character
454  $testLastBits = str_pad(decbin(ord(substr($testBin, $numFullCharactersUntilBoundary, 1))), 8, '0', STR_PAD_LEFT);
455  $baseIPLastBits = str_pad(decbin(ord(substr($baseIPBin, $numFullCharactersUntilBoundary, 1))), 8, '0', STR_PAD_LEFT);
456  if (strncmp($testLastBits, $baseIPLastBits, $maskIntModulo) != 0) {
457  $success = false;
458  }
459  }
460  }
461  }
462  if ($success) {
463  return true;
464  }
465  }
466  return false;
467  }
468 
476  public static function IPv6Hex2Bin($hex)
477  {
478  return inet_pton($hex);
479  }
480 
488  public static function IPv6Bin2Hex($bin)
489  {
490  return inet_ntop($bin);
491  }
492 
500  public static function normalizeIPv6($address)
501  {
502  $normalizedAddress = '';
503  $stageOneAddress = '';
504  // According to RFC lowercase-representation is recommended
505  $address = strtolower($address);
506  // Normalized representation has 39 characters (0000:0000:0000:0000:0000:0000:0000:0000)
507  if (strlen($address) == 39) {
508  // Already in full expanded form
509  return $address;
510  }
511  // Count 2 if if address has hidden zero blocks
512  $chunks = explode('::', $address);
513  if (count($chunks) === 2) {
514  $chunksLeft = explode(':', $chunks[0]);
515  $chunksRight = explode(':', $chunks[1]);
516  $left = count($chunksLeft);
517  $right = count($chunksRight);
518  // Special case: leading zero-only blocks count to 1, should be 0
519  if ($left == 1 && strlen($chunksLeft[0]) == 0) {
520  $left = 0;
521  }
522  $hiddenBlocks = 8 - ($left + $right);
523  $hiddenPart = '';
524  $h = 0;
525  while ($h < $hiddenBlocks) {
526  $hiddenPart .= '0000:';
527  $h++;
528  }
529  if ($left == 0) {
530  $stageOneAddress = $hiddenPart . $chunks[1];
531  } else {
532  $stageOneAddress = $chunks[0] . ':' . $hiddenPart . $chunks[1];
533  }
534  } else {
535  $stageOneAddress = $address;
536  }
537  // Normalize the blocks:
538  $blocks = explode(':', $stageOneAddress);
539  $divCounter = 0;
540  foreach ($blocks as $block) {
541  $tmpBlock = '';
542  $i = 0;
543  $hiddenZeros = 4 - strlen($block);
544  while ($i < $hiddenZeros) {
545  $tmpBlock .= '0';
546  $i++;
547  }
548  $normalizedAddress .= $tmpBlock . $block;
549  if ($divCounter < 7) {
550  $normalizedAddress .= ':';
551  $divCounter++;
552  }
553  }
554  return $normalizedAddress;
555  }
556 
564  public static function compressIPv6($address)
565  {
566  return inet_ntop(inet_pton($address));
567  }
568 
577  public static function validIP($ip)
578  {
579  return filter_var($ip, FILTER_VALIDATE_IP) !== false;
580  }
581 
590  public static function validIPv4($ip)
591  {
592  return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
593  }
594 
603  public static function validIPv6($ip)
604  {
605  return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
606  }
607 
615  public static function cmpFQDN($baseHost, $list)
616  {
617  $baseHost = trim($baseHost);
618  if (empty($baseHost)) {
619  return false;
620  }
621  if (self::validIPv4($baseHost) || self::validIPv6($baseHost)) {
622  // Resolve hostname
623  // Note: this is reverse-lookup and can be randomly set as soon as somebody is able to set
624  // the reverse-DNS for his IP (security when for example used with REMOTE_ADDR)
625  $baseHostName = gethostbyaddr($baseHost);
626  if ($baseHostName === $baseHost) {
627  // Unable to resolve hostname
628  return false;
629  }
630  } else {
631  $baseHostName = $baseHost;
632  }
633  $baseHostNameParts = explode('.', $baseHostName);
634  $values = self::trimExplode(',', $list, true);
635  foreach ($values as $test) {
636  $hostNameParts = explode('.', $test);
637  // To match hostNameParts can only be shorter (in case of wildcards) or equal
638  $hostNamePartsCount = count($hostNameParts);
639  $baseHostNamePartsCount = count($baseHostNameParts);
640  if ($hostNamePartsCount > $baseHostNamePartsCount) {
641  continue;
642  }
643  $yes = true;
644  foreach ($hostNameParts as $index => $val) {
645  $val = trim($val);
646  if ($val === '*') {
647  // Wildcard valid for one or more hostname-parts
648  $wildcardStart = $index + 1;
649  // Wildcard as last/only part always matches, otherwise perform recursive checks
650  if ($wildcardStart < $hostNamePartsCount) {
651  $wildcardMatched = false;
652  $tempHostName = implode('.', array_slice($hostNameParts, $index + 1));
653  while ($wildcardStart < $baseHostNamePartsCount && !$wildcardMatched) {
654  $tempBaseHostName = implode('.', array_slice($baseHostNameParts, $wildcardStart));
655  $wildcardMatched = self::cmpFQDN($tempBaseHostName, $tempHostName);
656  $wildcardStart++;
657  }
658  if ($wildcardMatched) {
659  // Match found by recursive compare
660  return true;
661  } else {
662  $yes = false;
663  }
664  }
665  } elseif ($baseHostNameParts[$index] !== $val) {
666  // In case of no match
667  $yes = false;
668  }
669  }
670  if ($yes) {
671  return true;
672  }
673  }
674  return false;
675  }
676 
684  public static function isOnCurrentHost($url)
685  {
686  return stripos($url . '/', self::getIndpEnv('TYPO3_REQUEST_HOST') . '/') === 0;
687  }
688 
697  public static function inList($list, $item)
698  {
699  return strpos(',' . $list . ',', ',' . $item . ',') !== false;
700  }
701 
712  public static function rmFromList($element, $list)
713  {
714  $items = explode(',', $list);
715  foreach ($items as $k => $v) {
716  if ($v == $element) {
717  unset($items[$k]);
718  }
719  }
720  return implode(',', $items);
721  }
722 
730  public static function expandList($list)
731  {
732  $items = explode(',', $list);
733  $list = [];
734  foreach ($items as $item) {
735  $range = explode('-', $item);
736  if (isset($range[1])) {
737  $runAwayBrake = 1000;
738  for ($n = $range[0]; $n <= $range[1]; $n++) {
739  $list[] = $n;
740  $runAwayBrake--;
741  if ($runAwayBrake <= 0) {
742  break;
743  }
744  }
745  } else {
746  $list[] = $item;
747  }
748  }
749  return implode(',', $list);
750  }
751 
760  public static function compat_version($verNumberStr)
761  {
763  }
764 
771  public static function md5int($str)
772  {
773  return hexdec(substr(md5($str), 0, 7));
774  }
775 
783  public static function shortMD5($input, $len = 10)
784  {
785  return substr(md5($input), 0, $len);
786  }
787 
795  public static function hmac($input, $additionalSecret = '')
796  {
797  $hashAlgorithm = 'sha1';
798  $hashBlocksize = 64;
799  $hmac = '';
800  $secret = $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] . $additionalSecret;
801  if (extension_loaded('hash') && function_exists('hash_hmac') && function_exists('hash_algos') && in_array($hashAlgorithm, hash_algos())) {
802  $hmac = hash_hmac($hashAlgorithm, $input, $secret);
803  } else {
804  // Outer padding
805  $opad = str_repeat(chr(92), $hashBlocksize);
806  // Inner padding
807  $ipad = str_repeat(chr(54), $hashBlocksize);
808  if (strlen($secret) > $hashBlocksize) {
809  // Keys longer than block size are shorten
810  $key = str_pad(pack('H*', call_user_func($hashAlgorithm, $secret)), $hashBlocksize, chr(0));
811  } else {
812  // Keys shorter than block size are zero-padded
813  $key = str_pad($secret, $hashBlocksize, chr(0));
814  }
815  $hmac = call_user_func($hashAlgorithm, ($key ^ $opad) . pack('H*', call_user_func($hashAlgorithm, (($key ^ $ipad) . $input))));
816  }
817  return $hmac;
818  }
819 
828  public static function uniqueList($in_list, $secondParameter = null)
829  {
830  if (is_array($in_list)) {
831  throw new \InvalidArgumentException('TYPO3 Fatal Error: TYPO3\\CMS\\Core\\Utility\\GeneralUtility::uniqueList() does NOT support array arguments anymore! Only string comma lists!', 1270853885);
832  }
833  if (isset($secondParameter)) {
834  throw new \InvalidArgumentException('TYPO3 Fatal Error: TYPO3\\CMS\\Core\\Utility\\GeneralUtility::uniqueList() does NOT support more than a single argument value anymore. You have specified more than one!', 1270853886);
835  }
836  return implode(',', array_unique(self::trimExplode(',', $in_list, true)));
837  }
838 
845  public static function split_fileref($fileNameWithPath)
846  {
847  $reg = [];
848  if (preg_match('/(.*\\/)(.*)$/', $fileNameWithPath, $reg)) {
849  $info['path'] = $reg[1];
850  $info['file'] = $reg[2];
851  } else {
852  $info['path'] = '';
853  $info['file'] = $fileNameWithPath;
854  }
855  $reg = '';
856  // If open_basedir is set and the fileName was supplied without a path the is_dir check fails
857  if (!is_dir($fileNameWithPath) && preg_match('/(.*)\\.([^\\.]*$)/', $info['file'], $reg)) {
858  $info['filebody'] = $reg[1];
859  $info['fileext'] = strtolower($reg[2]);
860  $info['realFileext'] = $reg[2];
861  } else {
862  $info['filebody'] = $info['file'];
863  $info['fileext'] = '';
864  }
865  reset($info);
866  return $info;
867  }
868 
884  public static function dirname($path)
885  {
886  $p = self::revExplode('/', $path, 2);
887  return count($p) === 2 ? $p[0] : '';
888  }
889 
901  public static function modifyHTMLColor($color, $R, $G, $B)
902  {
903  self::logDeprecatedFunction();
904  // This takes a hex-color (# included!) and adds $R, $G and $B to the HTML-color (format: #xxxxxx) and returns the new color
905  $nR = MathUtility::forceIntegerInRange(hexdec(substr($color, 1, 2)) + $R, 0, 255);
906  $nG = MathUtility::forceIntegerInRange(hexdec(substr($color, 3, 2)) + $G, 0, 255);
907  $nB = MathUtility::forceIntegerInRange(hexdec(substr($color, 5, 2)) + $B, 0, 255);
908  return '#' . substr(('0' . dechex($nR)), -2) . substr(('0' . dechex($nG)), -2) . substr(('0' . dechex($nB)), -2);
909  }
910 
920  public static function modifyHTMLColorAll($color, $all)
921  {
922  self::logDeprecatedFunction();
923  return self::modifyHTMLColor($color, $all, $all, $all);
924  }
925 
933  public static function isFirstPartOfStr($str, $partStr)
934  {
935  return $partStr != '' && strpos((string)$str, (string)$partStr, 0) === 0;
936  }
937 
946  public static function formatSize($sizeInBytes, $labels = '', $base = 0)
947  {
948  $defaultFormats = [
949  'iec' => ['base' => 1024, 'labels' => [' ', ' Ki', ' Mi', ' Gi', ' Ti', ' Pi', ' Ei', ' Zi', ' Yi']],
950  'si' => ['base' => 1000, 'labels' => [' ', ' k', ' M', ' G', ' T', ' P', ' E', ' Z', ' Y']],
951  ];
952  // Set labels and base:
953  if (empty($labels)) {
954  $labels = 'iec';
955  }
956  if (isset($defaultFormats[$labels])) {
957  $base = $defaultFormats[$labels]['base'];
958  $labelArr = $defaultFormats[$labels]['labels'];
959  } else {
960  $base = (int)$base;
961  if ($base !== 1000 && $base !== 1024) {
962  $base = 1024;
963  }
964  $labelArr = explode('|', str_replace('"', '', $labels));
965  }
966  // @todo find out which locale is used for current BE user to cover the BE case as well
967  $oldLocale = setlocale(LC_NUMERIC, 0);
968  $newLocale = isset($GLOBALS['TSFE']) ? $GLOBALS['TSFE']->config['config']['locale_all'] : '';
969  if ($newLocale) {
970  setlocale(LC_NUMERIC, $newLocale);
971  }
972  $localeInfo = localeconv();
973  if ($newLocale) {
974  setlocale(LC_NUMERIC, $oldLocale);
975  }
976  $sizeInBytes = max($sizeInBytes, 0);
977  $multiplier = floor(($sizeInBytes ? log($sizeInBytes) : 0) / log($base));
978  $sizeInUnits = $sizeInBytes / pow($base, $multiplier);
979  if ($sizeInUnits > ($base * .9)) {
980  $multiplier++;
981  }
982  $multiplier = min($multiplier, count($labelArr) - 1);
983  $sizeInUnits = $sizeInBytes / pow($base, $multiplier);
984  return number_format($sizeInUnits, (($multiplier > 0) && ($sizeInUnits < 20)) ? 2 : 0, $localeInfo['decimal_point'], '') . $labelArr[$multiplier];
985  }
986 
993  public static function convertMicrotime($microtime)
994  {
995  $parts = explode(' ', $microtime);
996  return round(($parts[0] + $parts[1]) * 1000);
997  }
998 
1007  public static function splitCalc($string, $operators)
1008  {
1009  $res = [];
1010  $sign = '+';
1011  while ($string) {
1012  $valueLen = strcspn($string, $operators);
1013  $value = substr($string, 0, $valueLen);
1014  $res[] = [$sign, trim($value)];
1015  $sign = substr($string, $valueLen, 1);
1016  $string = substr($string, $valueLen + 1);
1017  }
1018  reset($res);
1019  return $res;
1020  }
1021 
1031  public static function deHSCentities($str)
1032  {
1033  return preg_replace('/&amp;([#[:alnum:]]*;)/', '&\\1', $str);
1034  }
1035 
1044  public static function slashJS($string, $extended = false, $char = '\'')
1045  {
1046  if ($extended) {
1047  $string = str_replace('\\', '\\\\', $string);
1048  }
1049  return str_replace($char, '\\' . $char, $string);
1050  }
1051 
1059  public static function rawUrlEncodeJS($str)
1060  {
1061  return str_replace('%20', ' ', rawurlencode($str));
1062  }
1063 
1071  public static function rawUrlEncodeFP($str)
1072  {
1073  return str_replace('%2F', '/', rawurlencode($str));
1074  }
1075 
1094  public static function validEmail($email)
1095  {
1096  // Early return in case input is not a string
1097  if (!is_string($email)) {
1098  return false;
1099  }
1100  $atPosition = strrpos($email, '@');
1101  if (!$atPosition || $atPosition + 1 === strlen($email)) {
1102  // Return if no @ found or it is placed at the very beginning or end of the email
1103  return false;
1104  }
1105  $domain = substr($email, $atPosition + 1);
1106  $user = substr($email, 0, $atPosition);
1107  if (!preg_match('/^[a-z0-9.\\-]*$/i', $domain)) {
1108  try {
1109  $domain = self::idnaEncode($domain);
1110  } catch (\InvalidArgumentException $exception) {
1111  return false;
1112  }
1113  }
1114  return filter_var($user . '@' . $domain, FILTER_VALIDATE_EMAIL) !== false;
1115  }
1116 
1130  public static function isBrokenEmailEnvironment()
1131  {
1132  self::logDeprecatedFunction();
1133  return TYPO3_OS == 'WIN' || false !== strpos(ini_get('sendmail_path'), 'mini_sendmail');
1134  }
1135 
1143  public static function normalizeMailAddress($address)
1144  {
1145  self::logDeprecatedFunction();
1146  if (self::isBrokenEmailEnvironment() && false !== ($pos1 = strrpos($address, '<'))) {
1147  $pos2 = strpos($address, '>', $pos1);
1148  $address = substr($address, $pos1 + 1, ($pos2 ? $pos2 : strlen($address)) - $pos1 - 1);
1149  }
1150  return $address;
1151  }
1152 
1162  public static function formatForTextarea($content)
1163  {
1164  self::logDeprecatedFunction();
1165  return LF . htmlspecialchars($content);
1166  }
1167 
1177  public static function strtoupper($str)
1178  {
1179  return strtr((string)$str, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
1180  }
1181 
1191  public static function strtolower($str)
1192  {
1193  return strtr((string)$str, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
1194  }
1195 
1207  public static function generateRandomBytes($bytesToReturn)
1208  {
1209  // Cache 4k of the generated bytestream.
1210  static $bytes = '';
1211  $bytesToGenerate = max(4096, $bytesToReturn);
1212  // if we have not enough random bytes cached, we generate new ones
1213  if (!isset($bytes[$bytesToReturn - 1])) {
1214  if (TYPO3_OS === 'WIN') {
1215  // Openssl seems to be deadly slow on Windows, so try to use mcrypt
1216  $bytes .= self::generateRandomBytesMcrypt($bytesToGenerate);
1217  } else {
1218  // Try to use native PHP functions first, precedence has openssl
1219  $bytes .= self::generateRandomBytesOpenSsl($bytesToGenerate);
1220  if (!isset($bytes[$bytesToReturn - 1])) {
1221  $bytes .= self::generateRandomBytesMcrypt($bytesToGenerate);
1222  }
1223  // If openssl and mcrypt failed, try /dev/urandom
1224  if (!isset($bytes[$bytesToReturn - 1])) {
1225  $bytes .= self::generateRandomBytesUrandom($bytesToGenerate);
1226  }
1227  }
1228  // Fall back if other random byte generation failed until now
1229  if (!isset($bytes[$bytesToReturn - 1])) {
1230  $bytes .= self::generateRandomBytesFallback($bytesToReturn);
1231  }
1232  }
1233  // get first $bytesToReturn and remove it from the byte cache
1234  $output = substr($bytes, 0, $bytesToReturn);
1235  $bytes = substr($bytes, $bytesToReturn);
1236  return $output;
1237  }
1238 
1245  protected static function generateRandomBytesOpenSsl($bytesToGenerate)
1246  {
1247  if (!function_exists('openssl_random_pseudo_bytes')) {
1248  return '';
1249  }
1250  $isStrong = null;
1251  return (string)openssl_random_pseudo_bytes($bytesToGenerate, $isStrong);
1252  }
1253 
1260  protected static function generateRandomBytesMcrypt($bytesToGenerate)
1261  {
1262  if (!function_exists('mcrypt_create_iv')) {
1263  return '';
1264  }
1265  return (string)(@mcrypt_create_iv($bytesToGenerate, MCRYPT_DEV_URANDOM));
1266  }
1267 
1274  protected static function generateRandomBytesUrandom($bytesToGenerate)
1275  {
1276  $bytes = '';
1277  $fh = @fopen('/dev/urandom', 'rb');
1278  if ($fh) {
1279  // PHP only performs buffered reads, so in reality it will always read
1280  // at least 4096 bytes. Thus, it costs nothing extra to read and store
1281  // that much so as to speed any additional invocations.
1282  $bytes = fread($fh, $bytesToGenerate);
1283  fclose($fh);
1284  }
1285  return $bytes;
1286  }
1287 
1294  protected static function generateRandomBytesFallback($bytesToReturn)
1295  {
1296  $bytes = '';
1297  // We initialize with somewhat random.
1298  $randomState = $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] . base_convert(memory_get_usage() % pow(10, 6), 10, 2) . microtime() . StringUtility::getUniqueId() . getmypid();
1299  while (!isset($bytes[$bytesToReturn - 1])) {
1300  $randomState = sha1(microtime() . mt_rand() . $randomState);
1301  $bytes .= sha1(mt_rand() . $randomState, true);
1302  }
1303  return $bytes;
1304  }
1305 
1312  public static function idnaEncode($value)
1313  {
1314  if (isset(self::$idnaStringCache[$value])) {
1315  return self::$idnaStringCache[$value];
1316  } else {
1317  if (!self::$idnaConverter) {
1318  self::$idnaConverter = new \idna_convert(['idn_version' => 2008]);
1319  }
1320  self::$idnaStringCache[$value] = self::$idnaConverter->encode($value);
1321  return self::$idnaStringCache[$value];
1322  }
1323  }
1324 
1331  public static function getRandomHexString($count)
1332  {
1333  return substr(bin2hex(self::generateRandomBytes((int)(($count + 1) / 2))), 0, $count);
1334  }
1335 
1343  public static function underscoredToUpperCamelCase($string)
1344  {
1345  $upperCamelCase = str_replace(' ', '', ucwords(str_replace('_', ' ', self::strtolower($string))));
1346  return $upperCamelCase;
1347  }
1348 
1356  public static function underscoredToLowerCamelCase($string)
1357  {
1358  $upperCamelCase = str_replace(' ', '', ucwords(str_replace('_', ' ', self::strtolower($string))));
1359  $lowerCamelCase = self::lcfirst($upperCamelCase);
1360  return $lowerCamelCase;
1361  }
1362 
1370  public static function camelCaseToLowerCaseUnderscored($string)
1371  {
1372  return self::strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $string));
1373  }
1374 
1382  public static function lcfirst($string)
1383  {
1384  return self::strtolower($string[0]) . substr($string, 1);
1385  }
1386 
1411  public static function isValidUrl($url)
1412  {
1413  $parsedUrl = parse_url($url);
1414  if (!$parsedUrl || !isset($parsedUrl['scheme'])) {
1415  return false;
1416  }
1417  // HttpUtility::buildUrl() will always build urls with <scheme>://
1418  // our original $url might only contain <scheme>: (e.g. mail:)
1419  // so we convert that to the double-slashed version to ensure
1420  // our check against the $recomposedUrl is proper
1421  if (!self::isFirstPartOfStr($url, $parsedUrl['scheme'] . '://')) {
1422  $url = str_replace($parsedUrl['scheme'] . ':', $parsedUrl['scheme'] . '://', $url);
1423  }
1424  $recomposedUrl = HttpUtility::buildUrl($parsedUrl);
1425  if ($recomposedUrl !== $url) {
1426  // The parse_url() had to modify characters, so the URL is invalid
1427  return false;
1428  }
1429  if (isset($parsedUrl['host']) && !preg_match('/^[a-z0-9.\\-]*$/i', $parsedUrl['host'])) {
1430  try {
1431  $parsedUrl['host'] = self::idnaEncode($parsedUrl['host']);
1432  } catch (\InvalidArgumentException $exception) {
1433  return false;
1434  }
1435  }
1436  return filter_var(HttpUtility::buildUrl($parsedUrl), FILTER_VALIDATE_URL) !== false;
1437  }
1438 
1439  /*************************
1440  *
1441  * ARRAY FUNCTIONS
1442  *
1443  *************************/
1467  public static function inArray(array $in_array, $item)
1468  {
1469  static::logDeprecatedFunction();
1470  return ArrayUtility::inArray($in_array, $item);
1471  }
1472 
1483  public static function intExplode($delimiter, $string, $removeEmptyValues = false, $limit = 0)
1484  {
1485  $result = explode($delimiter, $string);
1486  foreach ($result as $key => &$value) {
1487  if ($removeEmptyValues && ($value === '' || trim($value) === '')) {
1488  unset($result[$key]);
1489  } else {
1490  $value = (int)$value;
1491  }
1492  }
1493  unset($value);
1494  if ($limit !== 0) {
1495  if ($limit < 0) {
1496  $result = array_slice($result, 0, $limit);
1497  } elseif (count($result) > $limit) {
1498  $lastElements = array_slice($result, $limit - 1);
1499  $result = array_slice($result, 0, $limit - 1);
1500  $result[] = implode($delimiter, $lastElements);
1501  }
1502  }
1503  return $result;
1504  }
1505 
1520  public static function revExplode($delimiter, $string, $count = 0)
1521  {
1522  // 2 is the (currently, as of 2014-02) most-used value for $count in the core, therefore we check it first
1523  if ($count === 2) {
1524  $position = strrpos($string, strrev($delimiter));
1525  if ($position !== false) {
1526  return [substr($string, 0, $position), substr($string, $position + strlen($delimiter))];
1527  } else {
1528  return [$string];
1529  }
1530  } elseif ($count <= 1) {
1531  return [$string];
1532  } else {
1533  $explodedValues = explode($delimiter, strrev($string), $count);
1534  $explodedValues = array_map('strrev', $explodedValues);
1535  return array_reverse($explodedValues);
1536  }
1537  }
1538 
1551  public static function trimExplode($delim, $string, $removeEmptyValues = false, $limit = 0)
1552  {
1553  $result = explode($delim, $string);
1554  if ($removeEmptyValues) {
1555  $temp = [];
1556  foreach ($result as $value) {
1557  if (trim($value) !== '') {
1558  $temp[] = $value;
1559  }
1560  }
1561  $result = $temp;
1562  }
1563  if ($limit > 0 && count($result) > $limit) {
1564  $lastElements = array_splice($result, $limit - 1);
1565  $result[] = implode($delim, $lastElements);
1566  } elseif ($limit < 0) {
1567  $result = array_slice($result, 0, $limit);
1568  }
1569  $result = array_map('trim', $result);
1570  return $result;
1571  }
1572 
1581  public static function removeArrayEntryByValue(array $array, $cmpValue)
1582  {
1583  static::logDeprecatedFunction();
1584  return ArrayUtility::removeArrayEntryByValue($array, $cmpValue);
1585  }
1586 
1611  public static function keepItemsInArray(array $array, $keepItems, $getValueFunc = null)
1612  {
1613  static::logDeprecatedFunction();
1614  return ArrayUtility::keepItemsInArray($array, $keepItems, $getValueFunc);
1615  }
1616 
1628  public static function implodeArrayForUrl($name, array $theArray, $str = '', $skipBlank = false, $rawurlencodeParamName = false)
1629  {
1630  foreach ($theArray as $Akey => $AVal) {
1631  $thisKeyName = $name ? $name . '[' . $Akey . ']' : $Akey;
1632  if (is_array($AVal)) {
1633  $str = self::implodeArrayForUrl($thisKeyName, $AVal, $str, $skipBlank, $rawurlencodeParamName);
1634  } else {
1635  if (!$skipBlank || (string)$AVal !== '') {
1636  $str .= '&' . ($rawurlencodeParamName ? rawurlencode($thisKeyName) : $thisKeyName) . '=' . rawurlencode($AVal);
1637  }
1638  }
1639  }
1640  return $str;
1641  }
1642 
1651  public static function explodeUrl2Array($string, $multidim = false)
1652  {
1653  $output = [];
1654  if ($multidim) {
1655  parse_str($string, $output);
1656  } else {
1657  $p = explode('&', $string);
1658  foreach ($p as $v) {
1659  if ($v !== '') {
1660  list($pK, $pV) = explode('=', $v, 2);
1661  $output[rawurldecode($pK)] = rawurldecode($pV);
1662  }
1663  }
1664  }
1665  return $output;
1666  }
1667 
1677  public static function compileSelectedGetVarsFromArray($varList, array $getArray, $GPvarAlt = true)
1678  {
1679  $keys = self::trimExplode(',', $varList, true);
1680  $outArr = [];
1681  foreach ($keys as $v) {
1682  if (isset($getArray[$v])) {
1683  $outArr[$v] = $getArray[$v];
1684  } elseif ($GPvarAlt) {
1685  $outArr[$v] = self::_GP($v);
1686  }
1687  }
1688  return $outArr;
1689  }
1690 
1701  public static function addSlashesOnArray(array &$theArray)
1702  {
1703  self::logDeprecatedFunction();
1704  foreach ($theArray as &$value) {
1705  if (is_array($value)) {
1706  self::addSlashesOnArray($value);
1707  } else {
1708  $value = addslashes($value);
1709  }
1710  }
1711  unset($value);
1712  reset($theArray);
1713  }
1714 
1725  public static function stripSlashesOnArray(array &$theArray)
1726  {
1727  foreach ($theArray as &$value) {
1728  if (is_array($value)) {
1729  self::stripSlashesOnArray($value);
1730  } else {
1731  $value = stripslashes($value);
1732  }
1733  }
1734  unset($value);
1735  reset($theArray);
1736  }
1737 
1746  public static function slashArray(array $arr, $cmd)
1747  {
1748  self::logDeprecatedFunction();
1749  if ($cmd == 'strip') {
1750  self::stripSlashesOnArray($arr);
1751  }
1752  if ($cmd == 'add') {
1753  self::addSlashesOnArray($arr);
1754  }
1755  return $arr;
1756  }
1757 
1765  public static function remapArrayKeys(&$array, $mappingTable)
1766  {
1767  static::logDeprecatedFunction();
1768  ArrayUtility::remapArrayKeys($array, $mappingTable);
1769  }
1770 
1779  public static function array_merge(array $arr1, array $arr2)
1780  {
1781  static::logDeprecatedFunction();
1782  return $arr2 + $arr1;
1783  }
1784 
1794  public static function arrayDiffAssocRecursive(array $array1, array $array2)
1795  {
1796  static::logDeprecatedFunction();
1797  return ArrayUtility::arrayDiffAssocRecursive($array1, $array2);
1798  }
1799 
1808  public static function csvValues(array $row, $delim = ',', $quote = '"')
1809  {
1810  $out = [];
1811  foreach ($row as $value) {
1812  $out[] = str_replace($quote, $quote . $quote, $value);
1813  }
1814  $str = $quote . implode(($quote . $delim . $quote), $out) . $quote;
1815  return $str;
1816  }
1817 
1825  public static function removeDotsFromTS(array $ts)
1826  {
1827  $out = [];
1828  foreach ($ts as $key => $value) {
1829  if (is_array($value)) {
1830  $key = rtrim($key, '.');
1831  $out[$key] = self::removeDotsFromTS($value);
1832  } else {
1833  $out[$key] = $value;
1834  }
1835  }
1836  return $out;
1837  }
1838 
1846  public static function naturalKeySortRecursive(&$array)
1847  {
1848  static::logDeprecatedFunction();
1850  }
1851 
1852  /*************************
1853  *
1854  * HTML/XML PROCESSING
1855  *
1856  *************************/
1865  public static function get_tag_attributes($tag)
1866  {
1867  $components = self::split_tag_attributes($tag);
1868  // Attribute name is stored here
1869  $name = '';
1870  $valuemode = false;
1871  $attributes = [];
1872  foreach ($components as $key => $val) {
1873  // Only if $name is set (if there is an attribute, that waits for a value), that valuemode is enabled. This ensures that the attribute is assigned it's value
1874  if ($val != '=') {
1875  if ($valuemode) {
1876  if ($name) {
1877  $attributes[$name] = $val;
1878  $name = '';
1879  }
1880  } else {
1881  if ($key = strtolower(preg_replace('/[^[:alnum:]_\\:\\-]/', '', $val))) {
1882  $attributes[$key] = '';
1883  $name = $key;
1884  }
1885  }
1886  $valuemode = false;
1887  } else {
1888  $valuemode = true;
1889  }
1890  }
1891  return $attributes;
1892  }
1893 
1901  public static function split_tag_attributes($tag)
1902  {
1903  $tag_tmp = trim(preg_replace('/^<[^[:space:]]*/', '', trim($tag)));
1904  // Removes any > in the end of the string
1905  $tag_tmp = trim(rtrim($tag_tmp, '>'));
1906  $value = [];
1907  // Compared with empty string instead , 030102
1908  while ($tag_tmp !== '') {
1909  $firstChar = $tag_tmp[0];
1910  if ($firstChar === '"' || $firstChar === '\'') {
1911  $reg = explode($firstChar, $tag_tmp, 3);
1912  $value[] = $reg[1];
1913  $tag_tmp = trim($reg[2]);
1914  } elseif ($firstChar === '=') {
1915  $value[] = '=';
1916  // Removes = chars.
1917  $tag_tmp = trim(substr($tag_tmp, 1));
1918  } else {
1919  // There are '' around the value. We look for the next ' ' or '>'
1920  $reg = preg_split('/[[:space:]=]/', $tag_tmp, 2);
1921  $value[] = trim($reg[0]);
1922  $tag_tmp = trim(substr($tag_tmp, strlen($reg[0]), 1) . $reg[1]);
1923  }
1924  }
1925  reset($value);
1926  return $value;
1927  }
1928 
1937  public static function implodeAttributes(array $arr, $xhtmlSafe = false, $dontOmitBlankAttribs = false)
1938  {
1939  if ($xhtmlSafe) {
1940  $newArr = [];
1941  foreach ($arr as $p => $v) {
1942  if (!isset($newArr[strtolower($p)])) {
1943  $newArr[strtolower($p)] = htmlspecialchars($v);
1944  }
1945  }
1946  $arr = $newArr;
1947  }
1948  $list = [];
1949  foreach ($arr as $p => $v) {
1950  if ((string)$v !== '' || $dontOmitBlankAttribs) {
1951  $list[] = $p . '="' . $v . '"';
1952  }
1953  }
1954  return implode(' ', $list);
1955  }
1956 
1966  public static function wrapJS($string, $linebreak = true)
1967  {
1968  if (trim($string)) {
1969  // <script wrapped in nl?
1970  $cr = $linebreak ? LF : '';
1971  // remove nl from the beginning
1972  $string = preg_replace('/^\\n+/', '', $string);
1973  // re-ident to one tab using the first line as reference
1974  $match = [];
1975  if (preg_match('/^(\\t+)/', $string, $match)) {
1976  $string = str_replace($match[1], TAB, $string);
1977  }
1978  $string = $cr . '<script type="text/javascript">
1979 /*<![CDATA[*/
1980 ' . $string . '
1981 /*]]>*/
1982 </script>' . $cr;
1983  }
1984  return trim($string);
1985  }
1986 
1995  public static function xml2tree($string, $depth = 999, $parserOptions = [])
1996  {
1997  // Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
1998  $previousValueOfEntityLoader = libxml_disable_entity_loader(true);
1999  $parser = xml_parser_create();
2000  $vals = [];
2001  $index = [];
2002  xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
2003  xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
2004  foreach ($parserOptions as $option => $value) {
2005  xml_parser_set_option($parser, $option, $value);
2006  }
2007  xml_parse_into_struct($parser, $string, $vals, $index);
2008  libxml_disable_entity_loader($previousValueOfEntityLoader);
2009  if (xml_get_error_code($parser)) {
2010  return 'Line ' . xml_get_current_line_number($parser) . ': ' . xml_error_string(xml_get_error_code($parser));
2011  }
2012  xml_parser_free($parser);
2013  $stack = [[]];
2014  $stacktop = 0;
2015  $startPoint = 0;
2016  $tagi = [];
2017  foreach ($vals as $key => $val) {
2018  $type = $val['type'];
2019  // open tag:
2020  if ($type == 'open' || $type == 'complete') {
2021  $stack[$stacktop++] = $tagi;
2022  if ($depth == $stacktop) {
2023  $startPoint = $key;
2024  }
2025  $tagi = ['tag' => $val['tag']];
2026  if (isset($val['attributes'])) {
2027  $tagi['attrs'] = $val['attributes'];
2028  }
2029  if (isset($val['value'])) {
2030  $tagi['values'][] = $val['value'];
2031  }
2032  }
2033  // finish tag:
2034  if ($type == 'complete' || $type == 'close') {
2035  $oldtagi = $tagi;
2036  $tagi = $stack[--$stacktop];
2037  $oldtag = $oldtagi['tag'];
2038  unset($oldtagi['tag']);
2039  if ($depth == $stacktop + 1) {
2040  if ($key - $startPoint > 0) {
2041  $partArray = array_slice($vals, $startPoint + 1, $key - $startPoint - 1);
2042  $oldtagi['XMLvalue'] = self::xmlRecompileFromStructValArray($partArray);
2043  } else {
2044  $oldtagi['XMLvalue'] = $oldtagi['values'][0];
2045  }
2046  }
2047  $tagi['ch'][$oldtag][] = $oldtagi;
2048  unset($oldtagi);
2049  }
2050  // cdata
2051  if ($type == 'cdata') {
2052  $tagi['values'][] = $val['value'];
2053  }
2054  }
2055  return $tagi['ch'];
2056  }
2057 
2068  public static function array2xml_cs(array $array, $docTag = 'phparray', array $options = [], $charset = '')
2069  {
2070  // Set default charset unless explicitly specified
2071  $charset = $charset ?: 'utf-8';
2072  // Return XML:
2073  return '<?xml version="1.0" encoding="' . htmlspecialchars($charset) . '" standalone="yes" ?>' . LF . self::array2xml($array, '', 0, $docTag, 0, $options);
2074  }
2075 
2098  public static function array2xml(array $array, $NSprefix = '', $level = 0, $docTag = 'phparray', $spaceInd = 0, array $options = [], array $stackData = [])
2099  {
2100  // The list of byte values which will trigger binary-safe storage. If any value has one of these char values in it, it will be encoded in base64
2101  $binaryChars = chr(0) . chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8) . chr(11) . chr(12) . chr(14) . chr(15) . chr(16) . chr(17) . chr(18) . chr(19) . chr(20) . chr(21) . chr(22) . chr(23) . chr(24) . chr(25) . chr(26) . chr(27) . chr(28) . chr(29) . chr(30) . chr(31);
2102  // Set indenting mode:
2103  $indentChar = $spaceInd ? ' ' : TAB;
2104  $indentN = $spaceInd > 0 ? $spaceInd : 1;
2105  $nl = $spaceInd >= 0 ? LF : '';
2106  // Init output variable:
2107  $output = '';
2108  // Traverse the input array
2109  foreach ($array as $k => $v) {
2110  $attr = '';
2111  $tagName = $k;
2112  // Construct the tag name.
2113  // Use tag based on grand-parent + parent tag name
2114  if (isset($options['grandParentTagMap'][$stackData['grandParentTagName'] . '/' . $stackData['parentTagName']])) {
2115  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2116  $tagName = (string)$options['grandParentTagMap'][$stackData['grandParentTagName'] . '/' . $stackData['parentTagName']];
2117  } elseif (isset($options['parentTagMap'][$stackData['parentTagName'] . ':_IS_NUM']) && MathUtility::canBeInterpretedAsInteger($tagName)) {
2118  // Use tag based on parent tag name + if current tag is numeric
2119  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2120  $tagName = (string)$options['parentTagMap'][$stackData['parentTagName'] . ':_IS_NUM'];
2121  } elseif (isset($options['parentTagMap'][$stackData['parentTagName'] . ':' . $tagName])) {
2122  // Use tag based on parent tag name + current tag
2123  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2124  $tagName = (string)$options['parentTagMap'][$stackData['parentTagName'] . ':' . $tagName];
2125  } elseif (isset($options['parentTagMap'][$stackData['parentTagName']])) {
2126  // Use tag based on parent tag name:
2127  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2128  $tagName = (string)$options['parentTagMap'][$stackData['parentTagName']];
2129  } elseif (MathUtility::canBeInterpretedAsInteger($tagName)) {
2130  // If integer...;
2131  if ($options['useNindex']) {
2132  // If numeric key, prefix "n"
2133  $tagName = 'n' . $tagName;
2134  } else {
2135  // Use special tag for num. keys:
2136  $attr .= ' index="' . $tagName . '"';
2137  $tagName = $options['useIndexTagForNum'] ?: 'numIndex';
2138  }
2139  } elseif ($options['useIndexTagForAssoc']) {
2140  // Use tag for all associative keys:
2141  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2142  $tagName = $options['useIndexTagForAssoc'];
2143  }
2144  // The tag name is cleaned up so only alphanumeric chars (plus - and _) are in there and not longer than 100 chars either.
2145  $tagName = substr(preg_replace('/[^[:alnum:]_-]/', '', $tagName), 0, 100);
2146  // If the value is an array then we will call this function recursively:
2147  if (is_array($v)) {
2148  // Sub elements:
2149  if ($options['alt_options'][$stackData['path'] . '/' . $tagName]) {
2150  $subOptions = $options['alt_options'][$stackData['path'] . '/' . $tagName];
2151  $clearStackPath = $subOptions['clearStackPath'];
2152  } else {
2153  $subOptions = $options;
2154  $clearStackPath = false;
2155  }
2156  if (empty($v)) {
2157  $content = '';
2158  } else {
2159  $content = $nl . self::array2xml($v, $NSprefix, ($level + 1), '', $spaceInd, $subOptions, [
2160  'parentTagName' => $tagName,
2161  'grandParentTagName' => $stackData['parentTagName'],
2162  'path' => ($clearStackPath ? '' : $stackData['path'] . '/' . $tagName)
2163  ]) . ($spaceInd >= 0 ? str_pad('', ($level + 1) * $indentN, $indentChar) : '');
2164  }
2165  // Do not set "type = array". Makes prettier XML but means that empty arrays are not restored with xml2array
2166  if ((int)$options['disableTypeAttrib'] != 2) {
2167  $attr .= ' type="array"';
2168  }
2169  } else {
2170  // Just a value:
2171  // Look for binary chars:
2172  $vLen = strlen($v);
2173  // Go for base64 encoding if the initial segment NOT matching any binary char has the same length as the whole string!
2174  if ($vLen && strcspn($v, $binaryChars) != $vLen) {
2175  // If the value contained binary chars then we base64-encode it an set an attribute to notify this situation:
2176  $content = $nl . chunk_split(base64_encode($v));
2177  $attr .= ' base64="1"';
2178  } else {
2179  // Otherwise, just htmlspecialchar the stuff:
2180  $content = htmlspecialchars($v);
2181  $dType = gettype($v);
2182  if ($dType == 'string') {
2183  if ($options['useCDATA'] && $content != $v) {
2184  $content = '<![CDATA[' . $v . ']]>';
2185  }
2186  } elseif (!$options['disableTypeAttrib']) {
2187  $attr .= ' type="' . $dType . '"';
2188  }
2189  }
2190  }
2191  if ((string)$tagName !== '') {
2192  // Add the element to the output string:
2193  $output .= ($spaceInd >= 0 ? str_pad('', ($level + 1) * $indentN, $indentChar) : '')
2194  . '<' . $NSprefix . $tagName . $attr . '>' . $content . '</' . $NSprefix . $tagName . '>' . $nl;
2195  }
2196  }
2197  // If we are at the outer-most level, then we finally wrap it all in the document tags and return that as the value:
2198  if (!$level) {
2199  $output = '<' . $docTag . '>' . $nl . $output . '</' . $docTag . '>';
2200  }
2201  return $output;
2202  }
2203 
2215  public static function xml2array($string, $NSprefix = '', $reportDocTag = false)
2216  {
2217  static $firstLevelCache = [];
2218  $identifier = md5($string . $NSprefix . ($reportDocTag ? '1' : '0'));
2219  // Look up in first level cache
2220  if (!empty($firstLevelCache[$identifier])) {
2221  $array = $firstLevelCache[$identifier];
2222  } else {
2223  // Look up in second level cache
2224  // @todo: Is this cache really required? It basically substitutes a little cpu work with a db query?
2225  $array = PageRepository::getHash($identifier, 0);
2226  if (!is_array($array)) {
2227  $array = self::xml2arrayProcess(trim($string), $NSprefix, $reportDocTag);
2228  PageRepository::storeHash($identifier, $array, 'ident_xml2array');
2229  }
2230  // Store content in first level cache
2231  $firstLevelCache[$identifier] = $array;
2232  }
2233  return $array;
2234  }
2235 
2246  protected static function xml2arrayProcess($string, $NSprefix = '', $reportDocTag = false)
2247  {
2248  // Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
2249  $previousValueOfEntityLoader = libxml_disable_entity_loader(true);
2250  // Create parser:
2251  $parser = xml_parser_create();
2252  $vals = [];
2253  $index = [];
2254  xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
2255  xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
2256  // Default output charset is UTF-8, only ASCII, ISO-8859-1 and UTF-8 are supported!!!
2257  $match = [];
2258  preg_match('/^[[:space:]]*<\\?xml[^>]*encoding[[:space:]]*=[[:space:]]*"([^"]*)"/', substr($string, 0, 200), $match);
2259  $theCharset = $match[1] ?: 'utf-8';
2260  // us-ascii / utf-8 / iso-8859-1
2261  xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $theCharset);
2262  // Parse content:
2263  xml_parse_into_struct($parser, $string, $vals, $index);
2264  libxml_disable_entity_loader($previousValueOfEntityLoader);
2265  // If error, return error message:
2266  if (xml_get_error_code($parser)) {
2267  return 'Line ' . xml_get_current_line_number($parser) . ': ' . xml_error_string(xml_get_error_code($parser));
2268  }
2269  xml_parser_free($parser);
2270  // Init vars:
2271  $stack = [[]];
2272  $stacktop = 0;
2273  $current = [];
2274  $tagName = '';
2275  $documentTag = '';
2276  // Traverse the parsed XML structure:
2277  foreach ($vals as $key => $val) {
2278  // First, process the tag-name (which is used in both cases, whether "complete" or "close")
2279  $tagName = $val['tag'];
2280  if (!$documentTag) {
2281  $documentTag = $tagName;
2282  }
2283  // Test for name space:
2284  $tagName = $NSprefix && substr($tagName, 0, strlen($NSprefix)) == $NSprefix ? substr($tagName, strlen($NSprefix)) : $tagName;
2285  // Test for numeric tag, encoded on the form "nXXX":
2286  $testNtag = substr($tagName, 1);
2287  // Closing tag.
2288  $tagName = $tagName[0] === 'n' && MathUtility::canBeInterpretedAsInteger($testNtag) ? (int)$testNtag : $tagName;
2289  // Test for alternative index value:
2290  if ((string)$val['attributes']['index'] !== '') {
2291  $tagName = $val['attributes']['index'];
2292  }
2293  // Setting tag-values, manage stack:
2294  switch ($val['type']) {
2295  case 'open':
2296  // If open tag it means there is an array stored in sub-elements. Therefore increase the stackpointer and reset the accumulation array:
2297  // Setting blank place holder
2298  $current[$tagName] = [];
2299  $stack[$stacktop++] = $current;
2300  $current = [];
2301  break;
2302  case 'close':
2303  // If the tag is "close" then it is an array which is closing and we decrease the stack pointer.
2304  $oldCurrent = $current;
2305  $current = $stack[--$stacktop];
2306  // Going to the end of array to get placeholder key, key($current), and fill in array next:
2307  end($current);
2308  $current[key($current)] = $oldCurrent;
2309  unset($oldCurrent);
2310  break;
2311  case 'complete':
2312  // If "complete", then it's a value. If the attribute "base64" is set, then decode the value, otherwise just set it.
2313  if ($val['attributes']['base64']) {
2314  $current[$tagName] = base64_decode($val['value']);
2315  } else {
2316  // Had to cast it as a string - otherwise it would be evaluate FALSE if tested with isset()!!
2317  $current[$tagName] = (string)$val['value'];
2318  // Cast type:
2319  switch ((string)$val['attributes']['type']) {
2320  case 'integer':
2321  $current[$tagName] = (int)$current[$tagName];
2322  break;
2323  case 'double':
2324  $current[$tagName] = (double) $current[$tagName];
2325  break;
2326  case 'boolean':
2327  $current[$tagName] = (bool)$current[$tagName];
2328  break;
2329  case 'NULL':
2330  $current[$tagName] = null;
2331  break;
2332  case 'array':
2333  // MUST be an empty array since it is processed as a value; Empty arrays would end up here because they would have no tags inside...
2334  $current[$tagName] = [];
2335  break;
2336  }
2337  }
2338  break;
2339  }
2340  }
2341  if ($reportDocTag) {
2342  $current[$tagName]['_DOCUMENT_TAG'] = $documentTag;
2343  }
2344  // Finally return the content of the document tag.
2345  return $current[$tagName];
2346  }
2347 
2354  public static function xmlRecompileFromStructValArray(array $vals)
2355  {
2356  $XMLcontent = '';
2357  foreach ($vals as $val) {
2358  $type = $val['type'];
2359  // Open tag:
2360  if ($type == 'open' || $type == 'complete') {
2361  $XMLcontent .= '<' . $val['tag'];
2362  if (isset($val['attributes'])) {
2363  foreach ($val['attributes'] as $k => $v) {
2364  $XMLcontent .= ' ' . $k . '="' . htmlspecialchars($v) . '"';
2365  }
2366  }
2367  if ($type == 'complete') {
2368  if (isset($val['value'])) {
2369  $XMLcontent .= '>' . htmlspecialchars($val['value']) . '</' . $val['tag'] . '>';
2370  } else {
2371  $XMLcontent .= '/>';
2372  }
2373  } else {
2374  $XMLcontent .= '>';
2375  }
2376  if ($type == 'open' && isset($val['value'])) {
2377  $XMLcontent .= htmlspecialchars($val['value']);
2378  }
2379  }
2380  // Finish tag:
2381  if ($type == 'close') {
2382  $XMLcontent .= '</' . $val['tag'] . '>';
2383  }
2384  // Cdata
2385  if ($type == 'cdata') {
2386  $XMLcontent .= htmlspecialchars($val['value']);
2387  }
2388  }
2389  return $XMLcontent;
2390  }
2391 
2398  public static function xmlGetHeaderAttribs($xmlData)
2399  {
2400  $match = [];
2401  if (preg_match('/^\\s*<\\?xml([^>]*)\\?\\>/', $xmlData, $match)) {
2402  return self::get_tag_attributes($match[1]);
2403  }
2404  }
2405 
2413  public static function minifyJavaScript($script, &$error = '')
2414  {
2415  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript'])) {
2416  $fakeThis = false;
2417  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript'] as $hookMethod) {
2418  try {
2419  $parameters = ['script' => $script];
2420  $script = static::callUserFunction($hookMethod, $parameters, $fakeThis);
2421  } catch (\Exception $e) {
2422  $errorMessage = 'Error minifying java script: ' . $e->getMessage();
2423  $error .= $errorMessage;
2424  static::devLog($errorMessage, \TYPO3\CMS\Core\Utility\GeneralUtility::class, 2, [
2425  'JavaScript' => $script,
2426  'Stack trace' => $e->getTrace(),
2427  'hook' => $hookMethod
2428  ]);
2429  }
2430  }
2431  }
2432  return $script;
2433  }
2434 
2435  /*************************
2436  *
2437  * FILES FUNCTIONS
2438  *
2439  *************************/
2450  public static function getUrl($url, $includeHeader = 0, $requestHeaders = false, &$report = null)
2451  {
2452  $content = false;
2453  if (isset($report)) {
2454  $report['error'] = 0;
2455  $report['message'] = '';
2456  }
2457  // Use cURL for: http, https, ftp, ftps, sftp and scp
2458  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlUse'] == '1' && preg_match('/^(?:http|ftp)s?|s(?:ftp|cp):/', $url)) {
2459  if (isset($report)) {
2460  $report['lib'] = 'cURL';
2461  }
2462  // External URL without error checking.
2463  if (!function_exists('curl_init') || !($ch = curl_init())) {
2464  if (isset($report)) {
2465  $report['error'] = -1;
2466  $report['message'] = 'Couldn\'t initialize cURL.';
2467  }
2468  return false;
2469  }
2470 
2471  $followLocationSucceeded = @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
2472 
2473  $curlIncludeHeaders = !$followLocationSucceeded || $includeHeader;
2474  curl_setopt($ch, CURLOPT_URL, $url);
2475  curl_setopt($ch, CURLOPT_HEADER, $curlIncludeHeaders ? 1 : 0);
2476  curl_setopt($ch, CURLOPT_NOBODY, $includeHeader == 2 ? 1 : 0);
2477  curl_setopt($ch, CURLOPT_HTTPGET, $includeHeader == 2 ? 'HEAD' : 'GET');
2478  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
2479  curl_setopt($ch, CURLOPT_FAILONERROR, 1);
2480  curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, max(0, (int)$GLOBALS['TYPO3_CONF_VARS']['SYS']['curlTimeout']));
2481 
2482  if (is_array($requestHeaders)) {
2483  curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);
2484  }
2485  // (Proxy support implemented by Arco <arco@appeltaart.mine.nu>)
2486  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']) {
2487  curl_setopt($ch, CURLOPT_PROXY, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']);
2488  curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, (bool)$GLOBALS['TYPO3_CONF_VARS']['HTTP']['ssl_verify_host']);
2489  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (bool)$GLOBALS['TYPO3_CONF_VARS']['HTTP']['ssl_verify_peer']);
2490  if ($GLOBALS['TYPO3_CONF_VARS']['HTTP']['ssl_verify_peer']) {
2491  if ($GLOBALS['TYPO3_CONF_VARS']['HTTP']['ssl_cafile']) {
2492  curl_setopt($ch, CURLOPT_CAINFO, $GLOBALS['TYPO3_CONF_VARS']['HTTP']['ssl_cafile']);
2493  }
2494  if ($GLOBALS['TYPO3_CONF_VARS']['HTTP']['ssl_capath']) {
2495  curl_setopt($ch, CURLOPT_CAPATH, $GLOBALS['TYPO3_CONF_VARS']['HTTP']['ssl_capath']);
2496  }
2497  }
2498  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyNTLM']) {
2499  curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
2500  }
2501  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyTunnel']) {
2502  curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyTunnel']);
2503  }
2504  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyUserPass']) {
2505  curl_setopt($ch, CURLOPT_PROXYUSERPWD, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyUserPass']);
2506  }
2507  }
2508  $content = curl_exec($ch);
2509  $curlInfo = curl_getinfo($ch);
2510 
2511  // Remove additional proxy header block, when proxy is used for https request and CURL_HEADER is enabled.
2512  // Most HTTPS proxies add a second header before the actual server headers in their response, as a
2513  // response to the CONNECT message sent by the client to the proxy. cURL does not strip this since 2005,
2514  // so there are two headers arriving here, of which the first is not of interest to us—therefore, we can
2515  // safely strip it.
2516  // Detecting two linebreaks followed by a "HTTP/" (as done here) is the only reliable way to detect the
2517  // proxy headers, as the relevant RFCs do not specify the exact status code (it might be any of 2xx) or
2518  // the status message. Therefore, we check if there is a second HTTP headers block and then strip the
2519  // first one.
2520  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']
2521  && $curlIncludeHeaders
2522  && preg_match('/^https:/', $url)
2523  && strpos($content, "\r\n\r\nHTTP/") !== false
2524  ) {
2525  $content = self::stripHttpHeaders($content);
2526  }
2527 
2528  if (!$followLocationSucceeded) {
2529  // Check if we need to do redirects
2530  if ($curlInfo['http_code'] >= 300 && $curlInfo['http_code'] < 400) {
2531  $locationUrl = $curlInfo['redirect_url'];
2532  if (!$locationUrl) {
2533  // Some curllib versions do not return redirect_url. Examine headers.
2534  $locationUrl = self::getRedirectUrlFromHttpHeaders($content);
2535  }
2536  if ($locationUrl) {
2537  $content = self::getUrl($locationUrl, $includeHeader, $requestHeaders, $report);
2538  $followLocationSucceeded = true;
2539  } else {
2540  // Failure: we got a redirection status code but not the URL to redirect to.
2541  $content = false;
2542  }
2543  }
2544  if ($content && !$includeHeader) {
2545  $content = self::stripHttpHeaders($content);
2546  }
2547  }
2548 
2549  if (isset($report)) {
2550  if (!$followLocationSucceeded && $curlInfo['http_code'] >= 300 && $curlInfo['http_code'] < 400) {
2551  $report['http_code'] = $curlInfo['http_code'];
2552  $report['content_type'] = $curlInfo['content_type'];
2553  $report['error'] = CURLE_GOT_NOTHING;
2554  $report['message'] = 'Expected "Location" header but got nothing.';
2555  } else {
2556  if ($content === false) {
2557  $report['error'] = curl_errno($ch);
2558  $report['message'] = curl_error($ch);
2559  }
2560  // Set only for $includeHeader to work exactly like PHP variant
2561  if ($includeHeader) {
2562  $report['http_code'] = $curlInfo['http_code'];
2563  $report['content_type'] = $curlInfo['content_type'];
2564  }
2565  }
2566  }
2567  curl_close($ch);
2568  } elseif ($includeHeader) {
2569  if (isset($report)) {
2570  $report['lib'] = 'socket';
2571  }
2572  $parsedURL = parse_url($url);
2573  if (!preg_match('/^https?/', $parsedURL['scheme'])) {
2574  if (isset($report)) {
2575  $report['error'] = -1;
2576  $report['message'] = 'Reading headers is not allowed for this protocol.';
2577  }
2578  return false;
2579  }
2580  $port = (int)$parsedURL['port'];
2581  if ($port < 1) {
2582  if ($parsedURL['scheme'] == 'http') {
2583  $port = $port > 0 ? $port : 80;
2584  $scheme = '';
2585  } else {
2586  $port = $port > 0 ? $port : 443;
2587  $scheme = 'ssl://';
2588  }
2589  }
2590  $errno = 0;
2591  $fp = @fsockopen(($scheme . $parsedURL['host']), $port, $errno, $errstr, 2.0);
2592  if (!$fp || $errno > 0) {
2593  if (isset($report)) {
2594  $report['error'] = $errno ?: -1;
2595  $report['message'] = $errno ? ($errstr ?: 'Socket error.') : 'Socket initialization error.';
2596  }
2597  return false;
2598  }
2599  $method = $includeHeader == 2 ? 'HEAD' : 'GET';
2600  $msg = $method . ' ' . (isset($parsedURL['path']) ? $parsedURL['path'] : '/')
2601  . ($parsedURL['query'] ? '?' . $parsedURL['query'] : '') . ' HTTP/1.0' . CRLF
2602  . 'Host: ' . $parsedURL['host'] . CRLF
2603  . 'Connection: close' . CRLF;
2604  if (is_array($requestHeaders)) {
2605  $msg .= implode(CRLF, $requestHeaders) . CRLF;
2606  }
2607  $msg .= CRLF;
2608  fwrite($fp, $msg);
2609  while (!feof($fp)) {
2610  $line = fgets($fp, 2048);
2611  if (isset($report)) {
2612  if (preg_match('|^HTTP/\\d\\.\\d +(\\d+)|', $line, $status)) {
2613  $report['http_code'] = $status[1];
2614  } elseif (preg_match('/^Content-Type: *(.*)/i', $line, $type)) {
2615  $report['content_type'] = $type[1];
2616  }
2617  }
2618  $content .= $line;
2619  if (trim($line) === '') {
2620  // Stop at the first empty line (= end of header)
2621  break;
2622  }
2623  }
2624  if ($includeHeader != 2) {
2625  $content .= stream_get_contents($fp);
2626  }
2627  fclose($fp);
2628  } elseif (is_array($requestHeaders)) {
2629  if (isset($report)) {
2630  $report['lib'] = 'file/context';
2631  }
2632  $parsedURL = parse_url($url);
2633  if (!preg_match('/^https?/', $parsedURL['scheme'])) {
2634  if (isset($report)) {
2635  $report['error'] = -1;
2636  $report['message'] = 'Sending request headers is not allowed for this protocol.';
2637  }
2638  return false;
2639  }
2640  $ctx = stream_context_get_default([
2641  'http' => [
2642  'header' => implode(CRLF, $requestHeaders)
2643  ]
2644  ]);
2645  $content = @file_get_contents($url, false, $ctx);
2646  if ($content === false && isset($report)) {
2647  $report['error'] = -1;
2648  $report['message'] = 'Couldn\'t get URL: ' . (isset($http_response_header) ? implode(LF, $http_response_header) : $url);
2649  }
2650  } else {
2651  if (isset($report)) {
2652  $report['lib'] = 'file';
2653  }
2654  $content = @file_get_contents($url);
2655  if ($content === false && isset($report)) {
2656  $report['error'] = -1;
2657  $report['message'] = 'Couldn\'t get URL: ' . (isset($http_response_header) ? implode(LF, $http_response_header) : $url);
2658  }
2659  }
2660  return $content;
2661  }
2662 
2670  protected static function getRedirectUrlFromHttpHeaders($content)
2671  {
2672  $result = '';
2673  $headers = explode("\r\n", $content);
2674  foreach ($headers as $header) {
2675  if ($header == '') {
2676  break;
2677  }
2678  if (preg_match('/^\s*Location\s*:/i', $header)) {
2679  list(, $result) = self::trimExplode(':', $header, false, 2);
2680  if ($result) {
2681  $result = self::locationHeaderUrl($result);
2682  }
2683  break;
2684  }
2685  }
2686  return $result;
2687  }
2688 
2695  protected static function stripHttpHeaders($content)
2696  {
2697  $headersEndPos = strpos($content, "\r\n\r\n");
2698  if ($headersEndPos) {
2699  $content = substr($content, $headersEndPos + 4);
2700  }
2701  return $content;
2702  }
2703 
2712  public static function writeFile($file, $content, $changePermissions = false)
2713  {
2714  if (!@is_file($file)) {
2715  $changePermissions = true;
2716  }
2717  if ($fd = fopen($file, 'wb')) {
2718  $res = fwrite($fd, $content);
2719  fclose($fd);
2720  if ($res === false) {
2721  return false;
2722  }
2723  // Change the permissions only if the file has just been created
2724  if ($changePermissions) {
2725  static::fixPermissions($file);
2726  }
2727  return true;
2728  }
2729  return false;
2730  }
2731 
2739  public static function fixPermissions($path, $recursive = false)
2740  {
2741  if (TYPO3_OS === 'WIN') {
2742  return true;
2743  }
2744  $result = false;
2745  // Make path absolute
2746  if (!static::isAbsPath($path)) {
2747  $path = static::getFileAbsFileName($path, false);
2748  }
2749  if (static::isAllowedAbsPath($path)) {
2750  if (@is_file($path)) {
2751  $targetPermissions = isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'])
2752  ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask']
2753  : '0644';
2754  } elseif (@is_dir($path)) {
2755  $targetPermissions = isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'])
2756  ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']
2757  : '0755';
2758  }
2759  if (!empty($targetPermissions)) {
2760  // make sure it's always 4 digits
2761  $targetPermissions = str_pad($targetPermissions, 4, 0, STR_PAD_LEFT);
2762  $targetPermissions = octdec($targetPermissions);
2763  // "@" is there because file is not necessarily OWNED by the user
2764  $result = @chmod($path, $targetPermissions);
2765  }
2766  // Set createGroup if not empty
2767  if (
2768  isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'])
2769  && $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] !== ''
2770  ) {
2771  // "@" is there because file is not necessarily OWNED by the user
2772  $changeGroupResult = @chgrp($path, $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup']);
2773  $result = $changeGroupResult ? $result : false;
2774  }
2775  // Call recursive if recursive flag if set and $path is directory
2776  if ($recursive && @is_dir($path)) {
2777  $handle = opendir($path);
2778  if (is_resource($handle)) {
2779  while (($file = readdir($handle)) !== false) {
2780  $recursionResult = null;
2781  if ($file !== '.' && $file !== '..') {
2782  if (@is_file(($path . '/' . $file))) {
2783  $recursionResult = static::fixPermissions($path . '/' . $file);
2784  } elseif (@is_dir(($path . '/' . $file))) {
2785  $recursionResult = static::fixPermissions($path . '/' . $file, true);
2786  }
2787  if (isset($recursionResult) && !$recursionResult) {
2788  $result = false;
2789  }
2790  }
2791  }
2792  closedir($handle);
2793  }
2794  }
2795  }
2796  return $result;
2797  }
2798 
2807  public static function writeFileToTypo3tempDir($filepath, $content)
2808  {
2809  if (!defined('PATH_site')) {
2810  return 'PATH_site constant was NOT defined!';
2811  }
2812 
2813  // Parse filepath into directory and basename:
2814  $fI = pathinfo($filepath);
2815  $fI['dirname'] .= '/';
2816  // Check parts:
2817  if (!static::validPathStr($filepath) || !$fI['basename'] || strlen($fI['basename']) >= 60) {
2818  return 'Input filepath "' . $filepath . '" was generally invalid!';
2819  }
2820  // Setting main temporary directory name (standard)
2821  $dirName = PATH_site . 'typo3temp/';
2822  if (!@is_dir($dirName)) {
2823  return 'PATH_site + "typo3temp/" was not a directory!';
2824  }
2825  if (!static::isFirstPartOfStr($fI['dirname'], $dirName)) {
2826  return '"' . $fI['dirname'] . '" was not within directory PATH_site + "typo3temp/"';
2827  }
2828  // Checking if the "subdir" is found:
2829  $subdir = substr($fI['dirname'], strlen($dirName));
2830  if ($subdir) {
2831  if (preg_match('/^[[:alnum:]_]+\\/$/', $subdir) || preg_match('/^[[:alnum:]_]+\\/[[:alnum:]_]+\\/$/', $subdir)) {
2832  $dirName .= $subdir;
2833  if (!@is_dir($dirName)) {
2834  static::mkdir_deep(PATH_site . 'typo3temp/', $subdir);
2835  }
2836  } else {
2837  return 'Subdir, "' . $subdir . '", was NOT on the form "[[:alnum:]_]/" or "[[:alnum:]_]/[[:alnum:]_]/"';
2838  }
2839  }
2840  // Checking dir-name again (sub-dir might have been created):
2841  if (@is_dir($dirName)) {
2842  if ($filepath == $dirName . $fI['basename']) {
2843  static::writeFile($filepath, $content);
2844  if (!@is_file($filepath)) {
2845  return 'The file was not written to the disk. Please, check that you have write permissions to the typo3temp/ directory.';
2846  }
2847  } else {
2848  return 'Calculated filelocation didn\'t match input "' . $filepath . '".';
2849  }
2850  } else {
2851  return '"' . $dirName . '" is not a directory!';
2852  }
2853  return null;
2854  }
2855 
2864  public static function mkdir($newFolder)
2865  {
2866  $result = @mkdir($newFolder, octdec($GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']));
2867  if ($result) {
2868  static::fixPermissions($newFolder);
2869  }
2870  return $result;
2871  }
2872 
2883  public static function mkdir_deep($directory, $deepDirectory = '')
2884  {
2885  if (!is_string($directory)) {
2886  throw new \InvalidArgumentException('The specified directory is of type "' . gettype($directory) . '" but a string is expected.', 1303662955);
2887  }
2888  if (!is_string($deepDirectory)) {
2889  throw new \InvalidArgumentException('The specified directory is of type "' . gettype($deepDirectory) . '" but a string is expected.', 1303662956);
2890  }
2891  // Ensure there is only one slash
2892  $fullPath = rtrim($directory, '/') . '/' . ltrim($deepDirectory, '/');
2893  if ($fullPath !== '/' && !is_dir($fullPath)) {
2894  $firstCreatedPath = static::createDirectoryPath($fullPath);
2895  if ($firstCreatedPath !== '') {
2896  static::fixPermissions($firstCreatedPath, true);
2897  }
2898  }
2899  }
2900 
2912  protected static function createDirectoryPath($fullDirectoryPath)
2913  {
2914  $currentPath = $fullDirectoryPath;
2915  $firstCreatedPath = '';
2916  $permissionMask = octdec($GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']);
2917  if (!@is_dir($currentPath)) {
2918  do {
2919  $firstCreatedPath = $currentPath;
2920  $separatorPosition = strrpos($currentPath, DIRECTORY_SEPARATOR);
2921  $currentPath = substr($currentPath, 0, $separatorPosition);
2922  } while (!is_dir($currentPath) && $separatorPosition !== false);
2923  $result = @mkdir($fullDirectoryPath, $permissionMask, true);
2924  // Check existence of directory again to avoid race condition. Directory could have get created by another process between previous is_dir() and mkdir()
2925  if (!$result && !@is_dir($fullDirectoryPath)) {
2926  throw new \RuntimeException('Could not create directory "' . $fullDirectoryPath . '"!', 1170251401);
2927  }
2928  }
2929  return $firstCreatedPath;
2930  }
2931 
2939  public static function rmdir($path, $removeNonEmpty = false)
2940  {
2941  $OK = false;
2942  // Remove trailing slash
2943  $path = preg_replace('|/$|', '', $path);
2944  if (file_exists($path)) {
2945  $OK = true;
2946  if (!is_link($path) && is_dir($path)) {
2947  if ($removeNonEmpty == true && ($handle = @opendir($path))) {
2948  while ($OK && false !== ($file = readdir($handle))) {
2949  if ($file == '.' || $file == '..') {
2950  continue;
2951  }
2952  $OK = static::rmdir($path . '/' . $file, $removeNonEmpty);
2953  }
2954  closedir($handle);
2955  }
2956  if ($OK) {
2957  $OK = @rmdir($path);
2958  }
2959  } elseif (is_link($path) && is_dir($path) && TYPO3_OS === 'WIN') {
2960  $OK = @rmdir($path);
2961  } else {
2962  // If $path is a file, simply remove it
2963  $OK = @unlink($path);
2964  }
2965  clearstatcache();
2966  } elseif (is_link($path)) {
2967  $OK = @unlink($path);
2968  clearstatcache();
2969  }
2970  return $OK;
2971  }
2972 
2983  public static function flushDirectory($directory, $keepOriginalDirectory = false, $flushOpcodeCache = false)
2984  {
2985  $result = false;
2986 
2987  if (is_dir($directory)) {
2988  $temporaryDirectory = rtrim($directory, '/') . '.' . StringUtility::getUniqueId('remove') . '/';
2989  if (rename($directory, $temporaryDirectory)) {
2990  if ($flushOpcodeCache) {
2991  self::makeInstance(OpcodeCacheService::class)->clearAllActive($directory);
2992  }
2993  if ($keepOriginalDirectory) {
2994  static::mkdir($directory);
2995  }
2996  clearstatcache();
2997  $result = static::rmdir($temporaryDirectory, true);
2998  }
2999  }
3000 
3001  return $result;
3002  }
3003 
3011  public static function get_dirs($path)
3012  {
3013  if ($path) {
3014  if (is_dir($path)) {
3015  $dir = scandir($path);
3016  $dirs = [];
3017  foreach ($dir as $entry) {
3018  if (is_dir($path . '/' . $entry) && $entry != '..' && $entry != '.') {
3019  $dirs[] = $entry;
3020  }
3021  }
3022  } else {
3023  $dirs = 'error';
3024  }
3025  }
3026  return $dirs;
3027  }
3028 
3041  public static function getFilesInDir($path, $extensionList = '', $prependPath = false, $order = '', $excludePattern = '')
3042  {
3043  $excludePattern = (string)$excludePattern;
3044  $path = rtrim($path, '/');
3045  if (!@is_dir($path)) {
3046  return [];
3047  }
3048 
3049  $rawFileList = scandir($path);
3050  if ($rawFileList === false) {
3051  return 'error opening path: "' . $path . '"';
3052  }
3053 
3054  $pathPrefix = $path . '/';
3055  $extensionList = ',' . $extensionList . ',';
3056  $files = [];
3057  foreach ($rawFileList as $entry) {
3058  $completePathToEntry = $pathPrefix . $entry;
3059  if (!@is_file($completePathToEntry)) {
3060  continue;
3061  }
3062 
3063  if (
3064  ($extensionList === ',,' || stripos($extensionList, ',' . pathinfo($entry, PATHINFO_EXTENSION) . ',') !== false)
3065  && ($excludePattern === '' || !preg_match(('/^' . $excludePattern . '$/'), $entry))
3066  ) {
3067  if ($order !== 'mtime') {
3068  $files[] = $entry;
3069  } else {
3070  // Store the value in the key so we can do a fast asort later.
3071  $files[$entry] = filemtime($completePathToEntry);
3072  }
3073  }
3074  }
3075 
3076  $valueName = 'value';
3077  if ($order === 'mtime') {
3078  asort($files);
3079  $valueName = 'key';
3080  }
3081 
3082  $valuePathPrefix = $prependPath ? $pathPrefix : '';
3083  $foundFiles = [];
3084  foreach ($files as $key => $value) {
3085  // Don't change this ever - extensions may depend on the fact that the hash is an md5 of the path! (import/export extension)
3086  $foundFiles[md5($pathPrefix . ${$valueName})] = $valuePathPrefix . ${$valueName};
3087  }
3088 
3089  return $foundFiles;
3090  }
3091 
3103  public static function getAllFilesAndFoldersInPath(array $fileArr, $path, $extList = '', $regDirs = false, $recursivityLevels = 99, $excludePattern = '')
3104  {
3105  if ($regDirs) {
3106  $fileArr[md5($path)] = $path;
3107  }
3108  $fileArr = array_merge($fileArr, self::getFilesInDir($path, $extList, 1, 1, $excludePattern));
3109  $dirs = self::get_dirs($path);
3110  if ($recursivityLevels > 0 && is_array($dirs)) {
3111  foreach ($dirs as $subdirs) {
3112  if ((string)$subdirs !== '' && ($excludePattern === '' || !preg_match(('/^' . $excludePattern . '$/'), $subdirs))) {
3113  $fileArr = self::getAllFilesAndFoldersInPath($fileArr, $path . $subdirs . '/', $extList, $regDirs, $recursivityLevels - 1, $excludePattern);
3114  }
3115  }
3116  }
3117  return $fileArr;
3118  }
3119 
3127  public static function removePrefixPathFromList(array $fileArr, $prefixToRemove)
3128  {
3129  foreach ($fileArr as $k => &$absFileRef) {
3130  if (self::isFirstPartOfStr($absFileRef, $prefixToRemove)) {
3131  $absFileRef = substr($absFileRef, strlen($prefixToRemove));
3132  } else {
3133  return 'ERROR: One or more of the files was NOT prefixed with the prefix-path!';
3134  }
3135  }
3136  unset($absFileRef);
3137  return $fileArr;
3138  }
3139 
3146  public static function fixWindowsFilePath($theFile)
3147  {
3148  return str_replace(['\\', '//'], '/', $theFile);
3149  }
3150 
3158  public static function resolveBackPath($pathStr)
3159  {
3160  if (strpos($pathStr, '..') === false) {
3161  return $pathStr;
3162  }
3163  $parts = explode('/', $pathStr);
3164  $output = [];
3165  $c = 0;
3166  foreach ($parts as $part) {
3167  if ($part === '..') {
3168  if ($c) {
3169  array_pop($output);
3170  --$c;
3171  } else {
3172  $output[] = $part;
3173  }
3174  } else {
3175  ++$c;
3176  $output[] = $part;
3177  }
3178  }
3179  return implode('/', $output);
3180  }
3181 
3192  public static function locationHeaderUrl($path)
3193  {
3194  $uI = parse_url($path);
3195  // relative to HOST
3196  if ($path[0] === '/') {
3197  $path = self::getIndpEnv('TYPO3_REQUEST_HOST') . $path;
3198  } elseif (!$uI['scheme']) {
3199  // No scheme either
3200  $path = self::getIndpEnv('TYPO3_REQUEST_DIR') . $path;
3201  }
3202  // Can be removed once minimum PHP requirement is at least 5.5.22 or 5.6.6
3203  if (strpbrk($path, "\r\n") !== false) {
3204  throw new \InvalidArgumentException('HTTP header injection attempt in "' . $path . '"', 1448194036);
3205  }
3206  return $path;
3207  }
3208 
3216  public static function getMaxUploadFileSize()
3217  {
3218  // Check for PHP restrictions of the maximum size of one of the $_FILES
3219  $phpUploadLimit = self::getBytesFromSizeMeasurement(ini_get('upload_max_filesize'));
3220  // Check for PHP restrictions of the maximum $_POST size
3221  $phpPostLimit = self::getBytesFromSizeMeasurement(ini_get('post_max_size'));
3222  // If the total amount of post data is smaller (!) than the upload_max_filesize directive,
3223  // then this is the real limit in PHP
3224  $phpUploadLimit = $phpPostLimit > 0 && $phpPostLimit < $phpUploadLimit ? $phpPostLimit : $phpUploadLimit;
3225  return floor(($phpUploadLimit)) / 1024;
3226  }
3227 
3234  public static function getBytesFromSizeMeasurement($measurement)
3235  {
3236  $bytes = floatval($measurement);
3237  if (stripos($measurement, 'G')) {
3238  $bytes *= 1024 * 1024 * 1024;
3239  } elseif (stripos($measurement, 'M')) {
3240  $bytes *= 1024 * 1024;
3241  } elseif (stripos($measurement, 'K')) {
3242  $bytes *= 1024;
3243  }
3244  return $bytes;
3245  }
3246 
3252  public static function getMaximumPathLength()
3253  {
3254  return PHP_MAXPATHLEN;
3255  }
3256 
3273  public static function createVersionNumberedFilename($file)
3274  {
3275  $lookupFile = explode('?', $file);
3276  $path = self::resolveBackPath(self::dirname(PATH_thisScript) . '/' . $lookupFile[0]);
3277 
3278  $doNothing = false;
3279  if (TYPO3_MODE == 'FE') {
3280  $mode = strtolower($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['versionNumberInFilename']);
3281  if ($mode === 'embed') {
3282  $mode = true;
3283  } else {
3284  if ($mode === 'querystring') {
3285  $mode = false;
3286  } else {
3287  $doNothing = true;
3288  }
3289  }
3290  } else {
3291  $mode = $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['versionNumberInFilename'];
3292  }
3293  if (!file_exists($path) || $doNothing) {
3294  // File not found, return filename unaltered
3295  $fullName = $file;
3296  } else {
3297  if (!$mode) {
3298  // If use of .htaccess rule is not configured,
3299  // we use the default query-string method
3300  if ($lookupFile[1]) {
3301  $separator = '&';
3302  } else {
3303  $separator = '?';
3304  }
3305  $fullName = $file . $separator . filemtime($path);
3306  } else {
3307  // Change the filename
3308  $name = explode('.', $lookupFile[0]);
3309  $extension = array_pop($name);
3310  array_push($name, filemtime($path), $extension);
3311  $fullName = implode('.', $name);
3312  // Append potential query string
3313  $fullName .= $lookupFile[1] ? '?' . $lookupFile[1] : '';
3314  }
3315  }
3316  return $fullName;
3317  }
3318 
3319  /*************************
3320  *
3321  * SYSTEM INFORMATION
3322  *
3323  *************************/
3330  public static function getThisUrl()
3331  {
3332  self::logDeprecatedFunction();
3333  // Url of this script
3334  $p = parse_url(self::getIndpEnv('TYPO3_REQUEST_SCRIPT'));
3335  $dir = self::dirname($p['path']) . '/';
3336  // Strip file
3337  $url = str_replace('//', '/', $p['host'] . ($p['port'] ? ':' . $p['port'] : '') . $dir);
3338  return $url;
3339  }
3340 
3349  public static function linkThisScript(array $getParams = [])
3350  {
3351  $parts = self::getIndpEnv('SCRIPT_NAME');
3352  $params = self::_GET();
3353  foreach ($getParams as $key => $value) {
3354  if ($value !== '') {
3355  $params[$key] = $value;
3356  } else {
3357  unset($params[$key]);
3358  }
3359  }
3360  $pString = self::implodeArrayForUrl('', $params);
3361  return $pString ? $parts . '?' . ltrim($pString, '&') : $parts;
3362  }
3363 
3372  public static function linkThisUrl($url, array $getParams = [])
3373  {
3374  $parts = parse_url($url);
3375  $getP = [];
3376  if ($parts['query']) {
3377  parse_str($parts['query'], $getP);
3378  }
3379  ArrayUtility::mergeRecursiveWithOverrule($getP, $getParams);
3380  $uP = explode('?', $url);
3381  $params = self::implodeArrayForUrl('', $getP);
3382  $outurl = $uP[0] . ($params ? '?' . substr($params, 1) : '');
3383  return $outurl;
3384  }
3385 
3394  public static function getIndpEnv($getEnvName)
3395  {
3396  if (isset(self::$indpEnvCache[$getEnvName])) {
3397  return self::$indpEnvCache[$getEnvName];
3398  }
3399 
3400  /*
3401  Conventions:
3402  output from parse_url():
3403  URL: http://username:password@192.168.1.4:8080/typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/?arg1,arg2,arg3&p1=parameter1&p2[key]=value#link1
3404  [scheme] => 'http'
3405  [user] => 'username'
3406  [pass] => 'password'
3407  [host] => '192.168.1.4'
3408  [port] => '8080'
3409  [path] => '/typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/'
3410  [query] => 'arg1,arg2,arg3&p1=parameter1&p2[key]=value'
3411  [fragment] => 'link1'Further definition: [path_script] = '/typo3/32/temp/phpcheck/index.php'
3412  [path_dir] = '/typo3/32/temp/phpcheck/'
3413  [path_info] = '/arg1/arg2/arg3/'
3414  [path] = [path_script/path_dir][path_info]Keys supported:URI______:
3415  REQUEST_URI = [path]?[query] = /typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/?arg1,arg2,arg3&p1=parameter1&p2[key]=value
3416  HTTP_HOST = [host][:[port]] = 192.168.1.4:8080
3417  SCRIPT_NAME = [path_script]++ = /typo3/32/temp/phpcheck/index.php // NOTICE THAT SCRIPT_NAME will return the php-script name ALSO. [path_script] may not do that (eg. '/somedir/' may result in SCRIPT_NAME '/somedir/index.php')!
3418  PATH_INFO = [path_info] = /arg1/arg2/arg3/
3419  QUERY_STRING = [query] = arg1,arg2,arg3&p1=parameter1&p2[key]=value
3420  HTTP_REFERER = [scheme]://[host][:[port]][path] = http://192.168.1.4:8080/typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/?arg1,arg2,arg3&p1=parameter1&p2[key]=value
3421  (Notice: NO username/password + NO fragment)CLIENT____:
3422  REMOTE_ADDR = (client IP)
3423  REMOTE_HOST = (client host)
3424  HTTP_USER_AGENT = (client user agent)
3425  HTTP_ACCEPT_LANGUAGE = (client accept language)SERVER____:
3426  SCRIPT_FILENAME = Absolute filename of script (Differs between windows/unix). On windows 'C:\\blabla\\blabl\\' will be converted to 'C:/blabla/blabl/'Special extras:
3427  TYPO3_HOST_ONLY = [host] = 192.168.1.4
3428  TYPO3_PORT = [port] = 8080 (blank if 80, taken from host value)
3429  TYPO3_REQUEST_HOST = [scheme]://[host][:[port]]
3430  TYPO3_REQUEST_URL = [scheme]://[host][:[port]][path]?[query] (scheme will by default be "http" until we can detect something different)
3431  TYPO3_REQUEST_SCRIPT = [scheme]://[host][:[port]][path_script]
3432  TYPO3_REQUEST_DIR = [scheme]://[host][:[port]][path_dir]
3433  TYPO3_SITE_URL = [scheme]://[host][:[port]][path_dir] of the TYPO3 website frontend
3434  TYPO3_SITE_PATH = [path_dir] of the TYPO3 website frontend
3435  TYPO3_SITE_SCRIPT = [script / Speaking URL] of the TYPO3 website
3436  TYPO3_DOCUMENT_ROOT = Absolute path of root of documents: TYPO3_DOCUMENT_ROOT.SCRIPT_NAME = SCRIPT_FILENAME (typically)
3437  TYPO3_SSL = Returns TRUE if this session uses SSL/TLS (https)
3438  TYPO3_PROXY = Returns TRUE if this session runs over a well known proxyNotice: [fragment] is apparently NEVER available to the script!Testing suggestions:
3439  - Output all the values.
3440  - In the script, make a link to the script it self, maybe add some parameters and click the link a few times so HTTP_REFERER is seen
3441  - ALSO TRY the script from the ROOT of a site (like 'http://www.mytest.com/' and not 'http://www.mytest.com/test/' !!)
3442  */
3443  $retVal = '';
3444  switch ((string)$getEnvName) {
3445  case 'SCRIPT_NAME':
3446  $retVal = self::isRunningOnCgiServerApi()
3447  && ($_SERVER['ORIG_PATH_INFO'] ?: $_SERVER['PATH_INFO'])
3448  ? ($_SERVER['ORIG_PATH_INFO'] ?: $_SERVER['PATH_INFO'])
3449  : ($_SERVER['ORIG_SCRIPT_NAME'] ?: $_SERVER['SCRIPT_NAME']);
3450  // Add a prefix if TYPO3 is behind a proxy: ext-domain.com => int-server.com/prefix
3451  if (self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) {
3452  if (self::getIndpEnv('TYPO3_SSL') && $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL']) {
3453  $retVal = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL'] . $retVal;
3454  } elseif ($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix']) {
3455  $retVal = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix'] . $retVal;
3456  }
3457  }
3458  break;
3459  case 'SCRIPT_FILENAME':
3460  $retVal = PATH_thisScript;
3461  break;
3462  case 'REQUEST_URI':
3463  // Typical application of REQUEST_URI is return urls, forms submitting to itself etc. Example: returnUrl='.rawurlencode(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('REQUEST_URI'))
3464  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['requestURIvar']) {
3465  // This is for URL rewriters that store the original URI in a server variable (eg ISAPI_Rewriter for IIS: HTTP_X_REWRITE_URL)
3466  list($v, $n) = explode('|', $GLOBALS['TYPO3_CONF_VARS']['SYS']['requestURIvar']);
3467  $retVal = $GLOBALS[$v][$n];
3468  } elseif (!$_SERVER['REQUEST_URI']) {
3469  // This is for ISS/CGI which does not have the REQUEST_URI available.
3470  $retVal = '/' . ltrim(self::getIndpEnv('SCRIPT_NAME'), '/') . ($_SERVER['QUERY_STRING'] ? '?' . $_SERVER['QUERY_STRING'] : '');
3471  } else {
3472  $retVal = '/' . ltrim($_SERVER['REQUEST_URI'], '/');
3473  }
3474  // Add a prefix if TYPO3 is behind a proxy: ext-domain.com => int-server.com/prefix
3475  if (self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) {
3476  if (self::getIndpEnv('TYPO3_SSL') && $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL']) {
3477  $retVal = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL'] . $retVal;
3478  } elseif ($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix']) {
3479  $retVal = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix'] . $retVal;
3480  }
3481  }
3482  break;
3483  case 'PATH_INFO':
3484  // $_SERVER['PATH_INFO'] != $_SERVER['SCRIPT_NAME'] is necessary because some servers (Windows/CGI)
3485  // are seen to set PATH_INFO equal to script_name
3486  // Further, there must be at least one '/' in the path - else the PATH_INFO value does not make sense.
3487  // IF 'PATH_INFO' never works for our purpose in TYPO3 with CGI-servers,
3488  // then 'PHP_SAPI=='cgi'' might be a better check.
3489  // Right now strcmp($_SERVER['PATH_INFO'], GeneralUtility::getIndpEnv('SCRIPT_NAME')) will always
3490  // return FALSE for CGI-versions, but that is only as long as SCRIPT_NAME is set equal to PATH_INFO
3491  // because of PHP_SAPI=='cgi' (see above)
3492  if (!self::isRunningOnCgiServerApi()) {
3493  $retVal = $_SERVER['PATH_INFO'];
3494  }
3495  break;
3496  case 'TYPO3_REV_PROXY':
3497  $retVal = self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP']);
3498  break;
3499  case 'REMOTE_ADDR':
3500  $retVal = $_SERVER['REMOTE_ADDR'];
3501  if (self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) {
3502  $ip = self::trimExplode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
3503  // Choose which IP in list to use
3504  if (!empty($ip)) {
3505  switch ($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyHeaderMultiValue']) {
3506  case 'last':
3507  $ip = array_pop($ip);
3508  break;
3509  case 'first':
3510  $ip = array_shift($ip);
3511  break;
3512  case 'none':
3513 
3514  default:
3515  $ip = '';
3516  }
3517  }
3518  if (self::validIP($ip)) {
3519  $retVal = $ip;
3520  }
3521  }
3522  break;
3523  case 'HTTP_HOST':
3524  // if it is not set we're most likely on the cli
3525  $retVal = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null;
3526  if (isset($_SERVER['REMOTE_ADDR']) && static::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) {
3527  $host = self::trimExplode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
3528  // Choose which host in list to use
3529  if (!empty($host)) {
3530  switch ($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyHeaderMultiValue']) {
3531  case 'last':
3532  $host = array_pop($host);
3533  break;
3534  case 'first':
3535  $host = array_shift($host);
3536  break;
3537  case 'none':
3538 
3539  default:
3540  $host = '';
3541  }
3542  }
3543  if ($host) {
3544  $retVal = $host;
3545  }
3546  }
3547  if (!static::isAllowedHostHeaderValue($retVal)) {
3548  throw new \UnexpectedValueException(
3549  'The current host header value does not match the configured trusted hosts pattern! Check the pattern defined in $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'trustedHostsPattern\'] and adapt it, if you want to allow the current host header \'' . $retVal . '\' for your installation.',
3550  1396795884
3551  );
3552  }
3553  break;
3554  case 'HTTP_REFERER':
3555 
3556  case 'HTTP_USER_AGENT':
3557 
3558  case 'HTTP_ACCEPT_ENCODING':
3559 
3560  case 'HTTP_ACCEPT_LANGUAGE':
3561 
3562  case 'REMOTE_HOST':
3563 
3564  case 'QUERY_STRING':
3565  $retVal = '';
3566  if (isset($_SERVER[$getEnvName])) {
3567  $retVal = $_SERVER[$getEnvName];
3568  }
3569  break;
3570  case 'TYPO3_DOCUMENT_ROOT':
3571  // Get the web root (it is not the root of the TYPO3 installation)
3572  // The absolute path of the script can be calculated with TYPO3_DOCUMENT_ROOT + SCRIPT_FILENAME
3573  // Some CGI-versions (LA13CGI) and mod-rewrite rules on MODULE versions will deliver a 'wrong' DOCUMENT_ROOT (according to our description). Further various aliases/mod_rewrite rules can disturb this as well.
3574  // Therefore the DOCUMENT_ROOT is now always calculated as the SCRIPT_FILENAME minus the end part shared with SCRIPT_NAME.
3575  $SFN = self::getIndpEnv('SCRIPT_FILENAME');
3576  $SN_A = explode('/', strrev(self::getIndpEnv('SCRIPT_NAME')));
3577  $SFN_A = explode('/', strrev($SFN));
3578  $acc = [];
3579  foreach ($SN_A as $kk => $vv) {
3580  if ((string)$SFN_A[$kk] === (string)$vv) {
3581  $acc[] = $vv;
3582  } else {
3583  break;
3584  }
3585  }
3586  $commonEnd = strrev(implode('/', $acc));
3587  if ((string)$commonEnd !== '') {
3588  $DR = substr($SFN, 0, -(strlen($commonEnd) + 1));
3589  }
3590  $retVal = $DR;
3591  break;
3592  case 'TYPO3_HOST_ONLY':
3593  $httpHost = self::getIndpEnv('HTTP_HOST');
3594  $httpHostBracketPosition = strpos($httpHost, ']');
3595  $httpHostParts = explode(':', $httpHost);
3596  $retVal = $httpHostBracketPosition !== false ? substr($httpHost, 0, $httpHostBracketPosition + 1) : array_shift($httpHostParts);
3597  break;
3598  case 'TYPO3_PORT':
3599  $httpHost = self::getIndpEnv('HTTP_HOST');
3600  $httpHostOnly = self::getIndpEnv('TYPO3_HOST_ONLY');
3601  $retVal = strlen($httpHost) > strlen($httpHostOnly) ? substr($httpHost, strlen($httpHostOnly) + 1) : '';
3602  break;
3603  case 'TYPO3_REQUEST_HOST':
3604  $retVal = (self::getIndpEnv('TYPO3_SSL') ? 'https://' : 'http://') . self::getIndpEnv('HTTP_HOST');
3605  break;
3606  case 'TYPO3_REQUEST_URL':
3607  $retVal = self::getIndpEnv('TYPO3_REQUEST_HOST') . self::getIndpEnv('REQUEST_URI');
3608  break;
3609  case 'TYPO3_REQUEST_SCRIPT':
3610  $retVal = self::getIndpEnv('TYPO3_REQUEST_HOST') . self::getIndpEnv('SCRIPT_NAME');
3611  break;
3612  case 'TYPO3_REQUEST_DIR':
3613  $retVal = self::getIndpEnv('TYPO3_REQUEST_HOST') . self::dirname(self::getIndpEnv('SCRIPT_NAME')) . '/';
3614  break;
3615  case 'TYPO3_SITE_URL':
3616  $url = self::getIndpEnv('TYPO3_REQUEST_DIR');
3617  // This can only be set by external entry scripts
3618  if (defined('TYPO3_PATH_WEB')) {
3619  $retVal = $url;
3620  } elseif (defined('PATH_thisScript') && defined('PATH_site')) {
3621  $lPath = PathUtility::stripPathSitePrefix(dirname(PATH_thisScript)) . '/';
3622  $siteUrl = substr($url, 0, -strlen($lPath));
3623  if (substr($siteUrl, -1) != '/') {
3624  $siteUrl .= '/';
3625  }
3626  $retVal = $siteUrl;
3627  }
3628  break;
3629  case 'TYPO3_SITE_PATH':
3630  $retVal = substr(self::getIndpEnv('TYPO3_SITE_URL'), strlen(self::getIndpEnv('TYPO3_REQUEST_HOST')));
3631  break;
3632  case 'TYPO3_SITE_SCRIPT':
3633  $retVal = substr(self::getIndpEnv('TYPO3_REQUEST_URL'), strlen(self::getIndpEnv('TYPO3_SITE_URL')));
3634  break;
3635  case 'TYPO3_SSL':
3636  $proxySSL = trim($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxySSL']);
3637  if ($proxySSL == '*') {
3638  $proxySSL = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'];
3639  }
3640  if (self::cmpIP($_SERVER['REMOTE_ADDR'], $proxySSL)) {
3641  $retVal = true;
3642  } else {
3643  $retVal = $_SERVER['SSL_SESSION_ID'] || strtolower($_SERVER['HTTPS']) === 'on' || (string)$_SERVER['HTTPS'] === '1';
3644  }
3645  break;
3646  case '_ARRAY':
3647  $out = [];
3648  // Here, list ALL possible keys to this function for debug display.
3649  $envTestVars = [
3650  'HTTP_HOST',
3651  'TYPO3_HOST_ONLY',
3652  'TYPO3_PORT',
3653  'PATH_INFO',
3654  'QUERY_STRING',
3655  'REQUEST_URI',
3656  'HTTP_REFERER',
3657  'TYPO3_REQUEST_HOST',
3658  'TYPO3_REQUEST_URL',
3659  'TYPO3_REQUEST_SCRIPT',
3660  'TYPO3_REQUEST_DIR',
3661  'TYPO3_SITE_URL',
3662  'TYPO3_SITE_SCRIPT',
3663  'TYPO3_SSL',
3664  'TYPO3_REV_PROXY',
3665  'SCRIPT_NAME',
3666  'TYPO3_DOCUMENT_ROOT',
3667  'SCRIPT_FILENAME',
3668  'REMOTE_ADDR',
3669  'REMOTE_HOST',
3670  'HTTP_USER_AGENT',
3671  'HTTP_ACCEPT_LANGUAGE'
3672  ];
3673  foreach ($envTestVars as $v) {
3674  $out[$v] = self::getIndpEnv($v);
3675  }
3676  reset($out);
3677  $retVal = $out;
3678  break;
3679  }
3680  self::$indpEnvCache[$getEnvName] = $retVal;
3681  return $retVal;
3682  }
3683 
3692  public static function isAllowedHostHeaderValue($hostHeaderValue)
3693  {
3694  if (static::$allowHostHeaderValue === true) {
3695  return true;
3696  }
3697 
3698  if (static::isInternalRequestType()) {
3699  return static::$allowHostHeaderValue = true;
3700  }
3701 
3702  // Deny the value if trusted host patterns is empty, which means we are early in the bootstrap
3703  if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'])) {
3704  return false;
3705  }
3706 
3707  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] === self::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL) {
3708  static::$allowHostHeaderValue = true;
3709  } else {
3710  static::$allowHostHeaderValue = static::hostHeaderValueMatchesTrustedHostsPattern($hostHeaderValue);
3711  }
3712 
3713  return static::$allowHostHeaderValue;
3714  }
3715 
3723  public static function hostHeaderValueMatchesTrustedHostsPattern($hostHeaderValue)
3724  {
3725  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] === self::ENV_TRUSTED_HOSTS_PATTERN_SERVER_NAME) {
3726  // Allow values that equal the server name
3727  // Note that this is only secure if name base virtual host are configured correctly in the webserver
3728  $defaultPort = self::getIndpEnv('TYPO3_SSL') ? '443' : '80';
3729  $parsedHostValue = parse_url('http://' . $hostHeaderValue);
3730  if (isset($parsedHostValue['port'])) {
3731  $hostMatch = (strtolower($parsedHostValue['host']) === strtolower($_SERVER['SERVER_NAME']) && (string)$parsedHostValue['port'] === $_SERVER['SERVER_PORT']);
3732  } else {
3733  $hostMatch = (strtolower($hostHeaderValue) === strtolower($_SERVER['SERVER_NAME']) && $defaultPort === $_SERVER['SERVER_PORT']);
3734  }
3735  } else {
3736  // In case name based virtual hosts are not possible, we allow setting a trusted host pattern
3737  // See https://typo3.org/teams/security/security-bulletins/typo3-core/typo3-core-sa-2014-001/ for further details
3738  $hostMatch = (bool)preg_match('/^' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] . '$/i', $hostHeaderValue);
3739  }
3740 
3741  return $hostMatch;
3742  }
3743 
3754  protected static function isInternalRequestType()
3755  {
3756  return !defined('TYPO3_REQUESTTYPE') || (defined('TYPO3_REQUESTTYPE') && TYPO3_REQUESTTYPE & (TYPO3_REQUESTTYPE_INSTALL | TYPO3_REQUESTTYPE_CLI));
3757  }
3758 
3764  public static function milliseconds()
3765  {
3766  return round(microtime(true) * 1000);
3767  }
3768 
3775  public static function clientInfo($useragent = '')
3776  {
3777  if (!$useragent) {
3778  $useragent = self::getIndpEnv('HTTP_USER_AGENT');
3779  }
3780  $bInfo = [];
3781  // Which browser?
3782  if (strpos($useragent, 'Konqueror') !== false) {
3783  $bInfo['BROWSER'] = 'konqu';
3784  } elseif (strpos($useragent, 'Opera') !== false) {
3785  $bInfo['BROWSER'] = 'opera';
3786  } elseif (strpos($useragent, 'MSIE') !== false) {
3787  $bInfo['BROWSER'] = 'msie';
3788  } elseif (strpos($useragent, 'Mozilla') !== false) {
3789  $bInfo['BROWSER'] = 'net';
3790  } elseif (strpos($useragent, 'Flash') !== false) {
3791  $bInfo['BROWSER'] = 'flash';
3792  }
3793  if (isset($bInfo['BROWSER'])) {
3794  // Browser version
3795  switch ($bInfo['BROWSER']) {
3796  case 'net':
3797  $bInfo['VERSION'] = floatval(substr($useragent, 8));
3798  if (strpos($useragent, 'Netscape6/') !== false) {
3799  $bInfo['VERSION'] = floatval(substr(strstr($useragent, 'Netscape6/'), 10));
3800  }
3801  // Will we ever know if this was a typo or intention...?! :-(
3802  if (strpos($useragent, 'Netscape/6') !== false) {
3803  $bInfo['VERSION'] = floatval(substr(strstr($useragent, 'Netscape/6'), 10));
3804  }
3805  if (strpos($useragent, 'Netscape/7') !== false) {
3806  $bInfo['VERSION'] = floatval(substr(strstr($useragent, 'Netscape/7'), 9));
3807  }
3808  break;
3809  case 'msie':
3810  $tmp = strstr($useragent, 'MSIE');
3811  $bInfo['VERSION'] = floatval(preg_replace('/^[^0-9]*/', '', substr($tmp, 4)));
3812  break;
3813  case 'opera':
3814  $tmp = strstr($useragent, 'Opera');
3815  $bInfo['VERSION'] = floatval(preg_replace('/^[^0-9]*/', '', substr($tmp, 5)));
3816  break;
3817  case 'konqu':
3818  $tmp = strstr($useragent, 'Konqueror/');
3819  $bInfo['VERSION'] = floatval(substr($tmp, 10));
3820  break;
3821  }
3822  // Client system
3823  if (strpos($useragent, 'Win') !== false) {
3824  $bInfo['SYSTEM'] = 'win';
3825  } elseif (strpos($useragent, 'Mac') !== false) {
3826  $bInfo['SYSTEM'] = 'mac';
3827  } elseif (strpos($useragent, 'Linux') !== false || strpos($useragent, 'X11') !== false || strpos($useragent, 'SGI') !== false || strpos($useragent, ' SunOS ') !== false || strpos($useragent, ' HP-UX ') !== false) {
3828  $bInfo['SYSTEM'] = 'unix';
3829  }
3830  }
3831  return $bInfo;
3832  }
3833 
3840  public static function getHostname($requestHost = true)
3841  {
3842  $host = '';
3843  // If not called from the command-line, resolve on getIndpEnv()
3844  // Note that TYPO3_REQUESTTYPE is not used here as it may not yet be defined
3845  if ($requestHost && (!defined('TYPO3_cliMode') || !TYPO3_cliMode)) {
3846  $host = self::getIndpEnv('HTTP_HOST');
3847  }
3848  if (!$host) {
3849  // will fail for PHP 4.1 and 4.2
3850  $host = @php_uname('n');
3851  // 'n' is ignored in broken installations
3852  if (strpos($host, ' ')) {
3853  $host = '';
3854  }
3855  }
3856  // We have not found a FQDN yet
3857  if ($host && strpos($host, '.') === false) {
3858  $ip = gethostbyname($host);
3859  // We got an IP address
3860  if ($ip != $host) {
3861  $fqdn = gethostbyaddr($ip);
3862  if ($ip != $fqdn) {
3863  $host = $fqdn;
3864  }
3865  }
3866  }
3867  if (!$host) {
3868  $host = 'localhost.localdomain';
3869  }
3870  return $host;
3871  }
3872 
3873  /*************************
3874  *
3875  * TYPO3 SPECIFIC FUNCTIONS
3876  *
3877  *************************/
3889  public static function getFileAbsFileName($filename, $onlyRelative = true, $relToTYPO3_mainDir = false)
3890  {
3891  if ((string)$filename === '') {
3892  return '';
3893  }
3894  $relPathPrefix = PATH_site;
3895  if ($relToTYPO3_mainDir) {
3896  $relPathPrefix = PATH_typo3;
3897  }
3898 
3899  // Extension
3900  if (strpos($filename, 'EXT:') === 0) {
3901  list($extKey, $local) = explode('/', substr($filename, 4), 2);
3902  $filename = '';
3903  if ((string)$extKey !== '' && ExtensionManagementUtility::isLoaded($extKey) && (string)$local !== '') {
3904  $filename = ExtensionManagementUtility::extPath($extKey) . $local;
3905  }
3906  } elseif (!static::isAbsPath($filename)) {
3907  // relative. Prepended with $relPathPrefix
3908  $filename = $relPathPrefix . $filename;
3909  } elseif ($onlyRelative && !static::isFirstPartOfStr($filename, $relPathPrefix)) {
3910  // absolute, but set to blank if not allowed
3911  $filename = '';
3912  }
3913  if ((string)$filename !== '' && static::validPathStr($filename)) {
3914  // checks backpath.
3915  return $filename;
3916  }
3917  return '';
3918  }
3919 
3931  public static function validPathStr($theFile)
3932  {
3933  return strpos($theFile, '//') === false && strpos($theFile, '\\') === false
3934  && preg_match('#(?:^\\.\\.|/\\.\\./|[[:cntrl:]])#u', $theFile) === 0;
3935  }
3936 
3943  public static function isAbsPath($path)
3944  {
3945  return $path[0] === '/' || TYPO3_OS === 'WIN' && (strpos($path, ':/') === 1 || strpos($path, ':\\') === 1);
3946  }
3947 
3954  public static function isAllowedAbsPath($path)
3955  {
3956  $lockRootPath = $GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'];
3957  return static::isAbsPath($path) && static::validPathStr($path)
3958  && (static::isFirstPartOfStr($path, PATH_site)
3959  || $lockRootPath && static::isFirstPartOfStr($path, $lockRootPath));
3960  }
3961 
3971  public static function verifyFilenameAgainstDenyPattern($filename)
3972  {
3973  $pattern = '/[[:cntrl:]]/';
3974  if ((string)$filename !== '' && (string)$GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] !== '') {
3975  $pattern = '/(?:[[:cntrl:]]|' . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] . ')/i';
3976  }
3977  return !preg_match($pattern, $filename);
3978  }
3979 
3986  public static function copyDirectory($source, $destination)
3987  {
3988  if (strpos($source, PATH_site) === false) {
3989  $source = PATH_site . $source;
3990  }
3991  if (strpos($destination, PATH_site) === false) {
3992  $destination = PATH_site . $destination;
3993  }
3994  if (static::isAllowedAbsPath($source) && static::isAllowedAbsPath($destination)) {
3995  $iterator = new \RecursiveIteratorIterator(
3996  new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS),
3997  \RecursiveIteratorIterator::SELF_FIRST
3998  );
3999  foreach ($iterator as $item) {
4000  $target = $destination . '/' . $iterator->getSubPathName();
4001  if ($item->isDir()) {
4002  static::mkdir($target);
4003  } else {
4004  static::upload_copy_move($item, $target);
4005  }
4006  }
4007  }
4008  }
4009 
4017  public static function sanitizeLocalUrl($url = '')
4018  {
4019  $sanitizedUrl = '';
4020  $decodedUrl = rawurldecode($url);
4021  if (!empty($url) && self::removeXSS($decodedUrl) === $decodedUrl) {
4022  $parsedUrl = parse_url($decodedUrl);
4023  $testAbsoluteUrl = self::resolveBackPath($decodedUrl);
4024  $testRelativeUrl = self::resolveBackPath(self::dirname(self::getIndpEnv('SCRIPT_NAME')) . '/' . $decodedUrl);
4025  // Pass if URL is on the current host:
4026  if (self::isValidUrl($decodedUrl)) {
4027  if (self::isOnCurrentHost($decodedUrl) && strpos($decodedUrl, self::getIndpEnv('TYPO3_SITE_URL')) === 0) {
4028  $sanitizedUrl = $url;
4029  }
4030  } elseif (self::isAbsPath($decodedUrl) && self::isAllowedAbsPath($decodedUrl)) {
4031  $sanitizedUrl = $url;
4032  } elseif (strpos($testAbsoluteUrl, self::getIndpEnv('TYPO3_SITE_PATH')) === 0 && $decodedUrl[0] === '/') {
4033  $sanitizedUrl = $url;
4034  } elseif (empty($parsedUrl['scheme']) && strpos($testRelativeUrl, self::getIndpEnv('TYPO3_SITE_PATH')) === 0 && $decodedUrl[0] !== '/') {
4035  $sanitizedUrl = $url;
4036  }
4037  }
4038  if (!empty($url) && empty($sanitizedUrl)) {
4039  self::sysLog('The URL "' . $url . '" is not considered to be local and was denied.', 'core', self::SYSLOG_SEVERITY_NOTICE);
4040  }
4041  return $sanitizedUrl;
4042  }
4043 
4052  public static function upload_copy_move($source, $destination)
4053  {
4054  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Core\Utility\GeneralUtility']['moveUploadedFile'])
4055  && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Core\Utility\GeneralUtility']['moveUploadedFile'])
4056  ) {
4057  $params = ['source' => $source, 'destination' => $destination, 'method' => 'upload_copy_move'];
4058  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Core\Utility\GeneralUtility']['moveUploadedFile'] as $hookMethod) {
4059  $fakeThis = false;
4060  self::callUserFunction($hookMethod, $params, $fakeThis);
4061  }
4062  }
4063 
4064  if (is_uploaded_file($source)) {
4065  $uploaded = true;
4066  // Return the value of move_uploaded_file, and if FALSE the temporary $source is still around so the user can use unlink to delete it:
4067  $uploadedResult = move_uploaded_file($source, $destination);
4068  } else {
4069  $uploaded = false;
4070  @copy($source, $destination);
4071  }
4072  // Change the permissions of the file
4073  self::fixPermissions($destination);
4074  // If here the file is copied and the temporary $source is still around, so when returning FALSE the user can try unlink to delete the $source
4075  return $uploaded ? $uploadedResult : false;
4076  }
4077 
4087  public static function upload_to_tempfile($uploadedFileName)
4088  {
4089  if (is_uploaded_file($uploadedFileName)) {
4090  $tempFile = self::tempnam('upload_temp_');
4091  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Core\Utility\GeneralUtility']['moveUploadedFile'])
4092  && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Core\Utility\GeneralUtility']['moveUploadedFile'])
4093  ) {
4094  $params = ['source' => $uploadedFileName, 'destination' => $tempFile, 'method' => 'upload_to_tempfile'];
4095  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Core\Utility\GeneralUtility']['moveUploadedFile'] as $hookMethod) {
4096  $fakeThis = false;
4097  self::callUserFunction($hookMethod, $params, $fakeThis);
4098  }
4099  }
4100 
4101  move_uploaded_file($uploadedFileName, $tempFile);
4102  return @is_file($tempFile) ? $tempFile : '';
4103  }
4104  }
4105 
4115  public static function unlink_tempfile($uploadedTempFileName)
4116  {
4117  if ($uploadedTempFileName) {
4118  $uploadedTempFileName = self::fixWindowsFilePath($uploadedTempFileName);
4119  if (
4120  self::validPathStr($uploadedTempFileName)
4121  && self::isFirstPartOfStr($uploadedTempFileName, PATH_site . 'typo3temp/')
4122  && @is_file($uploadedTempFileName)
4123  ) {
4124  if (unlink($uploadedTempFileName)) {
4125  return true;
4126  }
4127  }
4128  }
4129  }
4130 
4141  public static function tempnam($filePrefix, $fileSuffix = '')
4142  {
4143  $temporaryPath = PATH_site . 'typo3temp/';
4144  if ($fileSuffix === '') {
4145  $tempFileName = $temporaryPath . basename(tempnam($temporaryPath, $filePrefix));
4146  } else {
4147  do {
4148  $tempFileName = $temporaryPath . $filePrefix . mt_rand(1, PHP_INT_MAX) . $fileSuffix;
4149  } while (file_exists($tempFileName));
4150  touch($tempFileName);
4151  clearstatcache(null, $tempFileName);
4152  }
4153  return $tempFileName;
4154  }
4155 
4164  public static function stdAuthCode($uid_or_record, $fields = '', $codeLength = 8)
4165  {
4166  if (is_array($uid_or_record)) {
4167  $recCopy_temp = [];
4168  if ($fields) {
4169  $fieldArr = self::trimExplode(',', $fields, true);
4170  foreach ($fieldArr as $k => $v) {
4171  $recCopy_temp[$k] = $uid_or_record[$v];
4172  }
4173  } else {
4174  $recCopy_temp = $uid_or_record;
4175  }
4176  $preKey = implode('|', $recCopy_temp);
4177  } else {
4178  $preKey = $uid_or_record;
4179  }
4180  $authCode = $preKey . '||' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'];
4181  $authCode = substr(md5($authCode), 0, $codeLength);
4182  return $authCode;
4183  }
4184 
4191  public static function hideIfNotTranslated($l18n_cfg_fieldValue)
4192  {
4193  return $GLOBALS['TYPO3_CONF_VARS']['FE']['hidePagesIfNotTranslatedByDefault'] xor ($l18n_cfg_fieldValue & 2);
4194  }
4195 
4203  public static function hideIfDefaultLanguage($localizationConfiguration)
4204  {
4205  return (bool)($localizationConfiguration & 1);
4206  }
4207 
4218  public static function readLLfile($fileRef, $langKey, $charset = '', $errorMode = 0)
4219  {
4220  self::logDeprecatedFunction();
4222  $languageFactory = self::makeInstance(\TYPO3\CMS\Core\Localization\LocalizationFactory::class);
4223  return $languageFactory->getParsedData($fileRef, $langKey, $charset, $errorMode);
4224  }
4225 
4234  public static function llXmlAutoFileName($fileRef, $language, $sameLocation = false)
4235  {
4236  // If $fileRef is already prefixed with "[language key]" then we should return it as is
4237  $fileName = basename($fileRef);
4238  if (self::isFirstPartOfStr($fileName, $language . '.')) {
4239  return $fileRef;
4240  }
4241 
4242  if ($sameLocation) {
4243  return str_replace($fileName, $language . '.' . $fileName, $fileRef);
4244  }
4245 
4246  // Analyse file reference:
4247  // Is system:
4248  if (self::isFirstPartOfStr($fileRef, PATH_typo3 . 'sysext/')) {
4249  $validatedPrefix = PATH_typo3 . 'sysext/';
4250  } elseif (self::isFirstPartOfStr($fileRef, PATH_typo3 . 'ext/')) {
4251  // Is global:
4252  $validatedPrefix = PATH_typo3 . 'ext/';
4253  } elseif (self::isFirstPartOfStr($fileRef, PATH_typo3conf . 'ext/')) {
4254  // Is local:
4255  $validatedPrefix = PATH_typo3conf . 'ext/';
4256  } else {
4257  $validatedPrefix = '';
4258  }
4259  if ($validatedPrefix) {
4260  // Divide file reference into extension key, directory (if any) and base name:
4261  list($file_extKey, $file_extPath) = explode('/', substr($fileRef, strlen($validatedPrefix)), 2);
4262  $temp = self::revExplode('/', $file_extPath, 2);
4263  if (count($temp) === 1) {
4264  array_unshift($temp, '');
4265  }
4266  // Add empty first-entry if not there.
4267  list($file_extPath, $file_fileName) = $temp;
4268  // The filename is prefixed with "[language key]." because it prevents the llxmltranslate tool from detecting it.
4269  $location = 'typo3conf/l10n/' . $language . '/' . $file_extKey . '/' . ($file_extPath ? $file_extPath . '/' : '');
4270  return $location . $language . '.' . $file_fileName;
4271  }
4272  return null;
4273  }
4274 
4283  public static function resolveSheetDefInDS($dataStructArray, $sheet = 'sDEF')
4284  {
4285  if (!is_array($dataStructArray)) {
4286  return 'Data structure must be an array';
4287  }
4288  if (is_array($dataStructArray['sheets'])) {
4289  $singleSheet = false;
4290  if (!isset($dataStructArray['sheets'][$sheet])) {
4291  $sheet = 'sDEF';
4292  }
4293  $dataStruct = $dataStructArray['sheets'][$sheet];
4294  // If not an array, but still set, then regard it as a relative reference to a file:
4295  if ($dataStruct && !is_array($dataStruct)) {
4296  $file = self::getFileAbsFileName($dataStruct);
4297  if ($file && @is_file($file)) {
4298  $dataStruct = self::xml2array(self::getUrl($file));
4299  }
4300  }
4301  } else {
4302  $singleSheet = true;
4303  $dataStruct = $dataStructArray;
4304  if (isset($dataStruct['meta'])) {
4305  unset($dataStruct['meta']);
4306  }
4307  // Meta data should not appear there.
4308  // Default sheet
4309  $sheet = 'sDEF';
4310  }
4311  return [$dataStruct, $sheet, $singleSheet];
4312  }
4313 
4321  public static function resolveAllSheetsInDS(array $dataStructArray)
4322  {
4323  if (is_array($dataStructArray['sheets'])) {
4324  $out = ['sheets' => []];
4325  foreach ($dataStructArray['sheets'] as $sheetId => $sDat) {
4326  list($ds, $aS) = self::resolveSheetDefInDS($dataStructArray, $sheetId);
4327  if ($sheetId == $aS) {
4328  $out['sheets'][$aS] = $ds;
4329  }
4330  }
4331  } else {
4332  list($ds) = self::resolveSheetDefInDS($dataStructArray);
4333  $out = ['sheets' => ['sDEF' => $ds]];
4334  }
4335  return $out;
4336  }
4337 
4350  public static function callUserFunction($funcName, &$params, &$ref, $checkPrefix = '', $errorMode = 0)
4351  {
4352  $content = false;
4353  // Check if we're using a closure and invoke it directly.
4354  if (is_object($funcName) && is_a($funcName, 'Closure')) {
4355  return call_user_func_array($funcName, [&$params, &$ref]);
4356  }
4357  $funcName = trim($funcName);
4358  // Check persistent object and if found, call directly and exit.
4359  if (isset($GLOBALS['T3_VAR']['callUserFunction'][$funcName]) && is_array($GLOBALS['T3_VAR']['callUserFunction'][$funcName])) {
4360  return call_user_func_array([
4361  &$GLOBALS['T3_VAR']['callUserFunction'][$funcName]['obj'],
4362  $GLOBALS['T3_VAR']['callUserFunction'][$funcName]['method']
4363  ], [&$params, &$ref]);
4364  }
4365  // Check file-reference prefix; if found, require_once() the file (should be library of code)
4366  if (strpos($funcName, ':') !== false) {
4367  list($file, $funcRef) = self::revExplode(':', $funcName, 2);
4368  $requireFile = self::getFileAbsFileName($file);
4369  if ($requireFile) {
4370  self::requireOnce($requireFile);
4371  }
4372  } else {
4373  $funcRef = $funcName;
4374  }
4375  // Check for persistent object token, "&"
4376  if ($funcRef[0] === '&') {
4377  $funcRef = substr($funcRef, 1);
4378  $storePersistentObject = true;
4379  } else {
4380  $storePersistentObject = false;
4381  }
4382  // Call function or method:
4383  $parts = explode('->', $funcRef);
4384  if (count($parts) === 2) {
4385  // Class
4386  // Check if class/method exists:
4387  if (class_exists($parts[0])) {
4388  // Get/Create object of class:
4389  if ($storePersistentObject) {
4390  // Get reference to current instance of class:
4391  if (!is_object($GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]])) {
4392  $GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]] = self::makeInstance($parts[0]);
4393  }
4394  $classObj = $GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]];
4395  } else {
4396  // Create new object:
4397  $classObj = self::makeInstance($parts[0]);
4398  }
4399  if (method_exists($classObj, $parts[1])) {
4400  // If persistent object should be created, set reference:
4401  if ($storePersistentObject) {
4402  $GLOBALS['T3_VAR']['callUserFunction'][$funcName] = [
4403  'method' => $parts[1],
4404  'obj' => &$classObj
4405  ];
4406  }
4407  // Call method:
4408  $content = call_user_func_array([&$classObj, $parts[1]], [&$params, &$ref]);
4409  } else {
4410  $errorMsg = 'No method name \'' . $parts[1] . '\' in class ' . $parts[0];
4411  if ($errorMode == 2) {
4412  throw new \InvalidArgumentException($errorMsg, 1294585865);
4413  } elseif (!$errorMode) {
4414  debug($errorMsg, \TYPO3\CMS\Core\Utility\GeneralUtility::class . '::callUserFunction');
4415  }
4416  }
4417  } else {
4418  $errorMsg = 'No class named ' . $parts[0];
4419  if ($errorMode == 2) {
4420  throw new \InvalidArgumentException($errorMsg, 1294585866);
4421  } elseif (!$errorMode) {
4422  debug($errorMsg, \TYPO3\CMS\Core\Utility\GeneralUtility::class . '::callUserFunction');
4423  }
4424  }
4425  } else {
4426  // Function
4427  if (function_exists($funcRef)) {
4428  $content = call_user_func_array($funcRef, [&$params, &$ref]);
4429  } else {
4430  $errorMsg = 'No function named: ' . $funcRef;
4431  if ($errorMode == 2) {
4432  throw new \InvalidArgumentException($errorMsg, 1294585867);
4433  } elseif (!$errorMode) {
4434  debug($errorMsg, \TYPO3\CMS\Core\Utility\GeneralUtility::class . '::callUserFunction');
4435  }
4436  }
4437  }
4438  return $content;
4439  }
4440 
4458  public static function getUserObj($classRef)
4459  {
4460  // Check persistent object and if found, call directly and exit.
4461  if (is_object($GLOBALS['T3_VAR']['getUserObj'][$classRef])) {
4462  return $GLOBALS['T3_VAR']['getUserObj'][$classRef];
4463  } else {
4464  // Check file-reference prefix; if found, require_once() the file (should be library of code)
4465  if (strpos($classRef, ':') !== false) {
4466  list($file, $class) = self::revExplode(':', $classRef, 2);
4467  $requireFile = self::getFileAbsFileName($file);
4468  if ($requireFile) {
4469  self::requireOnce($requireFile);
4470  }
4471  } else {
4472  $class = $classRef;
4473  }
4474  // Check for persistent object token, "&"
4475  if ($class[0] === '&') {
4476  self::deprecationLog(
4477  'The persistent functionality of getUserObj(), prepending the class name with & is deprecated since'
4478  . ' TYPO3 CMS 7 and will be removed in TYPO3 CMS 8. To allow this functionality, implement '
4479  . ' the \\TYPO3\\CMS\\Core\\SingletonInterface in the class "' . $classRef . '" instead.'
4480  );
4481  $class = substr($class, 1);
4482  $storePersistentObject = true;
4483  } else {
4484  $storePersistentObject = false;
4485  }
4486  // Check if class exists:
4487  if (class_exists($class)) {
4488  $classObj = self::makeInstance($class);
4489  // If persistent object should be created, set reference:
4490  if ($storePersistentObject) {
4491  $GLOBALS['T3_VAR']['getUserObj'][$classRef] = $classObj;
4492  }
4493  return $classObj;
4494  }
4495  }
4496  }
4497 
4518  public static function makeInstance($className)
4519  {
4520  if (!is_string($className) || empty($className)) {
4521  throw new \InvalidArgumentException('$className must be a non empty string.', 1288965219);
4522  }
4523  // Never instantiate with a beginning backslash, otherwise things like singletons won't work.
4524  if ($className[0] === '\\') {
4525  throw new \InvalidArgumentException(
4526  '$className "' . $className . '" must not start with a backslash.', 1420281366
4527  );
4528  }
4529  if (isset(static::$finalClassNameCache[$className])) {
4530  $finalClassName = static::$finalClassNameCache[$className];
4531  } else {
4532  $finalClassName = self::getClassName($className);
4533  static::$finalClassNameCache[$className] = $finalClassName;
4534  }
4535  // Return singleton instance if it is already registered
4536  if (isset(self::$singletonInstances[$finalClassName])) {
4537  return self::$singletonInstances[$finalClassName];
4538  }
4539  // Return instance if it has been injected by addInstance()
4540  if (
4541  isset(self::$nonSingletonInstances[$finalClassName])
4542  && !empty(self::$nonSingletonInstances[$finalClassName])
4543  ) {
4544  return array_shift(self::$nonSingletonInstances[$finalClassName]);
4545  }
4546  // Create new instance and call constructor with parameters
4547  $instance = static::instantiateClass($finalClassName, func_get_args());
4548  // Register new singleton instance
4549  if ($instance instanceof SingletonInterface) {
4550  self::$singletonInstances[$finalClassName] = $instance;
4551  }
4552  return $instance;
4553  }
4554 
4562  protected static function instantiateClass($className, $arguments)
4563  {
4564  switch (count($arguments)) {
4565  case 1:
4566  $instance = new $className();
4567  break;
4568  case 2:
4569  $instance = new $className($arguments[1]);
4570  break;
4571  case 3:
4572  $instance = new $className($arguments[1], $arguments[2]);
4573  break;
4574  case 4:
4575  $instance = new $className($arguments[1], $arguments[2], $arguments[3]);
4576  break;
4577  case 5:
4578  $instance = new $className($arguments[1], $arguments[2], $arguments[3], $arguments[4]);
4579  break;
4580  case 6:
4581  $instance = new $className($arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5]);
4582  break;
4583  case 7:
4584  $instance = new $className($arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6]);
4585  break;
4586  case 8:
4587  $instance = new $className($arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6], $arguments[7]);
4588  break;
4589  case 9:
4590  $instance = new $className($arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6], $arguments[7], $arguments[8]);
4591  break;
4592  default:
4593  // The default case for classes with constructors that have more than 8 arguments.
4594  // This will fail when one of the arguments shall be passed by reference.
4595  // In case we really need to support this edge case, we can implement the solution from here: https://review.typo3.org/26344
4596  $class = new \ReflectionClass($className);
4597  array_shift($arguments);
4598  $instance = $class->newInstanceArgs($arguments);
4599  }
4600  return $instance;
4601  }
4602 
4610  protected static function getClassName($className)
4611  {
4612  if (class_exists($className)) {
4613  while (static::classHasImplementation($className)) {
4614  $className = static::getImplementationForClass($className);
4615  }
4616  }
4618  }
4619 
4626  protected static function getImplementationForClass($className)
4627  {
4628  return $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][$className]['className'];
4629  }
4630 
4637  protected static function classHasImplementation($className)
4638  {
4639  // If we are early in the bootstrap, the configuration might not yet be present
4640  if (!isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'])) {
4641  return false;
4642  }
4643 
4644  return isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][$className])
4645  && is_array($GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][$className])
4646  && !empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][$className]['className']);
4647  }
4648 
4667  public static function setSingletonInstance($className, SingletonInterface $instance)
4668  {
4669  self::checkInstanceClassName($className, $instance);
4670  self::$singletonInstances[$className] = $instance;
4671  }
4672 
4689  public static function removeSingletonInstance($className, SingletonInterface $instance)
4690  {
4691  self::checkInstanceClassName($className, $instance);
4692  if (!isset(self::$singletonInstances[$className])) {
4693  throw new \InvalidArgumentException('No Instance registered for ' . $className . '.', 1394099179);
4694  }
4695  if ($instance !== self::$singletonInstances[$className]) {
4696  throw new \InvalidArgumentException('The instance you are trying to remove has not been registered before.', 1394099256);
4697  }
4698  unset(self::$singletonInstances[$className]);
4699  }
4700 
4715  public static function resetSingletonInstances(array $newSingletonInstances)
4716  {
4717  static::$singletonInstances = [];
4718  foreach ($newSingletonInstances as $className => $instance) {
4719  static::setSingletonInstance($className, $instance);
4720  }
4721  }
4722 
4735  public static function getSingletonInstances()
4736  {
4737  return static::$singletonInstances;
4738  }
4739 
4755  public static function addInstance($className, $instance)
4756  {
4757  self::checkInstanceClassName($className, $instance);
4758  if ($instance instanceof SingletonInterface) {
4759  throw new \InvalidArgumentException('$instance must not be an instance of TYPO3\\CMS\\Core\\SingletonInterface. ' . 'For setting singletons, please use setSingletonInstance.', 1288969325);
4760  }
4761  if (!isset(self::$nonSingletonInstances[$className])) {
4762  self::$nonSingletonInstances[$className] = [];
4763  }
4764  self::$nonSingletonInstances[$className][] = $instance;
4765  }
4766 
4776  protected static function checkInstanceClassName($className, $instance)
4777  {
4778  if ($className === '') {
4779  throw new \InvalidArgumentException('$className must not be empty.', 1288967479);
4780  }
4781  if (!$instance instanceof $className) {
4782  throw new \InvalidArgumentException('$instance must be an instance of ' . $className . ', but actually is an instance of ' . get_class($instance) . '.', 1288967686);
4783  }
4784  }
4785 
4797  public static function purgeInstances()
4798  {
4799  self::$singletonInstances = [];
4800  self::$nonSingletonInstances = [];
4801  }
4802 
4811  public static function flushInternalRuntimeCaches()
4812  {
4813  self::$indpEnvCache = [];
4814  self::$idnaStringCache = [];
4815  }
4816 
4826  public static function makeInstanceService($serviceType, $serviceSubType = '', $excludeServiceKeys = [])
4827  {
4828  $error = false;
4829  if (!is_array($excludeServiceKeys)) {
4830  $excludeServiceKeys = self::trimExplode(',', $excludeServiceKeys, true);
4831  }
4832  $requestInfo = [
4833  'requestedServiceType' => $serviceType,
4834  'requestedServiceSubType' => $serviceSubType,
4835  'requestedExcludeServiceKeys' => $excludeServiceKeys
4836  ];
4837  while ($info = ExtensionManagementUtility::findService($serviceType, $serviceSubType, $excludeServiceKeys)) {
4838  // provide information about requested service to service object
4839  $info = array_merge($info, $requestInfo);
4840  // Check persistent object and if found, call directly and exit.
4841  if (is_object($GLOBALS['T3_VAR']['makeInstanceService'][$info['className']])) {
4842  // update request info in persistent object
4843  $GLOBALS['T3_VAR']['makeInstanceService'][$info['className']]->info = $info;
4844  // reset service and return object
4845  $GLOBALS['T3_VAR']['makeInstanceService'][$info['className']]->reset();
4846  return $GLOBALS['T3_VAR']['makeInstanceService'][$info['className']];
4847  } else {
4848  $obj = self::makeInstance($info['className']);
4849  if (is_object($obj)) {
4850  if (!@is_callable([$obj, 'init'])) {
4851  // use silent logging??? I don't think so.
4852  die('Broken service:' . DebugUtility::viewArray($info));
4853  }
4854  $obj->info = $info;
4855  // service available?
4856  if ($obj->init()) {
4857  // create persistent object
4858  $GLOBALS['T3_VAR']['makeInstanceService'][$info['className']] = $obj;
4859  return $obj;
4860  }
4861  $error = $obj->getLastErrorArray();
4862  unset($obj);
4863  }
4864  }
4865  // deactivate the service
4866  ExtensionManagementUtility::deactivateService($info['serviceType'], $info['serviceKey']);
4867  }
4868  return $error;
4869  }
4870 
4878  public static function requireOnce($requireFile)
4879  {
4880  // Needed for require_once
4881  global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS;
4882  require_once $requireFile;
4883  }
4884 
4893  public static function requireFile($requireFile)
4894  {
4895  // Needed for require
4896  global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS;
4897  require $requireFile;
4898  }
4899 
4909  public static function quoted_printable($string, $maxlen = 76)
4910  {
4911  static::logDeprecatedFunction();
4912  // Make sure the string contains only Unix line breaks
4913  // Replace Windows breaks (\r\n)
4914  $string = str_replace(CRLF, LF, $string);
4915  // Replace Mac breaks (\r)
4916  $string = str_replace(CR, LF, $string);
4917  // Default line break for Unix systems.
4918  $linebreak = LF;
4919  if (TYPO3_OS == 'WIN') {
4920  // Line break for Windows. This is needed because PHP on Windows systems send mails via SMTP instead of using sendmail, and thus the line break needs to be \r\n.
4921  $linebreak = CRLF;
4922  }
4923  $newString = '';
4924  // Split lines
4925  $theLines = explode(LF, $string);
4926  foreach ($theLines as $val) {
4927  $newVal = '';
4928  $theValLen = strlen($val);
4929  $len = 0;
4930  // Walk through each character of this line
4931  for ($index = 0; $index < $theValLen; $index++) {
4932  $char = substr($val, $index, 1);
4933  $ordVal = ord($char);
4934  if ($len > $maxlen - 4 || $len > $maxlen - 14 && $ordVal == 32) {
4935  // Add a line break
4936  $newVal .= '=' . $linebreak;
4937  // Reset the length counter
4938  $len = 0;
4939  }
4940  if ($ordVal >= 33 && $ordVal <= 60 || $ordVal >= 62 && $ordVal <= 126 || $ordVal == 9 || $ordVal == 32) {
4941  // This character is ok, add it to the message
4942  $newVal .= $char;
4943  $len++;
4944  } else {
4945  // Special character, needs to be encoded
4946  $newVal .= sprintf('=%02X', $ordVal);
4947  $len += 3;
4948  }
4949  }
4950  // Replaces a possible SPACE-character at the end of a line
4951  $newVal = preg_replace('/' . chr(32) . '$/', '=20', $newVal);
4952  // Replaces a possible TAB-character at the end of a line
4953  $newVal = preg_replace('/' . TAB . '$/', '=09', $newVal);
4954  $newString .= $newVal . $linebreak;
4955  }
4956  // Remove last newline
4957  return preg_replace('/' . $linebreak . '$/', '', $newString);
4958  }
4959 
4970  public static function encodeHeader($line, $enc = 'quoted-printable', $charset = 'utf-8')
4971  {
4972  static::logDeprecatedFunction();
4973  // Avoid problems if "###" is found in $line (would conflict with the placeholder which is used below)
4974  if (strpos($line, '###') !== false) {
4975  return $line;
4976  }
4977  // Check if any non-ASCII characters are found - otherwise encoding is not needed
4978  if (!preg_match(('/[^' . chr(32) . '-' . chr(127) . ']/'), $line)) {
4979  return $line;
4980  }
4981  // Wrap email addresses in a special marker
4982  $line = preg_replace('/([^ ]+@[^ ]+)/', '###$1###', $line);
4983  $matches = preg_split('/(.?###.+###.?|\\(|\\))/', $line, -1, PREG_SPLIT_NO_EMPTY);
4984  foreach ($matches as $part) {
4985  $oldPart = $part;
4986  $partWasQuoted = $part[0] == '"';
4987  $part = trim($part, '"');
4988  switch ((string)$enc) {
4989  case 'base64':
4990  $part = '=?' . $charset . '?B?' . base64_encode($part) . '?=';
4991  break;
4992  case 'quoted-printable':
4993 
4994  default:
4995  $qpValue = self::quoted_printable($part, 1000);
4996  if ($part != $qpValue) {
4997  // Encoded words in the header should not contain non-encoded:
4998  // * spaces. "_" is a shortcut for "=20". See RFC 2047 for details.
4999  // * question mark. See RFC 1342 (http://tools.ietf.org/html/rfc1342)
5000  $search = [' ', '?'];
5001  $replace = ['_', '=3F'];
5002  $qpValue = str_replace($search, $replace, $qpValue);
5003  $part = '=?' . $charset . '?Q?' . $qpValue . '?=';
5004  }
5005  }
5006  if ($partWasQuoted) {
5007  $part = '"' . $part . '"';
5008  }
5009  $line = str_replace($oldPart, $part, $line);
5010  }
5011  // Remove the wrappers
5012  $line = preg_replace('/###(.+?)###/', '$1', $line);
5013  return $line;
5014  }
5015 
5027  public static function substUrlsInPlainText($message, $urlmode = '76', $index_script_url = '')
5028  {
5029  static::logDeprecatedFunction();
5030  switch ((string)$urlmode) {
5031  case '':
5032  $lengthLimit = false;
5033  break;
5034  case 'all':
5035  $lengthLimit = 0;
5036  break;
5037  case '76':
5038 
5039  default:
5040  $lengthLimit = (int)$urlmode;
5041  }
5042  if ($lengthLimit === false) {
5043  // No processing
5044  $messageSubstituted = $message;
5045  } else {
5046  $messageSubstituted = preg_replace_callback(
5047  '/(http|https):\\/\\/.+(?=[\\]\\.\\?]*([\\! \'"()<>]+|$))/iU',
5048  function (array $matches) use ($lengthLimit, $index_script_url) {
5049  return GeneralUtility::makeRedirectUrl($matches[0], $lengthLimit, $index_script_url);
5050  },
5051  $message
5052  );
5053  }
5054  return $messageSubstituted;
5055  }
5056 
5065  public static function makeRedirectUrl($inUrl, $l = 0, $index_script_url = '')
5066  {
5067  if (strlen($inUrl) > $l) {
5068  $md5 = substr(md5($inUrl), 0, 20);
5069  $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('*', 'cache_md5params', 'md5hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($md5, 'cache_md5params'));
5070  if (!$count) {
5071  $insertFields = [
5072  'md5hash' => $md5,
5073  'tstamp' => $GLOBALS['EXEC_TIME'],
5074  'type' => 2,
5075  'params' => $inUrl
5076  ];
5077  $GLOBALS['TYPO3_DB']->exec_INSERTquery('cache_md5params', $insertFields);
5078  }
5079  $inUrl = ($index_script_url ? $index_script_url : self::getIndpEnv('TYPO3_REQUEST_DIR') . 'index.php') . '?RDCT=' . $md5;
5080  }
5081  return $inUrl;
5082  }
5083 
5091  public static function freetypeDpiComp($fontSize)
5092  {
5093  // FreeType 2 always has 96 dpi.
5094  $dpi = 96.0;
5095  return $fontSize / $dpi * 72;
5096  }
5097 
5104  public static function initSysLog()
5105  {
5106  // For CLI logging name is <fqdn-hostname>:<TYPO3-path>
5107  // Note that TYPO3_REQUESTTYPE is not used here as it may not yet be defined
5108  if (defined('TYPO3_cliMode') && TYPO3_cliMode) {
5109  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'] = self::getHostname(($requestHost = false)) . ':' . PATH_site;
5110  } else {
5111  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'] = self::getIndpEnv('TYPO3_SITE_URL');
5112  }
5113  // Init custom logging
5114  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'])) {
5115  $params = ['initLog' => true];
5116  $fakeThis = false;
5117  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'] as $hookMethod) {
5118  self::callUserFunction($hookMethod, $params, $fakeThis);
5119  }
5120  }
5121  // Init TYPO3 logging
5122  foreach (explode(';', $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLog'], 2) as $log) {
5123  list($type, $destination) = explode(',', $log, 3);
5124  if ($type == 'syslog') {
5125  if (TYPO3_OS == 'WIN') {
5126  $facility = LOG_USER;
5127  } else {
5128  $facility = constant('LOG_' . strtoupper($destination));
5129  }
5130  openlog($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'], LOG_ODELAY, $facility);
5131  }
5132  }
5133  $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLogLevel'] = MathUtility::forceIntegerInRange($GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLogLevel'], 0, 4);
5134  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogInit'] = true;
5135  }
5136 
5148  public static function sysLog($msg, $extKey, $severity = 0)
5149  {
5150  $severity = MathUtility::forceIntegerInRange($severity, 0, 4);
5151  // Is message worth logging?
5152  if ((int)$GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLogLevel'] > $severity) {
5153  return;
5154  }
5155  // Initialize logging
5156  if (!$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogInit']) {
5157  self::initSysLog();
5158  }
5159  // Do custom logging
5160  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'])) {
5161  $params = ['msg' => $msg, 'extKey' => $extKey, 'backTrace' => debug_backtrace(), 'severity' => $severity];
5162  $fakeThis = false;
5163  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'] as $hookMethod) {
5164  self::callUserFunction($hookMethod, $params, $fakeThis);
5165  }
5166  }
5167  // TYPO3 logging enabled?
5168  if (!$GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLog']) {
5169  return;
5170  }
5171  $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'];
5172  $timeFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
5173  // Use all configured logging options
5174  foreach (explode(';', $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLog'], 2) as $log) {
5175  list($type, $destination, $level) = explode(',', $log, 4);
5176  // Is message worth logging for this log type?
5177  if ((int)$level > $severity) {
5178  continue;
5179  }
5180  $msgLine = ' - ' . $extKey . ': ' . $msg;
5181  // Write message to a file
5182  if ($type == 'file') {
5183  $file = fopen($destination, 'a');
5184  if ($file) {
5185  fwrite($file, date(($dateFormat . ' ' . $timeFormat)) . $msgLine . LF);
5186  fclose($file);
5187  self::fixPermissions($destination);
5188  }
5189  } elseif ($type == 'mail') {
5190  list($to, $from) = explode('/', $destination);
5191  if (!self::validEmail($from)) {
5192  $from = MailUtility::getSystemFrom();
5193  }
5195  $mail = self::makeInstance(\TYPO3\CMS\Core\Mail\MailMessage::class);
5196  $mail->setTo($to)->setFrom($from)->setSubject('Warning - error in TYPO3 installation')->setBody('Host: ' . $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'] . LF . 'Extension: ' . $extKey . LF . 'Severity: ' . $severity . LF . LF . $msg);
5197  $mail->send();
5198  } elseif ($type == 'error_log') {
5199  error_log($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'] . $msgLine, 0);
5200  } elseif ($type == 'syslog') {
5201  $priority = [LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERR, LOG_CRIT];
5202  syslog($priority[(int)$severity], $msgLine);
5203  }
5204  }
5205  }
5206 
5221  public static function devLog($msg, $extKey, $severity = 0, $dataVar = false)
5222  {
5223  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['devLog'])) {
5224  $params = ['msg' => $msg, 'extKey' => $extKey, 'severity' => $severity, 'dataVar' => $dataVar];
5225  $fakeThis = false;
5226  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['devLog'] as $hookMethod) {
5227  self::callUserFunction($hookMethod, $params, $fakeThis);
5228  }
5229  }
5230  }
5231 
5238  public static function deprecationLog($msg)
5239  {
5240  if (!$GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog']) {
5241  return;
5242  }
5243  // Legacy values (no strict comparison, $log can be boolean, string or int)
5244  $log = $GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog'];
5245  if ($log === true || $log == '1') {
5246  $log = ['file'];
5247  } else {
5248  $log = self::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog'], true);
5249  }
5250  $date = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] . ': ');
5251  if (in_array('file', $log) !== false) {
5252  // Write a longer message to the deprecation log
5253  $destination = static::getDeprecationLogFileName();
5254  $file = @fopen($destination, 'a');
5255  if ($file) {
5256  @fwrite($file, ($date . $msg . LF));
5257  @fclose($file);
5258  self::fixPermissions($destination);
5259  }
5260  }
5261  if (in_array('devlog', $log) !== false) {
5262  // Copy message also to the developer log
5263  self::devLog($msg, 'Core', self::SYSLOG_SEVERITY_WARNING);
5264  }
5265  // Do not use console in login screen
5266  if (in_array('console', $log) !== false && isset($GLOBALS['BE_USER']->user['uid'])) {
5267  DebugUtility::debug($msg, $date, 'Deprecation Log');
5268  }
5269  }
5270 
5276  public static function getDeprecationLogFileName()
5277  {
5278  return PATH_typo3conf . 'deprecation_' . self::shortMD5((PATH_site . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) . '.log';
5279  }
5280 
5287  public static function logDeprecatedFunction()
5288  {
5289  if (!$GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog']) {
5290  return;
5291  }
5292  $trail = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
5293  if ($trail[1]['type']) {
5294  $function = new \ReflectionMethod($trail[1]['class'], $trail[1]['function']);
5295  } else {
5296  $function = new \ReflectionFunction($trail[1]['function']);
5297  }
5298  $msg = '';
5299  if (preg_match('/@deprecated\\s+(.*)/', $function->getDocComment(), $match)) {
5300  $msg = $match[1];
5301  }
5302  // Write a longer message to the deprecation log: <function> <annotion> - <trace> (<source>)
5303  $logMsg = $trail[1]['class'] . $trail[1]['type'] . $trail[1]['function'];
5304  $logMsg .= '() - ' . $msg . ' - ' . DebugUtility::debugTrail();
5305  $logMsg .= ' (' . PathUtility::stripPathSitePrefix($function->getFileName()) . '#' . $function->getStartLine() . ')';
5306  self::deprecationLog($logMsg);
5307  }
5308 
5318  public static function arrayToLogString(array $arr, $valueList = [], $valueLength = 20)
5319  {
5320  $str = '';
5321  if (!is_array($valueList)) {
5322  $valueList = self::trimExplode(',', $valueList, true);
5323  }
5324  $valListCnt = count($valueList);
5325  foreach ($arr as $key => $value) {
5326  if (!$valListCnt || in_array($key, $valueList)) {
5327  $str .= (string)$key . trim(': ' . self::fixed_lgd_cs(str_replace(LF, '|', (string)$value), $valueLength)) . '; ';
5328  }
5329  }
5330  return $str;
5331  }
5332 
5341  public static function imageMagickCommand($command, $parameters, $path = '')
5342  {
5343  return CommandUtility::imageMagickCommand($command, $parameters, $path);
5344  }
5345 
5355  public static function unQuoteFilenames($parameters, $unQuote = false)
5356  {
5357  $paramsArr = explode(' ', trim($parameters));
5358  // Whenever a quote character (") is found, $quoteActive is set to the element number inside of $params. A value of -1 means that there are not open quotes at the current position.
5359  $quoteActive = -1;
5360  foreach ($paramsArr as $k => $v) {
5361  if ($quoteActive > -1) {
5362  $paramsArr[$quoteActive] .= ' ' . $v;
5363  unset($paramsArr[$k]);
5364  if (substr($v, -1) === $paramsArr[$quoteActive][0]) {
5365  $quoteActive = -1;
5366  }
5367  } elseif (!trim($v)) {
5368  // Remove empty elements
5369  unset($paramsArr[$k]);
5370  } elseif (preg_match('/^(["\'])/', $v) && substr($v, -1) !== $v[0]) {
5371  $quoteActive = $k;
5372  }
5373  }
5374  if ($unQuote) {
5375  foreach ($paramsArr as $key => &$val) {
5376  $val = preg_replace('/(?:^"|"$)/', '', $val);
5377  $val = preg_replace('/(?:^\'|\'$)/', '', $val);
5378  }
5379  unset($val);
5380  }
5381  // Return reindexed array
5382  return array_values($paramsArr);
5383  }
5384 
5391  public static function quoteJSvalue($value)
5392  {
5393  return strtr(
5394  json_encode((string)$value, JSON_HEX_AMP|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_TAG),
5395  [
5396  '"' => '\'',
5397  '\\\\' => '\\u005C',
5398  ' ' => '\\u0020',
5399  '!' => '\\u0021',
5400  '\\t' => '\\u0009',
5401  '\\n' => '\\u000A',
5402  '\\r' => '\\u000D'
5403  ]
5404  );
5405  }
5406 
5413  public static function cleanOutputBuffers()
5414  {
5415  self::logDeprecatedFunction();
5416  while (ob_end_clean()) {
5417  }
5418  header('Content-Encoding: None', true);
5419  }
5420 
5426  public static function flushOutputBuffers()
5427  {
5428  $obContent = '';
5429  while ($content = ob_get_clean()) {
5430  $obContent .= $content;
5431  }
5432  // If previously a "Content-Encoding: whatever" has been set, we have to unset it
5433  if (!headers_sent()) {
5434  $headersList = headers_list();
5435  foreach ($headersList as $header) {
5436  // Split it up at the :
5437  list($key, $value) = self::trimExplode(':', $header, true);
5438  // Check if we have a Content-Encoding other than 'None'
5439  if (strtolower($key) === 'content-encoding' && strtolower($value) !== 'none') {
5440  header('Content-Encoding: None');
5441  break;
5442  }
5443  }
5444  }
5445  echo $obContent;
5446  }
5447 
5459  {
5460  if (is_null(static::$applicationContext)) {
5461  static::$applicationContext = $applicationContext;
5462  } else {
5463  throw new \RuntimeException('Trying to override applicationContext which has already been defined!', 1376084316);
5464  }
5465  }
5466 
5472  public static function getApplicationContext()
5473  {
5474  return static::$applicationContext;
5475  }
5476 
5481  public static function isRunningOnCgiServerApi()
5482  {
5483  return in_array(PHP_SAPI, self::$supportedCgiServerApis, true);
5484  }
5485 }
static devLog($msg, $extKey, $severity=0, $dataVar=false)
static modifyHTMLColor($color, $R, $G, $B)
static createDirectoryPath($fullDirectoryPath)
static compileSelectedGetVarsFromArray($varList, array $getArray, $GPvarAlt=true)
static imageMagickCommand($command, $parameters, $path='')
static stripSlashesOnArray(array &$theArray)
static minifyJavaScript($script, &$error='')
static unlink_tempfile($uploadedTempFileName)
static substUrlsInPlainText($message, $urlmode='76', $index_script_url='')
static intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
static mkdir_deep($directory, $deepDirectory='')
static removeSingletonInstance($className, SingletonInterface $instance)
if(!defined("DB_ERROR")) define("DB_ERROR"
static arrayToLogString(array $arr, $valueList=[], $valueLength=20)
static addInstance($className, $instance)
static array2xml(array $array, $NSprefix='', $level=0, $docTag='phparray', $spaceInd=0, array $options=[], array $stackData=[])
static implodeAttributes(array $arr, $xhtmlSafe=false, $dontOmitBlankAttribs=false)
static quoted_printable($string, $maxlen=76)
static getFilesInDir($path, $extensionList='', $prependPath=false, $order='', $excludePattern='')
static flushDirectory($directory, $keepOriginalDirectory=false, $flushOpcodeCache=false)
static stdAuthCode($uid_or_record, $fields='', $codeLength=8)
static array2xml_cs(array $array, $docTag='phparray', array $options=[], $charset='')
static debug($var='', $header='', $group='Debug')
debug($variable='', $name=' *variable *', $line=' *line *', $file=' *file *', $recursiveDepth=3, $debugLevel='E_DEBUG')
static isFirstPartOfStr($str, $partStr)
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:31
static setSingletonInstance($className, SingletonInterface $instance)
static csvValues(array $row, $delim=',', $quote='"')
static generateRandomBytesUrandom($bytesToGenerate)
static resolveAllSheetsInDS(array $dataStructArray)
static writeFileToTypo3tempDir($filepath, $content)
static hmac($input, $additionalSecret='')
static generateRandomBytes($bytesToReturn)
static isAllowedHostHeaderValue($hostHeaderValue)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static hideIfDefaultLanguage($localizationConfiguration)
static llXmlAutoFileName($fileRef, $language, $sameLocation=false)
static verifyFilenameAgainstDenyPattern($filename)
static remapArrayKeys(&$array, $mappingTable)
static copyDirectory($source, $destination)
static remapArrayKeys(array &$array, array $mappingTable)
static linkThisScript(array $getParams=[])
static callUserFunction($funcName, &$params, &$ref, $checkPrefix='', $errorMode=0)
static hideIfNotTranslated($l18n_cfg_fieldValue)
static _GETset($inputGet, $key='')
static naturalKeySortRecursive(array &$array)
static inArray(array $in_array, $item)
static imageMagickCommand($command, $parameters, $path='')
static resetSingletonInstances(array $newSingletonInstances)
static makeInstanceService($serviceType, $serviceSubType='', $excludeServiceKeys=[])
static getHostname($requestHost=true)
static split_fileref($fileNameWithPath)
static addSlashesOnArray(array &$theArray)
static unQuoteFilenames($parameters, $unQuote=false)
static read_png_gif($theFile, $output_png=false)
static resolveSheetDefInDS($dataStructArray, $sheet='sDEF')
static implodeArrayForUrl($name, array $theArray, $str='', $skipBlank=false, $rawurlencodeParamName=false)
static fixPermissions($path, $recursive=false)
static gif_compress($theFile, $type)
static keepItemsInArray(array $array, $keepItems, $getValueFunc=null)
static getAllFilesAndFoldersInPath(array $fileArr, $path, $extList='', $regDirs=false, $recursivityLevels=99, $excludePattern='')
static buildUrl(array $urlParts)
static getBytesFromSizeMeasurement($measurement)
static linkThisUrl($url, array $getParams=[])
static storeHash($hash, $data, $ident, $lifetime=0)
static inArray(array $in_array, $item)
static upload_to_tempfile($uploadedFileName)
static tempnam($filePrefix, $fileSuffix='')
static hostHeaderValueMatchesTrustedHostsPattern($hostHeaderValue)
static xml2tree($string, $depth=999, $parserOptions=[])
static rmdir($path, $removeNonEmpty=false)
static explodeUrl2Array($string, $multidim=false)
static generateRandomBytesMcrypt($bytesToGenerate)
static splitCalc($string, $operators)
static removeArrayEntryByValue(array $array, $cmpValue)
static keepItemsInArray(array $array, $keepItems, $getValueFunc=null)
static array_merge(array $arr1, array $arr2)
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
static getUrl($url, $includeHeader=0, $requestHeaders=false, &$report=null)
static xml2array($string, $NSprefix='', $reportDocTag=false)
static readPngGif($theFile, $output_png=false)
static fixed_lgd_cs($string, $chars, $appendString='...')
static findService($serviceType, $serviceSubType='', $excludeServiceKeys=[])
static getFileAbsFileName($filename, $onlyRelative=true, $relToTYPO3_mainDir=false)
static formatSize($sizeInBytes, $labels='', $base=0)
$host
Definition: server.php:37
static removeArrayEntryByValue(array $array, $cmpValue)
static revExplode($delimiter, $string, $count=0)
static checkInstanceClassName($className, $instance)
static generateRandomBytesFallback($bytesToReturn)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static removePrefixPathFromList(array $fileArr, $prefixToRemove)
static xml2arrayProcess($string, $NSprefix='', $reportDocTag=false)
static slashJS($string, $extended=false, $char='\'')
static xmlRecompileFromStructValArray(array $vals)
static encodeHeader($line, $enc='quoted-printable', $charset='utf-8')
static writeFile($file, $content, $changePermissions=false)
static arrayDiffAssocRecursive(array $array1, array $array2)
static wrapJS($string, $linebreak=true)
static generateRandomBytesOpenSsl($bytesToGenerate)
static makeRedirectUrl($inUrl, $l=0, $index_script_url='')
static uniqueList($in_list, $secondParameter=null)
static upload_copy_move($source, $destination)
static presetApplicationContext(ApplicationContext $applicationContext)
static arrayDiffAssocRecursive(array $array1, array $array2)
static instantiateClass($className, $arguments)