/** * Determine whether there is enough memory to load a particular image. * * @param string $serverFilename * * @return bool */ public static function hasMemoryForImage($serverFilename) { // find out how much total memory this script can access $memoryAvailable = self::sizeToBytes(ini_get('memory_limit')); // if memory is unlimited, it will return -1 and we don’t need to worry about it if ($memoryAvailable == -1) { return true; } // find out how much memory we are already using $memoryUsed = memory_get_usage(); try { $imgsize = getimagesize($serverFilename); } catch (\ErrorException $ex) { // Not an image, or not a valid image? $imgsize = false; } // find out how much memory this image needs for processing, probably only works for jpegs // from comments on http://www.php.net/imagecreatefromjpeg if ($imgsize && isset($imgsize['bits']) && isset($imgsize['channels'])) { $memoryNeeded = round(($imgsize[0] * $imgsize[1] * $imgsize['bits'] * $imgsize['channels'] / 8 + Pow(2, 16)) * 1.65); $memorySpare = $memoryAvailable - $memoryUsed - $memoryNeeded; if ($memorySpare > 0) { // we have enough memory to load this file return true; } else { // not enough memory to load this file $image_info = sprintf('%.2fKB, %d × %d %d bits %d channels', filesize($serverFilename) / 1024, $imgsize[0], $imgsize[1], $imgsize['bits'], $imgsize['channels']); Log::addMediaLog('Cannot create thumbnail ' . $serverFilename . ' (' . $image_info . ') memory avail: ' . $memoryAvailable . ' used: ' . $memoryUsed . ' needed: ' . $memoryNeeded . ' spare: ' . $memorySpare); return false; } } else { // assume there is enough memory // TODO find out how to check memory needs for gif and png return true; } }
/** * get image properties * * @param string $which specify either 'main' or 'thumb' * @param int $addWidth amount to add to width * @param int $addHeight amount to add to height * * @return array */ public function getImageAttributes($which = 'main', $addWidth = 0, $addHeight = 0) { $THUMBNAIL_WIDTH = $this->tree->getPreference('THUMBNAIL_WIDTH'); $var = $which . 'imagesize'; if (!empty($this->{$var})) { return $this->{$var}; } $imgsize = array(); if ($this->fileExists($which)) { try { $imgsize = getimagesize($this->getServerFilename($which)); if (is_array($imgsize) && !empty($imgsize['0'])) { // this is an image $imgsize[0] = $imgsize[0] + 0; $imgsize[1] = $imgsize[1] + 0; $imgsize['adjW'] = $imgsize[0] + $addWidth; // adjusted width $imgsize['adjH'] = $imgsize[1] + $addHeight; // adjusted height $imageTypes = array('', 'GIF', 'JPG', 'PNG', 'SWF', 'PSD', 'BMP', 'TIFF', 'TIFF', 'JPC', 'JP2', 'JPX', 'JB2', 'SWC', 'IFF', 'WBMP', 'XBM'); $imgsize['ext'] = $imageTypes[0 + $imgsize[2]]; // this is for display purposes, always show non-adjusted info $imgsize['WxH'] = I18N::translate('%1$s × %2$s pixels', I18N::number($imgsize['0']), I18N::number($imgsize['1'])); $imgsize['imgWH'] = ' width="' . $imgsize['adjW'] . '" height="' . $imgsize['adjH'] . '" '; if ($which == 'thumb' && $imgsize['0'] > $THUMBNAIL_WIDTH) { // don’t let large images break the dislay $imgsize['imgWH'] = ' width="' . $THUMBNAIL_WIDTH . '" '; } } } catch (\ErrorException $ex) { // Not an image, or not a valid image? $imgsize = false; } } if (!is_array($imgsize) || empty($imgsize['0'])) { // this is not an image, OR the file doesn’t exist OR it is a url $imgsize[0] = 0; $imgsize[1] = 0; $imgsize['adjW'] = 0; $imgsize['adjH'] = 0; $imgsize['ext'] = ''; $imgsize['mime'] = ''; $imgsize['WxH'] = ''; $imgsize['imgWH'] = ''; if ($this->isExternal()) { // don’t let large external images break the dislay $imgsize['imgWH'] = ' width="' . $THUMBNAIL_WIDTH . '" '; } } if (empty($imgsize['mime'])) { // this is not an image, OR the file doesn’t exist OR it is a url // set file type equal to the file extension - can’t use parse_url because this may not be a full url $exp = explode('?', $this->file); $imgsize['ext'] = strtoupper(pathinfo($exp[0], PATHINFO_EXTENSION)); // all mimetypes we wish to serve with the media firewall must be added to this array. $mime = array('DOC' => 'application/msword', 'MOV' => 'video/quicktime', 'MP3' => 'audio/mpeg', 'PDF' => 'application/pdf', 'PPT' => 'application/vnd.ms-powerpoint', 'RTF' => 'text/rtf', 'SID' => 'image/x-mrsid', 'TXT' => 'text/plain', 'XLS' => 'application/vnd.ms-excel', 'WMV' => 'video/x-ms-wmv'); if (empty($mime[$imgsize['ext']])) { // if we don’t know what the mimetype is, use something ambiguous $imgsize['mime'] = 'application/octet-stream'; if ($this->fileExists($which)) { // alert the admin if we cannot determine the mime type of an existing file // as the media firewall will be unable to serve this file properly Log::addMediaLog('Media Firewall error: >Unknown Mimetype< for file >' . $this->file . '<'); } } else { $imgsize['mime'] = $mime[$imgsize['ext']]; } } $this->{$var} = $imgsize; return $this->{$var}; }
FlashMessages::addMessage(I18N::translate('There was an error uploading your file.') . '<br>' . Functions::fileUploadErrorText($_FILES['mediafile']['error'])); break; } // Now copy the (optional) thumbnail if (!empty($_FILES['thumbnail']['name']) && preg_match('/^image\\/(png|gif|jpeg)/', $_FILES['thumbnail']['type'], $match)) { // Thumbnails have either // (a) the same filename as the main image // (b) the same filename as the main image - but with a .png extension if ($match[1] == 'png' && !preg_match('/\\.(png)$/i', $fileName)) { $thumbFile = preg_replace('/\\.[a-z0-9]{3,5}$/', '.png', $fileName); } else { $thumbFile = $fileName; } $serverFileName = WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName . $thumbFile; if (move_uploaded_file($_FILES['thumbnail']['tmp_name'], $serverFileName)) { Log::addMediaLog('Thumbnail file ' . $serverFileName . ' uploaded'); } } } $controller->pageHeader(); // Build the gedcom record $newged = "0 @new@ OBJE"; if ($tag[0] == 'FILE') { // The admin has an edit field to change the filename $text[0] = $folderName . $fileName; } else { // Users keep the original filename $newged .= "\n1 FILE " . $folderName . $fileName; } $newged = FunctionsEdit::handleUpdates($newged); $new_media = $WT_TREE->createRecord($newged);
/** * Render the image to the output. */ public function render() { if (!$this->media || !$this->media->canShow()) { Log::addMediaLog('Image Builder error: >' . I18N::translate('Missing or private media object.')); $this->renderError(); } $serverFilename = $this->media->getServerFilename(); if (!file_exists($serverFilename)) { Log::addMediaLog('Image Builder error: >' . I18N::translate('The media object does not exist.') . '< for path >' . $serverFilename . '<'); $this->renderError(); } $mimetype = $this->media->mimeType(); $imgsize = $this->media->getImageAttributes(); $filetime = $this->media->getFiletime(); $filetimeHeader = gmdate('D, d M Y H:i:s', $filetime) . ' GMT'; $expireHeader = gmdate('D, d M Y H:i:s', WT_TIMESTAMP + $this->getExpireOffset()) . ' GMT'; $type = Functions::isImageTypeSupported($imgsize['ext']); $usewatermark = false; // if this image supports watermarks and the watermark module is intalled... if ($type) { $usewatermark = $this->isShowWatermark(); } // determine whether we have enough memory to watermark this image if ($usewatermark) { if (!FunctionsMedia::hasMemoryForImage($serverFilename)) { // not enough memory to watermark this file $usewatermark = false; } } $etag = $this->media->getEtag(); // parse IF_MODIFIED_SINCE header from client $if_modified_since = 'x'; if (!empty(Filter::server('HTTP_IF_MODIFIED_SINCE'))) { $if_modified_since = preg_replace('/;.*$/', '', Filter::server('HTTP_IF_MODIFIED_SINCE')); } // parse IF_NONE_MATCH header from client $if_none_match = 'x'; if (!empty(Filter::server('HTTP_IF_NONE_MATCH'))) { $if_none_match = str_replace('"', '', Filter::server('HTTP_IF_NONE_MATCH')); } // add caching headers. allow browser to cache file, but not proxy header('Last-Modified: ' . $filetimeHeader); header('ETag: "' . $etag . '"'); header('Expires: ' . $expireHeader); header('Cache-Control: max-age=' . $this->getExpireOffset() . ', s-maxage=0, proxy-revalidate'); // if this file is already in the user’s cache, don’t resend it // first check if the if_modified_since param matches if ($if_modified_since === $filetimeHeader) { // then check if the etag matches if ($if_none_match === $etag) { http_response_code(304); return; } } // send headers for the image header('Content-Type: ' . $mimetype); header('Content-Disposition: filename="' . addslashes(basename($this->media->getFilename())) . '"'); if ($usewatermark) { // generate the watermarked image $imCreateFunc = 'imagecreatefrom' . $type; $imSendFunc = 'image' . $type; if (function_exists($imCreateFunc) && function_exists($imSendFunc)) { $im = $imCreateFunc($serverFilename); $im = $this->applyWatermark($im); // send the image $imSendFunc($im); imagedestroy($im); return; } else { // this image is defective. log it Log::addMediaLog('Image Builder error: >' . I18N::translate('This media file is broken and cannot be watermarked.') . '< in file >' . $serverFilename . '< memory used: ' . memory_get_usage()); } } // determine filesize of image (could be original or watermarked version) $filesize = filesize($serverFilename); // set content-length header, send file header('Content-Length: ' . $filesize); // Some servers disable fpassthru() and readfile() if (function_exists('readfile')) { readfile($serverFilename); } else { $fp = fopen($serverFilename, 'rb'); if (function_exists('fpassthru')) { fpassthru($fp); } else { while (!feof($fp)) { echo fread($fp, 65536); } } fclose($fp); } }
$im = $imCreateFunc($serverFilename); $im = applyWatermark($im, $WT_TREE); // save the image, if preferences allow if ($which === 'thumb' && $WT_TREE->getPreference('SAVE_WATERMARK_THUMB') || $which === 'main' && $WT_TREE->getPreference('SAVE_WATERMARK_IMAGE')) { // make sure the folder exists File::mkdir(dirname($watermarkfile)); // save the image $imSendFunc($im, $watermarkfile); } // send the image $imSendFunc($im); imagedestroy($im); return; } else { // this image is defective. log it Log::addMediaLog('Media Firewall error: >' . I18N::translate('This media file is broken and cannot be watermarked.') . '< in file >' . $serverFilename . '< memory used: ' . memory_get_usage()); // set usewatermark to false so image will simply be passed through below $usewatermark = false; } } // pass the image through without manipulating it if ($usewatermark) { // the stored watermarked image is good, lets use it $serverFilename = $watermarkfile; } // determine filesize of image (could be original or watermarked version) $filesize = filesize($serverFilename); // set content-length header, send file header('Content-Length: ' . $filesize); // Some servers disable fpassthru() and readfile() if (function_exists('readfile')) {