|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* |
|
4
|
|
|
* This file is part of the TYPO3 CMS project. |
|
5
|
|
|
* |
|
6
|
|
|
* It is free software; you can redistribute it and/or modify it under |
|
7
|
|
|
* the terms of the GNU General Public License, either version 2 |
|
8
|
|
|
* of the License, or any later version. |
|
9
|
|
|
* |
|
10
|
|
|
* For the full copyright and license information, please read the |
|
11
|
|
|
* LICENSE.txt file that was distributed with this source code. |
|
12
|
|
|
* |
|
13
|
|
|
* The TYPO3 project - inspiring people to share! |
|
14
|
|
|
*/ |
|
15
|
|
|
|
|
16
|
|
|
namespace TYPO3\CMS\Core\Utility\File; |
|
17
|
|
|
|
|
18
|
|
|
use TYPO3\CMS\Core\Charset\CharsetConverter; |
|
19
|
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility; |
|
20
|
|
|
use TYPO3\CMS\Core\Utility\PathUtility; |
|
21
|
|
|
|
|
22
|
|
|
/** |
|
23
|
|
|
* Contains class with basic file management functions |
|
24
|
|
|
* |
|
25
|
|
|
* Contains functions for management, validation etc of files in TYPO3. |
|
26
|
|
|
* |
|
27
|
|
|
* @internal All methods in this class should not be used anymore since TYPO3 6.0, this class is therefore marked |
|
28
|
|
|
* as internal. |
|
29
|
|
|
* Please use corresponding \TYPO3\CMS\Core\Resource\ResourceStorage |
|
30
|
|
|
* (fetched via BE_USERS->getFileStorages()), as all functions should be |
|
31
|
|
|
* found there (in a cleaner manner). |
|
32
|
|
|
*/ |
|
33
|
|
|
class BasicFileUtility |
|
34
|
|
|
{ |
|
35
|
|
|
/** |
|
36
|
|
|
* @var string |
|
37
|
|
|
*/ |
|
38
|
|
|
const UNSAFE_FILENAME_CHARACTER_EXPRESSION = '\\x00-\\x2C\\/\\x3A-\\x3F\\x5B-\\x60\\x7B-\\xBF'; |
|
39
|
|
|
|
|
40
|
|
|
/** |
|
41
|
|
|
* This number decides the highest allowed appended number used on a filename before we use naming with unique strings |
|
42
|
|
|
* |
|
43
|
|
|
* @var int |
|
44
|
|
|
*/ |
|
45
|
|
|
public $maxNumber = 99; |
|
46
|
|
|
|
|
47
|
|
|
/** |
|
48
|
|
|
* 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. |
|
49
|
|
|
* |
|
50
|
|
|
* @var int |
|
51
|
|
|
*/ |
|
52
|
|
|
public $uniquePrecision = 6; |
|
53
|
|
|
|
|
54
|
|
|
/** |
|
55
|
|
|
* Cleans $theDir for slashes in the end of the string and returns the new path, if it exists on the server. |
|
56
|
|
|
* |
|
57
|
|
|
* @param string $theDir Directory path to check |
|
58
|
|
|
* @return bool|string Returns the cleaned up directory name if OK, otherwise FALSE. |
|
59
|
|
|
* @todo: should go into the LocalDriver in a protected way (not important to the outside world) |
|
60
|
|
|
*/ |
|
61
|
|
|
protected function sanitizeFolderPath($theDir) |
|
62
|
|
|
{ |
|
63
|
|
|
if (!GeneralUtility::validPathStr($theDir)) { |
|
64
|
|
|
return false; |
|
65
|
|
|
} |
|
66
|
|
|
$theDir = PathUtility::getCanonicalPath($theDir); |
|
67
|
|
|
if (@is_dir($theDir)) { |
|
68
|
|
|
return $theDir; |
|
69
|
|
|
} |
|
70
|
|
|
return false; |
|
71
|
|
|
} |
|
72
|
|
|
|
|
73
|
|
|
/** |
|
74
|
|
|
* Returns the destination path/filename of a unique filename/foldername in that path. |
|
75
|
|
|
* If $theFile exists in $theDest (directory) the file have numbers appended up to $this->maxNumber. Hereafter a unique string will be appended. |
|
76
|
|
|
* This function is used by fx. DataHandler when files are attached to records and needs to be uniquely named in the uploads/* folders |
|
77
|
|
|
* |
|
78
|
|
|
* @param string $theFile The input filename to check |
|
79
|
|
|
* @param string $theDest The directory for which to return a unique filename for $theFile. $theDest MUST be a valid directory. Should be absolute. |
|
80
|
|
|
* @param bool $dontCheckForUnique If set the filename is returned with the path prepended without checking whether it already existed! |
|
81
|
|
|
* @return string|null The destination absolute filepath (not just the name!) of a unique filename/foldername in that path. |
|
82
|
|
|
* @internal May be removed without further notice. Method has been marked as deprecated for various versions but is still used in core. |
|
83
|
|
|
* @todo: should go into the LocalDriver in a protected way (not important to the outside world) |
|
84
|
|
|
*/ |
|
85
|
|
|
public function getUniqueName($theFile, $theDest, $dontCheckForUnique = false) |
|
86
|
|
|
{ |
|
87
|
|
|
// $theDest is cleaned up |
|
88
|
|
|
$theDest = $this->sanitizeFolderPath($theDest); |
|
89
|
|
|
if ($theDest) { |
|
90
|
|
|
// Fetches info about path, name, extension of $theFile |
|
91
|
|
|
$origFileInfo = GeneralUtility::split_fileref($theFile); |
|
92
|
|
|
// Check if the file exists and if not - return the filename... |
|
93
|
|
|
$fileInfo = $origFileInfo; |
|
94
|
|
|
$theDestFile = $theDest . '/' . $fileInfo['file']; |
|
95
|
|
|
// The destinations file |
|
96
|
|
|
if (!file_exists($theDestFile) || $dontCheckForUnique) { |
|
97
|
|
|
// If the file does NOT exist we return this filename |
|
98
|
|
|
return $theDestFile; |
|
99
|
|
|
} |
|
100
|
|
|
// 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... |
|
101
|
|
|
$theTempFileBody = preg_replace('/_[0-9][0-9]$/', '', $origFileInfo['filebody']); |
|
102
|
|
|
// This removes _xx if appended to the file |
|
103
|
|
|
$theOrigExt = $origFileInfo['realFileext'] ? '.' . $origFileInfo['realFileext'] : ''; |
|
104
|
|
|
for ($a = 1; $a <= $this->maxNumber + 1; $a++) { |
|
105
|
|
|
if ($a <= $this->maxNumber) { |
|
106
|
|
|
// First we try to append numbers |
|
107
|
|
|
$insert = '_' . sprintf('%02d', $a); |
|
108
|
|
|
} else { |
|
109
|
|
|
// .. then we try unique-strings... |
|
110
|
|
|
$insert = '_' . substr(md5(uniqid('', true)), 0, $this->uniquePrecision); |
|
111
|
|
|
} |
|
112
|
|
|
$theTestFile = $theTempFileBody . $insert . $theOrigExt; |
|
113
|
|
|
$theDestFile = $theDest . '/' . $theTestFile; |
|
114
|
|
|
// The destinations file |
|
115
|
|
|
if (!file_exists($theDestFile)) { |
|
116
|
|
|
// If the file does NOT exist we return this filename |
|
117
|
|
|
return $theDestFile; |
|
118
|
|
|
} |
|
119
|
|
|
} |
|
120
|
|
|
} |
|
121
|
|
|
|
|
122
|
|
|
return null; |
|
123
|
|
|
} |
|
124
|
|
|
|
|
125
|
|
|
/** |
|
126
|
|
|
* Returns a string where any character not matching [.a-zA-Z0-9_-] is substituted by '_' |
|
127
|
|
|
* Trailing dots are removed |
|
128
|
|
|
* |
|
129
|
|
|
* @param string $fileName Input string, typically the body of a filename |
|
130
|
|
|
* @return string Output string with any characters not matching [.a-zA-Z0-9_-] is substituted by '_' and trailing dots removed |
|
131
|
|
|
* @internal May be removed without further notice. Method has been marked as deprecated for various versions but is still used in core. |
|
132
|
|
|
*/ |
|
133
|
|
|
public function cleanFileName($fileName) |
|
134
|
|
|
{ |
|
135
|
|
|
// Handle UTF-8 characters |
|
136
|
|
|
if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) { |
|
137
|
|
|
// allow ".", "-", 0-9, a-z, A-Z and everything beyond U+C0 (latin capital letter a with grave) |
|
138
|
|
|
$cleanFileName = preg_replace('/[' . self::UNSAFE_FILENAME_CHARACTER_EXPRESSION . ']/u', '_', trim($fileName)); |
|
139
|
|
|
} else { |
|
140
|
|
|
$fileName = GeneralUtility::makeInstance(CharsetConverter::class)->utf8_char_mapping($fileName); |
|
141
|
|
|
// Replace unwanted characters by underscores |
|
142
|
|
|
$cleanFileName = preg_replace('/[' . self::UNSAFE_FILENAME_CHARACTER_EXPRESSION . '\\xC0-\\xFF]/', '_', trim($fileName)); |
|
143
|
|
|
} |
|
144
|
|
|
// Strip trailing dots and return |
|
145
|
|
|
return rtrim($cleanFileName, '.'); |
|
146
|
|
|
} |
|
147
|
|
|
} |
|
148
|
|
|
|