TYPO3 CMS  TYPO3_8-7
BasicFileUtility.php
Go to the documentation of this file.
1 <?php
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 
20 
32 {
36  const UNSAFE_FILENAME_CHARACTER_EXPRESSION = '\\x00-\\x2C\\/\\x3A-\\x3F\\x5B-\\x60\\x7B-\\xBF';
37 
43  public $maxNumber = 99;
44 
50  public $uniquePrecision = 6;
51 
56  protected $fileExtensionPermissions = [];
57 
58  /**********************************
59  *
60  * Checking functions
61  *
62  **********************************/
63 
68  public function __construct()
69  {
70  $this->fileExtensionPermissions['allow'] = GeneralUtility::uniqueList(strtolower($GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']['webspace']['allow']));
71  $this->fileExtensionPermissions['deny'] = GeneralUtility::uniqueList(strtolower($GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']['webspace']['deny']));
72  }
73 
80  public function setFileExtensionPermissions($allowedFilePermissions, $deniedFilePermissions)
81  {
82  $this->fileExtensionPermissions['allow'] = GeneralUtility::uniqueList(strtolower($allowedFilePermissions));
83  $this->fileExtensionPermissions['deny'] = GeneralUtility::uniqueList(strtolower($deniedFilePermissions));
84  }
85 
92  protected function is_allowed($fileExtension)
93  {
94  $fileExtension = strtolower($fileExtension);
95  if ($fileExtension) {
96  // If the extension is found amongst the allowed types, we return TRUE immediately
97  if ($this->fileExtensionPermissions['allow'] === '*' || GeneralUtility::inList($this->fileExtensionPermissions['allow'], $fileExtension)) {
98  return true;
99  }
100  // If the extension is found amongst the denied types, we return FALSE immediately
101  if ($this->fileExtensionPermissions['deny'] === '*' || GeneralUtility::inList($this->fileExtensionPermissions['deny'], $fileExtension)) {
102  return false;
103  }
104  } else {
105  // If no extension
106  if ($this->fileExtensionPermissions['allow'] === '*') {
107  return true;
108  }
109  if ($this->fileExtensionPermissions['deny'] === '*') {
110  return false;
111  }
112  }
113  // If no match we return TRUE
114  return true;
115  }
116 
128  public function checkIfAllowed($ext, $_, $filename = '')
129  {
130  return GeneralUtility::verifyFilenameAgainstDenyPattern($filename) && $this->is_allowed($ext);
131  }
132 
139  protected function is_directory($theDir)
140  {
141  // @todo: should go into the LocalDriver in a protected way (not important to the outside world)
142  if (GeneralUtility::validPathStr($theDir)) {
143  $theDir = PathUtility::getCanonicalPath($theDir);
144  if (@is_dir($theDir)) {
145  return $theDir;
146  }
147  }
148  return false;
149  }
150 
164  public function getUniqueName($theFile, $theDest, $dontCheckForUnique = false)
165  {
166  // @todo: should go into the LocalDriver in a protected way (not important to the outside world)
167  $theDest = $this->is_directory($theDest);
168  // $theDest is cleaned up
169  $origFileInfo = GeneralUtility::split_fileref($theFile);
170  // Fetches info about path, name, extension of $theFile
171  if ($theDest) {
172  // Check if the file exists and if not - return the filename...
173  $fileInfo = $origFileInfo;
174  $theDestFile = $theDest . '/' . $fileInfo['file'];
175  // The destinations file
176  if (!file_exists($theDestFile) || $dontCheckForUnique) {
177  // If the file does NOT exist we return this filename
178  return $theDestFile;
179  }
180  // Well the filename in its pure form existed. Now we try to append numbers / unique-strings and see if we can find an available filename...
181  $theTempFileBody = preg_replace('/_[0-9][0-9]$/', '', $origFileInfo['filebody']);
182  // This removes _xx if appended to the file
183  $theOrigExt = $origFileInfo['realFileext'] ? '.' . $origFileInfo['realFileext'] : '';
184  for ($a = 1; $a <= $this->maxNumber + 1; $a++) {
185  if ($a <= $this->maxNumber) {
186  // First we try to append numbers
187  $insert = '_' . sprintf('%02d', $a);
188  } else {
189  // .. then we try unique-strings...
190  $insert = '_' . substr(md5(uniqid('', true)), 0, $this->uniquePrecision);
191  }
192  $theTestFile = $theTempFileBody . $insert . $theOrigExt;
193  $theDestFile = $theDest . '/' . $theTestFile;
194  // The destinations file
195  if (!file_exists($theDestFile)) {
196  // If the file does NOT exist we return this filename
197  return $theDestFile;
198  }
199  }
200  }
201  }
202 
203  /*********************
204  *
205  * Cleaning functions
206  *
207  *********************/
208 
218  public function cleanFileName($fileName)
219  {
220  // Handle UTF-8 characters
221  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
222  // allow ".", "-", 0-9, a-z, A-Z and everything beyond U+C0 (latin capital letter a with grave)
223  $cleanFileName = preg_replace('/[' . self::UNSAFE_FILENAME_CHARACTER_EXPRESSION . ']/u', '_', trim($fileName));
224  } else {
225  $fileName = GeneralUtility::makeInstance(CharsetConverter::class)->specCharsToASCII('utf-8', $fileName);
226  // Replace unwanted characters by underscores
227  $cleanFileName = preg_replace('/[' . self::UNSAFE_FILENAME_CHARACTER_EXPRESSION . '\\xC0-\\xFF]/', '_', trim($fileName));
228  }
229  // Strip trailing dots and return
230  return rtrim($cleanFileName, '.');
231  }
232 }
setFileExtensionPermissions($allowedFilePermissions, $deniedFilePermissions)
static verifyFilenameAgainstDenyPattern($filename)
static makeInstance($className,... $constructorArguments)
static split_fileref($fileNameWithPath)
getUniqueName($theFile, $theDest, $dontCheckForUnique=false)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static uniqueList($in_list, $secondParameter=null)