TYPO3  7.6
BasicFileUtility.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\Utility\File;
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 
19 
33 {
37  const UNSAFE_FILENAME_CHARACTER_EXPRESSION = '\\x00-\\x2C\\/\\x3A-\\x3F\\x5B-\\x60\\x7B-\\xBF';
38 
44  public $getUniqueNamePrefix = '';
45 
51  public $maxNumber = 99;
52 
58  public $uniquePrecision = 6;
59 
65  public $maxInputNameLen = 60;
66 
72  public $tempFN = '_temp_';
73 
77  public $f_ext = array();
78 
84  public $mounts = array();
85 
91  public $webPath = '';
92 
98  public $isInit = 0;
99 
103  public $csConvObj;
104 
105  /**********************************
106  *
107  * Checking functions
108  *
109  **********************************/
139  public function init($mounts, $f_ext)
140  {
141  $this->f_ext['webspace']['allow'] = GeneralUtility::uniqueList(strtolower($f_ext['webspace']['allow']));
142  $this->f_ext['webspace']['deny'] = GeneralUtility::uniqueList(strtolower($f_ext['webspace']['deny']));
143  $this->f_ext['ftpspace']['allow'] = GeneralUtility::uniqueList(strtolower($f_ext['ftpspace']['allow']));
144  $this->f_ext['ftpspace']['deny'] = GeneralUtility::uniqueList(strtolower($f_ext['ftpspace']['deny']));
145 
146  $this->mounts = (!empty($mounts) ? $mounts : array());
147  $this->webPath = GeneralUtility::getIndpEnv('TYPO3_DOCUMENT_ROOT');
148  $this->isInit = 1;
149  $this->maxInputNameLen = $GLOBALS['TYPO3_CONF_VARS']['SYS']['maxFileNameLength'] ?: $this->maxInputNameLen;
150  }
151 
161  public function is_allowed($iconkey, $type)
162  {
163  if (isset($this->f_ext[$type])) {
164  $ik = strtolower($iconkey);
165  if ($ik) {
166  // If the extension is found amongst the allowed types, we return TRUE immediately
167  if ($this->f_ext[$type]['allow'] == '*' || GeneralUtility::inList($this->f_ext[$type]['allow'], $ik)) {
168  return true;
169  }
170  // If the extension is found amongst the denied types, we return FALSE immediately
171  if ($this->f_ext[$type]['deny'] == '*' || GeneralUtility::inList($this->f_ext[$type]['deny'], $ik)) {
172  return false;
173  }
174  // If no match we return TRUE
175  return true;
176  } else {
177  // If no extension:
178  if ($this->f_ext[$type]['allow'] == '*') {
179  return true;
180  }
181  if ($this->f_ext[$type]['deny'] == '*') {
182  return false;
183  }
184  return true;
185  }
186  }
187  return false;
188  }
189 
198  public function checkIfFullAccess($theDest)
199  {
200  $type = $this->is_webpath($theDest) ? 'webspace' : 'ftpspace';
201  if (isset($this->f_ext[$type])) {
202  if ((string)$this->f_ext[$type]['deny'] == '' || $this->f_ext[$type]['allow'] == '*') {
203  return true;
204  }
205  }
206  }
207 
217  public function is_webpath($path)
218  {
219  if ($this->isInit) {
220  $testPath = $this->slashPath($path);
221  $testPathWeb = $this->slashPath($this->webPath);
222  if ($testPathWeb && $testPath) {
223  return GeneralUtility::isFirstPartOfStr($testPath, $testPathWeb);
224  }
225  }
226  return true;
227  }
228 
240  public function checkIfAllowed($ext, $theDest, $filename = '')
241  {
242  return GeneralUtility::verifyFilenameAgainstDenyPattern($filename) && $this->is_allowed($ext, ($this->is_webpath($theDest) ? 'webspace' : 'ftpspace'));
243  }
244 
253  public function is_directory($theDir)
254  {
255  // @todo: should go into the LocalDriver in a protected way (not important to the outside world)
256  if (GeneralUtility::validPathStr($theDir)) {
257  $theDir = PathUtility::getCanonicalPath($theDir);
258  if (@is_dir($theDir)) {
259  return $theDir;
260  }
261  }
262  return false;
263  }
264 
278  public function getUniqueName($theFile, $theDest, $dontCheckForUnique = 0)
279  {
280  // @todo: should go into the LocalDriver in a protected way (not important to the outside world)
281  $theDest = $this->is_directory($theDest);
282  // $theDest is cleaned up
283  $origFileInfo = GeneralUtility::split_fileref($theFile);
284  // Fetches info about path, name, extension of $theFile
285  if ($theDest) {
286  if ($this->getUniqueNamePrefix) {
287  // Adds prefix
288  $origFileInfo['file'] = $this->getUniqueNamePrefix . $origFileInfo['file'];
289  $origFileInfo['filebody'] = $this->getUniqueNamePrefix . $origFileInfo['filebody'];
290  }
291  // Check if the file exists and if not - return the filename...
292  $fileInfo = $origFileInfo;
293  $theDestFile = $theDest . '/' . $fileInfo['file'];
294  // The destinations file
295  if (!file_exists($theDestFile) || $dontCheckForUnique) {
296  // If the file does NOT exist we return this filename
297  return $theDestFile;
298  }
299  // 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...
300  $theTempFileBody = preg_replace('/_[0-9][0-9]$/', '', $origFileInfo['filebody']);
301  // This removes _xx if appended to the file
302  $theOrigExt = $origFileInfo['realFileext'] ? '.' . $origFileInfo['realFileext'] : '';
303  for ($a = 1; $a <= $this->maxNumber + 1; $a++) {
304  if ($a <= $this->maxNumber) {
305  // First we try to append numbers
306  $insert = '_' . sprintf('%02d', $a);
307  } else {
308  // .. then we try unique-strings...
309  $insert = '_' . substr(md5(uniqid('', true)), 0, $this->uniquePrecision);
310  }
311  $theTestFile = $theTempFileBody . $insert . $theOrigExt;
312  $theDestFile = $theDest . '/' . $theTestFile;
313  // The destinations file
314  if (!file_exists($theDestFile)) {
315  // If the file does NOT exist we return this filename
316  return $theDestFile;
317  }
318  }
319  }
320  }
321 
332  public function checkPathAgainstMounts($thePath)
333  {
334  if ($thePath && GeneralUtility::validPathStr($thePath) && is_array($this->mounts)) {
335  foreach ($this->mounts as $k => $val) {
336  if (GeneralUtility::isFirstPartOfStr($thePath, $val['path'])) {
337  return $k;
338  }
339  }
340  }
341  }
342 
350  public function findFirstWebFolder()
351  {
352  // @todo: where and when to use this function?
353  if (is_array($this->mounts)) {
354  foreach ($this->mounts as $k => $val) {
355  if (GeneralUtility::isFirstPartOfStr($val['path'], PATH_site . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'])) {
356  return $k;
357  }
358  }
359  }
360  }
361 
362  /*********************
363  *
364  * Cleaning functions
365  *
366  *********************/
375  public function slashPath($path)
376  {
377  // @todo: should go into the LocalDriver in a protected way (not important to the outside world)
378  // @todo: should be done with rtrim($path, '/') . '/';
379  if (substr($path, -1) != '/') {
380  return $path . '/';
381  }
382  return $path;
383  }
384 
395  public function cleanFileName($fileName, $charset = '')
396  {
397  // Handle UTF-8 characters
398  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
399  // allow ".", "-", 0-9, a-z, A-Z and everything beyond U+C0 (latin capital letter a with grave)
400  $cleanFileName = preg_replace('/[' . self::UNSAFE_FILENAME_CHARACTER_EXPRESSION . ']/u', '_', trim($fileName));
401  } else {
402  // Get conversion object or initialize if needed
403  if (!is_object($this->csConvObj)) {
404  if (TYPO3_MODE == 'FE') {
405  $this->csConvObj = $GLOBALS['TSFE']->csConvObj;
406  } elseif (is_object($GLOBALS['LANG'])) {
407  // BE assumed:
408  $this->csConvObj = $GLOBALS['LANG']->csConvObj;
409  } else {
410  // The object may not exist yet, so we need to create it now. Happens in the Install Tool for example.
411  $this->csConvObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Charset\CharsetConverter::class);
412  }
413  }
414  // Define character set
415  if (!$charset) {
416  if (TYPO3_MODE == 'FE') {
417  $charset = $GLOBALS['TSFE']->renderCharset;
418  } else {
419  // Backend
420  $charset = 'utf-8';
421  }
422  }
423  // If a charset was found, convert filename
424  if ($charset) {
425  $fileName = $this->csConvObj->specCharsToASCII($charset, $fileName);
426  }
427  // Replace unwanted characters by underscores
428  $cleanFileName = preg_replace('/[' . self::UNSAFE_FILENAME_CHARACTER_EXPRESSION . '\\xC0-\\xFF]/', '_', trim($fileName));
429  }
430  // Strip trailing dots and return
431  return rtrim($cleanFileName, '.');
432  }
433 }