TYPO3 CMS  TYPO3_6-2
ThumbnailView.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Backend\View;
3 
21 
33 
37  public $include_once = array();
38 
39  // The output directory of temporary files in PATH_site
43  public $outdir = 'typo3temp/';
44 
48  public $output = '';
49 
53  public $sizeDefault = '64x64';
54 
55  // Coming from $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
59  public $imageList;
60 
67  public $image;
68 
69  // Internal, static: GPvar:
70  // Holds the input filename (GET: file)
74  public $file;
75 
76  // Holds the input size (GET: size)
80  public $size;
81 
82  // Last modification time of the supplied file
86  public $mTime = 0;
87 
96  public function init() {
97  // Setting GPvars:
98  // Only needed for MD5 sum calculation of backwards-compatibility uploads/ files thumbnails.
99  $size = GeneralUtility::_GP('size');
100  $filePathOrCombinedFileIdentifier = rawurldecode(GeneralUtility::_GP('file'));
101  $md5sum = GeneralUtility::_GP('md5sum');
102  // Image extension list is set:
103  // valid extensions. OBS: No spaces in the list, all lowercase...
104  $this->imageList = $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'];
105  // Check if we got a combined file identifier of the form storageUid:fileIdentifer.
106  // We need to distinguish it from absolute Windows paths by cbecking for an integer as first part.
107  $parts = GeneralUtility::trimExplode(':', $filePathOrCombinedFileIdentifier);
108  // Best case: we get a sys_file UID
109  if (!empty($filePathOrCombinedFileIdentifier) && MathUtility::canBeInterpretedAsInteger($filePathOrCombinedFileIdentifier)) {
111  $fileObject = ResourceFactory::getInstance()->getFileObject($filePathOrCombinedFileIdentifier);
112  } elseif (count($parts) <= 1 || !MathUtility::canBeInterpretedAsInteger($parts[0])) {
113  // TODO: Historically, the input parameter could also be an absolute path. This should be supported again to stay compatible.
114  // We assume the FilePath to be a relative file path (as in backwards compatibility mode)
115  $relativeFilePath = $filePathOrCombinedFileIdentifier;
116  // The incoming relative path is relative to the typo3/ directory, but we need it relative to PATH_site. This is corrected here:
117  if (substr($relativeFilePath, 0, 3) == '../') {
118  $relativeFilePath = substr($relativeFilePath, 3);
119  } else {
120  $relativeFilePath = 'typo3/' . $relativeFilePath;
121  }
122  $relativeFilePath = ltrim($relativeFilePath, '/');
123  $mTime = 0;
124  // Checking for backpath and double slashes + the thumbnail can be made from files which are in the PATH_site OR the lockRootPath only!
125  if (GeneralUtility::isAllowedAbsPath(PATH_site . $relativeFilePath)) {
126  $mTime = filemtime(PATH_site . $relativeFilePath);
127  }
128  if (strstr($relativeFilePath, '../') !== FALSE) {
129  // Maybe this could be relaxed to not throw an error as long as the path is still within PATH_site
130  $this->errorGif('File path', 'must not contain', '"../"');
131  }
132  if ($relativeFilePath && file_exists(PATH_site . $relativeFilePath)) {
133  // Check file extension:
134  $reg = array();
135  if (preg_match('/(.*)\\.([^\\.]*$)/', $relativeFilePath, $reg)) {
136  $ext = strtolower($reg[2]);
137  $ext = $ext == 'jpeg' ? 'jpg' : $ext;
138  if (!GeneralUtility::inList($this->imageList, $ext)) {
139  $this->errorGif('Not imagefile!', $ext, basename($relativeFilePath));
140  }
141  } else {
142  $this->errorGif('Not imagefile!', 'No ext!', basename($relativeFilePath));
143  }
144  } else {
145  $this->errorGif('Input file not found.', 'not found in thumbs.php', basename($relativeFilePath));
146  }
147  // Do an MD5 check to prevent viewing of images without permission
148  $OK = FALSE;
149  if ($mTime) {
150  // Always use the absolute path for this check!
151  $check = basename($relativeFilePath) . ':' . $mTime . ':' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'];
152  if (GeneralUtility::shortMD5($check) === (string)$md5sum) {
153  $OK = TRUE;
154  }
155  }
156  $combinedIdentifier = '0:' . $relativeFilePath;
157  } else {
158  $combinedIdentifier = $filePathOrCombinedFileIdentifier;
159  $OK = FALSE;
160  }
161  if (empty($fileObject)) {
162  $fileObject = ResourceFactory::getInstance()->getFileObjectFromCombinedIdentifier($combinedIdentifier);
163  }
164  if (empty($OK)) {
165  $OK = $fileObject !== NULL && $fileObject->checkActionPermission('read') && $fileObject->calculateChecksum() == $md5sum;
166  }
167  if ($OK) {
168  $this->image = $fileObject;
169  $this->size = $size;
170  } else {
171  // Hide the path to the document root;
172  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);
173  }
174  }
175 
183  public function main() {
184  // Clean output buffer to ensure no extraneous output exists
185  ob_clean();
186  // If file exists, we make a thumbnail of the file.
187  if (is_object($this->image)) {
188  // Check file extension:
189  if ($this->image->getExtension() == 'ttf') {
190  // Make font preview... (will not return)
191  $this->fontGif($this->image);
192  } elseif ($this->image->getType() != File::FILETYPE_IMAGE && !GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $this->image->getExtension())) {
193  $this->errorGif('Not imagefile!', 'No ext!', $this->image->getName());
194  }
195  // ... so we passed the extension test meaning that we are going to make a thumbnail here:
196  // default
197  if (!$this->size) {
198  $this->size = $this->sizeDefault;
199  }
200  // I added extra check, so that the size input option could not be fooled to pass other values.
201  // That means the value is exploded, evaluated to an integer and the imploded to [value]x[value].
202  // Furthermore you can specify: size=340 and it'll be translated to 340x340.
203  // explodes the input size (and if no "x" is found this will add size again so it is the same for both dimensions)
204  $sizeParts = explode('x', $this->size . 'x' . $this->size);
205  // Cleaning it up, only two parameters now.
206  $sizeParts = array(MathUtility::forceIntegerInRange($sizeParts[0], 1, 1000), MathUtility::forceIntegerInRange($sizeParts[1], 1, 1000));
207  // Imploding the cleaned size-value back to the internal variable
208  $this->size = implode('x', $sizeParts);
209  // Getting max value
210  $sizeMax = max($sizeParts);
211  // Init
212  $outpath = PATH_site . $this->outdir;
213  // Should be - ? 'png' : 'gif' - , but doesn't work (ImageMagick prob.?)
214  // René: png work for me
215  $thmMode = MathUtility::forceIntegerInRange($GLOBALS['TYPO3_CONF_VARS']['GFX']['thumbnails_png'], 0);
216  $outext = $this->image->getExtension() != 'jpg' || $thmMode & 2 ? ($thmMode & 1 ? 'png' : 'gif') : 'jpg';
217  $outfile = 'tmb_' . substr(md5(($this->image->getName() . $this->mtime . $this->size)), 0, 10) . '.' . $outext;
218  $this->output = $outpath . $outfile;
219  if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['im']) {
220  // If thumbnail does not exist, we generate it
221  if (!file_exists($this->output)) {
222  $parameters = '-sample ' . $this->size . ' ' . $this->wrapFileName($this->image->getForLocalProcessing(FALSE)) . '[0] ' . $this->wrapFileName($this->output);
225  if (!file_exists($this->output)) {
226  $this->errorGif('No thumb', 'generated!', $this->image->getName());
227  } else {
228  GeneralUtility::fixPermissions($this->output);
229  }
230  }
231  // The thumbnail is read and output to the browser
232  if ($fd = @fopen($this->output, 'rb')) {
233  $fileModificationTime = filemtime($this->output);
234  header('Content-Type: image/' . ($outext === 'jpg' ? 'jpeg' : $outext));
235  header('Last-Modified: ' . date('r', $fileModificationTime));
236  header('ETag: ' . md5($this->output) . '-' . $fileModificationTime);
237  // Expiration time is chosen arbitrary to 1 month
238  header('Expires: ' . date('r', ($fileModificationTime + 30 * 24 * 60 * 60)));
239  fpassthru($fd);
240  fclose($fd);
241  } else {
242  $this->errorGif('Read problem!', '', $this->output);
243  }
244  } else {
245  die;
246  }
247  } else {
248  $this->errorGif('No valid', 'inputfile!', basename($this->image));
249  }
250  }
251 
252  /***************************
253  *
254  * OTHER FUNCTIONS:
255  *
256  ***************************/
268  public function errorGif($l1, $l2, $l3) {
269  if (!$GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib']) {
270  throw new \RuntimeException('TYPO3 Fatal Error: No gdlib. ' . $l1 . ' ' . $l2 . ' ' . $l3, 1270853954);
271  }
272  // Creates the basis for the error image
273  if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib_png']) {
274  header('Content-type: image/png');
275  $im = imagecreatefrompng(PATH_typo3 . 'gfx/notfound_thumb.png');
276  } else {
277  header('Content-type: image/gif');
278  $im = imagecreatefromgif(PATH_typo3 . 'gfx/notfound_thumb.gif');
279  }
280  // Sets background color and print color.
281  $white = imageColorAllocate($im, 255, 255, 255);
282  $black = imageColorAllocate($im, 0, 0, 0);
283  // Prints the text strings with the build-in font functions of GD
284  $x = 0;
285  $font = 0;
286  if ($l1) {
287  imagefilledrectangle($im, $x, 9, 56, 16, $white);
288  imageString($im, $font, $x, 9, $l1, $black);
289  }
290  if ($l2) {
291  imagefilledrectangle($im, $x, 19, 56, 26, $white);
292  imageString($im, $font, $x, 19, $l2, $black);
293  }
294  if ($l3) {
295  imagefilledrectangle($im, $x, 29, 56, 36, $white);
296  imageString($im, $font, $x, 29, substr($l3, -14), $black);
297  }
298  // Outputting the image stream and exit
299  if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib_png']) {
300  imagePng($im);
301  } else {
302  imageGif($im);
303  }
304  imagedestroy($im);
305  die;
306  }
307 
318  public function fontGif($font) {
319  if (!$GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib']) {
320  throw new \RuntimeException('TYPO3 Fatal Error: No gdlib.', 1270853953);
321  }
322  // Create image and set background color to white.
323  $im = imageCreate(250, 76);
324  $white = imageColorAllocate($im, 255, 255, 255);
325  $col = imageColorAllocate($im, 0, 0, 0);
326  // The test string and offset in x-axis.
327  $string = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzÆæØøÅåÄäÖöÜüß';
328  $x = 13;
329  // Print (with non-ttf font) the size displayed
330  imagestring($im, 1, 0, 2, '10', $col);
331  imagestring($im, 1, 0, 15, '12', $col);
332  imagestring($im, 1, 0, 30, '14', $col);
333  imagestring($im, 1, 0, 47, '18', $col);
334  imagestring($im, 1, 0, 68, '24', $col);
335  // Print with ttf-font the test string
336  imagettftext($im, GeneralUtility::freetypeDpiComp(10), 0, $x, 8, $col, $font, $string);
337  imagettftext($im, GeneralUtility::freetypeDpiComp(12), 0, $x, 21, $col, $font, $string);
338  imagettftext($im, GeneralUtility::freetypeDpiComp(14), 0, $x, 36, $col, $font, $string);
339  imagettftext($im, GeneralUtility::freetypeDpiComp(18), 0, $x, 53, $col, $font, $string);
340  imagettftext($im, GeneralUtility::freetypeDpiComp(24), 0, $x, 74, $col, $font, $string);
341  // Output PNG or GIF based on $GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib_png']
342  if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib_png']) {
343  header('Content-type: image/png');
344  imagePng($im);
345  } else {
346  header('Content-type: image/gif');
347  imageGif($im);
348  }
349  imagedestroy($im);
350  die;
351  }
352 
359  protected function wrapFileName($inputName) {
360  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
361  $currentLocale = setlocale(LC_CTYPE, 0);
362  setlocale(LC_CTYPE, $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']);
363  }
364  $escapedInputName = escapeshellarg($inputName);
365  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
366  setlocale(LC_CTYPE, $currentLocale);
367  }
368  return $escapedInputName;
369  }
370 
371 }
static imageMagickCommand($command, $parameters, $path='')
$parameters
Definition: FileDumpEID.php:15
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:32
static fixPermissions($path, $recursive=FALSE)
die
Definition: index.php:6
static trimExplode($delim, $string, $removeEmptyValues=FALSE, $limit=0)
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]
static exec($command, &$output=NULL, &$returnValue=0)