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