TYPO3 CMS  TYPO3_6-2
BasicFileUtility.php
Go to the documentation of this file.
1 <?php
3 
19 
38  const UNSAFE_FILENAME_CHARACTER_EXPRESSION = '\\x00-\\x2C\\/\\x3A-\\x3F\\x5B-\\x60\\x7B-\\xBF';
39 
43  public $getUniqueNamePrefix = '';
44 
45  // Prefix which will be prepended the file when using the getUniqueName-function
49  public $maxNumber = 99;
50 
51  // This number decides the highest allowed appended number used on a filename before we use naming with unique strings
55  public $uniquePrecision = 6;
56 
57  // This number decides how many characters out of a unique MD5-hash that is appended to a filename if getUniqueName is asked to find an available filename.
61  public $maxInputNameLen = 60;
62 
63  // This is the maximum length of names treated by cleanFileName()
67  public $tempFN = '_temp_';
68 
69  // Temp-foldername. A folder in the root of one of the mounts with this name is regarded a TEMP-folder (used for upload from clipboard)
70  // internal
74  public $f_ext = array();
75 
76  // See comment in header
80  public $mounts = array();
81 
82  // See comment in header
86  public $webPath = '';
87 
88  // Set to DOCUMENT_ROOT.
92  public $isInit = 0;
93 
97  public $csConvObj;
98 
99  // Set to TRUE after init()/start();
100  /**********************************
101  *
102  * Checking functions
103  *
104  **********************************/
136  public function init($mounts, $f_ext) {
137  $this->f_ext['webspace']['allow'] = GeneralUtility::uniqueList(strtolower($f_ext['webspace']['allow']));
138  $this->f_ext['webspace']['deny'] = GeneralUtility::uniqueList(strtolower($f_ext['webspace']['deny']));
139  $this->f_ext['ftpspace']['allow'] = GeneralUtility::uniqueList(strtolower($f_ext['ftpspace']['allow']));
140  $this->f_ext['ftpspace']['deny'] = GeneralUtility::uniqueList(strtolower($f_ext['ftpspace']['deny']));
141 
142  $this->mounts = (!empty($mounts) ? $mounts : array());
143  $this->webPath = GeneralUtility::getIndpEnv('TYPO3_DOCUMENT_ROOT');
144  $this->isInit = 1;
145  $this->maxInputNameLen = $GLOBALS['TYPO3_CONF_VARS']['SYS']['maxFileNameLength'] ?: $this->maxInputNameLen;
146  }
147 
170  public function getTotalFileInfo($wholePath) {
172  $theuser = getmyuid();
173  $info = GeneralUtility::split_fileref($wholePath);
174  $info['tstamp'] = @filemtime($wholePath);
175  $info['size'] = @filesize($wholePath);
176  $info['type'] = @filetype($wholePath);
177  $info['owner'] = @fileowner($wholePath);
178  $info['perms'] = @fileperms($wholePath);
179  $info['writable'] = !@is_writable($wholePath);
180  $info['readable'] = !@is_readable($wholePath);
181  return $info;
182  }
183 
193  public function is_allowed($iconkey, $type) {
194  if (isset($this->f_ext[$type])) {
195  $ik = strtolower($iconkey);
196  if ($ik) {
197  // If the extension is found amongst the allowed types, we return TRUE immediately
198  if ($this->f_ext[$type]['allow'] == '*' || GeneralUtility::inList($this->f_ext[$type]['allow'], $ik)) {
199  return TRUE;
200  }
201  // If the extension is found amongst the denied types, we return FALSE immediately
202  if ($this->f_ext[$type]['deny'] == '*' || GeneralUtility::inList($this->f_ext[$type]['deny'], $ik)) {
203  return FALSE;
204  }
205  // If no match we return TRUE
206  return TRUE;
207  } else {
208  // If no extension:
209  if ($this->f_ext[$type]['allow'] == '*') {
210  return TRUE;
211  }
212  if ($this->f_ext[$type]['deny'] == '*') {
213  return FALSE;
214  }
215  return TRUE;
216  }
217  }
218  return FALSE;
219  }
220 
229  public function checkIfFullAccess($theDest) {
230  $type = $this->is_webpath($theDest) ? 'webspace' : 'ftpspace';
231  if (isset($this->f_ext[$type])) {
232  if ((string) $this->f_ext[$type]['deny'] == '' || $this->f_ext[$type]['allow'] == '*') {
233  return TRUE;
234  }
235  }
236  }
237 
247  public function is_webpath($path) {
248  if ($this->isInit) {
249  $testPath = $this->slashPath($path);
250  $testPathWeb = $this->slashPath($this->webPath);
251  if ($testPathWeb && $testPath) {
252  return GeneralUtility::isFirstPartOfStr($testPath, $testPathWeb);
253  }
254  }
255  return TRUE;
256  }
257 
269  public function checkIfAllowed($ext, $theDest, $filename = '') {
270  return GeneralUtility::verifyFilenameAgainstDenyPattern($filename) && $this->is_allowed($ext, ($this->is_webpath($theDest) ? 'webspace' : 'ftpspace'));
271  }
272 
281  public function checkFileNameLen($fileName) {
282  // @todo: should go into the LocalDriver in a protected way (not important to the outside world)
284  return strlen($fileName) <= $this->maxInputNameLen;
285  }
286 
295  public function is_directory($theDir) {
296  // @todo: should go into the LocalDriver in a protected way (not important to the outside world)
297  if (GeneralUtility::validPathStr($theDir)) {
298  $theDir = PathUtility::getCanonicalPath($theDir);
299  if (@is_dir($theDir)) {
300  return $theDir;
301  }
302  }
303  return FALSE;
304  }
305 
314  public function isPathValid($theFile) {
315  // @todo: should go into the LocalDriver in a protected way (not important to the outside world)
317  return GeneralUtility::validPathStr($theFile);
318  }
319 
333  public function getUniqueName($theFile, $theDest, $dontCheckForUnique = 0) {
334  // @todo: should go into the LocalDriver in a protected way (not important to the outside world)
335  $theDest = $this->is_directory($theDest);
336  // $theDest is cleaned up
337  $origFileInfo = GeneralUtility::split_fileref($theFile);
338  // Fetches info about path, name, extension of $theFile
339  if ($theDest) {
340  if ($this->getUniqueNamePrefix) {
341  // Adds prefix
342  $origFileInfo['file'] = $this->getUniqueNamePrefix . $origFileInfo['file'];
343  $origFileInfo['filebody'] = $this->getUniqueNamePrefix . $origFileInfo['filebody'];
344  }
345  // Check if the file exists and if not - return the filename...
346  $fileInfo = $origFileInfo;
347  $theDestFile = $theDest . '/' . $fileInfo['file'];
348  // The destinations file
349  if (!file_exists($theDestFile) || $dontCheckForUnique) {
350  // If the file does NOT exist we return this filename
351  return $theDestFile;
352  }
353  // 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...
354  $theTempFileBody = preg_replace('/_[0-9][0-9]$/', '', $origFileInfo['filebody']);
355  // This removes _xx if appended to the file
356  $theOrigExt = $origFileInfo['realFileext'] ? '.' . $origFileInfo['realFileext'] : '';
357  for ($a = 1; $a <= $this->maxNumber + 1; $a++) {
358  if ($a <= $this->maxNumber) {
359  // First we try to append numbers
360  $insert = '_' . sprintf('%02d', $a);
361  } else {
362  // .. then we try unique-strings...
363  $insert = '_' . substr(md5(uniqid('', TRUE)), 0, $this->uniquePrecision);
364  }
365  $theTestFile = $theTempFileBody . $insert . $theOrigExt;
366  $theDestFile = $theDest . '/' . $theTestFile;
367  // The destinations file
368  if (!file_exists($theDestFile)) {
369  // If the file does NOT exist we return this filename
370  return $theDestFile;
371  }
372  }
373  }
374  }
375 
386  public function checkPathAgainstMounts($thePath) {
387  if ($thePath && GeneralUtility::validPathStr($thePath) && is_array($this->mounts)) {
388  foreach ($this->mounts as $k => $val) {
389  if (GeneralUtility::isFirstPartOfStr($thePath, $val['path'])) {
390  return $k;
391  }
392  }
393  }
394  }
395 
403  public function findFirstWebFolder() {
404  // @todo: where and when to use this function?
405  if (is_array($this->mounts)) {
406  foreach ($this->mounts as $k => $val) {
407  if (GeneralUtility::isFirstPartOfStr($val['path'], PATH_site . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'])) {
408  return $k;
409  }
410  }
411  }
412  }
413 
422  public function blindPath($thePath) {
423  // @todo: where and when to use this function?
425  $k = $this->checkPathAgainstMounts($thePath);
426  if ($k) {
427  $name = '';
428  $name .= '[' . $this->mounts[$k]['name'] . ']: ';
429  $name .= substr($thePath, strlen($this->mounts[$k]['path']));
430  return $name;
431  }
432  }
433 
441  public function findTempFolder() {
442  // @todo: where and when to use this function?
444  if ($this->tempFN && is_array($this->mounts)) {
445  foreach ($this->mounts as $k => $val) {
446  $tDir = $val['path'] . $this->tempFN;
447  if (@is_dir($tDir)) {
448  return $tDir;
449  }
450  }
451  }
452  }
453 
454  /*********************
455  *
456  * Cleaning functions
457  *
458  *********************/
466  public function cleanDirectoryName($theDir) {
468  return PathUtility::getCanonicalPath($theDir);
469  }
470 
478  public function rmDoubleSlash($string) {
480  return str_replace('//', '/', $string);
481  }
482 
491  public function slashPath($path) {
492  // @todo: should go into the LocalDriver in a protected way (not important to the outside world)
493  // @todo: should be done with rtrim($path, '/') . '/';
494  if (substr($path, -1) != '/') {
495  return $path . '/';
496  }
497  return $path;
498  }
499 
510  public function cleanFileName($fileName, $charset = '') {
511  // Handle UTF-8 characters
512  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
513  // allow ".", "-", 0-9, a-z, A-Z and everything beyond U+C0 (latin capital letter a with grave)
514  $cleanFileName = preg_replace('/[' . self::UNSAFE_FILENAME_CHARACTER_EXPRESSION . ']/u', '_', trim($fileName));
515  } else {
516  // Get conversion object or initialize if needed
517  if (!is_object($this->csConvObj)) {
518  if (TYPO3_MODE == 'FE') {
519  $this->csConvObj = $GLOBALS['TSFE']->csConvObj;
520  } elseif (is_object($GLOBALS['LANG'])) {
521  // BE assumed:
522  $this->csConvObj = $GLOBALS['LANG']->csConvObj;
523  } else {
524  // The object may not exist yet, so we need to create it now. Happens in the Install Tool for example.
525  $this->csConvObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Charset\\CharsetConverter');
526  }
527  }
528  // Define character set
529  if (!$charset) {
530  if (TYPO3_MODE == 'FE') {
531  $charset = $GLOBALS['TSFE']->renderCharset;
532  } else {
533  // Backend
534  $charset = 'utf-8';
535  }
536  }
537  // If a charset was found, convert filename
538  if ($charset) {
539  $fileName = $this->csConvObj->specCharsToASCII($charset, $fileName);
540  }
541  // Replace unwanted characters by underscores
542  $cleanFileName = preg_replace('/[' . self::UNSAFE_FILENAME_CHARACTER_EXPRESSION . '\\xC0-\\xFF]/', '_', trim($fileName));
543  }
544  // Strip trailing dots and return
545  return preg_replace('/\\.*$/', '', $cleanFileName);
546  }
547 
548 }
static uniqueList($in_list, $secondParameter=NULL)
static isFirstPartOfStr($str, $partStr)
const TYPO3_MODE
Definition: init.php:40
getUniqueName($theFile, $theDest, $dontCheckForUnique=0)
static verifyFilenameAgainstDenyPattern($filename)
static split_fileref($fileNameWithPath)
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]
checkIfAllowed($ext, $theDest, $filename='')