[BUGFIX] Moving files in filelist renames file to "1"
[Packages/TYPO3.CMS.git] / t3lib / thumbs.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 /**
29 * Generates a thumbnail and returns an image stream, either GIF/PNG or JPG
30 *
31 * Revised for TYPO3 3.6 July/2003 by Kasper Skårhøj
32 *
33 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
34 */
35 /**
36 * Class for generating a thumbnail from the input parameters given to the script
37 *
38 * Input GET var, &file: relative or absolute reference to an imagefile. WILL be validated against PATH_site / lockRootPath
39 * Input GET var, &size: integer-values defining size of thumbnail, format '[int]' or '[int]x[int]'
40 *
41 * Relative paths MUST BE the first two characters ONLY: eg: '../dir/file.gif', otherwise it is expect to be absolute
42 *
43 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
44 * @package TYPO3
45 * @subpackage t3lib
46 */
47 class SC_t3lib_thumbs {
48 var $include_once = array();
49
50 // The output directory of temporary files in PATH_site
51 var $outdir = 'typo3temp/';
52 var $output = '';
53 var $sizeDefault = '64x64';
54
55 // Coming from $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
56 var $imageList;
57 /**
58 * will hold the file Object
59 *
60 * @var t3lib_file_File $input
61 */
62 var $image;
63
64 // Internal, static: GPvar:
65 // Holds the input filename (GET: file)
66 var $file;
67 // Holds the input size (GET: size)
68 var $size;
69 // Last modification time of the supplied file
70 var $mTime = 0;
71
72
73 /**
74 * Initialize; reading parameters with GPvar and checking file path
75 * Results in internal var, $this->file, being set to the file object
76 * which should be used to make a thumbnail.
77 *
78 * @return void
79 */
80 function init() {
81 // Setting GPvars:
82 // Only needed for MD5 sum calculation of backwards-compatibility uploads/ files thumbnails.
83 $size = t3lib_div::_GP('size');
84 $filePathOrCombinedFileIdentifier = rawurldecode(t3lib_div::_GP('file'));
85 $md5sum = t3lib_div::_GP('md5sum');
86
87 // Image extension list is set:
88 // valid extensions. OBS: No spaces in the list, all lowercase...
89 $this->imageList = $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'];
90
91 // Check if we got a combined file identifier of the form storageUid:fileIdentifer.
92 // We need to distinguish it from absolute Windows paths by cbecking for an integer as first part.
93 $parts = t3lib_div::trimExplode(':', $filePathOrCombinedFileIdentifier);
94
95 // Best case: we get a sys_file UID
96 if (t3lib_utility_Math::canBeInterpretedAsInteger($filePathOrCombinedFileIdentifier)) {
97 /** @var t3lib_file_File $filePathOrCombinedFileIdentifier */
98 $fileObject = t3lib_file_Factory::getInstance()->getFileObject($filePathOrCombinedFileIdentifier);
99
100 } elseif (count($parts) <= 1 || !t3lib_utility_Math::canBeInterpretedAsInteger($parts[0])) {
101 // TODO: Historically, the input parameter could also be an absolute path. This should be supported again to stay compatible.
102 // We assume the FilePath to be a relative file path (as in backwards compatibility mode)
103 $relativeFilePath = $filePathOrCombinedFileIdentifier;
104
105 // The incoming relative path is relative to the typo3/ directory, but we need it relative to PATH_site. This is corrected here:
106 if (substr($relativeFilePath, 0, 3) == '../') {
107 $relativeFilePath = substr($relativeFilePath, 3);
108 } else {
109 $relativeFilePath = 'typo3/' . $relativeFilePath;
110 }
111
112 $relativeFilePath = ltrim($relativeFilePath, '/');
113 $mTime = 0;
114
115 // Checking for backpath and double slashes + the thumbnail can be made from files which are in the PATH_site OR the lockRootPath only!
116 if (t3lib_div::isAllowedAbsPath(PATH_site . $relativeFilePath)) {
117 $mTime = filemtime(PATH_site . $relativeFilePath);
118 }
119
120 if (strstr($relativeFilePath, '../') !== FALSE) {
121 // Maybe this could be relaxed to not throw an error as long as the path is still within PATH_site
122 $this->errorGif('File path', 'must not contain', '"../"');
123 }
124
125 if ($relativeFilePath && file_exists(PATH_site . $relativeFilePath)) {
126 // Check file extension:
127 $reg = array();
128 if (preg_match('/(.*)\.([^\.]*$)/', $relativeFilePath, $reg)) {
129 $ext = strtolower($reg[2]);
130 $ext = ($ext=='jpeg') ? 'jpg' : $ext;
131 if (!t3lib_div::inList($this->imageList, $ext)) {
132 $this->errorGif('Not imagefile!', $ext, basename($relativeFilePath));
133 }
134 } else {
135 $this->errorGif('Not imagefile!', 'No ext!', basename($relativeFilePath));
136 }
137 } else{
138 $this->errorGif('Input file not found.', 'not found in thumbs.php', basename($relativeFilePath));
139 }
140
141 // Do an MD5 check to prevent viewing of images without permission
142 $OK = FALSE;
143 if ($mTime) {
144 // Always use the absolute path for this check!
145 $check = basename($relativeFilePath) . ':' . $mTime . ':' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'];
146 $md5_real = t3lib_div::shortMD5($check);
147 if (!strcmp($md5_real, $md5sum)) {
148 $OK = TRUE;
149 }
150 }
151
152 $combinedIdentifier = '0:' . $relativeFilePath;
153 } else {
154 $combinedIdentifier = $filePathOrCombinedFileIdentifier;
155 $OK = FALSE;
156 }
157
158 if (empty($fileObject)) {
159 $fileObject = t3lib_file_Factory::getInstance()->getFileObjectFromCombinedIdentifier($combinedIdentifier);
160 }
161
162 if (empty($OK)) {
163 $OK = $fileObject !== NULL && $fileObject->checkActionPermission('read') && $fileObject->calculateChecksum() == $md5sum;
164 }
165
166 if ($OK) {
167 $this->image = $fileObject;
168 $this->size = $size;
169 } else {
170 // Hide the path to the document root;
171 throw new RuntimeException('TYPO3 Fatal Error: The requested image does not exist and/or MD5 checksum did not match. If the target file exists and its file name contains special characters, the setting of $TYPO3_CONF_VARS[SYS][systemLocale] might be wrong.', 1270853950);
172 }
173 }
174
175 /**
176 * Create the thumbnail
177 * Will exit before return if all is well.
178 *
179 * @return void
180 */
181 function main() {
182 // If file exists, we make a thumbnail of the file.
183 if (is_object($this->image)) {
184
185 // Check file extension:
186 if ($this->image->getExtension() == 'ttf') {
187 // Make font preview... (will not return)
188 $this->fontGif($this->image);
189 } elseif (($this->image->getType() != t3lib_file_File::FILETYPE_IMAGE) && !t3lib_div::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $this->image->getExtension())) {
190 $this->errorGif('Not imagefile!', 'No ext!', $this->image->getName());
191 }
192
193 // ... so we passed the extension test meaning that we are going to make a thumbnail here:
194 // default
195 if (!$this->size) {
196 $this->size = $this->sizeDefault;
197 }
198
199 // I added extra check, so that the size input option could not be fooled to pass other values.
200 // That means the value is exploded, evaluated to an integer and the imploded to [value]x[value].
201 // Furthermore you can specify: size=340 and it'll be translated to 340x340.
202 // explodes the input size (and if no "x" is found this will add size again so it is the same for both dimensions)
203 $sizeParts = explode('x', $this->size . 'x' . $this->size);
204 // Cleaning it up, only two parameters now.
205 $sizeParts = array(t3lib_utility_Math::forceIntegerInRange($sizeParts[0], 1, 1000), t3lib_utility_Math::forceIntegerInRange($sizeParts[1], 1, 1000));
206 // Imploding the cleaned size-value back to the internal variable
207 $this->size = implode('x', $sizeParts);
208 // Getting max value
209 $sizeMax = max($sizeParts);
210
211 // Init
212 $outpath = PATH_site . $this->outdir;
213
214 // Should be - ? 'png' : 'gif' - , but doesn't work (ImageMagick prob.?)
215 // René: png work for me
216 $thmMode = t3lib_utility_Math::forceIntegerInRange($GLOBALS['TYPO3_CONF_VARS']['GFX']['thumbnails_png'], 0);
217 $outext = ($this->image->getExtension() != 'jpg' || ($thmMode & 2)) ? ($thmMode & 1 ? 'png' : 'gif') : 'jpg';
218
219 $outfile = 'tmb_' . substr(md5($this->image->getName() . $this->mtime . $this->size), 0, 10) . '.' . $outext;
220 $this->output = $outpath . $outfile;
221
222 if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['im']) {
223 // If thumbnail does not exist, we generate it
224 if (!file_exists($this->output)) {
225 $parameters = '-sample ' . $this->size . ' ' . $this->wrapFileName($this->image->getForLocalProcessing(FALSE)) . '[0] ' . $this->wrapFileName($this->output);
226 $cmd = t3lib_div::imageMagickCommand('convert', $parameters);
227 t3lib_utility_Command::exec($cmd);
228 if (!file_exists($this->output)) {
229 $this->errorGif('No thumb', 'generated!', $this->image->getName());
230 } else {
231 t3lib_div::fixPermissions($this->output);
232 }
233 }
234 // The thumbnail is read and output to the browser
235 if (($fd = @fopen($this->output, 'rb'))) {
236 $fileModificationTime = filemtime($this->output);
237 header('Content-type: image/' . $outext);
238 header('Last-Modified: '. date('r', $fileModificationTime));
239 header('Etag: ' . md5($this->output) . '-' . $fileModificationTime);
240 // Expiration time is choosen arbitrary to 1 month
241 header('Expires: ' . date('r', $fileModificationTime + 30*24*60*60));
242 fpassthru($fd);
243 fclose($fd);
244 } else {
245 $this->errorGif('Read problem!', '', $this->output);
246 }
247 } else {
248 exit;
249 }
250 } else {
251 $this->errorGif('No valid', 'inputfile!', basename($this->image));
252 }
253 }
254
255 /***************************
256 *
257 * OTHER FUNCTIONS:
258 *
259 ***************************/
260
261 /**
262 * Creates error image based on gfx/notfound_thumb.png
263 * Requires GD lib enabled, otherwise it will exit with the three textstrings outputted as text.
264 * Outputs the image stream to browser and exits!
265 *
266 * @param string $l1 Text line 1
267 * @param string $l2 Text line 2
268 * @param string $l3 Text line 3
269 * @return void
270 */
271 function errorGif($l1, $l2, $l3) {
272 if (!$GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib']) {
273 throw new RuntimeException(
274 'TYPO3 Fatal Error: No gdlib. ' . $l1 . ' ' . $l2 . ' ' . $l3,
275 1270853952
276 );
277 }
278
279 // Creates the basis for the error image
280 if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib_png']) {
281 header('Content-type: image/png');
282 $im = imagecreatefrompng(PATH_typo3 . 'gfx/notfound_thumb.png');
283 } else {
284 header('Content-type: image/gif');
285 $im = imagecreatefromgif(PATH_typo3 . 'gfx/notfound_thumb.gif');
286 }
287 // Sets background color and print color.
288 $white = imageColorAllocate($im, 255, 255, 255);
289 $black = imageColorAllocate($im, 0, 0, 0);
290
291 // Prints the text strings with the build-in font functions of GD
292 $x = 0;
293 $font = 0;
294 if ($l1) {
295 imagefilledrectangle($im, $x, 9, 56, 16, $white);
296 imageString($im, $font, $x, 9, $l1, $black);
297 }
298 if ($l2) {
299 imagefilledrectangle($im, $x, 19, 56, 26, $white);
300 imageString($im, $font, $x, 19, $l2, $black);
301 }
302 if ($l3) {
303 imagefilledrectangle($im, $x, 29, 56, 36, $white);
304 imageString($im, $font, $x, 29, substr($l3, -14), $black);
305 }
306
307 // Outputting the image stream and exit
308 if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib_png']) {
309 imagePng($im);
310 } else {
311 imageGif($im);
312 }
313 imagedestroy($im);
314 exit;
315 }
316
317 /**
318 * Creates a font-preview thumbnail.
319 * This means a PNG/GIF file with the text "AaBbCc...." set with the font-file given as input and in various sizes to show how the font looks
320 * Requires GD lib enabled.
321 * Outputs the image stream to browser and exits!
322 *
323 * @param string $font The filepath to the font file (absolute, probably)
324 * @return void
325 */
326 function fontGif($font) {
327 if (!$GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib']) {
328 throw new RuntimeException(
329 'TYPO3 Fatal Error: No gdlib.',
330 1270853953
331 );
332 }
333
334 // Create image and set background color to white.
335 $im = imageCreate(250, 76);
336 $white = imageColorAllocate($im, 255, 255, 255);
337 $col = imageColorAllocate($im, 0, 0, 0);
338
339 // The test string and offset in x-axis.
340 $string = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzÆæØøÅåÄäÖöÜüß';
341 $x=13;
342
343 // Print (with non-ttf font) the size displayed
344 imagestring ($im, 1, 0, 2, '10', $col);
345 imagestring ($im, 1, 0, 15, '12', $col);
346 imagestring ($im, 1, 0, 30, '14', $col);
347 imagestring ($im, 1, 0, 47, '18', $col);
348 imagestring ($im, 1, 0, 68, '24', $col);
349
350 // Print with ttf-font the test string
351 imagettftext ($im, t3lib_div::freetypeDpiComp(10), 0, $x, 8, $col, $font, $string);
352 imagettftext ($im, t3lib_div::freetypeDpiComp(12), 0, $x, 21, $col, $font, $string);
353 imagettftext ($im, t3lib_div::freetypeDpiComp(14), 0, $x, 36, $col, $font, $string);
354 imagettftext ($im, t3lib_div::freetypeDpiComp(18), 0, $x, 53, $col, $font, $string);
355 imagettftext ($im, t3lib_div::freetypeDpiComp(24), 0, $x, 74, $col, $font, $string);
356
357 // Output PNG or GIF based on $GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib_png']
358 if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib_png']) {
359 header('Content-type: image/png');
360 imagePng($im);
361 } else {
362 header('Content-type: image/gif');
363 imageGif($im);
364 }
365 imagedestroy($im);
366 exit;
367 }
368
369 /**
370 * Escapes a file name so it can safely be used on the command line.
371 *
372 * @param string $inputName Filename to safeguard, must not be empty
373 * @return string $inputName escaped as needed
374 */
375 protected function wrapFileName($inputName) {
376 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
377 $currentLocale = setlocale(LC_CTYPE, 0);
378 setlocale(LC_CTYPE, $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']);
379 }
380 $escapedInputName = escapeshellarg($inputName);
381 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
382 setlocale(LC_CTYPE, $currentLocale);
383 }
384 return $escapedInputName;
385 }
386 }
387
388 // Make instance:
389 $SOBE = t3lib_div::makeInstance('SC_t3lib_thumbs');
390 $SOBE->init();
391 $SOBE->main();
392 ?>