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 = array();
63 
69  protected static $nonSingletonInstances = array();
70 
76  protected static $finalClassNameCache = array();
77 
83  protected static $applicationContext = null;
84 
90  protected static $idnaStringCache = array();
91 
97  protected static $idnaConverter = null;
98 
104  protected static $supportedCgiServerApis = array(
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  $value = isset($_POST[$var]) ? $_POST[$var] : $_GET[$var];
145  // This is there for backwards-compatibility, in order to avoid NULL
146  if (isset($value) && !is_array($value)) {
147  $value = (string)$value;
148  }
149  return $value;
150  }
151 
158  public static function _GPmerged($parameter)
159  {
160  $postParameter = isset($_POST[$parameter]) && is_array($_POST[$parameter]) ? $_POST[$parameter] : array();
161  $getParameter = isset($_GET[$parameter]) && is_array($_GET[$parameter]) ? $_GET[$parameter] : array();
162  $mergedParameters = $getParameter;
163  ArrayUtility::mergeRecursiveWithOverrule($mergedParameters, $postParameter);
164  return $mergedParameters;
165  }
166 
176  public static function _GET($var = null)
177  {
178  $value = $var === null ? $_GET : (empty($var) ? null : $_GET[$var]);
179  // This is there for backwards-compatibility, in order to avoid NULL
180  if (isset($value) && !is_array($value)) {
181  $value = (string)$value;
182  }
183  return $value;
184  }
185 
194  public static function _POST($var = null)
195  {
196  $value = $var === null ? $_POST : (empty($var) ? null : $_POST[$var]);
197  // This is there for backwards-compatibility, in order to avoid NULL
198  if (isset($value) && !is_array($value)) {
199  $value = (string)$value;
200  }
201  return $value;
202  }
203 
211  public static function _GETset($inputGet, $key = '')
212  {
213  if ($key != '') {
214  if (strpos($key, '|') !== false) {
215  $pieces = explode('|', $key);
216  $newGet = array();
217  $pointer = &$newGet;
218  foreach ($pieces as $piece) {
219  $pointer = &$pointer[$piece];
220  }
221  $pointer = $inputGet;
222  $mergedGet = $_GET;
223  ArrayUtility::mergeRecursiveWithOverrule($mergedGet, $newGet);
224  $_GET = $mergedGet;
225  $GLOBALS['HTTP_GET_VARS'] = $mergedGet;
226  } else {
227  $_GET[$key] = $inputGet;
228  $GLOBALS['HTTP_GET_VARS'][$key] = $inputGet;
229  }
230  } elseif (is_array($inputGet)) {
231  $_GET = $inputGet;
232  $GLOBALS['HTTP_GET_VARS'] = $inputGet;
233  }
234  }
235 
245  public static function removeXSS($string)
246  {
247  return \RemoveXSS::process($string);
248  }
249 
250  /*************************
251  *
252  * IMAGE FUNCTIONS
253  *
254  *************************/
274  public static function gif_compress($theFile, $type)
275  {
276  static::logDeprecatedFunction();
277  $returnCode = GraphicalFunctions::gifCompress($theFile, $type);
278  return $returnCode;
279  }
280 
289  public static function png_to_gif_by_imagemagick($theFile)
290  {
291  static::logDeprecatedFunction();
292  $newFile = GraphicalFunctions::pngToGifByImagemagick($theFile);
293  return $newFile;
294  }
295 
305  public static function read_png_gif($theFile, $output_png = false)
306  {
307  static::logDeprecatedFunction();
308  $newFile = GraphicalFunctions::readPngGif($theFile, $output_png);
309  return $newFile;
310  }
311 
312  /*************************
313  *
314  * STRING FUNCTIONS
315  *
316  *************************/
325  public static function fixed_lgd_cs($string, $chars, $appendString = '...')
326  {
327  if (is_object($GLOBALS['LANG'])) {
328  return $GLOBALS['LANG']->csConvObj->crop($GLOBALS['LANG']->charSet, $string, $chars, $appendString);
329  } elseif (is_object($GLOBALS['TSFE']) && is_object($GLOBALS['TSFE']->csConvObj)) {
330  $charSet = $GLOBALS['TSFE']->renderCharset != '' ? $GLOBALS['TSFE']->renderCharset : $GLOBALS['TSFE']->defaultCharSet;
331  return $GLOBALS['TSFE']->csConvObj->crop($charSet, $string, $chars, $appendString);
332  } else {
333  // This case should not happen
334  $csConvObj = self::makeInstance(\TYPO3\CMS\Core\Charset\CharsetConverter::class);
335  return $csConvObj->crop('utf-8', $string, $chars, $appendString);
336  }
337  }
338 
347  public static function cmpIP($baseIP, $list)
348  {
349  $list = trim($list);
350  if ($list === '') {
351  return false;
352  } elseif ($list === '*') {
353  return true;
354  }
355  if (strpos($baseIP, ':') !== false && self::validIPv6($baseIP)) {
356  return self::cmpIPv6($baseIP, $list);
357  } else {
358  return self::cmpIPv4($baseIP, $list);
359  }
360  }
361 
369  public static function cmpIPv4($baseIP, $list)
370  {
371  $IPpartsReq = explode('.', $baseIP);
372  if (count($IPpartsReq) === 4) {
373  $values = self::trimExplode(',', $list, true);
374  foreach ($values as $test) {
375  $testList = explode('/', $test);
376  if (count($testList) === 2) {
377  list($test, $mask) = $testList;
378  } else {
379  $mask = false;
380  }
381  if ((int)$mask) {
382  // "192.168.3.0/24"
383  $lnet = ip2long($test);
384  $lip = ip2long($baseIP);
385  $binnet = str_pad(decbin($lnet), 32, '0', STR_PAD_LEFT);
386  $firstpart = substr($binnet, 0, $mask);
387  $binip = str_pad(decbin($lip), 32, '0', STR_PAD_LEFT);
388  $firstip = substr($binip, 0, $mask);
389  $yes = $firstpart === $firstip;
390  } else {
391  // "192.168.*.*"
392  $IPparts = explode('.', $test);
393  $yes = 1;
394  foreach ($IPparts as $index => $val) {
395  $val = trim($val);
396  if ($val !== '*' && $IPpartsReq[$index] !== $val) {
397  $yes = 0;
398  }
399  }
400  }
401  if ($yes) {
402  return true;
403  }
404  }
405  }
406  return false;
407  }
408 
416  public static function cmpIPv6($baseIP, $list)
417  {
418  // Policy default: Deny connection
419  $success = false;
420  $baseIP = self::normalizeIPv6($baseIP);
421  $values = self::trimExplode(',', $list, true);
422  foreach ($values as $test) {
423  $testList = explode('/', $test);
424  if (count($testList) === 2) {
425  list($test, $mask) = $testList;
426  } else {
427  $mask = false;
428  }
429  if (self::validIPv6($test)) {
430  $test = self::normalizeIPv6($test);
431  $maskInt = (int)$mask ?: 128;
432  // Special case; /0 is an allowed mask - equals a wildcard
433  if ($mask === '0') {
434  $success = true;
435  } elseif ($maskInt == 128) {
436  $success = $test === $baseIP;
437  } else {
438  $testBin = self::IPv6Hex2Bin($test);
439  $baseIPBin = self::IPv6Hex2Bin($baseIP);
440  $success = true;
441  // Modulo is 0 if this is a 8-bit-boundary
442  $maskIntModulo = $maskInt % 8;
443  $numFullCharactersUntilBoundary = (int)($maskInt / 8);
444  if (substr($testBin, 0, $numFullCharactersUntilBoundary) !== substr($baseIPBin, 0, $numFullCharactersUntilBoundary)) {
445  $success = false;
446  } elseif ($maskIntModulo > 0) {
447  // If not an 8-bit-boundary, check bits of last character
448  $testLastBits = str_pad(decbin(ord(substr($testBin, $numFullCharactersUntilBoundary, 1))), 8, '0', STR_PAD_LEFT);
449  $baseIPLastBits = str_pad(decbin(ord(substr($baseIPBin, $numFullCharactersUntilBoundary, 1))), 8, '0', STR_PAD_LEFT);
450  if (strncmp($testLastBits, $baseIPLastBits, $maskIntModulo) != 0) {
451  $success = false;
452  }
453  }
454  }
455  }
456  if ($success) {
457  return true;
458  }
459  }
460  return false;
461  }
462 
470  public static function IPv6Hex2Bin($hex)
471  {
472  return inet_pton($hex);
473  }
474 
482  public static function IPv6Bin2Hex($bin)
483  {
484  return inet_ntop($bin);
485  }
486 
494  public static function normalizeIPv6($address)
495  {
496  $normalizedAddress = '';
497  $stageOneAddress = '';
498  // According to RFC lowercase-representation is recommended
499  $address = strtolower($address);
500  // Normalized representation has 39 characters (0000:0000:0000:0000:0000:0000:0000:0000)
501  if (strlen($address) == 39) {
502  // Already in full expanded form
503  return $address;
504  }
505  // Count 2 if if address has hidden zero blocks
506  $chunks = explode('::', $address);
507  if (count($chunks) === 2) {
508  $chunksLeft = explode(':', $chunks[0]);
509  $chunksRight = explode(':', $chunks[1]);
510  $left = count($chunksLeft);
511  $right = count($chunksRight);
512  // Special case: leading zero-only blocks count to 1, should be 0
513  if ($left == 1 && strlen($chunksLeft[0]) == 0) {
514  $left = 0;
515  }
516  $hiddenBlocks = 8 - ($left + $right);
517  $hiddenPart = '';
518  $h = 0;
519  while ($h < $hiddenBlocks) {
520  $hiddenPart .= '0000:';
521  $h++;
522  }
523  if ($left == 0) {
524  $stageOneAddress = $hiddenPart . $chunks[1];
525  } else {
526  $stageOneAddress = $chunks[0] . ':' . $hiddenPart . $chunks[1];
527  }
528  } else {
529  $stageOneAddress = $address;
530  }
531  // Normalize the blocks:
532  $blocks = explode(':', $stageOneAddress);
533  $divCounter = 0;
534  foreach ($blocks as $block) {
535  $tmpBlock = '';
536  $i = 0;
537  $hiddenZeros = 4 - strlen($block);
538  while ($i < $hiddenZeros) {
539  $tmpBlock .= '0';
540  $i++;
541  }
542  $normalizedAddress .= $tmpBlock . $block;
543  if ($divCounter < 7) {
544  $normalizedAddress .= ':';
545  $divCounter++;
546  }
547  }
548  return $normalizedAddress;
549  }
550 
558  public static function compressIPv6($address)
559  {
560  return inet_ntop(inet_pton($address));
561  }
562 
571  public static function validIP($ip)
572  {
573  return filter_var($ip, FILTER_VALIDATE_IP) !== false;
574  }
575 
584  public static function validIPv4($ip)
585  {
586  return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
587  }
588 
597  public static function validIPv6($ip)
598  {
599  return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
600  }
601 
609  public static function cmpFQDN($baseHost, $list)
610  {
611  $baseHost = trim($baseHost);
612  if (empty($baseHost)) {
613  return false;
614  }
615  if (self::validIPv4($baseHost) || self::validIPv6($baseHost)) {
616  // Resolve hostname
617  // Note: this is reverse-lookup and can be randomly set as soon as somebody is able to set
618  // the reverse-DNS for his IP (security when for example used with REMOTE_ADDR)
619  $baseHostName = gethostbyaddr($baseHost);
620  if ($baseHostName === $baseHost) {
621  // Unable to resolve hostname
622  return false;
623  }
624  } else {
625  $baseHostName = $baseHost;
626  }
627  $baseHostNameParts = explode('.', $baseHostName);
628  $values = self::trimExplode(',', $list, true);
629  foreach ($values as $test) {
630  $hostNameParts = explode('.', $test);
631  // To match hostNameParts can only be shorter (in case of wildcards) or equal
632  $hostNamePartsCount = count($hostNameParts);
633  $baseHostNamePartsCount = count($baseHostNameParts);
634  if ($hostNamePartsCount > $baseHostNamePartsCount) {
635  continue;
636  }
637  $yes = true;
638  foreach ($hostNameParts as $index => $val) {
639  $val = trim($val);
640  if ($val === '*') {
641  // Wildcard valid for one or more hostname-parts
642  $wildcardStart = $index + 1;
643  // Wildcard as last/only part always matches, otherwise perform recursive checks
644  if ($wildcardStart < $hostNamePartsCount) {
645  $wildcardMatched = false;
646  $tempHostName = implode('.', array_slice($hostNameParts, $index + 1));
647  while ($wildcardStart < $baseHostNamePartsCount && !$wildcardMatched) {
648  $tempBaseHostName = implode('.', array_slice($baseHostNameParts, $wildcardStart));
649  $wildcardMatched = self::cmpFQDN($tempBaseHostName, $tempHostName);
650  $wildcardStart++;
651  }
652  if ($wildcardMatched) {
653  // Match found by recursive compare
654  return true;
655  } else {
656  $yes = false;
657  }
658  }
659  } elseif ($baseHostNameParts[$index] !== $val) {
660  // In case of no match
661  $yes = false;
662  }
663  }
664  if ($yes) {
665  return true;
666  }
667  }
668  return false;
669  }
670 
678  public static function isOnCurrentHost($url)
679  {
680  return stripos($url . '/', self::getIndpEnv('TYPO3_REQUEST_HOST') . '/') === 0;
681  }
682 
691  public static function inList($list, $item)
692  {
693  return strpos(',' . $list . ',', ',' . $item . ',') !== false;
694  }
695 
706  public static function rmFromList($element, $list)
707  {
708  $items = explode(',', $list);
709  foreach ($items as $k => $v) {
710  if ($v == $element) {
711  unset($items[$k]);
712  }
713  }
714  return implode(',', $items);
715  }
716 
724  public static function expandList($list)
725  {
726  $items = explode(',', $list);
727  $list = array();
728  foreach ($items as $item) {
729  $range = explode('-', $item);
730  if (isset($range[1])) {
731  $runAwayBrake = 1000;
732  for ($n = $range[0]; $n <= $range[1]; $n++) {
733  $list[] = $n;
734  $runAwayBrake--;
735  if ($runAwayBrake <= 0) {
736  break;
737  }
738  }
739  } else {
740  $list[] = $item;
741  }
742  }
743  return implode(',', $list);
744  }
745 
754  public static function compat_version($verNumberStr)
755  {
757  }
758 
765  public static function md5int($str)
766  {
767  return hexdec(substr(md5($str), 0, 7));
768  }
769 
777  public static function shortMD5($input, $len = 10)
778  {
779  return substr(md5($input), 0, $len);
780  }
781 
789  public static function hmac($input, $additionalSecret = '')
790  {
791  $hashAlgorithm = 'sha1';
792  $hashBlocksize = 64;
793  $hmac = '';
794  $secret = $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] . $additionalSecret;
795  if (extension_loaded('hash') && function_exists('hash_hmac') && function_exists('hash_algos') && in_array($hashAlgorithm, hash_algos())) {
796  $hmac = hash_hmac($hashAlgorithm, $input, $secret);
797  } else {
798  // Outer padding
799  $opad = str_repeat(chr(92), $hashBlocksize);
800  // Inner padding
801  $ipad = str_repeat(chr(54), $hashBlocksize);
802  if (strlen($secret) > $hashBlocksize) {
803  // Keys longer than block size are shorten
804  $key = str_pad(pack('H*', call_user_func($hashAlgorithm, $secret)), $hashBlocksize, chr(0));
805  } else {
806  // Keys shorter than block size are zero-padded
807  $key = str_pad($secret, $hashBlocksize, chr(0));
808  }
809  $hmac = call_user_func($hashAlgorithm, ($key ^ $opad) . pack('H*', call_user_func($hashAlgorithm, (($key ^ $ipad) . $input))));
810  }
811  return $hmac;
812  }
813 
822  public static function uniqueList($in_list, $secondParameter = null)
823  {
824  if (is_array($in_list)) {
825  throw new \InvalidArgumentException('TYPO3 Fatal Error: TYPO3\\CMS\\Core\\Utility\\GeneralUtility::uniqueList() does NOT support array arguments anymore! Only string comma lists!', 1270853885);
826  }
827  if (isset($secondParameter)) {
828  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);
829  }
830  return implode(',', array_unique(self::trimExplode(',', $in_list, true)));
831  }
832 
839  public static function split_fileref($fileNameWithPath)
840  {
841  $reg = array();
842  if (preg_match('/(.*\\/)(.*)$/', $fileNameWithPath, $reg)) {
843  $info['path'] = $reg[1];
844  $info['file'] = $reg[2];
845  } else {
846  $info['path'] = '';
847  $info['file'] = $fileNameWithPath;
848  }
849  $reg = '';
850  // If open_basedir is set and the fileName was supplied without a path the is_dir check fails
851  if (!is_dir($fileNameWithPath) && preg_match('/(.*)\\.([^\\.]*$)/', $info['file'], $reg)) {
852  $info['filebody'] = $reg[1];
853  $info['fileext'] = strtolower($reg[2]);
854  $info['realFileext'] = $reg[2];
855  } else {
856  $info['filebody'] = $info['file'];
857  $info['fileext'] = '';
858  }
859  reset($info);
860  return $info;
861  }
862 
878  public static function dirname($path)
879  {
880  $p = self::revExplode('/', $path, 2);
881  return count($p) === 2 ? $p[0] : '';
882  }
883 
895  public static function modifyHTMLColor($color, $R, $G, $B)
896  {
897  self::logDeprecatedFunction();
898  // This takes a hex-color (# included!) and adds $R, $G and $B to the HTML-color (format: #xxxxxx) and returns the new color
899  $nR = MathUtility::forceIntegerInRange(hexdec(substr($color, 1, 2)) + $R, 0, 255);
900  $nG = MathUtility::forceIntegerInRange(hexdec(substr($color, 3, 2)) + $G, 0, 255);
901  $nB = MathUtility::forceIntegerInRange(hexdec(substr($color, 5, 2)) + $B, 0, 255);
902  return '#' . substr(('0' . dechex($nR)), -2) . substr(('0' . dechex($nG)), -2) . substr(('0' . dechex($nB)), -2);
903  }
904 
914  public static function modifyHTMLColorAll($color, $all)
915  {
916  self::logDeprecatedFunction();
917  return self::modifyHTMLColor($color, $all, $all, $all);
918  }
919 
927  public static function isFirstPartOfStr($str, $partStr)
928  {
929  return $partStr != '' && strpos((string)$str, (string)$partStr, 0) === 0;
930  }
931 
940  public static function formatSize($sizeInBytes, $labels = '', $base = 0)
941  {
942  $defaultFormats = array(
943  'iec' => array('base' => 1024, 'labels' => array(' ', ' Ki', ' Mi', ' Gi', ' Ti', ' Pi', ' Ei', ' Zi', ' Yi')),
944  'si' => array('base' => 1000, 'labels' => array(' ', ' k', ' M', ' G', ' T', ' P', ' E', ' Z', ' Y')),
945  );
946  // Set labels and base:
947  if (empty($labels)) {
948  $labels = 'iec';
949  }
950  if (isset($defaultFormats[$labels])) {
951  $base = $defaultFormats[$labels]['base'];
952  $labelArr = $defaultFormats[$labels]['labels'];
953  } else {
954  $base = (int)$base;
955  if ($base !== 1000 && $base !== 1024) {
956  $base = 1024;
957  }
958  $labelArr = explode('|', str_replace('"', '', $labels));
959  }
960  // @todo find out which locale is used for current BE user to cover the BE case as well
961  $oldLocale = setlocale(LC_NUMERIC, 0);
962  $newLocale = is_object($GLOBALS['TSFE']) ? $GLOBALS['TSFE']->config['config']['locale_all'] : '';
963  if ($newLocale) {
964  setlocale(LC_NUMERIC, $newLocale);
965  }
966  $localeInfo = localeconv();
967  if ($newLocale) {
968  setlocale(LC_NUMERIC, $oldLocale);
969  }
970  $sizeInBytes = max($sizeInBytes, 0);
971  $multiplier = floor(($sizeInBytes ? log($sizeInBytes) : 0) / log($base));
972  $sizeInUnits = $sizeInBytes / pow($base, $multiplier);
973  if ($sizeInUnits > ($base * .9)) {
974  $multiplier++;
975  }
976  $multiplier = min($multiplier, count($labelArr) - 1);
977  $sizeInUnits = $sizeInBytes / pow($base, $multiplier);
978  return number_format($sizeInUnits, (($multiplier > 0) && ($sizeInUnits < 20)) ? 2 : 0, $localeInfo['decimal_point'], '') . $labelArr[$multiplier];
979  }
980 
987  public static function convertMicrotime($microtime)
988  {
989  $parts = explode(' ', $microtime);
990  return round(($parts[0] + $parts[1]) * 1000);
991  }
992 
1001  public static function splitCalc($string, $operators)
1002  {
1003  $res = array();
1004  $sign = '+';
1005  while ($string) {
1006  $valueLen = strcspn($string, $operators);
1007  $value = substr($string, 0, $valueLen);
1008  $res[] = array($sign, trim($value));
1009  $sign = substr($string, $valueLen, 1);
1010  $string = substr($string, $valueLen + 1);
1011  }
1012  reset($res);
1013  return $res;
1014  }
1015 
1025  public static function deHSCentities($str)
1026  {
1027  return preg_replace('/&amp;([#[:alnum:]]*;)/', '&\\1', $str);
1028  }
1029 
1038  public static function slashJS($string, $extended = false, $char = '\'')
1039  {
1040  if ($extended) {
1041  $string = str_replace('\\', '\\\\', $string);
1042  }
1043  return str_replace($char, '\\' . $char, $string);
1044  }
1045 
1053  public static function rawUrlEncodeJS($str)
1054  {
1055  return str_replace('%20', ' ', rawurlencode($str));
1056  }
1057 
1065  public static function rawUrlEncodeFP($str)
1066  {
1067  return str_replace('%2F', '/', rawurlencode($str));
1068  }
1069 
1088  public static function validEmail($email)
1089  {
1090  // Early return in case input is not a string
1091  if (!is_string($email)) {
1092  return false;
1093  }
1094  $atPosition = strrpos($email, '@');
1095  if (!$atPosition || $atPosition + 1 === strlen($email)) {
1096  // Return if no @ found or it is placed at the very beginning or end of the email
1097  return false;
1098  }
1099  $domain = substr($email, $atPosition + 1);
1100  $user = substr($email, 0, $atPosition);
1101  if (!preg_match('/^[a-z0-9.\\-]*$/i', $domain)) {
1102  $domain = self::idnaEncode($domain);
1103  }
1104  return filter_var($user . '@' . $domain, FILTER_VALIDATE_EMAIL) !== false;
1105  }
1106 
1120  public static function isBrokenEmailEnvironment()
1121  {
1122  self::logDeprecatedFunction();
1123  return TYPO3_OS == 'WIN' || false !== strpos(ini_get('sendmail_path'), 'mini_sendmail');
1124  }
1125 
1133  public static function normalizeMailAddress($address)
1134  {
1135  self::logDeprecatedFunction();
1136  if (self::isBrokenEmailEnvironment() && false !== ($pos1 = strrpos($address, '<'))) {
1137  $pos2 = strpos($address, '>', $pos1);
1138  $address = substr($address, $pos1 + 1, ($pos2 ? $pos2 : strlen($address)) - $pos1 - 1);
1139  }
1140  return $address;
1141  }
1142 
1152  public static function formatForTextarea($content)
1153  {
1154  self::logDeprecatedFunction();
1155  return LF . htmlspecialchars($content);
1156  }
1157 
1167  public static function strtoupper($str)
1168  {
1169  return strtr((string)$str, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
1170  }
1171 
1181  public static function strtolower($str)
1182  {
1183  return strtr((string)$str, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
1184  }
1185 
1197  public static function generateRandomBytes($bytesToReturn)
1198  {
1199  // Cache 4k of the generated bytestream.
1200  static $bytes = '';
1201  $bytesToGenerate = max(4096, $bytesToReturn);
1202  // if we have not enough random bytes cached, we generate new ones
1203  if (!isset($bytes[($bytesToReturn - 1)])) {
1204  if (TYPO3_OS === 'WIN') {
1205  // Openssl seems to be deadly slow on Windows, so try to use mcrypt
1206  $bytes .= self::generateRandomBytesMcrypt($bytesToGenerate);
1207  } else {
1208  // Try to use native PHP functions first, precedence has openssl
1209  $bytes .= self::generateRandomBytesOpenSsl($bytesToGenerate);
1210  if (!isset($bytes[($bytesToReturn - 1)])) {
1211  $bytes .= self::generateRandomBytesMcrypt($bytesToGenerate);
1212  }
1213  // If openssl and mcrypt failed, try /dev/urandom
1214  if (!isset($bytes[($bytesToReturn - 1)])) {
1215  $bytes .= self::generateRandomBytesUrandom($bytesToGenerate);
1216  }
1217  }
1218  // Fall back if other random byte generation failed until now
1219  if (!isset($bytes[($bytesToReturn - 1)])) {
1220  $bytes .= self::generateRandomBytesFallback($bytesToReturn);
1221  }
1222  }
1223  // get first $bytesToReturn and remove it from the byte cache
1224  $output = substr($bytes, 0, $bytesToReturn);
1225  $bytes = substr($bytes, $bytesToReturn);
1226  return $output;
1227  }
1228 
1235  protected static function generateRandomBytesOpenSsl($bytesToGenerate)
1236  {
1237  if (!function_exists('openssl_random_pseudo_bytes')) {
1238  return '';
1239  }
1240  $isStrong = null;
1241  return (string)openssl_random_pseudo_bytes($bytesToGenerate, $isStrong);
1242  }
1243 
1250  protected static function generateRandomBytesMcrypt($bytesToGenerate)
1251  {
1252  if (!function_exists('mcrypt_create_iv')) {
1253  return '';
1254  }
1255  return (string)(@mcrypt_create_iv($bytesToGenerate, MCRYPT_DEV_URANDOM));
1256  }
1257 
1264  protected static function generateRandomBytesUrandom($bytesToGenerate)
1265  {
1266  $bytes = '';
1267  $fh = @fopen('/dev/urandom', 'rb');
1268  if ($fh) {
1269  // PHP only performs buffered reads, so in reality it will always read
1270  // at least 4096 bytes. Thus, it costs nothing extra to read and store
1271  // that much so as to speed any additional invocations.
1272  $bytes = fread($fh, $bytesToGenerate);
1273  fclose($fh);
1274  }
1275  return $bytes;
1276  }
1277 
1284  protected static function generateRandomBytesFallback($bytesToReturn)
1285  {
1286  $bytes = '';
1287  // We initialize with somewhat random.
1288  $randomState = $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] . base_convert(memory_get_usage() % pow(10, 6), 10, 2) . microtime() . StringUtility::getUniqueId() . getmypid();
1289  while (!isset($bytes[($bytesToReturn - 1)])) {
1290  $randomState = sha1(microtime() . mt_rand() . $randomState);
1291  $bytes .= sha1(mt_rand() . $randomState, true);
1292  }
1293  return $bytes;
1294  }
1295 
1302  public static function idnaEncode($value)
1303  {
1304  if (isset(self::$idnaStringCache[$value])) {
1305  return self::$idnaStringCache[$value];
1306  } else {
1307  if (!self::$idnaConverter) {
1308  self::$idnaConverter = new \idna_convert(array('idn_version' => 2008));
1309  }
1310  self::$idnaStringCache[$value] = self::$idnaConverter->encode($value);
1311  return self::$idnaStringCache[$value];
1312  }
1313  }
1314 
1321  public static function getRandomHexString($count)
1322  {
1323  return substr(bin2hex(self::generateRandomBytes((int)(($count + 1) / 2))), 0, $count);
1324  }
1325 
1333  public static function underscoredToUpperCamelCase($string)
1334  {
1335  $upperCamelCase = str_replace(' ', '', ucwords(str_replace('_', ' ', self::strtolower($string))));
1336  return $upperCamelCase;
1337  }
1338 
1346  public static function underscoredToLowerCamelCase($string)
1347  {
1348  $upperCamelCase = str_replace(' ', '', ucwords(str_replace('_', ' ', self::strtolower($string))));
1349  $lowerCamelCase = self::lcfirst($upperCamelCase);
1350  return $lowerCamelCase;
1351  }
1352 
1360  public static function camelCaseToLowerCaseUnderscored($string)
1361  {
1362  return self::strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $string));
1363  }
1364 
1372  public static function lcfirst($string)
1373  {
1374  return self::strtolower($string[0]) . substr($string, 1);
1375  }
1376 
1401  public static function isValidUrl($url)
1402  {
1403  $parsedUrl = parse_url($url);
1404  if (!$parsedUrl || !isset($parsedUrl['scheme'])) {
1405  return false;
1406  }
1407  // HttpUtility::buildUrl() will always build urls with <scheme>://
1408  // our original $url might only contain <scheme>: (e.g. mail:)
1409  // so we convert that to the double-slashed version to ensure
1410  // our check against the $recomposedUrl is proper
1411  if (!self::isFirstPartOfStr($url, $parsedUrl['scheme'] . '://')) {
1412  $url = str_replace($parsedUrl['scheme'] . ':', $parsedUrl['scheme'] . '://', $url);
1413  }
1414  $recomposedUrl = HttpUtility::buildUrl($parsedUrl);
1415  if ($recomposedUrl !== $url) {
1416  // The parse_url() had to modify characters, so the URL is invalid
1417  return false;
1418  }
1419  if (isset($parsedUrl['host']) && !preg_match('/^[a-z0-9.\\-]*$/i', $parsedUrl['host'])) {
1420  $parsedUrl['host'] = self::idnaEncode($parsedUrl['host']);
1421  }
1422  return filter_var(HttpUtility::buildUrl($parsedUrl), FILTER_VALIDATE_URL) !== false;
1423  }
1424 
1425  /*************************
1426  *
1427  * ARRAY FUNCTIONS
1428  *
1429  *************************/
1453  public static function inArray(array $in_array, $item)
1454  {
1455  static::logDeprecatedFunction();
1456  return ArrayUtility::inArray($in_array, $item);
1457  }
1458 
1469  public static function intExplode($delimiter, $string, $removeEmptyValues = false, $limit = 0)
1470  {
1471  $result = explode($delimiter, $string);
1472  foreach ($result as $key => &$value) {
1473  if ($removeEmptyValues && ($value === '' || trim($value) === '')) {
1474  unset($result[$key]);
1475  } else {
1476  $value = (int)$value;
1477  }
1478  }
1479  unset($value);
1480  if ($limit !== 0) {
1481  if ($limit < 0) {
1482  $result = array_slice($result, 0, $limit);
1483  } elseif (count($result) > $limit) {
1484  $lastElements = array_slice($result, $limit - 1);
1485  $result = array_slice($result, 0, $limit - 1);
1486  $result[] = implode($delimiter, $lastElements);
1487  }
1488  }
1489  return $result;
1490  }
1491 
1506  public static function revExplode($delimiter, $string, $count = 0)
1507  {
1508  // 2 is the (currently, as of 2014-02) most-used value for $count in the core, therefore we check it first
1509  if ($count === 2) {
1510  $position = strrpos($string, strrev($delimiter));
1511  if ($position !== false) {
1512  return array(substr($string, 0, $position), substr($string, $position + strlen($delimiter)));
1513  } else {
1514  return array($string);
1515  }
1516  } elseif ($count <= 1) {
1517  return array($string);
1518  } else {
1519  $explodedValues = explode($delimiter, strrev($string), $count);
1520  $explodedValues = array_map('strrev', $explodedValues);
1521  return array_reverse($explodedValues);
1522  }
1523  }
1524 
1537  public static function trimExplode($delim, $string, $removeEmptyValues = false, $limit = 0)
1538  {
1539  $result = explode($delim, $string);
1540  if ($removeEmptyValues) {
1541  $temp = array();
1542  foreach ($result as $value) {
1543  if (trim($value) !== '') {
1544  $temp[] = $value;
1545  }
1546  }
1547  $result = $temp;
1548  }
1549  if ($limit > 0 && count($result) > $limit) {
1550  $lastElements = array_splice($result, $limit - 1);
1551  $result[] = implode($delim, $lastElements);
1552  } elseif ($limit < 0) {
1553  $result = array_slice($result, 0, $limit);
1554  }
1555  $result = array_map('trim', $result);
1556  return $result;
1557  }
1558 
1567  public static function removeArrayEntryByValue(array $array, $cmpValue)
1568  {
1569  static::logDeprecatedFunction();
1570  return ArrayUtility::removeArrayEntryByValue($array, $cmpValue);
1571  }
1572 
1597  public static function keepItemsInArray(array $array, $keepItems, $getValueFunc = null)
1598  {
1599  static::logDeprecatedFunction();
1600  return ArrayUtility::keepItemsInArray($array, $keepItems, $getValueFunc);
1601  }
1602 
1614  public static function implodeArrayForUrl($name, array $theArray, $str = '', $skipBlank = false, $rawurlencodeParamName = false)
1615  {
1616  foreach ($theArray as $Akey => $AVal) {
1617  $thisKeyName = $name ? $name . '[' . $Akey . ']' : $Akey;
1618  if (is_array($AVal)) {
1619  $str = self::implodeArrayForUrl($thisKeyName, $AVal, $str, $skipBlank, $rawurlencodeParamName);
1620  } else {
1621  if (!$skipBlank || (string)$AVal !== '') {
1622  $str .= '&' . ($rawurlencodeParamName ? rawurlencode($thisKeyName) : $thisKeyName) . '=' . rawurlencode($AVal);
1623  }
1624  }
1625  }
1626  return $str;
1627  }
1628 
1637  public static function explodeUrl2Array($string, $multidim = false)
1638  {
1639  $output = array();
1640  if ($multidim) {
1641  parse_str($string, $output);
1642  } else {
1643  $p = explode('&', $string);
1644  foreach ($p as $v) {
1645  if ($v !== '') {
1646  list($pK, $pV) = explode('=', $v, 2);
1647  $output[rawurldecode($pK)] = rawurldecode($pV);
1648  }
1649  }
1650  }
1651  return $output;
1652  }
1653 
1663  public static function compileSelectedGetVarsFromArray($varList, array $getArray, $GPvarAlt = true)
1664  {
1665  $keys = self::trimExplode(',', $varList, true);
1666  $outArr = array();
1667  foreach ($keys as $v) {
1668  if (isset($getArray[$v])) {
1669  $outArr[$v] = $getArray[$v];
1670  } elseif ($GPvarAlt) {
1671  $outArr[$v] = self::_GP($v);
1672  }
1673  }
1674  return $outArr;
1675  }
1676 
1687  public static function addSlashesOnArray(array &$theArray)
1688  {
1689  self::logDeprecatedFunction();
1690  foreach ($theArray as &$value) {
1691  if (is_array($value)) {
1692  self::addSlashesOnArray($value);
1693  } else {
1694  $value = addslashes($value);
1695  }
1696  }
1697  unset($value);
1698  reset($theArray);
1699  }
1700 
1711  public static function stripSlashesOnArray(array &$theArray)
1712  {
1713  foreach ($theArray as &$value) {
1714  if (is_array($value)) {
1715  self::stripSlashesOnArray($value);
1716  } else {
1717  $value = stripslashes($value);
1718  }
1719  }
1720  unset($value);
1721  reset($theArray);
1722  }
1723 
1732  public static function slashArray(array $arr, $cmd)
1733  {
1734  self::logDeprecatedFunction();
1735  if ($cmd == 'strip') {
1736  self::stripSlashesOnArray($arr);
1737  }
1738  if ($cmd == 'add') {
1739  self::addSlashesOnArray($arr);
1740  }
1741  return $arr;
1742  }
1743 
1751  public static function remapArrayKeys(&$array, $mappingTable)
1752  {
1753  static::logDeprecatedFunction();
1754  ArrayUtility::remapArrayKeys($array, $mappingTable);
1755  }
1756 
1765  public static function array_merge(array $arr1, array $arr2)
1766  {
1767  static::logDeprecatedFunction();
1768  return $arr2 + $arr1;
1769  }
1770 
1780  public static function arrayDiffAssocRecursive(array $array1, array $array2)
1781  {
1782  static::logDeprecatedFunction();
1783  return ArrayUtility::arrayDiffAssocRecursive($array1, $array2);
1784  }
1785 
1794  public static function csvValues(array $row, $delim = ',', $quote = '"')
1795  {
1796  $out = array();
1797  foreach ($row as $value) {
1798  $out[] = str_replace($quote, $quote . $quote, $value);
1799  }
1800  $str = $quote . implode(($quote . $delim . $quote), $out) . $quote;
1801  return $str;
1802  }
1803 
1811  public static function removeDotsFromTS(array $ts)
1812  {
1813  $out = array();
1814  foreach ($ts as $key => $value) {
1815  if (is_array($value)) {
1816  $key = rtrim($key, '.');
1817  $out[$key] = self::removeDotsFromTS($value);
1818  } else {
1819  $out[$key] = $value;
1820  }
1821  }
1822  return $out;
1823  }
1824 
1832  public static function naturalKeySortRecursive(&$array)
1833  {
1834  static::logDeprecatedFunction();
1836  }
1837 
1838  /*************************
1839  *
1840  * HTML/XML PROCESSING
1841  *
1842  *************************/
1851  public static function get_tag_attributes($tag)
1852  {
1853  $components = self::split_tag_attributes($tag);
1854  // Attribute name is stored here
1855  $name = '';
1856  $valuemode = false;
1857  $attributes = array();
1858  foreach ($components as $key => $val) {
1859  // 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
1860  if ($val != '=') {
1861  if ($valuemode) {
1862  if ($name) {
1863  $attributes[$name] = $val;
1864  $name = '';
1865  }
1866  } else {
1867  if ($key = strtolower(preg_replace('/[^[:alnum:]_\\:\\-]/', '', $val))) {
1868  $attributes[$key] = '';
1869  $name = $key;
1870  }
1871  }
1872  $valuemode = false;
1873  } else {
1874  $valuemode = true;
1875  }
1876  }
1877  return $attributes;
1878  }
1879 
1887  public static function split_tag_attributes($tag)
1888  {
1889  $tag_tmp = trim(preg_replace('/^<[^[:space:]]*/', '', trim($tag)));
1890  // Removes any > in the end of the string
1891  $tag_tmp = trim(rtrim($tag_tmp, '>'));
1892  $value = array();
1893  // Compared with empty string instead , 030102
1894  while ($tag_tmp !== '') {
1895  $firstChar = $tag_tmp[0];
1896  if ($firstChar === '"' || $firstChar === '\'') {
1897  $reg = explode($firstChar, $tag_tmp, 3);
1898  $value[] = $reg[1];
1899  $tag_tmp = trim($reg[2]);
1900  } elseif ($firstChar === '=') {
1901  $value[] = '=';
1902  // Removes = chars.
1903  $tag_tmp = trim(substr($tag_tmp, 1));
1904  } else {
1905  // There are '' around the value. We look for the next ' ' or '>'
1906  $reg = preg_split('/[[:space:]=]/', $tag_tmp, 2);
1907  $value[] = trim($reg[0]);
1908  $tag_tmp = trim(substr($tag_tmp, strlen($reg[0]), 1) . $reg[1]);
1909  }
1910  }
1911  reset($value);
1912  return $value;
1913  }
1914 
1923  public static function implodeAttributes(array $arr, $xhtmlSafe = false, $dontOmitBlankAttribs = false)
1924  {
1925  if ($xhtmlSafe) {
1926  $newArr = array();
1927  foreach ($arr as $p => $v) {
1928  if (!isset($newArr[strtolower($p)])) {
1929  $newArr[strtolower($p)] = htmlspecialchars($v);
1930  }
1931  }
1932  $arr = $newArr;
1933  }
1934  $list = array();
1935  foreach ($arr as $p => $v) {
1936  if ((string)$v !== '' || $dontOmitBlankAttribs) {
1937  $list[] = $p . '="' . $v . '"';
1938  }
1939  }
1940  return implode(' ', $list);
1941  }
1942 
1952  public static function wrapJS($string, $linebreak = true)
1953  {
1954  if (trim($string)) {
1955  // <script wrapped in nl?
1956  $cr = $linebreak ? LF : '';
1957  // remove nl from the beginning
1958  $string = preg_replace('/^\\n+/', '', $string);
1959  // re-ident to one tab using the first line as reference
1960  $match = array();
1961  if (preg_match('/^(\\t+)/', $string, $match)) {
1962  $string = str_replace($match[1], TAB, $string);
1963  }
1964  $string = $cr . '<script type="text/javascript">
1965 /*<![CDATA[*/
1966 ' . $string . '
1967 /*]]>*/
1968 </script>' . $cr;
1969  }
1970  return trim($string);
1971  }
1972 
1981  public static function xml2tree($string, $depth = 999, $parserOptions = array())
1982  {
1983  $parser = xml_parser_create();
1984  $vals = array();
1985  $index = array();
1986  xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
1987  xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
1988  foreach ($parserOptions as $option => $value) {
1989  xml_parser_set_option($parser, $option, $value);
1990  }
1991  xml_parse_into_struct($parser, $string, $vals, $index);
1992  if (xml_get_error_code($parser)) {
1993  return 'Line ' . xml_get_current_line_number($parser) . ': ' . xml_error_string(xml_get_error_code($parser));
1994  }
1995  xml_parser_free($parser);
1996  $stack = array(array());
1997  $stacktop = 0;
1998  $startPoint = 0;
1999  $tagi = array();
2000  foreach ($vals as $key => $val) {
2001  $type = $val['type'];
2002  // open tag:
2003  if ($type == 'open' || $type == 'complete') {
2004  $stack[$stacktop++] = $tagi;
2005  if ($depth == $stacktop) {
2006  $startPoint = $key;
2007  }
2008  $tagi = array('tag' => $val['tag']);
2009  if (isset($val['attributes'])) {
2010  $tagi['attrs'] = $val['attributes'];
2011  }
2012  if (isset($val['value'])) {
2013  $tagi['values'][] = $val['value'];
2014  }
2015  }
2016  // finish tag:
2017  if ($type == 'complete' || $type == 'close') {
2018  $oldtagi = $tagi;
2019  $tagi = $stack[--$stacktop];
2020  $oldtag = $oldtagi['tag'];
2021  unset($oldtagi['tag']);
2022  if ($depth == $stacktop + 1) {
2023  if ($key - $startPoint > 0) {
2024  $partArray = array_slice($vals, $startPoint + 1, $key - $startPoint - 1);
2025  $oldtagi['XMLvalue'] = self::xmlRecompileFromStructValArray($partArray);
2026  } else {
2027  $oldtagi['XMLvalue'] = $oldtagi['values'][0];
2028  }
2029  }
2030  $tagi['ch'][$oldtag][] = $oldtagi;
2031  unset($oldtagi);
2032  }
2033  // cdata
2034  if ($type == 'cdata') {
2035  $tagi['values'][] = $val['value'];
2036  }
2037  }
2038  return $tagi['ch'];
2039  }
2040 
2051  public static function array2xml_cs(array $array, $docTag = 'phparray', array $options = array(), $charset = '')
2052  {
2053  // Set default charset unless explicitly specified
2054  $charset = $charset ?: 'utf-8';
2055  // Return XML:
2056  return '<?xml version="1.0" encoding="' . htmlspecialchars($charset) . '" standalone="yes" ?>' . LF . self::array2xml($array, '', 0, $docTag, 0, $options);
2057  }
2058 
2081  public static function array2xml(array $array, $NSprefix = '', $level = 0, $docTag = 'phparray', $spaceInd = 0, array $options = array(), array $stackData = array())
2082  {
2083  // 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
2084  $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);
2085  // Set indenting mode:
2086  $indentChar = $spaceInd ? ' ' : TAB;
2087  $indentN = $spaceInd > 0 ? $spaceInd : 1;
2088  $nl = $spaceInd >= 0 ? LF : '';
2089  // Init output variable:
2090  $output = '';
2091  // Traverse the input array
2092  foreach ($array as $k => $v) {
2093  $attr = '';
2094  $tagName = $k;
2095  // Construct the tag name.
2096  // Use tag based on grand-parent + parent tag name
2097  if (isset($options['grandParentTagMap'][$stackData['grandParentTagName'] . '/' . $stackData['parentTagName']])) {
2098  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2099  $tagName = (string)$options['grandParentTagMap'][($stackData['grandParentTagName'] . '/' . $stackData['parentTagName'])];
2100  } elseif (isset($options['parentTagMap'][$stackData['parentTagName'] . ':_IS_NUM']) && MathUtility::canBeInterpretedAsInteger($tagName)) {
2101  // Use tag based on parent tag name + if current tag is numeric
2102  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2103  $tagName = (string)$options['parentTagMap'][($stackData['parentTagName'] . ':_IS_NUM')];
2104  } elseif (isset($options['parentTagMap'][$stackData['parentTagName'] . ':' . $tagName])) {
2105  // Use tag based on parent tag name + current tag
2106  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2107  $tagName = (string)$options['parentTagMap'][($stackData['parentTagName'] . ':' . $tagName)];
2108  } elseif (isset($options['parentTagMap'][$stackData['parentTagName']])) {
2109  // Use tag based on parent tag name:
2110  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2111  $tagName = (string)$options['parentTagMap'][$stackData['parentTagName']];
2113  // If integer...;
2114  if ($options['useNindex']) {
2115  // If numeric key, prefix "n"
2116  $tagName = 'n' . $tagName;
2117  } else {
2118  // Use special tag for num. keys:
2119  $attr .= ' index="' . $tagName . '"';
2120  $tagName = $options['useIndexTagForNum'] ?: 'numIndex';
2121  }
2122  } elseif ($options['useIndexTagForAssoc']) {
2123  // Use tag for all associative keys:
2124  $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2125  $tagName = $options['useIndexTagForAssoc'];
2126  }
2127  // The tag name is cleaned up so only alphanumeric chars (plus - and _) are in there and not longer than 100 chars either.
2128  $tagName = substr(preg_replace('/[^[:alnum:]_-]/', '', $tagName), 0, 100);
2129  // If the value is an array then we will call this function recursively:
2130  if (is_array($v)) {
2131  // Sub elements:
2132  if ($options['alt_options'][$stackData['path'] . '/' . $tagName]) {
2133  $subOptions = $options['alt_options'][$stackData['path'] . '/' . $tagName];
2134  $clearStackPath = $subOptions['clearStackPath'];
2135  } else {
2136  $subOptions = $options;
2137  $clearStackPath = false;
2138  }
2139  if (empty($v)) {
2140  $content = '';
2141  } else {
2142  $content = $nl . self::array2xml($v, $NSprefix, ($level + 1), '', $spaceInd, $subOptions, array(
2143  'parentTagName' => $tagName,
2144  'grandParentTagName' => $stackData['parentTagName'],
2145  'path' => ($clearStackPath ? '' : $stackData['path'] . '/' . $tagName)
2146  )) . ($spaceInd >= 0 ? str_pad('', ($level + 1) * $indentN, $indentChar) : '');
2147  }
2148  // Do not set "type = array". Makes prettier XML but means that empty arrays are not restored with xml2array
2149  if ((int)$options['disableTypeAttrib'] != 2) {
2150  $attr .= ' type="array"';
2151  }
2152  } else {
2153  // Just a value:
2154  // Look for binary chars:
2155  $vLen = strlen($v);
2156  // Go for base64 encoding if the initial segment NOT matching any binary char has the same length as the whole string!
2157  if ($vLen && strcspn($v, $binaryChars) != $vLen) {
2158  // If the value contained binary chars then we base64-encode it an set an attribute to notify this situation:
2159  $content = $nl . chunk_split(base64_encode($v));
2160  $attr .= ' base64="1"';
2161  } else {
2162  // Otherwise, just htmlspecialchar the stuff:
2163  $content = htmlspecialchars($v);
2164  $dType = gettype($v);
2165  if ($dType == 'string') {
2166  if ($options['useCDATA'] && $content != $v) {
2167  $content = '<![CDATA[' . $v . ']]>';
2168  }
2169  } elseif (!$options['disableTypeAttrib']) {
2170  $attr .= ' type="' . $dType . '"';
2171  }
2172  }
2173  }
2174  if ((string)$tagName !== '') {
2175  // Add the element to the output string:
2176  $output .= ($spaceInd >= 0 ? str_pad('', ($level + 1) * $indentN, $indentChar) : '')
2177  . '<' . $NSprefix . $tagName . $attr . '>' . $content . '</' . $NSprefix . $tagName . '>' . $nl;
2178  }
2179  }
2180  // If we are at the outer-most level, then we finally wrap it all in the document tags and return that as the value:
2181  if (!$level) {
2182  $output = '<' . $docTag . '>' . $nl . $output . '</' . $docTag . '>';
2183  }
2184  return $output;
2185  }
2186 
2198  public static function xml2array($string, $NSprefix = '', $reportDocTag = false)
2199  {
2200  static $firstLevelCache = array();
2201  $identifier = md5($string . $NSprefix . ($reportDocTag ? '1' : '0'));
2202  // Look up in first level cache
2203  if (!empty($firstLevelCache[$identifier])) {
2204  $array = $firstLevelCache[$identifier];
2205  } else {
2206  // Look up in second level cache
2207  // @todo: Is this cache really required? It basically substitutes a little cpu work with a db query?
2208  $array = PageRepository::getHash($identifier, 0);
2209  if (!is_array($array)) {
2210  $array = self::xml2arrayProcess($string, $NSprefix, $reportDocTag);
2211  PageRepository::storeHash($identifier, $array, 'ident_xml2array');
2212  }
2213  // Store content in first level cache
2214  $firstLevelCache[$identifier] = $array;
2215  }
2216  return $array;
2217  }
2218 
2229  protected static function xml2arrayProcess($string, $NSprefix = '', $reportDocTag = false)
2230  {
2231  // Create parser:
2232  $parser = xml_parser_create();
2233  $vals = array();
2234  $index = array();
2235  xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
2236  xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
2237  // Default output charset is UTF-8, only ASCII, ISO-8859-1 and UTF-8 are supported!!!
2238  $match = array();
2239  preg_match('/^[[:space:]]*<\\?xml[^>]*encoding[[:space:]]*=[[:space:]]*"([^"]*)"/', substr($string, 0, 200), $match);
2240  $theCharset = $match[1] ?: 'utf-8';
2241  // us-ascii / utf-8 / iso-8859-1
2242  xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $theCharset);
2243  // Parse content:
2244  xml_parse_into_struct($parser, $string, $vals, $index);
2245  // If error, return error message:
2246  if (xml_get_error_code($parser)) {
2247  return 'Line ' . xml_get_current_line_number($parser) . ': ' . xml_error_string(xml_get_error_code($parser));
2248  }
2249  xml_parser_free($parser);
2250  // Init vars:
2251  $stack = array(array());
2252  $stacktop = 0;
2253  $current = array();
2254  $tagName = '';
2255  $documentTag = '';
2256  // Traverse the parsed XML structure:
2257  foreach ($vals as $key => $val) {
2258  // First, process the tag-name (which is used in both cases, whether "complete" or "close")
2259  $tagName = $val['tag'];
2260  if (!$documentTag) {
2261  $documentTag = $tagName;
2262  }
2263  // Test for name space:
2264  $tagName = $NSprefix && substr($tagName, 0, strlen($NSprefix)) == $NSprefix ? substr($tagName, strlen($NSprefix)) : $tagName;
2265  // Test for numeric tag, encoded on the form "nXXX":
2266  $testNtag = substr($tagName, 1);
2267  // Closing tag.
2268  $tagName = $tagName[0] === 'n' && MathUtility::canBeInterpretedAsInteger($testNtag) ? (int)$testNtag : $tagName;
2269  // Test for alternative index value:
2270  if ((string)$val['attributes']['index'] !== '') {
2271  $tagName = $val['attributes']['index'];
2272  }
2273  // Setting tag-values, manage stack:
2274  switch ($val['type']) {
2275  case 'open':
2276  // If open tag it means there is an array stored in sub-elements. Therefore increase the stackpointer and reset the accumulation array:
2277  // Setting blank place holder
2278  $current[$tagName] = array();
2279  $stack[$stacktop++] = $current;
2280  $current = array();
2281  break;
2282  case 'close':
2283  // If the tag is "close" then it is an array which is closing and we decrease the stack pointer.
2284  $oldCurrent = $current;
2285  $current = $stack[--$stacktop];
2286  // Going to the end of array to get placeholder key, key($current), and fill in array next:
2287  end($current);
2288  $current[key($current)] = $oldCurrent;
2289  unset($oldCurrent);
2290  break;
2291  case 'complete':
2292  // If "complete", then it's a value. If the attribute "base64" is set, then decode the value, otherwise just set it.
2293  if ($val['attributes']['base64']) {
2294  $current[$tagName] = base64_decode($val['value']);
2295  } else {
2296  // Had to cast it as a string - otherwise it would be evaluate FALSE if tested with isset()!!
2297  $current[$tagName] = (string)$val['value'];
2298  // Cast type:
2299  switch ((string)$val['attributes']['type']) {
2300  case 'integer':
2301  $current[$tagName] = (int)$current[$tagName];
2302  break;
2303  case 'double':
2304  $current[$tagName] = (double) $current[$tagName];
2305  break;
2306  case 'boolean':
2307  $current[$tagName] = (bool)$current[$tagName];
2308  break;
2309  case 'NULL':
2310  $current[$tagName] = null;
2311  break;
2312  case 'array':
2313  // 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...
2314  $current[$tagName] = array();
2315  break;
2316  }
2317  }
2318  break;
2319  }
2320  }
2321  if ($reportDocTag) {
2322  $current[$tagName]['_DOCUMENT_TAG'] = $documentTag;
2323  }
2324  // Finally return the content of the document tag.
2325  return $current[$tagName];
2326  }
2327 
2334  public static function xmlRecompileFromStructValArray(array $vals)
2335  {
2336  $XMLcontent = '';
2337  foreach ($vals as $val) {
2338  $type = $val['type'];
2339  // Open tag:
2340  if ($type == 'open' || $type == 'complete') {
2341  $XMLcontent .= '<' . $val['tag'];
2342  if (isset($val['attributes'])) {
2343  foreach ($val['attributes'] as $k => $v) {
2344  $XMLcontent .= ' ' . $k . '="' . htmlspecialchars($v) . '"';
2345  }
2346  }
2347  if ($type == 'complete') {
2348  if (isset($val['value'])) {
2349  $XMLcontent .= '>' . htmlspecialchars($val['value']) . '</' . $val['tag'] . '>';
2350  } else {
2351  $XMLcontent .= '/>';
2352  }
2353  } else {
2354  $XMLcontent .= '>';
2355  }
2356  if ($type == 'open' && isset($val['value'])) {
2357  $XMLcontent .= htmlspecialchars($val['value']);
2358  }
2359  }
2360  // Finish tag:
2361  if ($type == 'close') {
2362  $XMLcontent .= '</' . $val['tag'] . '>';
2363  }
2364  // Cdata
2365  if ($type == 'cdata') {
2366  $XMLcontent .= htmlspecialchars($val['value']);
2367  }
2368  }
2369  return $XMLcontent;
2370  }
2371 
2378  public static function xmlGetHeaderAttribs($xmlData)
2379  {
2380  $match = array();
2381  if (preg_match('/^\\s*<\\?xml([^>]*)\\?\\>/', $xmlData, $match)) {
2382  return self::get_tag_attributes($match[1]);
2383  }
2384  }
2385 
2393  public static function minifyJavaScript($script, &$error = '')
2394  {
2395  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript'])) {
2396  $fakeThis = false;
2397  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['minifyJavaScript'] as $hookMethod) {
2398  try {
2399  $parameters = array('script' => $script);
2400  $script = static::callUserFunction($hookMethod, $parameters, $fakeThis);
2401  } catch (\Exception $e) {
2402  $errorMessage = 'Error minifying java script: ' . $e->getMessage();
2403  $error .= $errorMessage;
2404  static::devLog($errorMessage, \TYPO3\CMS\Core\Utility\GeneralUtility::class, 2, array(
2405  'JavaScript' => $script,
2406  'Stack trace' => $e->getTrace(),
2407  'hook' => $hookMethod
2408  ));
2409  }
2410  }
2411  }
2412  return $script;
2413  }
2414 
2415  /*************************
2416  *
2417  * FILES FUNCTIONS
2418  *
2419  *************************/
2430  public static function getUrl($url, $includeHeader = 0, $requestHeaders = false, &$report = null)
2431  {
2432  $content = false;
2433  if (isset($report)) {
2434  $report['error'] = 0;
2435  $report['message'] = '';
2436  }
2437  // Use cURL for: http, https, ftp, ftps, sftp and scp
2438  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlUse'] == '1' && preg_match('/^(?:http|ftp)s?|s(?:ftp|cp):/', $url)) {
2439  if (isset($report)) {
2440  $report['lib'] = 'cURL';
2441  }
2442  // External URL without error checking.
2443  if (!function_exists('curl_init') || !($ch = curl_init())) {
2444  if (isset($report)) {
2445  $report['error'] = -1;
2446  $report['message'] = 'Couldn\'t initialize cURL.';
2447  }
2448  return false;
2449  }
2450 
2451  $followLocationSucceeded = @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
2452 
2453  curl_setopt($ch, CURLOPT_URL, $url);
2454  curl_setopt($ch, CURLOPT_HEADER, !$followLocationSucceeded || $includeHeader ? 1 : 0);
2455  curl_setopt($ch, CURLOPT_NOBODY, $includeHeader == 2 ? 1 : 0);
2456  curl_setopt($ch, CURLOPT_HTTPGET, $includeHeader == 2 ? 'HEAD' : 'GET');
2457  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
2458  curl_setopt($ch, CURLOPT_FAILONERROR, 1);
2459  curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, max(0, (int)$GLOBALS['TYPO3_CONF_VARS']['SYS']['curlTimeout']));
2460 
2461  if (is_array($requestHeaders)) {
2462  curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);
2463  }
2464  // (Proxy support implemented by Arco <arco@appeltaart.mine.nu>)
2465  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']) {
2466  curl_setopt($ch, CURLOPT_PROXY, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']);
2467  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyNTLM']) {
2468  curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
2469  }
2470  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyTunnel']) {
2471  curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyTunnel']);
2472  }
2473  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyUserPass']) {
2474  curl_setopt($ch, CURLOPT_PROXYUSERPWD, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyUserPass']);
2475  }
2476  }
2477  $content = curl_exec($ch);
2478  $curlInfo = curl_getinfo($ch);
2479 
2480  if (!$followLocationSucceeded) {
2481  // Check if we need to do redirects
2482  if ($curlInfo['http_code'] >= 300 && $curlInfo['http_code'] < 400) {
2483  $locationUrl = $curlInfo['redirect_url'];
2484  if (!$locationUrl) {
2485  // Some curllib versions do not return redirect_url. Examine headers.
2486  $locationUrl = self::getRedirectUrlFromHttpHeaders($content);
2487  }
2488  if ($locationUrl) {
2489  $content = self::getUrl($locationUrl, $includeHeader, $requestHeaders, $report);
2490  $followLocationSucceeded = true;
2491  } else {
2492  // Failure: we got a redirection status code but not the URL to redirect to.
2493  $content = false;
2494  }
2495  }
2496  if ($content && !$includeHeader) {
2497  $content = self::stripHttpHeaders($content);
2498  }
2499  }
2500 
2501  if (isset($report)) {
2502  if (!$followLocationSucceeded && $curlInfo['http_code'] >= 300 && $curlInfo['http_code'] < 400) {
2503  $report['http_code'] = $curlInfo['http_code'];
2504  $report['content_type'] = $curlInfo['content_type'];
2505  $report['error'] = CURLE_GOT_NOTHING;
2506  $report['message'] = 'Expected "Location" header but got nothing.';
2507  } elseif ($content === false) {
2508  $report['error'] = curl_errno($ch);
2509  $report['message'] = curl_error($ch);
2510  } elseif ($includeHeader) {
2511  // Set only for $includeHeader to work exactly like PHP variant
2512  $report['http_code'] = $curlInfo['http_code'];
2513  $report['content_type'] = $curlInfo['content_type'];
2514  }
2515  }
2516  curl_close($ch);
2517  } elseif ($includeHeader) {
2518  if (isset($report)) {
2519  $report['lib'] = 'socket';
2520  }
2521  $parsedURL = parse_url($url);
2522  if (!preg_match('/^https?/', $parsedURL['scheme'])) {
2523  if (isset($report)) {
2524  $report['error'] = -1;
2525  $report['message'] = 'Reading headers is not allowed for this protocol.';
2526  }
2527  return false;
2528  }
2529  $port = (int)$parsedURL['port'];
2530  if ($port < 1) {
2531  if ($parsedURL['scheme'] == 'http') {
2532  $port = $port > 0 ? $port : 80;
2533  $scheme = '';
2534  } else {
2535  $port = $port > 0 ? $port : 443;
2536  $scheme = 'ssl://';
2537  }
2538  }
2539  $errno = 0;
2540  $fp = @fsockopen(($scheme . $parsedURL['host']), $port, $errno, $errstr, 2.0);
2541  if (!$fp || $errno > 0) {
2542  if (isset($report)) {
2543  $report['error'] = $errno ?: -1;
2544  $report['message'] = $errno ? ($errstr ?: 'Socket error.') : 'Socket initialization error.';
2545  }
2546  return false;
2547  }
2548  $method = $includeHeader == 2 ? 'HEAD' : 'GET';
2549  $msg = $method . ' ' . (isset($parsedURL['path']) ? $parsedURL['path'] : '/') . ($parsedURL['query'] ? '?' . $parsedURL['query'] : '') . ' HTTP/1.0' . CRLF . 'Host: ' . $parsedURL['host'] . '
2550 
2551 Connection: close
2552 
2553 ';
2554  if (is_array($requestHeaders)) {
2555  $msg .= implode(CRLF, $requestHeaders) . CRLF;
2556  }
2557  $msg .= CRLF;
2558  fputs($fp, $msg);
2559  while (!feof($fp)) {
2560  $line = fgets($fp, 2048);
2561  if (isset($report)) {
2562  if (preg_match('|^HTTP/\\d\\.\\d +(\\d+)|', $line, $status)) {
2563  $report['http_code'] = $status[1];
2564  } elseif (preg_match('/^Content-Type: *(.*)/i', $line, $type)) {
2565  $report['content_type'] = $type[1];
2566  }
2567  }
2568  $content .= $line;
2569  if (trim($line) === '') {
2570  // Stop at the first empty line (= end of header)
2571  break;
2572  }
2573  }
2574  if ($includeHeader != 2) {
2575  $content .= stream_get_contents($fp);
2576  }
2577  fclose($fp);
2578  } elseif (is_array($requestHeaders)) {
2579  if (isset($report)) {
2580  $report['lib'] = 'file/context';
2581  }
2582  $parsedURL = parse_url($url);
2583  if (!preg_match('/^https?/', $parsedURL['scheme'])) {
2584  if (isset($report)) {
2585  $report['error'] = -1;
2586  $report['message'] = 'Sending request headers is not allowed for this protocol.';
2587  }
2588  return false;
2589  }
2590  $ctx = stream_context_create(array(
2591  'http' => array(
2592  'header' => implode(CRLF, $requestHeaders)
2593  )
2594  ));
2595  $defaultCtx = stream_context_get_default();
2596  if ($defaultCtx) {
2597  $content = @file_get_contents($url);
2598  } else {
2599  $content = @file_get_contents($url, false, $ctx);
2600  }
2601  if ($content === false && isset($report)) {
2602  $report['error'] = -1;
2603  $report['message'] = 'Couldn\'t get URL: ' . (isset($http_response_header) ? implode(LF, $http_response_header) : $url);
2604  }
2605  } else {
2606  if (isset($report)) {
2607  $report['lib'] = 'file';
2608  }
2609  $content = @file_get_contents($url);
2610  if ($content === false && isset($report)) {
2611  $report['error'] = -1;
2612  $report['message'] = 'Couldn\'t get URL: ' . (isset($http_response_header) ? implode(LF, $http_response_header) : $url);
2613  }
2614  }
2615  return $content;
2616  }
2617 
2625  protected static function getRedirectUrlFromHttpHeaders($content)
2626  {
2627  $result = '';
2628  $headers = explode("\r\n", $content);
2629  foreach ($headers as $header) {
2630  if ($header == '') {
2631  break;
2632  }
2633  if (preg_match('/^\s*Location\s*:/i', $header)) {
2634  list(, $result) = self::trimExplode(':', $header, false, 2);
2635  if ($result) {
2636  $result = self::locationHeaderUrl($result);
2637  }
2638  break;
2639  }
2640  }
2641  return $result;
2642  }
2643 
2650  protected static function stripHttpHeaders($content)
2651  {
2652  $headersEndPos = strpos($content, "\r\n\r\n");
2653  if ($headersEndPos) {
2654  $content = substr($content, $headersEndPos + 4);
2655  }
2656  return $content;
2657  }
2658 
2667  public static function writeFile($file, $content, $changePermissions = false)
2668  {
2669  if (!@is_file($file)) {
2670  $changePermissions = true;
2671  }
2672  if ($fd = fopen($file, 'wb')) {
2673  $res = fwrite($fd, $content);
2674  fclose($fd);
2675  if ($res === false) {
2676  return false;
2677  }
2678  // Change the permissions only if the file has just been created
2679  if ($changePermissions) {
2680  static::fixPermissions($file);
2681  }
2682  return true;
2683  }
2684  return false;
2685  }
2686 
2694  public static function fixPermissions($path, $recursive = false)
2695  {
2696  if (TYPO3_OS === 'WIN') {
2697  return true;
2698  }
2699  $result = false;
2700  // Make path absolute
2701  if (!static::isAbsPath($path)) {
2702  $path = static::getFileAbsFileName($path, false);
2703  }
2704  if (static::isAllowedAbsPath($path)) {
2705  if (@is_file($path)) {
2706  $targetPermissions = isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'])
2707  ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask']
2708  : '0644';
2709  } elseif (@is_dir($path)) {
2710  $targetPermissions = isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'])
2711  ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']
2712  : '0755';
2713  }
2714  if (!empty($targetPermissions)) {
2715  // make sure it's always 4 digits
2716  $targetPermissions = str_pad($targetPermissions, 4, 0, STR_PAD_LEFT);
2717  $targetPermissions = octdec($targetPermissions);
2718  // "@" is there because file is not necessarily OWNED by the user
2719  $result = @chmod($path, $targetPermissions);
2720  }
2721  // Set createGroup if not empty
2722  if (
2723  isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'])
2724  && $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup'] !== ''
2725  ) {
2726  // "@" is there because file is not necessarily OWNED by the user
2727  $changeGroupResult = @chgrp($path, $GLOBALS['TYPO3_CONF_VARS']['SYS']['createGroup']);
2728  $result = $changeGroupResult ? $result : false;
2729  }
2730  // Call recursive if recursive flag if set and $path is directory
2731  if ($recursive && @is_dir($path)) {
2732  $handle = opendir($path);
2733  while (($file = readdir($handle)) !== false) {
2734  $recursionResult = null;
2735  if ($file !== '.' && $file !== '..') {
2736  if (@is_file(($path . '/' . $file))) {
2737  $recursionResult = static::fixPermissions($path . '/' . $file);
2738  } elseif (@is_dir(($path . '/' . $file))) {
2739  $recursionResult = static::fixPermissions($path . '/' . $file, true);
2740  }
2741  if (isset($recursionResult) && !$recursionResult) {
2742  $result = false;
2743  }
2744  }
2745  }
2746  closedir($handle);
2747  }
2748  }
2749  return $result;
2750  }
2751 
2760  public static function writeFileToTypo3tempDir($filepath, $content)
2761  {
2762  if (!defined('PATH_site')) {
2763  return 'PATH_site constant was NOT defined!';
2764  }
2765 
2766  // Parse filepath into directory and basename:
2767  $fI = pathinfo($filepath);
2768  $fI['dirname'] .= '/';
2769  // Check parts:
2770  if (!static::validPathStr($filepath) || !$fI['basename'] || strlen($fI['basename']) >= 60) {
2771  return 'Input filepath "' . $filepath . '" was generally invalid!';
2772  }
2773  // Setting main temporary directory name (standard)
2774  $dirName = PATH_site . 'typo3temp/';
2775  if (!@is_dir($dirName)) {
2776  return 'PATH_site + "typo3temp/" was not a directory!';
2777  }
2778  if (!static::isFirstPartOfStr($fI['dirname'], $dirName)) {
2779  return '"' . $fI['dirname'] . '" was not within directory PATH_site + "typo3temp/"';
2780  }
2781  // Checking if the "subdir" is found:
2782  $subdir = substr($fI['dirname'], strlen($dirName));
2783  if ($subdir) {
2784  if (preg_match('/^[[:alnum:]_]+\\/$/', $subdir) || preg_match('/^[[:alnum:]_]+\\/[[:alnum:]_]+\\/$/', $subdir)) {
2785  $dirName .= $subdir;
2786  if (!@is_dir($dirName)) {
2787  static::mkdir_deep(PATH_site . 'typo3temp/', $subdir);
2788  }
2789  } else {
2790  return 'Subdir, "' . $subdir . '", was NOT on the form "[[:alnum:]_]/" or "[[:alnum:]_]/[[:alnum:]_]/"';
2791  }
2792  }
2793  // Checking dir-name again (sub-dir might have been created):
2794  if (@is_dir($dirName)) {
2795  if ($filepath == $dirName . $fI['basename']) {
2796  static::writeFile($filepath, $content);
2797  if (!@is_file($filepath)) {
2798  return 'The file was not written to the disk. Please, check that you have write permissions to the typo3temp/ directory.';
2799  }
2800  } else {
2801  return 'Calculated filelocation didn\'t match input "' . $filepath . '".';
2802  }
2803  } else {
2804  return '"' . $dirName . '" is not a directory!';
2805  }
2806  return null;
2807  }
2808 
2817  public static function mkdir($newFolder)
2818  {
2819  $result = @mkdir($newFolder, octdec($GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']));
2820  if ($result) {
2821  static::fixPermissions($newFolder);
2822  }
2823  return $result;
2824  }
2825 
2836  public static function mkdir_deep($directory, $deepDirectory = '')
2837  {
2838  if (!is_string($directory)) {
2839  throw new \InvalidArgumentException('The specified directory is of type "' . gettype($directory) . '" but a string is expected.', 1303662955);
2840  }
2841  if (!is_string($deepDirectory)) {
2842  throw new \InvalidArgumentException('The specified directory is of type "' . gettype($deepDirectory) . '" but a string is expected.', 1303662956);
2843  }
2844  // Ensure there is only one slash
2845  $fullPath = rtrim($directory, '/') . '/' . ltrim($deepDirectory, '/');
2846  if ($fullPath !== '' && !is_dir($fullPath)) {
2847  $firstCreatedPath = static::createDirectoryPath($fullPath);
2848  if ($firstCreatedPath !== '') {
2849  static::fixPermissions($firstCreatedPath, true);
2850  }
2851  }
2852  }
2853 
2865  protected static function createDirectoryPath($fullDirectoryPath)
2866  {
2867  $currentPath = $fullDirectoryPath;
2868  $firstCreatedPath = '';
2869  $permissionMask = octdec($GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']);
2870  if (!@is_dir($currentPath)) {
2871  do {
2872  $firstCreatedPath = $currentPath;
2873  $separatorPosition = strrpos($currentPath, DIRECTORY_SEPARATOR);
2874  $currentPath = substr($currentPath, 0, $separatorPosition);
2875  } while (!is_dir($currentPath) && $separatorPosition !== false);
2876  $result = @mkdir($fullDirectoryPath, $permissionMask, true);
2877  // Check existence of directory again to avoid race condition. Directory could have get created by another process between previous is_dir() and mkdir()
2878  if (!$result && !@is_dir($fullDirectoryPath)) {
2879  throw new \RuntimeException('Could not create directory "' . $fullDirectoryPath . '"!', 1170251401);
2880  }
2881  }
2882  return $firstCreatedPath;
2883  }
2884 
2892  public static function rmdir($path, $removeNonEmpty = false)
2893  {
2894  $OK = false;
2895  // Remove trailing slash
2896  $path = preg_replace('|/$|', '', $path);
2897  if (file_exists($path)) {
2898  $OK = true;
2899  if (!is_link($path) && is_dir($path)) {
2900  if ($removeNonEmpty == true && ($handle = @opendir($path))) {
2901  while ($OK && false !== ($file = readdir($handle))) {
2902  if ($file == '.' || $file == '..') {
2903  continue;
2904  }
2905  $OK = static::rmdir($path . '/' . $file, $removeNonEmpty);
2906  }
2907  closedir($handle);
2908  }
2909  if ($OK) {
2910  $OK = @rmdir($path);
2911  }
2912  } elseif (is_link($path) && is_dir($path) && TYPO3_OS === 'WIN') {
2913  $OK = @rmdir($path);
2914  } else {
2915  // If $path is a file, simply remove it
2916  $OK = @unlink($path);
2917  }
2918  clearstatcache();
2919  } elseif (is_link($path)) {
2920  $OK = @unlink($path);
2921  clearstatcache();
2922  }
2923  return $OK;
2924  }
2925 
2936  public static function flushDirectory($directory, $keepOriginalDirectory = false, $flushOpcodeCache = false)
2937  {
2938  $result = false;
2939 
2940  if (is_dir($directory)) {
2941  $temporaryDirectory = rtrim($directory, '/') . '.' . StringUtility::getUniqueId('remove') . '/';
2942  if (rename($directory, $temporaryDirectory)) {
2943  if ($flushOpcodeCache) {
2944  GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive($directory);
2945  }
2946  if ($keepOriginalDirectory) {
2947  static::mkdir($directory);
2948  }
2949  clearstatcache();
2950  $result = static::rmdir($temporaryDirectory, true);
2951  }
2952  }
2953 
2954  return $result;
2955  }
2956 
2964  public static function get_dirs($path)
2965  {
2966  if ($path) {
2967  if (is_dir($path)) {
2968  $dir = scandir($path);
2969  $dirs = array();
2970  foreach ($dir as $entry) {
2971  if (is_dir($path . '/' . $entry) && $entry != '..' && $entry != '.') {
2972  $dirs[] = $entry;
2973  }
2974  }
2975  } else {
2976  $dirs = 'error';
2977  }
2978  }
2979  return $dirs;
2980  }
2981 
2994  public static function getFilesInDir($path, $extensionList = '', $prependPath = false, $order = '', $excludePattern = '')
2995  {
2996  $excludePattern = (string)$excludePattern;
2997  $path = rtrim($path, '/');
2998  if (!@is_dir($path)) {
2999  return array();
3000  }
3001 
3002  $rawFileList = scandir($path);
3003  if ($rawFileList === false) {
3004  return 'error opening path: "' . $path . '"';
3005  }
3006 
3007  $pathPrefix = $path . '/';
3008  $extensionList = ',' . $extensionList . ',';
3009  $files = array();
3010  foreach ($rawFileList as $entry) {
3011  $completePathToEntry = $pathPrefix . $entry;
3012  if (!@is_file($completePathToEntry)) {
3013  continue;
3014  }
3015 
3016  if (
3017  ($extensionList === ',,' || stripos($extensionList, ',' . pathinfo($entry, PATHINFO_EXTENSION) . ',') !== false)
3018  && ($excludePattern === '' || !preg_match(('/^' . $excludePattern . '$/'), $entry))
3019  ) {
3020  if ($order !== 'mtime') {
3021  $files[] = $entry;
3022  } else {
3023  // Store the value in the key so we can do a fast asort later.
3024  $files[$entry] = filemtime($completePathToEntry);
3025  }
3026  }
3027  }
3028 
3029  $valueName = 'value';
3030  if ($order === 'mtime') {
3031  asort($files);
3032  $valueName = 'key';
3033  }
3034 
3035  $valuePathPrefix = $prependPath ? $pathPrefix : '';
3036  $foundFiles = array();
3037  foreach ($files as $key => $value) {
3038  // Don't change this ever - extensions may depend on the fact that the hash is an md5 of the path! (import/export extension)
3039  $foundFiles[md5($pathPrefix . ${$valueName})] = $valuePathPrefix . ${$valueName};
3040  }
3041 
3042  return $foundFiles;
3043  }
3044 
3056  public static function getAllFilesAndFoldersInPath(array $fileArr, $path, $extList = '', $regDirs = false, $recursivityLevels = 99, $excludePattern = '')
3057  {
3058  if ($regDirs) {
3059  $fileArr[md5($path)] = $path;
3060  }
3061  $fileArr = array_merge($fileArr, self::getFilesInDir($path, $extList, 1, 1, $excludePattern));
3062  $dirs = self::get_dirs($path);
3063  if ($recursivityLevels > 0 && is_array($dirs)) {
3064  foreach ($dirs as $subdirs) {
3065  if ((string)$subdirs !== '' && ($excludePattern === '' || !preg_match(('/^' . $excludePattern . '$/'), $subdirs))) {
3066  $fileArr = self::getAllFilesAndFoldersInPath($fileArr, $path . $subdirs . '/', $extList, $regDirs, $recursivityLevels - 1, $excludePattern);
3067  }
3068  }
3069  }
3070  return $fileArr;
3071  }
3072 
3080  public static function removePrefixPathFromList(array $fileArr, $prefixToRemove)
3081  {
3082  foreach ($fileArr as $k => &$absFileRef) {
3083  if (self::isFirstPartOfStr($absFileRef, $prefixToRemove)) {
3084  $absFileRef = substr($absFileRef, strlen($prefixToRemove));
3085  } else {
3086  return 'ERROR: One or more of the files was NOT prefixed with the prefix-path!';
3087  }
3088  }
3089  unset($absFileRef);
3090  return $fileArr;
3091  }
3092 
3099  public static function fixWindowsFilePath($theFile)
3100  {
3101  return str_replace(array('\\', '//'), '/', $theFile);
3102  }
3103 
3111  public static function resolveBackPath($pathStr)
3112  {
3113  if (strpos($pathStr, '..') === false) {
3114  return $pathStr;
3115  }
3116  $parts = explode('/', $pathStr);
3117  $output = array();
3118  $c = 0;
3119  foreach ($parts as $part) {
3120  if ($part === '..') {
3121  if ($c) {
3122  array_pop($output);
3123  --$c;
3124  } else {
3125  $output[] = $part;
3126  }
3127  } else {
3128  ++$c;
3129  $output[] = $part;
3130  }
3131  }
3132  return implode('/', $output);
3133  }
3134 
3144  public static function locationHeaderUrl($path)
3145  {
3146  $uI = parse_url($path);
3147  // relative to HOST
3148  if ($path[0] === '/') {
3149  $path = self::getIndpEnv('TYPO3_REQUEST_HOST') . $path;
3150  } elseif (!$uI['scheme']) {
3151  // No scheme either
3152  $path = self::getIndpEnv('TYPO3_REQUEST_DIR') . $path;
3153  }
3154  return $path;
3155  }
3156 
3164  public static function getMaxUploadFileSize()
3165  {
3166  // Check for PHP restrictions of the maximum size of one of the $_FILES
3167  $phpUploadLimit = self::getBytesFromSizeMeasurement(ini_get('upload_max_filesize'));
3168  // Check for PHP restrictions of the maximum $_POST size
3169  $phpPostLimit = self::getBytesFromSizeMeasurement(ini_get('post_max_size'));
3170  // If the total amount of post data is smaller (!) than the upload_max_filesize directive,
3171  // then this is the real limit in PHP
3172  $phpUploadLimit = $phpPostLimit > 0 && $phpPostLimit < $phpUploadLimit ? $phpPostLimit : $phpUploadLimit;
3173  return floor(($phpUploadLimit)) / 1024;
3174  }
3175 
3182  public static function getBytesFromSizeMeasurement($measurement)
3183  {
3184  $bytes = doubleval($measurement);
3185  if (stripos($measurement, 'G')) {
3186  $bytes *= 1024 * 1024 * 1024;
3187  } elseif (stripos($measurement, 'M')) {
3188  $bytes *= 1024 * 1024;
3189  } elseif (stripos($measurement, 'K')) {
3190  $bytes *= 1024;
3191  }
3192  return $bytes;
3193  }
3194 
3200  public static function getMaximumPathLength()
3201  {
3202  return PHP_MAXPATHLEN;
3203  }
3204 
3221  public static function createVersionNumberedFilename($file)
3222  {
3223  $lookupFile = explode('?', $file);
3224  $path = self::resolveBackPath(self::dirname(PATH_thisScript) . '/' . $lookupFile[0]);
3225 
3226  $doNothing = false;
3227  if (TYPO3_MODE == 'FE') {
3228  $mode = strtolower($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['versionNumberInFilename']);
3229  if ($mode === 'embed') {
3230  $mode = true;
3231  } else {
3232  if ($mode === 'querystring') {
3233  $mode = false;
3234  } else {
3235  $doNothing = true;
3236  }
3237  }
3238  } else {
3239  $mode = $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['versionNumberInFilename'];
3240  }
3241  if (!file_exists($path) || $doNothing) {
3242  // File not found, return filename unaltered
3243  $fullName = $file;
3244  } else {
3245  if (!$mode) {
3246  // If use of .htaccess rule is not configured,
3247  // we use the default query-string method
3248  if ($lookupFile[1]) {
3249  $separator = '&';
3250  } else {
3251  $separator = '?';
3252  }
3253  $fullName = $file . $separator . filemtime($path);
3254  } else {
3255  // Change the filename
3256  $name = explode('.', $lookupFile[0]);
3257  $extension = array_pop($name);
3258  array_push($name, filemtime($path), $extension);
3259  $fullName = implode('.', $name);
3260  // Append potential query string
3261  $fullName .= $lookupFile[1] ? '?' . $lookupFile[1] : '';
3262  }
3263  }
3264  return $fullName;
3265  }
3266 
3267  /*************************
3268  *
3269  * SYSTEM INFORMATION
3270  *
3271  *************************/
3278  public static function getThisUrl()
3279  {
3280  self::logDeprecatedFunction();
3281  // Url of this script
3282  $p = parse_url(self::getIndpEnv('TYPO3_REQUEST_SCRIPT'));
3283  $dir = self::dirname($p['path']) . '/';
3284  // Strip file
3285  $url = str_replace('//', '/', $p['host'] . ($p['port'] ? ':' . $p['port'] : '') . $dir);
3286  return $url;
3287  }
3288 
3297  public static function linkThisScript(array $getParams = array())
3298  {
3299  $parts = self::getIndpEnv('SCRIPT_NAME');
3300  $params = self::_GET();
3301  foreach ($getParams as $key => $value) {
3302  if ($value !== '') {
3303  $params[$key] = $value;
3304  } else {
3305  unset($params[$key]);
3306  }
3307  }
3308  $pString = self::implodeArrayForUrl('', $params);
3309  return $pString ? $parts . '?' . ltrim($pString, '&') : $parts;
3310  }
3311 
3320  public static function linkThisUrl($url, array $getParams = array())
3321  {
3322  $parts = parse_url($url);
3323  $getP = array();
3324  if ($parts['query']) {
3325  parse_str($parts['query'], $getP);
3326  }
3327  ArrayUtility::mergeRecursiveWithOverrule($getP, $getParams);
3328  $uP = explode('?', $url);
3329  $params = self::implodeArrayForUrl('', $getP);
3330  $outurl = $uP[0] . ($params ? '?' . substr($params, 1) : '');
3331  return $outurl;
3332  }
3333 
3342  public static function getIndpEnv($getEnvName)
3343  {
3344  if (isset(self::$indpEnvCache[$getEnvName])) {
3345  return self::$indpEnvCache[$getEnvName];
3346  }
3347 
3348  /*
3349  Conventions:
3350  output from parse_url():
3351  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
3352  [scheme] => 'http'
3353  [user] => 'username'
3354  [pass] => 'password'
3355  [host] => '192.168.1.4'
3356  [port] => '8080'
3357  [path] => '/typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/'
3358  [query] => 'arg1,arg2,arg3&p1=parameter1&p2[key]=value'
3359  [fragment] => 'link1'Further definition: [path_script] = '/typo3/32/temp/phpcheck/index.php'
3360  [path_dir] = '/typo3/32/temp/phpcheck/'
3361  [path_info] = '/arg1/arg2/arg3/'
3362  [path] = [path_script/path_dir][path_info]Keys supported:URI______:
3363  REQUEST_URI = [path]?[query] = /typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/?arg1,arg2,arg3&p1=parameter1&p2[key]=value
3364  HTTP_HOST = [host][:[port]] = 192.168.1.4:8080
3365  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')!
3366  PATH_INFO = [path_info] = /arg1/arg2/arg3/
3367  QUERY_STRING = [query] = arg1,arg2,arg3&p1=parameter1&p2[key]=value
3368  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
3369  (Notice: NO username/password + NO fragment)CLIENT____:
3370  REMOTE_ADDR = (client IP)
3371  REMOTE_HOST = (client host)
3372  HTTP_USER_AGENT = (client user agent)
3373  HTTP_ACCEPT_LANGUAGE = (client accept language)SERVER____:
3374  SCRIPT_FILENAME = Absolute filename of script (Differs between windows/unix). On windows 'C:\\blabla\\blabl\\' will be converted to 'C:/blabla/blabl/'Special extras:
3375  TYPO3_HOST_ONLY = [host] = 192.168.1.4
3376  TYPO3_PORT = [port] = 8080 (blank if 80, taken from host value)
3377  TYPO3_REQUEST_HOST = [scheme]://[host][:[port]]
3378  TYPO3_REQUEST_URL = [scheme]://[host][:[port]][path]?[query] (scheme will by default be "http" until we can detect something different)
3379  TYPO3_REQUEST_SCRIPT = [scheme]://[host][:[port]][path_script]
3380  TYPO3_REQUEST_DIR = [scheme]://[host][:[port]][path_dir]
3381  TYPO3_SITE_URL = [scheme]://[host][:[port]][path_dir] of the TYPO3 website frontend
3382  TYPO3_SITE_PATH = [path_dir] of the TYPO3 website frontend
3383  TYPO3_SITE_SCRIPT = [script / Speaking URL] of the TYPO3 website
3384  TYPO3_DOCUMENT_ROOT = Absolute path of root of documents: TYPO3_DOCUMENT_ROOT.SCRIPT_NAME = SCRIPT_FILENAME (typically)
3385  TYPO3_SSL = Returns TRUE if this session uses SSL/TLS (https)
3386  TYPO3_PROXY = Returns TRUE if this session runs over a well known proxyNotice: [fragment] is apparently NEVER available to the script!Testing suggestions:
3387  - Output all the values.
3388  - 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
3389  - ALSO TRY the script from the ROOT of a site (like 'http://www.mytest.com/' and not 'http://www.mytest.com/test/' !!)
3390  */
3391  $retVal = '';
3392  switch ((string)$getEnvName) {
3393  case 'SCRIPT_NAME':
3394  $retVal = self::isRunningOnCgiServerApi()
3395  && ($_SERVER['ORIG_PATH_INFO'] ?: $_SERVER['PATH_INFO'])
3396  ? ($_SERVER['ORIG_PATH_INFO'] ?: $_SERVER['PATH_INFO'])
3397  : ($_SERVER['ORIG_SCRIPT_NAME'] ?: $_SERVER['SCRIPT_NAME']);
3398  // Add a prefix if TYPO3 is behind a proxy: ext-domain.com => int-server.com/prefix
3399  if (self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) {
3400  if (self::getIndpEnv('TYPO3_SSL') && $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL']) {
3401  $retVal = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL'] . $retVal;
3402  } elseif ($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix']) {
3403  $retVal = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix'] . $retVal;
3404  }
3405  }
3406  break;
3407  case 'SCRIPT_FILENAME':
3408  $retVal = PATH_thisScript;
3409  break;
3410  case 'REQUEST_URI':
3411  // Typical application of REQUEST_URI is return urls, forms submitting to itself etc. Example: returnUrl='.rawurlencode(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('REQUEST_URI'))
3412  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['requestURIvar']) {
3413  // This is for URL rewriters that store the original URI in a server variable (eg ISAPI_Rewriter for IIS: HTTP_X_REWRITE_URL)
3414  list($v, $n) = explode('|', $GLOBALS['TYPO3_CONF_VARS']['SYS']['requestURIvar']);
3415  $retVal = $GLOBALS[$v][$n];
3416  } elseif (!$_SERVER['REQUEST_URI']) {
3417  // This is for ISS/CGI which does not have the REQUEST_URI available.
3418  $retVal = '/' . ltrim(self::getIndpEnv('SCRIPT_NAME'), '/') . ($_SERVER['QUERY_STRING'] ? '?' . $_SERVER['QUERY_STRING'] : '');
3419  } else {
3420  $retVal = '/' . ltrim($_SERVER['REQUEST_URI'], '/');
3421  }
3422  // Add a prefix if TYPO3 is behind a proxy: ext-domain.com => int-server.com/prefix
3423  if (self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) {
3424  if (self::getIndpEnv('TYPO3_SSL') && $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL']) {
3425  $retVal = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL'] . $retVal;
3426  } elseif ($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix']) {
3427  $retVal = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix'] . $retVal;
3428  }
3429  }
3430  break;
3431  case 'PATH_INFO':
3432  // $_SERVER['PATH_INFO'] != $_SERVER['SCRIPT_NAME'] is necessary because some servers (Windows/CGI)
3433  // are seen to set PATH_INFO equal to script_name
3434  // Further, there must be at least one '/' in the path - else the PATH_INFO value does not make sense.
3435  // IF 'PATH_INFO' never works for our purpose in TYPO3 with CGI-servers,
3436  // then 'PHP_SAPI=='cgi'' might be a better check.
3437  // Right now strcmp($_SERVER['PATH_INFO'], GeneralUtility::getIndpEnv('SCRIPT_NAME')) will always
3438  // return FALSE for CGI-versions, but that is only as long as SCRIPT_NAME is set equal to PATH_INFO
3439  // because of PHP_SAPI=='cgi' (see above)
3440  if (!self::isRunningOnCgiServerApi()) {
3441  $retVal = $_SERVER['PATH_INFO'];
3442  }
3443  break;
3444  case 'TYPO3_REV_PROXY':
3445  $retVal = self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP']);
3446  break;
3447  case 'REMOTE_ADDR':
3448  $retVal = $_SERVER['REMOTE_ADDR'];
3449  if (self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) {
3450  $ip = self::trimExplode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
3451  // Choose which IP in list to use
3452  if (!empty($ip)) {
3453  switch ($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyHeaderMultiValue']) {
3454  case 'last':
3455  $ip = array_pop($ip);
3456  break;
3457  case 'first':
3458  $ip = array_shift($ip);
3459  break;
3460  case 'none':
3461 
3462  default:
3463  $ip = '';
3464  }
3465  }
3466  if (self::validIP($ip)) {
3467  $retVal = $ip;
3468  }
3469  }
3470  break;
3471  case 'HTTP_HOST':
3472  $retVal = $_SERVER['HTTP_HOST'];
3473  if (self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) {
3474  $host = self::trimExplode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
3475  // Choose which host in list to use
3476  if (!empty($host)) {
3477  switch ($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyHeaderMultiValue']) {
3478  case 'last':
3479  $host = array_pop($host);
3480  break;
3481  case 'first':
3482  $host = array_shift($host);
3483  break;
3484  case 'none':
3485 
3486  default:
3487  $host = '';
3488  }
3489  }
3490  if ($host) {
3491  $retVal = $host;
3492  }
3493  }
3494  if (!static::isAllowedHostHeaderValue($retVal)) {
3495  throw new \UnexpectedValueException(
3496  '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.',
3497  1396795884
3498  );
3499  }
3500  break;
3501  case 'HTTP_REFERER':
3502 
3503  case 'HTTP_USER_AGENT':
3504 
3505  case 'HTTP_ACCEPT_ENCODING':
3506 
3507  case 'HTTP_ACCEPT_LANGUAGE':
3508 
3509  case 'REMOTE_HOST':
3510 
3511  case 'QUERY_STRING':
3512  $retVal = '';
3513  if (isset($_SERVER[$getEnvName])) {
3514  $retVal = $_SERVER[$getEnvName];
3515  }
3516  break;
3517  case 'TYPO3_DOCUMENT_ROOT':
3518  // Get the web root (it is not the root of the TYPO3 installation)
3519  // The absolute path of the script can be calculated with TYPO3_DOCUMENT_ROOT + SCRIPT_FILENAME
3520  // 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.
3521  // Therefore the DOCUMENT_ROOT is now always calculated as the SCRIPT_FILENAME minus the end part shared with SCRIPT_NAME.
3522  $SFN = self::getIndpEnv('SCRIPT_FILENAME');
3523  $SN_A = explode('/', strrev(self::getIndpEnv('SCRIPT_NAME')));
3524  $SFN_A = explode('/', strrev($SFN));
3525  $acc = array();
3526  foreach ($SN_A as $kk => $vv) {
3527  if ((string)$SFN_A[$kk] === (string)$vv) {
3528  $acc[] = $vv;
3529  } else {
3530  break;
3531  }
3532  }
3533  $commonEnd = strrev(implode('/', $acc));
3534  if ((string)$commonEnd !== '') {
3535  $DR = substr($SFN, 0, -(strlen($commonEnd) + 1));
3536  }
3537  $retVal = $DR;
3538  break;
3539  case 'TYPO3_HOST_ONLY':
3540  $httpHost = self::getIndpEnv('HTTP_HOST');
3541  $httpHostBracketPosition = strpos($httpHost, ']');
3542  $httpHostParts = explode(':', $httpHost);
3543  $retVal = $httpHostBracketPosition !== false ? substr($httpHost, 0, $httpHostBracketPosition + 1) : array_shift($httpHostParts);
3544  break;
3545  case 'TYPO3_PORT':
3546  $httpHost = self::getIndpEnv('HTTP_HOST');
3547  $httpHostOnly = self::getIndpEnv('TYPO3_HOST_ONLY');
3548  $retVal = strlen($httpHost) > strlen($httpHostOnly) ? substr($httpHost, strlen($httpHostOnly) + 1) : '';
3549  break;
3550  case 'TYPO3_REQUEST_HOST':
3551  $retVal = (self::getIndpEnv('TYPO3_SSL') ? 'https://' : 'http://') . self::getIndpEnv('HTTP_HOST');
3552  break;
3553  case 'TYPO3_REQUEST_URL':
3554  $retVal = self::getIndpEnv('TYPO3_REQUEST_HOST') . self::getIndpEnv('REQUEST_URI');
3555  break;
3556  case 'TYPO3_REQUEST_SCRIPT':
3557  $retVal = self::getIndpEnv('TYPO3_REQUEST_HOST') . self::getIndpEnv('SCRIPT_NAME');
3558  break;
3559  case 'TYPO3_REQUEST_DIR':
3560  $retVal = self::getIndpEnv('TYPO3_REQUEST_HOST') . self::dirname(self::getIndpEnv('SCRIPT_NAME')) . '/';
3561  break;
3562  case 'TYPO3_SITE_URL':
3563  $url = self::getIndpEnv('TYPO3_REQUEST_DIR');
3564  // This can only be set by external entry scripts
3565  if (defined('TYPO3_PATH_WEB')) {
3566  $retVal = $url;
3567  } elseif (defined('PATH_thisScript') && defined('PATH_site')) {
3568  $lPath = PathUtility::stripPathSitePrefix(dirname(PATH_thisScript)) . '/';
3569  $siteUrl = substr($url, 0, -strlen($lPath));
3570  if (substr($siteUrl, -1) != '/') {
3571  $siteUrl .= '/';
3572  }
3573  $retVal = $siteUrl;
3574  }
3575  break;
3576  case 'TYPO3_SITE_PATH':
3577  $retVal = substr(self::getIndpEnv('TYPO3_SITE_URL'), strlen(self::getIndpEnv('TYPO3_REQUEST_HOST')));
3578  break;
3579  case 'TYPO3_SITE_SCRIPT':
3580  $retVal = substr(self::getIndpEnv('TYPO3_REQUEST_URL'), strlen(self::getIndpEnv('TYPO3_SITE_URL')));
3581  break;
3582  case 'TYPO3_SSL':
3583  $proxySSL = trim($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxySSL']);
3584  if ($proxySSL == '*') {
3585  $proxySSL = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'];
3586  }
3587  if (self::cmpIP($_SERVER['REMOTE_ADDR'], $proxySSL)) {
3588  $retVal = true;
3589  } else {
3590  $retVal = $_SERVER['SSL_SESSION_ID'] || strtolower($_SERVER['HTTPS']) === 'on' || (string)$_SERVER['HTTPS'] === '1';
3591  }
3592  break;
3593  case '_ARRAY':
3594  $out = array();
3595  // Here, list ALL possible keys to this function for debug display.
3596  $envTestVars = array(
3597  'HTTP_HOST',
3598  'TYPO3_HOST_ONLY',
3599  'TYPO3_PORT',
3600  'PATH_INFO',
3601  'QUERY_STRING',
3602  'REQUEST_URI',
3603  'HTTP_REFERER',
3604  'TYPO3_REQUEST_HOST',
3605  'TYPO3_REQUEST_URL',
3606  'TYPO3_REQUEST_SCRIPT',
3607  'TYPO3_REQUEST_DIR',
3608  'TYPO3_SITE_URL',
3609  'TYPO3_SITE_SCRIPT',
3610  'TYPO3_SSL',
3611  'TYPO3_REV_PROXY',
3612  'SCRIPT_NAME',
3613  'TYPO3_DOCUMENT_ROOT',
3614  'SCRIPT_FILENAME',
3615  'REMOTE_ADDR',
3616  'REMOTE_HOST',
3617  'HTTP_USER_AGENT',
3618  'HTTP_ACCEPT_LANGUAGE'
3619  );
3620  foreach ($envTestVars as $v) {
3621  $out[$v] = self::getIndpEnv($v);
3622  }
3623  reset($out);
3624  $retVal = $out;
3625  break;
3626  }
3627  self::$indpEnvCache[$getEnvName] = $retVal;
3628  return $retVal;
3629  }
3630 
3639  public static function isAllowedHostHeaderValue($hostHeaderValue)
3640  {
3641  if (static::$allowHostHeaderValue === true) {
3642  return true;
3643  }
3644 
3645  if (static::isInternalRequestType()) {
3646  return static::$allowHostHeaderValue = true;
3647  }
3648 
3649  // Deny the value if trusted host patterns is empty, which means we are early in the bootstrap
3650  if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'])) {
3651  return false;
3652  }
3653 
3654  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] === self::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL) {
3655  static::$allowHostHeaderValue = true;
3656  } else {
3657  static::$allowHostHeaderValue = static::hostHeaderValueMatchesTrustedHostsPattern($hostHeaderValue);
3658  }
3659 
3660  return static::$allowHostHeaderValue;
3661  }
3662 
3670  public static function hostHeaderValueMatchesTrustedHostsPattern($hostHeaderValue)
3671  {
3672  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] === self::ENV_TRUSTED_HOSTS_PATTERN_SERVER_NAME) {
3673  // Allow values that equal the server name
3674  // Note that this is only secure if name base virtual host are configured correctly in the webserver
3675  $defaultPort = self::getIndpEnv('TYPO3_SSL') ? '443' : '80';
3676  $parsedHostValue = parse_url('http://' . $hostHeaderValue);
3677  if (isset($parsedHostValue['port'])) {
3678  $hostMatch = (strtolower($parsedHostValue['host']) === strtolower($_SERVER['SERVER_NAME']) && (string)$parsedHostValue['port'] === $_SERVER['SERVER_PORT']);
3679  } else {
3680  $hostMatch = (strtolower($hostHeaderValue) === strtolower($_SERVER['SERVER_NAME']) && $defaultPort === $_SERVER['SERVER_PORT']);
3681  }
3682  } else {
3683  // In case name based virtual hosts are not possible, we allow setting a trusted host pattern
3684  // See https://typo3.org/teams/security/security-bulletins/typo3-core/typo3-core-sa-2014-001/ for further details
3685  $hostMatch = (bool)preg_match('/^' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] . '$/i', $hostHeaderValue);
3686  }
3687 
3688  return $hostMatch;
3689  }
3690 
3701  protected static function isInternalRequestType()
3702  {
3703  return (!defined('TYPO3_REQUESTTYPE') || (defined('TYPO3_REQUESTTYPE') && TYPO3_REQUESTTYPE & (TYPO3_REQUESTTYPE_INSTALL | TYPO3_REQUESTTYPE_CLI)));
3704  }
3705 
3711  public static function milliseconds()
3712  {
3713  return round(microtime(true) * 1000);
3714  }
3715 
3722  public static function clientInfo($useragent = '')
3723  {
3724  if (!$useragent) {
3725  $useragent = self::getIndpEnv('HTTP_USER_AGENT');
3726  }
3727  $bInfo = array();
3728  // Which browser?
3729  if (strpos($useragent, 'Konqueror') !== false) {
3730  $bInfo['BROWSER'] = 'konqu';
3731  } elseif (strpos($useragent, 'Opera') !== false) {
3732  $bInfo['BROWSER'] = 'opera';
3733  } elseif (strpos($useragent, 'MSIE') !== false) {
3734  $bInfo['BROWSER'] = 'msie';
3735  } elseif (strpos($useragent, 'Mozilla') !== false) {
3736  $bInfo['BROWSER'] = 'net';
3737  } elseif (strpos($useragent, 'Flash') !== false) {
3738  $bInfo['BROWSER'] = 'flash';
3739  }
3740  if (isset($bInfo['BROWSER'])) {
3741  // Browser version
3742  switch ($bInfo['BROWSER']) {
3743  case 'net':
3744  $bInfo['VERSION'] = doubleval(substr($useragent, 8));
3745  if (strpos($useragent, 'Netscape6/') !== false) {
3746  $bInfo['VERSION'] = doubleval(substr(strstr($useragent, 'Netscape6/'), 10));
3747  }
3748  // Will we ever know if this was a typo or intention...?! :-(
3749  if (strpos($useragent, 'Netscape/6') !== false) {
3750  $bInfo['VERSION'] = doubleval(substr(strstr($useragent, 'Netscape/6'), 10));
3751  }
3752  if (strpos($useragent, 'Netscape/7') !== false) {
3753  $bInfo['VERSION'] = doubleval(substr(strstr($useragent, 'Netscape/7'), 9));
3754  }
3755  break;
3756  case 'msie':
3757  $tmp = strstr($useragent, 'MSIE');
3758  $bInfo['VERSION'] = doubleval(preg_replace('/^[^0-9]*/', '', substr($tmp, 4)));
3759  break;
3760  case 'opera':
3761  $tmp = strstr($useragent, 'Opera');
3762  $bInfo['VERSION'] = doubleval(preg_replace('/^[^0-9]*/', '', substr($tmp, 5)));
3763  break;
3764  case 'konqu':
3765  $tmp = strstr($useragent, 'Konqueror/');
3766  $bInfo['VERSION'] = doubleval(substr($tmp, 10));
3767  break;
3768  }
3769  // Client system
3770  if (strpos($useragent, 'Win') !== false) {
3771  $bInfo['SYSTEM'] = 'win';
3772  } elseif (strpos($useragent, 'Mac') !== false) {
3773  $bInfo['SYSTEM'] = 'mac';
3774  } elseif (strpos($useragent, 'Linux') !== false || strpos($useragent, 'X11') !== false || strpos($useragent, 'SGI') !== false || strpos($useragent, ' SunOS ') !== false || strpos($useragent, ' HP-UX ') !== false) {
3775  $bInfo['SYSTEM'] = 'unix';
3776  }
3777  }
3778  return $bInfo;
3779  }
3780 
3787  public static function getHostname($requestHost = true)
3788  {
3789  $host = '';
3790  // If not called from the command-line, resolve on getIndpEnv()
3791  // Note that TYPO3_REQUESTTYPE is not used here as it may not yet be defined
3792  if ($requestHost && (!defined('TYPO3_cliMode') || !TYPO3_cliMode)) {
3793  $host = self::getIndpEnv('HTTP_HOST');
3794  }
3795  if (!$host) {
3796  // will fail for PHP 4.1 and 4.2
3797  $host = @php_uname('n');
3798  // 'n' is ignored in broken installations
3799  if (strpos($host, ' ')) {
3800  $host = '';
3801  }
3802  }
3803  // We have not found a FQDN yet
3804  if ($host && strpos($host, '.') === false) {
3805  $ip = gethostbyname($host);
3806  // We got an IP address
3807  if ($ip != $host) {
3808  $fqdn = gethostbyaddr($ip);
3809  if ($ip != $fqdn) {
3810  $host = $fqdn;
3811  }
3812  }
3813  }
3814  if (!$host) {
3815  $host = 'localhost.localdomain';
3816  }
3817  return $host;
3818  }
3819 
3820  /*************************
3821  *
3822  * TYPO3 SPECIFIC FUNCTIONS
3823  *
3824  *************************/
3836  public static function getFileAbsFileName($filename, $onlyRelative = true, $relToTYPO3_mainDir = false)
3837  {
3838  if ((string)$filename === '') {
3839  return '';
3840  }
3841  $relPathPrefix = PATH_site;
3842  if ($relToTYPO3_mainDir) {
3843  $relPathPrefix = PATH_typo3;
3844  }
3845 
3846  // Extension
3847  if (strpos($filename, 'EXT:') === 0) {
3848  list($extKey, $local) = explode('/', substr($filename, 4), 2);
3849  $filename = '';
3850  if ((string)$extKey !== '' && ExtensionManagementUtility::isLoaded($extKey) && (string)$local !== '') {
3851  $filename = ExtensionManagementUtility::extPath($extKey) . $local;
3852  }
3853  } elseif (!static::isAbsPath($filename)) {
3854  // relative. Prepended with $relPathPrefix
3855  $filename = $relPathPrefix . $filename;
3856  } elseif ($onlyRelative && !static::isFirstPartOfStr($filename, $relPathPrefix)) {
3857  // absolute, but set to blank if not allowed
3858  $filename = '';
3859  }
3860  if ((string)$filename !== '' && static::validPathStr($filename)) {
3861  // checks backpath.
3862  return $filename;
3863  }
3864  return '';
3865  }
3866 
3879  public static function validPathStr($theFile)
3880  {
3881  return strpos($theFile, '//') === false && strpos($theFile, '\\') === false
3882  && !preg_match('#(?:^\\.\\.|/\\.\\./|[[:cntrl:]])#u', $theFile);
3883  }
3884 
3891  public static function isAbsPath($path)
3892  {
3893  return $path[0] === '/' || TYPO3_OS === 'WIN' && (strpos($path, ':/') === 1 || strpos($path, ':\\') === 1);
3894  }
3895 
3902  public static function isAllowedAbsPath($path)
3903  {
3904  $lockRootPath = $GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'];
3905  return static::isAbsPath($path) && static::validPathStr($path)
3906  && (static::isFirstPartOfStr($path, PATH_site)
3907  || $lockRootPath && static::isFirstPartOfStr($path, $lockRootPath));
3908  }
3909 
3920  {
3921  $pattern = '/[[:cntrl:]]/';
3922  if ((string)$filename !== '' && (string)$GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] !== '') {
3923  $pattern = '/(?:[[:cntrl:]]|' . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] . ')/i';
3924  }
3925  return !preg_match($pattern, $filename);
3926  }
3927 
3934  public static function copyDirectory($source, $destination)
3935  {
3936  if (strpos($source, PATH_site) === false) {
3937  $source = PATH_site . $source;
3938  }
3939  if (strpos($destination, PATH_site) === false) {
3940  $destination = PATH_site . $destination;
3941  }
3942  if (static::isAllowedAbsPath($source) && static::isAllowedAbsPath($destination)) {
3943  $iterator = new \RecursiveIteratorIterator(
3944  new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS),
3945  \RecursiveIteratorIterator::SELF_FIRST
3946  );
3947  foreach ($iterator as $item) {
3948  $target = $destination . '/' . $iterator->getSubPathName();
3949  if ($item->isDir()) {
3950  static::mkdir($target);
3951  } else {
3952  static::upload_copy_move($item, $target);
3953  }
3954  }
3955  }
3956  }
3957 
3965  public static function sanitizeLocalUrl($url = '')
3966  {
3967  $sanitizedUrl = '';
3968  $decodedUrl = rawurldecode($url);
3969  if (!empty($url) && self::removeXSS($decodedUrl) === $decodedUrl) {
3970  $parsedUrl = parse_url($decodedUrl);
3971  $testAbsoluteUrl = self::resolveBackPath($decodedUrl);
3972  $testRelativeUrl = self::resolveBackPath(self::dirname(self::getIndpEnv('SCRIPT_NAME')) . '/' . $decodedUrl);
3973  // Pass if URL is on the current host:
3974  if (self::isValidUrl($decodedUrl)) {
3975  if (self::isOnCurrentHost($decodedUrl) && strpos($decodedUrl, self::getIndpEnv('TYPO3_SITE_URL')) === 0) {
3976  $sanitizedUrl = $url;
3977  }
3978  } elseif (self::isAbsPath($decodedUrl) && self::isAllowedAbsPath($decodedUrl)) {
3979  $sanitizedUrl = $url;
3980  } elseif (strpos($testAbsoluteUrl, self::getIndpEnv('TYPO3_SITE_PATH')) === 0 && $decodedUrl[0] === '/') {
3981  $sanitizedUrl = $url;
3982  } elseif (empty($parsedUrl['scheme']) && strpos($testRelativeUrl, self::getIndpEnv('TYPO3_SITE_PATH')) === 0 && $decodedUrl[0] !== '/') {
3983  $sanitizedUrl = $url;
3984  }
3985  }
3986  if (!empty($url) && empty($sanitizedUrl)) {
3987  self::sysLog('The URL "' . $url . '" is not considered to be local and was denied.', 'core', self::SYSLOG_SEVERITY_NOTICE);
3988  }
3989  return $sanitizedUrl;
3990  }
3991 
4000  public static function upload_copy_move($source, $destination)
4001  {
4002  if (is_uploaded_file($source)) {
4003  $uploaded = true;
4004  // 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:
4005  $uploadedResult = move_uploaded_file($source, $destination);
4006  } else {
4007  $uploaded = false;
4008  @copy($source, $destination);
4009  }
4010  // Change the permissions of the file
4011  self::fixPermissions($destination);
4012  // 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
4013  return $uploaded ? $uploadedResult : false;
4014  }
4015 
4025  public static function upload_to_tempfile($uploadedFileName)
4026  {
4027  if (is_uploaded_file($uploadedFileName)) {
4028  $tempFile = self::tempnam('upload_temp_');
4029  move_uploaded_file($uploadedFileName, $tempFile);
4030  return @is_file($tempFile) ? $tempFile : '';
4031  }
4032  }
4033 
4043  public static function unlink_tempfile($uploadedTempFileName)
4044  {
4045  if ($uploadedTempFileName) {
4046  $uploadedTempFileName = self::fixWindowsFilePath($uploadedTempFileName);
4047  if (
4048  self::validPathStr($uploadedTempFileName)
4049  && self::isFirstPartOfStr($uploadedTempFileName, PATH_site . 'typo3temp/')
4050  && @is_file($uploadedTempFileName)
4051  ) {
4052  if (unlink($uploadedTempFileName)) {
4053  return true;
4054  }
4055  }
4056  }
4057  }
4058 
4069  public static function tempnam($filePrefix, $fileSuffix = '')
4070  {
4071  $temporaryPath = PATH_site . 'typo3temp/';
4072  if ($fileSuffix === '') {
4073  $tempFileName = static::fixWindowsFilePath(tempnam($temporaryPath, $filePrefix));
4074  } else {
4075  do {
4076  $tempFileName = $temporaryPath . $filePrefix . mt_rand(1, PHP_INT_MAX) . $fileSuffix;
4077  } while (file_exists($tempFileName));
4078  touch($tempFileName);
4079  clearstatcache(null, $tempFileName);
4080  }
4081  return $tempFileName;
4082  }
4083 
4092  public static function stdAuthCode($uid_or_record, $fields = '', $codeLength = 8)
4093  {
4094  if (is_array($uid_or_record)) {
4095  $recCopy_temp = array();
4096  if ($fields) {
4097  $fieldArr = self::trimExplode(',', $fields, true);
4098  foreach ($fieldArr as $k => $v) {
4099  $recCopy_temp[$k] = $uid_or_record[$v];
4100  }
4101  } else {
4102  $recCopy_temp = $uid_or_record;
4103  }
4104  $preKey = implode('|', $recCopy_temp);
4105  } else {
4106  $preKey = $uid_or_record;
4107  }
4108  $authCode = $preKey . '||' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'];
4109  $authCode = substr(md5($authCode), 0, $codeLength);
4110  return $authCode;
4111  }
4112 
4119  public static function hideIfNotTranslated($l18n_cfg_fieldValue)
4120  {
4121  return $GLOBALS['TYPO3_CONF_VARS']['FE']['hidePagesIfNotTranslatedByDefault'] xor ($l18n_cfg_fieldValue & 2);
4122  }
4123 
4131  public static function hideIfDefaultLanguage($localizationConfiguration)
4132  {
4133  return (bool)($localizationConfiguration & 1);
4134  }
4135 
4146  public static function readLLfile($fileRef, $langKey, $charset = '', $errorMode = 0)
4147  {
4148  self::logDeprecatedFunction();
4150  $languageFactory = self::makeInstance(\TYPO3\CMS\Core\Localization\LocalizationFactory::class);
4151  return $languageFactory->getParsedData($fileRef, $langKey, $charset, $errorMode);
4152  }
4153 
4162  public static function llXmlAutoFileName($fileRef, $language, $sameLocation = false)
4163  {
4164  // If $fileRef is already prefixed with "[language key]" then we should return it as is
4165  $fileName = basename($fileRef);
4166  if (self::isFirstPartOfStr($fileName, $language . '.')) {
4167  return $fileRef;
4168  }
4169 
4170  if ($sameLocation) {
4171  return str_replace($fileName, $language . '.' . $fileName, $fileRef);
4172  }
4173 
4174  // Analyse file reference:
4175  // Is system:
4176  if (self::isFirstPartOfStr($fileRef, PATH_typo3 . 'sysext/')) {
4177  $validatedPrefix = PATH_typo3 . 'sysext/';
4178  } elseif (self::isFirstPartOfStr($fileRef, PATH_typo3 . 'ext/')) {
4179  // Is global:
4180  $validatedPrefix = PATH_typo3 . 'ext/';
4181  } elseif (self::isFirstPartOfStr($fileRef, PATH_typo3conf . 'ext/')) {
4182  // Is local:
4183  $validatedPrefix = PATH_typo3conf . 'ext/';
4184  } else {
4185  $validatedPrefix = '';
4186  }
4187  if ($validatedPrefix) {
4188  // Divide file reference into extension key, directory (if any) and base name:
4189  list($file_extKey, $file_extPath) = explode('/', substr($fileRef, strlen($validatedPrefix)), 2);
4190  $temp = self::revExplode('/', $file_extPath, 2);
4191  if (count($temp) === 1) {
4192  array_unshift($temp, '');
4193  }
4194  // Add empty first-entry if not there.
4195  list($file_extPath, $file_fileName) = $temp;
4196  // The filename is prefixed with "[language key]." because it prevents the llxmltranslate tool from detecting it.
4197  $location = 'typo3conf/l10n/' . $language . '/' . $file_extKey . '/' . ($file_extPath ? $file_extPath . '/' : '');
4198  return $location . $language . '.' . $file_fileName;
4199  }
4200  return null;
4201  }
4202 
4211  public static function resolveSheetDefInDS($dataStructArray, $sheet = 'sDEF')
4212  {
4213  if (!is_array($dataStructArray)) {
4214  return 'Data structure must be an array';
4215  }
4216  if (is_array($dataStructArray['sheets'])) {
4217  $singleSheet = false;
4218  if (!isset($dataStructArray['sheets'][$sheet])) {
4219  $sheet = 'sDEF';
4220  }
4221  $dataStruct = $dataStructArray['sheets'][$sheet];
4222  // If not an array, but still set, then regard it as a relative reference to a file:
4223  if ($dataStruct && !is_array($dataStruct)) {
4224  $file = self::getFileAbsFileName($dataStruct);
4225  if ($file && @is_file($file)) {
4226  $dataStruct = self::xml2array(self::getUrl($file));
4227  }
4228  }
4229  } else {
4230  $singleSheet = true;
4231  $dataStruct = $dataStructArray;
4232  if (isset($dataStruct['meta'])) {
4233  unset($dataStruct['meta']);
4234  }
4235  // Meta data should not appear there.
4236  // Default sheet
4237  $sheet = 'sDEF';
4238  }
4239  return array($dataStruct, $sheet, $singleSheet);
4240  }
4241 
4249  public static function resolveAllSheetsInDS(array $dataStructArray)
4250  {
4251  if (is_array($dataStructArray['sheets'])) {
4252  $out = array('sheets' => array());
4253  foreach ($dataStructArray['sheets'] as $sheetId => $sDat) {
4254  list($ds, $aS) = self::resolveSheetDefInDS($dataStructArray, $sheetId);
4255  if ($sheetId == $aS) {
4256  $out['sheets'][$aS] = $ds;
4257  }
4258  }
4259  } else {
4260  list($ds) = self::resolveSheetDefInDS($dataStructArray);
4261  $out = array('sheets' => array('sDEF' => $ds));
4262  }
4263  return $out;
4264  }
4265 
4278  public static function callUserFunction($funcName, &$params, &$ref, $checkPrefix = '', $errorMode = 0)
4279  {
4280  $content = false;
4281  // Check if we're using a closure and invoke it directly.
4282  if (is_object($funcName) && is_a($funcName, 'Closure')) {
4283  return call_user_func_array($funcName, array(&$params, &$ref));
4284  }
4285  // Check persistent object and if found, call directly and exit.
4286  if (isset($GLOBALS['T3_VAR']['callUserFunction'][$funcName]) && is_array($GLOBALS['T3_VAR']['callUserFunction'][$funcName])) {
4287  return call_user_func_array(array(
4288  &$GLOBALS['T3_VAR']['callUserFunction'][$funcName]['obj'],
4289  $GLOBALS['T3_VAR']['callUserFunction'][$funcName]['method']
4290  ), array(&$params, &$ref));
4291  }
4292  // Check file-reference prefix; if found, require_once() the file (should be library of code)
4293  if (strpos($funcName, ':') !== false) {
4294  list($file, $funcRef) = self::revExplode(':', $funcName, 2);
4295  $requireFile = self::getFileAbsFileName($file);
4296  if ($requireFile) {
4297  self::requireOnce($requireFile);
4298  }
4299  } else {
4300  $funcRef = $funcName;
4301  }
4302  // Check for persistent object token, "&"
4303  if ($funcRef[0] === '&') {
4304  $funcRef = substr($funcRef, 1);
4305  $storePersistentObject = true;
4306  } else {
4307  $storePersistentObject = false;
4308  }
4309  // Call function or method:
4310  $parts = explode('->', $funcRef);
4311  if (count($parts) === 2) {
4312  // Class
4313  // Check if class/method exists:
4314  if (class_exists($parts[0])) {
4315  // Get/Create object of class:
4316  if ($storePersistentObject) {
4317  // Get reference to current instance of class:
4318  if (!is_object($GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]])) {
4319  $GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]] = self::makeInstance($parts[0]);
4320  }
4321  $classObj = $GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]];
4322  } else {
4323  // Create new object:
4324  $classObj = self::makeInstance($parts[0]);
4325  }
4326  if (method_exists($classObj, $parts[1])) {
4327  // If persistent object should be created, set reference:
4328  if ($storePersistentObject) {
4329  $GLOBALS['T3_VAR']['callUserFunction'][$funcName] = array(
4330  'method' => $parts[1],
4331  'obj' => &$classObj
4332  );
4333  }
4334  // Call method:
4335  $content = call_user_func_array(array(&$classObj, $parts[1]), array(&$params, &$ref));
4336  } else {
4337  $errorMsg = 'No method name \'' . $parts[1] . '\' in class ' . $parts[0];
4338  if ($errorMode == 2) {
4339  throw new \InvalidArgumentException($errorMsg, 1294585865);
4340  } elseif (!$errorMode) {
4341  debug($errorMsg, \TYPO3\CMS\Core\Utility\GeneralUtility::class . '::callUserFunction');
4342  }
4343  }
4344  } else {
4345  $errorMsg = 'No class named ' . $parts[0];
4346  if ($errorMode == 2) {
4347  throw new \InvalidArgumentException($errorMsg, 1294585866);
4348  } elseif (!$errorMode) {
4349  debug($errorMsg, \TYPO3\CMS\Core\Utility\GeneralUtility::class . '::callUserFunction');
4350  }
4351  }
4352  } else {
4353  // Function
4354  if (function_exists($funcRef)) {
4355  $content = call_user_func_array($funcRef, array(&$params, &$ref));
4356  } else {
4357  $errorMsg = 'No function named: ' . $funcRef;
4358  if ($errorMode == 2) {
4359  throw new \InvalidArgumentException($errorMsg, 1294585867);
4360  } elseif (!$errorMode) {
4361  debug($errorMsg, \TYPO3\CMS\Core\Utility\GeneralUtility::class . '::callUserFunction');
4362  }
4363  }
4364  }
4365  return $content;
4366  }
4367 
4385  public static function getUserObj($classRef)
4386  {
4387  // Check persistent object and if found, call directly and exit.
4388  if (is_object($GLOBALS['T3_VAR']['getUserObj'][$classRef])) {
4389  return $GLOBALS['T3_VAR']['getUserObj'][$classRef];
4390  } else {
4391  // Check file-reference prefix; if found, require_once() the file (should be library of code)
4392  if (strpos($classRef, ':') !== false) {
4393  list($file, $class) = self::revExplode(':', $classRef, 2);
4394  $requireFile = self::getFileAbsFileName($file);
4395  if ($requireFile) {
4396  self::requireOnce($requireFile);
4397  }
4398  } else {
4399  $class = $classRef;
4400  }
4401  // Check for persistent object token, "&"
4402  if ($class[0] === '&') {
4403  self::deprecationLog(
4404  'The persistent functionality of getUserObj(), prepending the class name with & is deprecated since'
4405  . ' TYPO3 CMS 7 and will be removed in TYPO3 CMS 8. To allow this functionality, implement '
4406  . ' the \\TYPO3\\CMS\\Core\\SingletonInterface in the class "' . $classRef . '" instead.'
4407  );
4408  $class = substr($class, 1);
4409  $storePersistentObject = true;
4410  } else {
4411  $storePersistentObject = false;
4412  }
4413  // Check if class exists:
4414  if (class_exists($class)) {
4415  $classObj = self::makeInstance($class);
4416  // If persistent object should be created, set reference:
4417  if ($storePersistentObject) {
4418  $GLOBALS['T3_VAR']['getUserObj'][$classRef] = $classObj;
4419  }
4420  return $classObj;
4421  }
4422  }
4423  }
4424 
4445  public static function makeInstance($className)
4446  {
4447  if (!is_string($className) || empty($className)) {
4448  throw new \InvalidArgumentException('$className must be a non empty string.', 1288965219);
4449  }
4450  // Never instantiate with a beginning backslash, otherwise things like singletons won't work.
4451  if ($className[0] === '\\') {
4452  throw new \InvalidArgumentException(
4453  '$className "' . $className . '" must not start with a backslash.', 1420281366
4454  );
4455  }
4456  if (isset(static::$finalClassNameCache[$className])) {
4457  $finalClassName = static::$finalClassNameCache[$className];
4458  } else {
4459  $finalClassName = self::getClassName($className);
4460  static::$finalClassNameCache[$className] = $finalClassName;
4461  }
4462  // Return singleton instance if it is already registered
4463  if (isset(self::$singletonInstances[$finalClassName])) {
4464  return self::$singletonInstances[$finalClassName];
4465  }
4466  // Return instance if it has been injected by addInstance()
4467  if (
4468  isset(self::$nonSingletonInstances[$finalClassName])
4469  && !empty(self::$nonSingletonInstances[$finalClassName])
4470  ) {
4471  return array_shift(self::$nonSingletonInstances[$finalClassName]);
4472  }
4473  // Create new instance and call constructor with parameters
4474  $instance = static::instantiateClass($finalClassName, func_get_args());
4475  // Register new singleton instance
4476  if ($instance instanceof SingletonInterface) {
4477  self::$singletonInstances[$finalClassName] = $instance;
4478  }
4479  return $instance;
4480  }
4481 
4489  protected static function instantiateClass($className, $arguments)
4490  {
4491  switch (count($arguments)) {
4492  case 1:
4493  $instance = new $className();
4494  break;
4495  case 2:
4496  $instance = new $className($arguments[1]);
4497  break;
4498  case 3:
4499  $instance = new $className($arguments[1], $arguments[2]);
4500  break;
4501  case 4:
4502  $instance = new $className($arguments[1], $arguments[2], $arguments[3]);
4503  break;
4504  case 5:
4505  $instance = new $className($arguments[1], $arguments[2], $arguments[3], $arguments[4]);
4506  break;
4507  case 6:
4508  $instance = new $className($arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5]);
4509  break;
4510  case 7:
4511  $instance = new $className($arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6]);
4512  break;
4513  case 8:
4514  $instance = new $className($arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6], $arguments[7]);
4515  break;
4516  case 9:
4517  $instance = new $className($arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6], $arguments[7], $arguments[8]);
4518  break;
4519  default:
4520  // The default case for classes with constructors that have more than 8 arguments.
4521  // This will fail when one of the arguments shall be passed by reference.
4522  // In case we really need to support this edge case, we can implement the solution from here: https://review.typo3.org/26344
4523  $class = new \ReflectionClass($className);
4524  array_shift($arguments);
4525  $instance = $class->newInstanceArgs($arguments);
4526  }
4527  return $instance;
4528  }
4529 
4537  protected static function getClassName($className)
4538  {
4539  if (class_exists($className)) {
4540  while (static::classHasImplementation($className)) {
4541  $className = static::getImplementationForClass($className);
4542  }
4543  }
4545  }
4546 
4553  protected static function getImplementationForClass($className)
4554  {
4555  return $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][$className]['className'];
4556  }
4557 
4564  protected static function classHasImplementation($className)
4565  {
4566  // If we are early in the bootstrap, the configuration might not yet be present
4567  if (!isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'])) {
4568  return false;
4569  }
4570 
4571  return isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][$className])
4572  && is_array($GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][$className])
4573  && !empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][$className]['className']);
4574  }
4575 
4594  public static function setSingletonInstance($className, SingletonInterface $instance)
4595  {
4596  self::checkInstanceClassName($className, $instance);
4597  self::$singletonInstances[$className] = $instance;
4598  }
4599 
4616  public static function removeSingletonInstance($className, SingletonInterface $instance)
4617  {
4618  self::checkInstanceClassName($className, $instance);
4619  if (!isset(self::$singletonInstances[$className])) {
4620  throw new \InvalidArgumentException('No Instance registered for ' . $className . '.', 1394099179);
4621  }
4622  if ($instance !== self::$singletonInstances[$className]) {
4623  throw new \InvalidArgumentException('The instance you are trying to remove has not been registered before.', 1394099256);
4624  }
4625  unset(self::$singletonInstances[$className]);
4626  }
4627 
4642  public static function resetSingletonInstances(array $newSingletonInstances)
4643  {
4644  static::$singletonInstances = array();
4645  foreach ($newSingletonInstances as $className => $instance) {
4646  static::setSingletonInstance($className, $instance);
4647  }
4648  }
4649 
4662  public static function getSingletonInstances()
4663  {
4664  return static::$singletonInstances;
4665  }
4666 
4682  public static function addInstance($className, $instance)
4683  {
4684  self::checkInstanceClassName($className, $instance);
4685  if ($instance instanceof SingletonInterface) {
4686  throw new \InvalidArgumentException('$instance must not be an instance of TYPO3\\CMS\\Core\\SingletonInterface. ' . 'For setting singletons, please use setSingletonInstance.', 1288969325);
4687  }
4688  if (!isset(self::$nonSingletonInstances[$className])) {
4689  self::$nonSingletonInstances[$className] = array();
4690  }
4691  self::$nonSingletonInstances[$className][] = $instance;
4692  }
4693 
4703  protected static function checkInstanceClassName($className, $instance)
4704  {
4705  if ($className === '') {
4706  throw new \InvalidArgumentException('$className must not be empty.', 1288967479);
4707  }
4708  if (!$instance instanceof $className) {
4709  throw new \InvalidArgumentException('$instance must be an instance of ' . $className . ', but actually is an instance of ' . get_class($instance) . '.', 1288967686);
4710  }
4711  }
4712 
4724  public static function purgeInstances()
4725  {
4726  self::$singletonInstances = array();
4727  self::$nonSingletonInstances = array();
4728  }
4729 
4738  public static function flushInternalRuntimeCaches()
4739  {
4740  self::$indpEnvCache = [];
4741  self::$idnaStringCache = [];
4742  }
4743 
4753  public static function makeInstanceService($serviceType, $serviceSubType = '', $excludeServiceKeys = array())
4754  {
4755  $error = false;
4756  if (!is_array($excludeServiceKeys)) {
4757  $excludeServiceKeys = self::trimExplode(',', $excludeServiceKeys, true);
4758  }
4759  $requestInfo = array(
4760  'requestedServiceType' => $serviceType,
4761  'requestedServiceSubType' => $serviceSubType,
4762  'requestedExcludeServiceKeys' => $excludeServiceKeys
4763  );
4764  while ($info = ExtensionManagementUtility::findService($serviceType, $serviceSubType, $excludeServiceKeys)) {
4765  // provide information about requested service to service object
4766  $info = array_merge($info, $requestInfo);
4767  // Check persistent object and if found, call directly and exit.
4768  if (is_object($GLOBALS['T3_VAR']['makeInstanceService'][$info['className']])) {
4769  // update request info in persistent object
4770  $GLOBALS['T3_VAR']['makeInstanceService'][$info['className']]->info = $info;
4771  // reset service and return object
4772  $GLOBALS['T3_VAR']['makeInstanceService'][$info['className']]->reset();
4773  return $GLOBALS['T3_VAR']['makeInstanceService'][$info['className']];
4774  } else {
4775  $obj = self::makeInstance($info['className']);
4776  if (is_object($obj)) {
4777  if (!@is_callable(array($obj, 'init'))) {
4778  // use silent logging??? I don't think so.
4779  die('Broken service:' . DebugUtility::viewArray($info));
4780  }
4781  $obj->info = $info;
4782  // service available?
4783  if ($obj->init()) {
4784  // create persistent object
4785  $GLOBALS['T3_VAR']['makeInstanceService'][$info['className']] = $obj;
4786  return $obj;
4787  }
4788  $error = $obj->getLastErrorArray();
4789  unset($obj);
4790  }
4791  }
4792  // deactivate the service
4793  ExtensionManagementUtility::deactivateService($info['serviceType'], $info['serviceKey']);
4794  }
4795  return $error;
4796  }
4797 
4805  public static function requireOnce($requireFile)
4806  {
4807  // Needed for require_once
4808  global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS;
4809  require_once $requireFile;
4810  }
4811 
4820  public static function requireFile($requireFile)
4821  {
4822  // Needed for require
4823  global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS;
4824  require $requireFile;
4825  }
4826 
4836  public static function quoted_printable($string, $maxlen = 76)
4837  {
4838  static::logDeprecatedFunction();
4839  // Make sure the string contains only Unix line breaks
4840  // Replace Windows breaks (\r\n)
4841  $string = str_replace(CRLF, LF, $string);
4842  // Replace Mac breaks (\r)
4843  $string = str_replace(CR, LF, $string);
4844  // Default line break for Unix systems.
4845  $linebreak = LF;
4846  if (TYPO3_OS == 'WIN') {
4847  // 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.
4848  $linebreak = CRLF;
4849  }
4850  $newString = '';
4851  // Split lines
4852  $theLines = explode(LF, $string);
4853  foreach ($theLines as $val) {
4854  $newVal = '';
4855  $theValLen = strlen($val);
4856  $len = 0;
4857  // Walk through each character of this line
4858  for ($index = 0; $index < $theValLen; $index++) {
4859  $char = substr($val, $index, 1);
4860  $ordVal = ord($char);
4861  if ($len > $maxlen - 4 || $len > $maxlen - 14 && $ordVal == 32) {
4862  // Add a line break
4863  $newVal .= '=' . $linebreak;
4864  // Reset the length counter
4865  $len = 0;
4866  }
4867  if ($ordVal >= 33 && $ordVal <= 60 || $ordVal >= 62 && $ordVal <= 126 || $ordVal == 9 || $ordVal == 32) {
4868  // This character is ok, add it to the message
4869  $newVal .= $char;
4870  $len++;
4871  } else {
4872  // Special character, needs to be encoded
4873  $newVal .= sprintf('=%02X', $ordVal);
4874  $len += 3;
4875  }
4876  }
4877  // Replaces a possible SPACE-character at the end of a line
4878  $newVal = preg_replace('/' . chr(32) . '$/', '=20', $newVal);
4879  // Replaces a possible TAB-character at the end of a line
4880  $newVal = preg_replace('/' . TAB . '$/', '=09', $newVal);
4881  $newString .= $newVal . $linebreak;
4882  }
4883  // Remove last newline
4884  return preg_replace('/' . $linebreak . '$/', '', $newString);
4885  }
4886 
4897  public static function encodeHeader($line, $enc = 'quoted-printable', $charset = 'utf-8')
4898  {
4899  static::logDeprecatedFunction();
4900  // Avoid problems if "###" is found in $line (would conflict with the placeholder which is used below)
4901  if (strpos($line, '###') !== false) {
4902  return $line;
4903  }
4904  // Check if any non-ASCII characters are found - otherwise encoding is not needed
4905  if (!preg_match(('/[^' . chr(32) . '-' . chr(127) . ']/'), $line)) {
4906  return $line;
4907  }
4908  // Wrap email addresses in a special marker
4909  $line = preg_replace('/([^ ]+@[^ ]+)/', '###$1###', $line);
4910  $matches = preg_split('/(.?###.+###.?|\\(|\\))/', $line, -1, PREG_SPLIT_NO_EMPTY);
4911  foreach ($matches as $part) {
4912  $oldPart = $part;
4913  $partWasQuoted = $part[0] == '"';
4914  $part = trim($part, '"');
4915  switch ((string)$enc) {
4916  case 'base64':
4917  $part = '=?' . $charset . '?B?' . base64_encode($part) . '?=';
4918  break;
4919  case 'quoted-printable':
4920 
4921  default:
4922  $qpValue = self::quoted_printable($part, 1000);
4923  if ($part != $qpValue) {
4924  // Encoded words in the header should not contain non-encoded:
4925  // * spaces. "_" is a shortcut for "=20". See RFC 2047 for details.
4926  // * question mark. See RFC 1342 (http://tools.ietf.org/html/rfc1342)
4927  $search = array(' ', '?');
4928  $replace = array('_', '=3F');
4929  $qpValue = str_replace($search, $replace, $qpValue);
4930  $part = '=?' . $charset . '?Q?' . $qpValue . '?=';
4931  }
4932  }
4933  if ($partWasQuoted) {
4934  $part = '"' . $part . '"';
4935  }
4936  $line = str_replace($oldPart, $part, $line);
4937  }
4938  // Remove the wrappers
4939  $line = preg_replace('/###(.+?)###/', '$1', $line);
4940  return $line;
4941  }
4942 
4954  public static function substUrlsInPlainText($message, $urlmode = '76', $index_script_url = '')
4955  {
4956  static::logDeprecatedFunction();
4957  switch ((string)$urlmode) {
4958  case '':
4959  $lengthLimit = false;
4960  break;
4961  case 'all':
4962  $lengthLimit = 0;
4963  break;
4964  case '76':
4965 
4966  default:
4967  $lengthLimit = (int)$urlmode;
4968  }
4969  if ($lengthLimit === false) {
4970  // No processing
4971  $messageSubstituted = $message;
4972  } else {
4973  $messageSubstituted = preg_replace_callback(
4974  '/(http|https):\\/\\/.+(?=[\\]\\.\\?]*([\\! \'"()<>]+|$))/iU',
4975  function (array $matches) use ($lengthLimit, $index_script_url) {
4976  return GeneralUtility::makeRedirectUrl($matches[0], $lengthLimit, $index_script_url);
4977  },
4978  $message
4979  );
4980  }
4981  return $messageSubstituted;
4982  }
4983 
4992  public static function makeRedirectUrl($inUrl, $l = 0, $index_script_url = '')
4993  {
4994  if (strlen($inUrl) > $l) {
4995  $md5 = substr(md5($inUrl), 0, 20);
4996  $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('*', 'cache_md5params', 'md5hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($md5, 'cache_md5params'));
4997  if (!$count) {
4998  $insertFields = array(
4999  'md5hash' => $md5,
5000  'tstamp' => $GLOBALS['EXEC_TIME'],
5001  'type' => 2,
5002  'params' => $inUrl
5003  );
5004  $GLOBALS['TYPO3_DB']->exec_INSERTquery('cache_md5params', $insertFields);
5005  }
5006  $inUrl = ($index_script_url ? $index_script_url : self::getIndpEnv('TYPO3_REQUEST_DIR') . 'index.php') . '?RDCT=' . $md5;
5007  }
5008  return $inUrl;
5009  }
5010 
5018  public static function freetypeDpiComp($fontSize)
5019  {
5020  // FreeType 2 always has 96 dpi.
5021  $dpi = 96.0;
5022  return $fontSize / $dpi * 72;
5023  }
5024 
5031  public static function initSysLog()
5032  {
5033  // For CLI logging name is <fqdn-hostname>:<TYPO3-path>
5034  // Note that TYPO3_REQUESTTYPE is not used here as it may not yet be defined
5035  if (defined('TYPO3_cliMode') && TYPO3_cliMode) {
5036  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'] = self::getHostname(($requestHost = false)) . ':' . PATH_site;
5037  } else {
5038  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'] = self::getIndpEnv('TYPO3_SITE_URL');
5039  }
5040  // Init custom logging
5041  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'])) {
5042  $params = array('initLog' => true);
5043  $fakeThis = false;
5044  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'] as $hookMethod) {
5045  self::callUserFunction($hookMethod, $params, $fakeThis);
5046  }
5047  }
5048  // Init TYPO3 logging
5049  foreach (explode(';', $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLog'], 2) as $log) {
5050  list($type, $destination) = explode(',', $log, 3);
5051  if ($type == 'syslog') {
5052  if (TYPO3_OS == 'WIN') {
5053  $facility = LOG_USER;
5054  } else {
5055  $facility = constant('LOG_' . strtoupper($destination));
5056  }
5057  openlog($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'], LOG_ODELAY, $facility);
5058  }
5059  }
5060  $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLogLevel'] = MathUtility::forceIntegerInRange($GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLogLevel'], 0, 4);
5061  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogInit'] = true;
5062  }
5063 
5075  public static function sysLog($msg, $extKey, $severity = 0)
5076  {
5077  $severity = MathUtility::forceIntegerInRange($severity, 0, 4);
5078  // Is message worth logging?
5079  if ((int)$GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLogLevel'] > $severity) {
5080  return;
5081  }
5082  // Initialize logging
5083  if (!$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogInit']) {
5084  self::initSysLog();
5085  }
5086  // Do custom logging
5087  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'])) {
5088  $params = array('msg' => $msg, 'extKey' => $extKey, 'backTrace' => debug_backtrace(), 'severity' => $severity);
5089  $fakeThis = false;
5090  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'] as $hookMethod) {
5091  self::callUserFunction($hookMethod, $params, $fakeThis);
5092  }
5093  }
5094  // TYPO3 logging enabled?
5095  if (!$GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLog']) {
5096  return;
5097  }
5098  $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'];
5099  $timeFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
5100  // Use all configured logging options
5101  foreach (explode(';', $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLog'], 2) as $log) {
5102  list($type, $destination, $level) = explode(',', $log, 4);
5103  // Is message worth logging for this log type?
5104  if ((int)$level > $severity) {
5105  continue;
5106  }
5107  $msgLine = ' - ' . $extKey . ': ' . $msg;
5108  // Write message to a file
5109  if ($type == 'file') {
5110  $file = fopen($destination, 'a');
5111  if ($file) {
5112  fwrite($file, date(($dateFormat . ' ' . $timeFormat)) . $msgLine . LF);
5113  fclose($file);
5114  self::fixPermissions($destination);
5115  }
5116  } elseif ($type == 'mail') {
5117  list($to, $from) = explode('/', $destination);
5118  if (!self::validEmail($from)) {
5119  $from = MailUtility::getSystemFrom();
5120  }
5122  $mail = self::makeInstance(\TYPO3\CMS\Core\Mail\MailMessage::class);
5123  $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);
5124  $mail->send();
5125  } elseif ($type == 'error_log') {
5126  error_log($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'] . $msgLine, 0);
5127  } elseif ($type == 'syslog') {
5128  $priority = array(LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERR, LOG_CRIT);
5129  syslog($priority[(int)$severity], $msgLine);
5130  }
5131  }
5132  }
5133 
5148  public static function devLog($msg, $extKey, $severity = 0, $dataVar = false)
5149  {
5150  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['devLog'])) {
5151  $params = array('msg' => $msg, 'extKey' => $extKey, 'severity' => $severity, 'dataVar' => $dataVar);
5152  $fakeThis = false;
5153  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['devLog'] as $hookMethod) {
5154  self::callUserFunction($hookMethod, $params, $fakeThis);
5155  }
5156  }
5157  }
5158 
5165  public static function deprecationLog($msg)
5166  {
5167  if (!$GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog']) {
5168  return;
5169  }
5170  // Legacy values (no strict comparison, $log can be boolean, string or int)
5171  $log = $GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog'];
5172  if ($log === true || $log == '1') {
5173  $log = array('file');
5174  } else {
5175  $log = self::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog'], true);
5176  }
5177  $date = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] . ': ');
5178  if (in_array('file', $log) !== false) {
5179  // Write a longer message to the deprecation log
5180  $destination = static::getDeprecationLogFileName();
5181  $file = @fopen($destination, 'a');
5182  if ($file) {
5183  @fwrite($file, ($date . $msg . LF));
5184  @fclose($file);
5185  self::fixPermissions($destination);
5186  }
5187  }
5188  if (in_array('devlog', $log) !== false) {
5189  // Copy message also to the developer log
5190  self::devLog($msg, 'Core', self::SYSLOG_SEVERITY_WARNING);
5191  }
5192  // Do not use console in login screen
5193  if (in_array('console', $log) !== false && isset($GLOBALS['BE_USER']->user['uid'])) {
5194  DebugUtility::debug($msg, $date, 'Deprecation Log');
5195  }
5196  }
5197 
5203  public static function getDeprecationLogFileName()
5204  {
5205  return PATH_typo3conf . 'deprecation_' . self::shortMD5((PATH_site . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) . '.log';
5206  }
5207 
5214  public static function logDeprecatedFunction()
5215  {
5216  if (!$GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog']) {
5217  return;
5218  }
5219  $trail = debug_backtrace();
5220  if ($trail[1]['type']) {
5221  $function = new \ReflectionMethod($trail[1]['class'], $trail[1]['function']);
5222  } else {
5223  $function = new \ReflectionFunction($trail[1]['function']);
5224  }
5225  $msg = '';
5226  if (preg_match('/@deprecated\\s+(.*)/', $function->getDocComment(), $match)) {
5227  $msg = $match[1];
5228  }
5229  // Write a longer message to the deprecation log: <function> <annotion> - <trace> (<source>)
5230  $logMsg = $trail[1]['class'] . $trail[1]['type'] . $trail[1]['function'];
5231  $logMsg .= '() - ' . $msg . ' - ' . DebugUtility::debugTrail();
5232  $logMsg .= ' (' . PathUtility::stripPathSitePrefix($function->getFileName()) . '#' . $function->getStartLine() . ')';
5233  self::deprecationLog($logMsg);
5234  }
5235 
5245  public static function arrayToLogString(array $arr, $valueList = array(), $valueLength = 20)
5246  {
5247  $str = '';
5248  if (!is_array($valueList)) {
5249  $valueList = self::trimExplode(',', $valueList, true);
5250  }
5251  $valListCnt = count($valueList);
5252  foreach ($arr as $key => $value) {
5253  if (!$valListCnt || in_array($key, $valueList)) {
5254  $str .= (string)$key . trim(': ' . self::fixed_lgd_cs(str_replace(LF, '|', (string)$value), $valueLength)) . '; ';
5255  }
5256  }
5257  return $str;
5258  }
5259 
5268  public static function imageMagickCommand($command, $parameters, $path = '')
5269  {
5270  return CommandUtility::imageMagickCommand($command, $parameters, $path);
5271  }
5272 
5282  public static function unQuoteFilenames($parameters, $unQuote = false)
5283  {
5284  $paramsArr = explode(' ', trim($parameters));
5285  // 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.
5286  $quoteActive = -1;
5287  foreach ($paramsArr as $k => $v) {
5288  if ($quoteActive > -1) {
5289  $paramsArr[$quoteActive] .= ' ' . $v;
5290  unset($paramsArr[$k]);
5291  if (substr($v, -1) === $paramsArr[$quoteActive][0]) {
5292  $quoteActive = -1;
5293  }
5294  } elseif (!trim($v)) {
5295  // Remove empty elements
5296  unset($paramsArr[$k]);
5297  } elseif (preg_match('/^(["\'])/', $v) && substr($v, -1) !== $v[0]) {
5298  $quoteActive = $k;
5299  }
5300  }
5301  if ($unQuote) {
5302  foreach ($paramsArr as $key => &$val) {
5303  $val = preg_replace('/(?:^"|"$)/', '', $val);
5304  $val = preg_replace('/(?:^\'|\'$)/', '', $val);
5305  }
5306  unset($val);
5307  }
5308  // Return reindexed array
5309  return array_values($paramsArr);
5310  }
5311 
5318  public static function quoteJSvalue($value)
5319  {
5320  return strtr(
5321  json_encode((string)$value, JSON_HEX_AMP|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_TAG),
5322  array(
5323  '"' => '\'',
5324  '\\\\' => '\\u005C',
5325  ' ' => '\\u0020',
5326  '!' => '\\u0021',
5327  '\\t' => '\\u0009',
5328  '\\n' => '\\u000A',
5329  '\\r' => '\\u000D'
5330  )
5331  );
5332  }
5333 
5340  public static function cleanOutputBuffers()
5341  {
5342  self::logDeprecatedFunction();
5343  while (ob_end_clean()) {
5344  }
5345  header('Content-Encoding: None', true);
5346  }
5347 
5353  public static function flushOutputBuffers()
5354  {
5355  $obContent = '';
5356  while ($content = ob_get_clean()) {
5357  $obContent .= $content;
5358  }
5359  // If previously a "Content-Encoding: whatever" has been set, we have to unset it
5360  if (!headers_sent()) {
5361  $headersList = headers_list();
5362  foreach ($headersList as $header) {
5363  // Split it up at the :
5364  list($key, $value) = self::trimExplode(':', $header, true);
5365  // Check if we have a Content-Encoding other than 'None'
5366  if (strtolower($key) === 'content-encoding' && strtolower($value) !== 'none') {
5367  header('Content-Encoding: None');
5368  break;
5369  }
5370  }
5371  }
5372  echo $obContent;
5373  }
5374 
5386  {
5387  if (is_null(static::$applicationContext)) {
5388  static::$applicationContext = $applicationContext;
5389  } else {
5390  throw new \RuntimeException('Trying to override applicationContext which has already been defined!', 1376084316);
5391  }
5392  }
5393 
5399  public static function getApplicationContext()
5400  {
5401  return static::$applicationContext;
5402  }
5403 
5408  public static function isRunningOnCgiServerApi()
5409  {
5410  return in_array(PHP_SAPI, self::$supportedCgiServerApis, true);
5411  }
5412 }