18f5f5da8SGreg Roach<?php 28f5f5da8SGreg Roach/** 38f5f5da8SGreg Roach * webtrees: online genealogy 48f5f5da8SGreg Roach * Copyright (C) 2017 webtrees development team 58f5f5da8SGreg Roach * This program is free software: you can redistribute it and/or modify 68f5f5da8SGreg Roach * it under the terms of the GNU General Public License as published by 78f5f5da8SGreg Roach * the Free Software Foundation, either version 3 of the License, or 88f5f5da8SGreg Roach * (at your option) any later version. 98f5f5da8SGreg Roach * This program is distributed in the hope that it will be useful, 108f5f5da8SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 118f5f5da8SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 128f5f5da8SGreg Roach * GNU General Public License for more details. 138f5f5da8SGreg Roach * You should have received a copy of the GNU General Public License 148f5f5da8SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 158f5f5da8SGreg Roach */ 168f5f5da8SGreg Roachnamespace Fisharebest\Webtrees; 178f5f5da8SGreg Roach 184a9f750fSGreg Roachuse ErrorException; 198f5f5da8SGreg Roachuse League\Glide\Urls\UrlBuilderFactory; 208f5f5da8SGreg Roach 218f5f5da8SGreg Roach/** 228f5f5da8SGreg Roach * A GEDCOM media file. A media object can contain many media files, 238f5f5da8SGreg Roach * such as scans of both sides of a document, the transcript of an audio 248f5f5da8SGreg Roach * recording, etc. 258f5f5da8SGreg Roach */ 268f5f5da8SGreg Roachclass MediaFile { 278f5f5da8SGreg Roach /** @var string The filename */ 288f5f5da8SGreg Roach private $multimedia_file_refn = ''; 298f5f5da8SGreg Roach 308f5f5da8SGreg Roach /** @var string The file extension; jpeg, txt, mp4, etc. */ 318f5f5da8SGreg Roach private $multimedia_format = ''; 328f5f5da8SGreg Roach 338f5f5da8SGreg Roach /** @var string The type of document; newspaper, microfiche, etc. */ 348f5f5da8SGreg Roach private $source_media_type = ''; 358f5f5da8SGreg Roach /** @var string The filename */ 368f5f5da8SGreg Roach 378f5f5da8SGreg Roach /** @var string The name of the document */ 388f5f5da8SGreg Roach private $descriptive_title = ''; 398f5f5da8SGreg Roach 408f5f5da8SGreg Roach /** @var Media $media The media object to which this file belongs */ 418f5f5da8SGreg Roach private $media; 428f5f5da8SGreg Roach 4364b90bf1SGreg Roach /** @var string */ 4464b90bf1SGreg Roach private $fact_id; 4564b90bf1SGreg Roach 468f5f5da8SGreg Roach /** 478f5f5da8SGreg Roach * Create a MediaFile from raw GEDCOM data. 488f5f5da8SGreg Roach * 498f5f5da8SGreg Roach * @param string $gedcom 508f5f5da8SGreg Roach * @param Media $media 518f5f5da8SGreg Roach */ 528f5f5da8SGreg Roach public function __construct($gedcom, Media $media) { 538f5f5da8SGreg Roach $this->media = $media; 5464b90bf1SGreg Roach $this->fact_id = md5($gedcom); 558f5f5da8SGreg Roach 568f5f5da8SGreg Roach if (preg_match('/^\d FILE (.+)/m', $gedcom, $match)) { 578f5f5da8SGreg Roach $this->multimedia_file_refn = $match[1]; 588f5f5da8SGreg Roach } 598f5f5da8SGreg Roach 608f5f5da8SGreg Roach if (preg_match('/^\d FORM (.+)/m', $gedcom, $match)) { 618f5f5da8SGreg Roach $this->multimedia_format = $match[1]; 628f5f5da8SGreg Roach } 638f5f5da8SGreg Roach 648f5f5da8SGreg Roach if (preg_match('/^\d TYPE (.+)/m', $gedcom, $match)) { 658f5f5da8SGreg Roach $this->source_media_type = $match[1]; 668f5f5da8SGreg Roach } 678f5f5da8SGreg Roach 688f5f5da8SGreg Roach if (preg_match('/^\d TITL (.+)/m', $gedcom, $match)) { 698f5f5da8SGreg Roach $this->descriptive_title = $match[1]; 708f5f5da8SGreg Roach } 718f5f5da8SGreg Roach } 728f5f5da8SGreg Roach 738f5f5da8SGreg Roach /** 7464b90bf1SGreg Roach * Get the filename. 758f5f5da8SGreg Roach * 768f5f5da8SGreg Roach * @return string 778f5f5da8SGreg Roach */ 786e6a9947SGreg Roach public function filename(): string { 796e6a9947SGreg Roach return $this->multimedia_file_refn; 808f5f5da8SGreg Roach } 818f5f5da8SGreg Roach 828f5f5da8SGreg Roach /** 8364b90bf1SGreg Roach * Get the format. 848f5f5da8SGreg Roach * 858f5f5da8SGreg Roach * @return string 868f5f5da8SGreg Roach */ 876e6a9947SGreg Roach public function format(): string { 888f5f5da8SGreg Roach return $this->multimedia_format; 898f5f5da8SGreg Roach } 908f5f5da8SGreg Roach 918f5f5da8SGreg Roach /** 9264b90bf1SGreg Roach * Get the type. 938f5f5da8SGreg Roach * 948f5f5da8SGreg Roach * @return string 958f5f5da8SGreg Roach */ 966e6a9947SGreg Roach public function type(): string { 978f5f5da8SGreg Roach return $this->source_media_type; 988f5f5da8SGreg Roach } 998f5f5da8SGreg Roach 1008f5f5da8SGreg Roach /** 10164b90bf1SGreg Roach * Get the title. 1028f5f5da8SGreg Roach * 1038f5f5da8SGreg Roach * @return string 1048f5f5da8SGreg Roach */ 1056e6a9947SGreg Roach public function title(): string { 1068f5f5da8SGreg Roach return $this->descriptive_title; 1078f5f5da8SGreg Roach } 1088f5f5da8SGreg Roach 1098f5f5da8SGreg Roach /** 11064b90bf1SGreg Roach * Get the fact ID. 11164b90bf1SGreg Roach * 11264b90bf1SGreg Roach * @return string 11364b90bf1SGreg Roach */ 11464b90bf1SGreg Roach public function factId(): string { 11564b90bf1SGreg Roach return $this->fact_id; 11664b90bf1SGreg Roach } 11764b90bf1SGreg Roach 11864b90bf1SGreg Roach /** 11964b90bf1SGreg Roach * Display an image-thumbnail or a media-icon, and add markup for image viewers such as colorbox. 12064b90bf1SGreg Roach * 12164b90bf1SGreg Roach * @param int $width Pixels 12264b90bf1SGreg Roach * @param int $height Pixels 12364b90bf1SGreg Roach * @param string $fit "crop" or "contain" 12464b90bf1SGreg Roach * @param string[] $attributes Additional HTML attributes 12564b90bf1SGreg Roach * 12664b90bf1SGreg Roach * @return string 12764b90bf1SGreg Roach */ 12864b90bf1SGreg Roach public function displayImage($width, $height, $fit, $attributes = []) { 12964b90bf1SGreg Roach if ($this->isExternal()) { 13064b90bf1SGreg Roach $src = $this->multimedia_file_refn; 13164b90bf1SGreg Roach $srcset = []; 13264b90bf1SGreg Roach } else { 13364b90bf1SGreg Roach // Generate multiple images for displays with higher pixel densities. 13464b90bf1SGreg Roach $src = $this->imageUrl($width, $height, $fit); 13564b90bf1SGreg Roach $srcset = []; 13664b90bf1SGreg Roach foreach ([2, 3, 4] as $x) { 13764b90bf1SGreg Roach $srcset[] = $this->imageUrl($width * $x, $height * $x, $fit) . ' ' . $x . 'x'; 13864b90bf1SGreg Roach } 13964b90bf1SGreg Roach } 14064b90bf1SGreg Roach 14164b90bf1SGreg Roach $image = '<img ' . Html::attributes($attributes + [ 14264b90bf1SGreg Roach 'dir' => 'auto', 14364b90bf1SGreg Roach 'src' => $src, 14464b90bf1SGreg Roach 'srcset' => implode(',', $srcset), 14564b90bf1SGreg Roach 'alt' => strip_tags($this->media->getFullName()), 14664b90bf1SGreg Roach ]) . '>'; 14764b90bf1SGreg Roach 14864b90bf1SGreg Roach $attributes = Html::attributes([ 14964b90bf1SGreg Roach 'class' => 'gallery', 15064b90bf1SGreg Roach 'type' => $this->mimeType(), 15164b90bf1SGreg Roach 'href' => $this->imageUrl(0, 0, ''), 15264b90bf1SGreg Roach ]); 15364b90bf1SGreg Roach 15464b90bf1SGreg Roach return '<a ' . $attributes . '>' . $image . '</a>'; 15564b90bf1SGreg Roach } 15664b90bf1SGreg Roach 1574a9f750fSGreg Roach /** 1584a9f750fSGreg Roach * A list of image attributes 1594a9f750fSGreg Roach * 1604a9f750fSGreg Roach * @return string[] 1614a9f750fSGreg Roach */ 1624a9f750fSGreg Roach public function attributes(): array { 1634a9f750fSGreg Roach $attributes = []; 16464b90bf1SGreg Roach 1654a9f750fSGreg Roach if (!$this->isExternal() || $this->fileExists()) { 1664a9f750fSGreg Roach $file = $this->folder() . $this->multimedia_file_refn; 1674a9f750fSGreg Roach 1684a9f750fSGreg Roach $attributes['__FILE_SIZE__'] = $this->fileSizeKB(); 1694a9f750fSGreg Roach 1704a9f750fSGreg Roach $imgsize = getimagesize($file); 1714a9f750fSGreg Roach if (is_array($imgsize) && !empty($imgsize['0'])) { 1724a9f750fSGreg Roach $attributes['__IMAGE_SIZE__'] = I18N::translate('%1$s × %2$s pixels', I18N::number($imgsize['0']), I18N::number($imgsize['1'])); 1734a9f750fSGreg Roach } 1744a9f750fSGreg Roach } 1754a9f750fSGreg Roach 1764a9f750fSGreg Roach return $attributes; 1774a9f750fSGreg Roach } 1784a9f750fSGreg Roach 1794a9f750fSGreg Roach /** 1804a9f750fSGreg Roach * check if the file exists on this server 1814a9f750fSGreg Roach * 1824a9f750fSGreg Roach * @return bool 1834a9f750fSGreg Roach */ 1844a9f750fSGreg Roach public function fileExists() { 1854a9f750fSGreg Roach return file_exists($this->folder() . $this->multimedia_file_refn); 1864a9f750fSGreg Roach } 1874a9f750fSGreg Roach 1884a9f750fSGreg Roach /** 1894a9f750fSGreg Roach * Is the media file actually a URL? 1904a9f750fSGreg Roach */ 1914a9f750fSGreg Roach public function isExternal(): bool { 1924a9f750fSGreg Roach return strpos($this->multimedia_file_refn, '://') !== false; 1934a9f750fSGreg Roach } 1944a9f750fSGreg Roach 1954a9f750fSGreg Roach /** 1964a9f750fSGreg Roach * Is the media file an image? 1974a9f750fSGreg Roach */ 1984a9f750fSGreg Roach public function isImage(): bool { 1994a9f750fSGreg Roach return in_array($this->extension(), ['jpeg', 'jpg', 'gif', 'png']); 2004a9f750fSGreg Roach } 2014a9f750fSGreg Roach 2024a9f750fSGreg Roach /** 203*44379edcSGreg Roach * Where is the file stored on disk? 204*44379edcSGreg Roach */ 205*44379edcSGreg Roach public function folder(): string { 206*44379edcSGreg Roach return WT_DATA_DIR . $this->media->getTree()->getPreference('MEDIA_DIRECTORY'); 207*44379edcSGreg Roach } 208*44379edcSGreg Roach 209*44379edcSGreg Roach /** 2104a9f750fSGreg Roach * A user-friendly view of the file size 2114a9f750fSGreg Roach * 2124a9f750fSGreg Roach * @return int 2134a9f750fSGreg Roach */ 2144a9f750fSGreg Roach private function fileSizeBytes(): int { 2154a9f750fSGreg Roach try { 2164a9f750fSGreg Roach return filesize($this->folder() . $this->multimedia_file_refn); 2174a9f750fSGreg Roach } catch (ErrorException $ex) { 2184a9f750fSGreg Roach DebugBar::addThrowable($ex); 2194a9f750fSGreg Roach 2204a9f750fSGreg Roach return 0; 2214a9f750fSGreg Roach } 2224a9f750fSGreg Roach } 2234a9f750fSGreg Roach 2244a9f750fSGreg Roach /** 2254a9f750fSGreg Roach * get the media file size in KB 2264a9f750fSGreg Roach * 2274a9f750fSGreg Roach * @return string 2284a9f750fSGreg Roach */ 2294a9f750fSGreg Roach public function fileSizeKB() { 2304a9f750fSGreg Roach $size = $this->filesizeBytes(); 2314a9f750fSGreg Roach $size = (int) (($size + 1023) / 1024); 2324a9f750fSGreg Roach 2334a9f750fSGreg Roach return /* I18N: size of file in KB */ I18N::translate('%s KB', I18N::number($size)); 2344a9f750fSGreg Roach } 23564b90bf1SGreg Roach 23664b90bf1SGreg Roach /////////////////////////////////////////////////////////////////////////// 23764b90bf1SGreg Roach 23864b90bf1SGreg Roach /** 2398f5f5da8SGreg Roach * Get the filename on the server - for those (very few!) functions which actually 2408f5f5da8SGreg Roach * need the filename, such as mediafirewall.php and the PDF reports. 2418f5f5da8SGreg Roach * 2428f5f5da8SGreg Roach * @return string 2438f5f5da8SGreg Roach */ 2448f5f5da8SGreg Roach public function getServerFilename() { 2458f5f5da8SGreg Roach $MEDIA_DIRECTORY = $this->media->getTree()->getPreference('MEDIA_DIRECTORY'); 2468f5f5da8SGreg Roach 24764b90bf1SGreg Roach if ($this->isExternal() || !$this->multimedia_file_refn) { 2488f5f5da8SGreg Roach // External image, or (in the case of corrupt GEDCOM data) no image at all 24964b90bf1SGreg Roach return $this->multimedia_file_refn; 2508f5f5da8SGreg Roach } else { 2518f5f5da8SGreg Roach // Main image 25264b90bf1SGreg Roach return WT_DATA_DIR . $MEDIA_DIRECTORY . $this->multimedia_file_refn; 2538f5f5da8SGreg Roach } 2548f5f5da8SGreg Roach } 2558f5f5da8SGreg Roach 2568f5f5da8SGreg Roach /** 2578f5f5da8SGreg Roach * get image properties 2588f5f5da8SGreg Roach * 2598f5f5da8SGreg Roach * @return array 2608f5f5da8SGreg Roach */ 2618f5f5da8SGreg Roach public function getImageAttributes() { 2628f5f5da8SGreg Roach $imgsize = []; 2638f5f5da8SGreg Roach if ($this->fileExists()) { 2648f5f5da8SGreg Roach try { 2658f5f5da8SGreg Roach $imgsize = getimagesize($this->getServerFilename()); 2668f5f5da8SGreg Roach if (is_array($imgsize) && !empty($imgsize['0'])) { 2678f5f5da8SGreg Roach // this is an image 2688f5f5da8SGreg Roach $imageTypes = ['', 'GIF', 'JPG', 'PNG', 'SWF', 'PSD', 'BMP', 'TIFF', 'TIFF', 'JPC', 'JP2', 'JPX', 'JB2', 'SWC', 'IFF', 'WBMP', 'XBM']; 2698f5f5da8SGreg Roach $imgsize['ext'] = $imageTypes[0 + $imgsize[2]]; 2708f5f5da8SGreg Roach // this is for display purposes, always show non-adjusted info 2718f5f5da8SGreg Roach $imgsize['WxH'] = /* I18N: image dimensions, width × height */ 2728f5f5da8SGreg Roach I18N::translate('%1$s × %2$s pixels', I18N::number($imgsize['0']), I18N::number($imgsize['1'])); 2738f5f5da8SGreg Roach } 2744a9f750fSGreg Roach } catch (ErrorException $ex) { 2758f5f5da8SGreg Roach DebugBar::addThrowable($ex); 2768f5f5da8SGreg Roach 2778f5f5da8SGreg Roach // Not an image, or not a valid image? 2788f5f5da8SGreg Roach $imgsize = false; 2798f5f5da8SGreg Roach } 2808f5f5da8SGreg Roach } 2818f5f5da8SGreg Roach 2828f5f5da8SGreg Roach if (!is_array($imgsize) || empty($imgsize['0'])) { 2838f5f5da8SGreg Roach // this is not an image, OR the file doesn’t exist OR it is a url 2848f5f5da8SGreg Roach $imgsize[0] = 0; 2858f5f5da8SGreg Roach $imgsize[1] = 0; 2868f5f5da8SGreg Roach $imgsize['ext'] = ''; 2878f5f5da8SGreg Roach $imgsize['mime'] = ''; 2888f5f5da8SGreg Roach $imgsize['WxH'] = ''; 2898f5f5da8SGreg Roach } 2908f5f5da8SGreg Roach 2918f5f5da8SGreg Roach if (empty($imgsize['mime'])) { 2928f5f5da8SGreg Roach // this is not an image, OR the file doesn’t exist OR it is a url 2938f5f5da8SGreg Roach // set file type equal to the file extension - can’t use parse_url because this may not be a full url 2944a9f750fSGreg Roach $exp = explode('?', $this->multimedia_file_refn); 2958f5f5da8SGreg Roach $imgsize['ext'] = strtoupper(pathinfo($exp[0], PATHINFO_EXTENSION)); 2968f5f5da8SGreg Roach // all mimetypes we wish to serve with the media firewall must be added to this array. 2978f5f5da8SGreg Roach $mime = [ 2988f5f5da8SGreg Roach 'DOC' => 'application/msword', 2998f5f5da8SGreg Roach 'MOV' => 'video/quicktime', 3008f5f5da8SGreg Roach 'MP3' => 'audio/mpeg', 3018f5f5da8SGreg Roach 'PDF' => 'application/pdf', 3028f5f5da8SGreg Roach 'PPT' => 'application/vnd.ms-powerpoint', 3038f5f5da8SGreg Roach 'RTF' => 'text/rtf', 3048f5f5da8SGreg Roach 'SID' => 'image/x-mrsid', 3058f5f5da8SGreg Roach 'TXT' => 'text/plain', 3068f5f5da8SGreg Roach 'XLS' => 'application/vnd.ms-excel', 3078f5f5da8SGreg Roach 'WMV' => 'video/x-ms-wmv', 3088f5f5da8SGreg Roach ]; 3098f5f5da8SGreg Roach if (empty($mime[$imgsize['ext']])) { 3108f5f5da8SGreg Roach // if we don’t know what the mimetype is, use something ambiguous 3118f5f5da8SGreg Roach $imgsize['mime'] = 'application/octet-stream'; 3128f5f5da8SGreg Roach if ($this->fileExists()) { 3138f5f5da8SGreg Roach // alert the admin if we cannot determine the mime type of an existing file 3148f5f5da8SGreg Roach // as the media firewall will be unable to serve this file properly 3154a9f750fSGreg Roach Log::addMediaLog('Media Firewall error: >Unknown Mimetype< for file >' . $this->multimedia_file_refn . '<'); 3168f5f5da8SGreg Roach } 3178f5f5da8SGreg Roach } else { 3188f5f5da8SGreg Roach $imgsize['mime'] = $mime[$imgsize['ext']]; 3198f5f5da8SGreg Roach } 3208f5f5da8SGreg Roach } 3218f5f5da8SGreg Roach 3228f5f5da8SGreg Roach return $imgsize; 3238f5f5da8SGreg Roach } 3248f5f5da8SGreg Roach 3258f5f5da8SGreg Roach /** 3268f5f5da8SGreg Roach * Generate a URL for an image. 3278f5f5da8SGreg Roach * 3288f5f5da8SGreg Roach * @param int $width Maximum width in pixels 3298f5f5da8SGreg Roach * @param int $height Maximum height in pixels 3308f5f5da8SGreg Roach * @param string $fit "crop" or "contain" 3318f5f5da8SGreg Roach * 3328f5f5da8SGreg Roach * @return string 3338f5f5da8SGreg Roach */ 3348f5f5da8SGreg Roach public function imageUrl($width, $height, $fit) { 3358f5f5da8SGreg Roach // Sign the URL, to protect against mass-resize attacks. 3368f5f5da8SGreg Roach $glide_key = Site::getPreference('glide-key'); 3378f5f5da8SGreg Roach if (empty($glide_key)) { 3388f5f5da8SGreg Roach $glide_key = bin2hex(random_bytes(128)); 3398f5f5da8SGreg Roach Site::setPreference('glide-key', $glide_key); 3408f5f5da8SGreg Roach } 3418f5f5da8SGreg Roach 34264b90bf1SGreg Roach if (Auth::accessLevel($this->media->getTree()) > $this->media->getTree()->getPreference('SHOW_NO_WATERMARK')) { 3438f5f5da8SGreg Roach $mark = 'watermark.png'; 3448f5f5da8SGreg Roach } else { 3458f5f5da8SGreg Roach $mark = ''; 3468f5f5da8SGreg Roach } 3478f5f5da8SGreg Roach 3484a9f750fSGreg Roach $url_builder = UrlBuilderFactory::create(WT_BASE_URL, $glide_key); 3494a9f750fSGreg Roach 3504a9f750fSGreg Roach $url = $url_builder->getUrl('index.php', [ 3514a9f750fSGreg Roach 'route' => 'media-thumbnail', 3524a9f750fSGreg Roach 'xref' => $this->media->getXref(), 35364b90bf1SGreg Roach 'ged' => $this->media->getTree()->getName(), 3544a9f750fSGreg Roach 'fact_id' => $this->fact_id, 3558f5f5da8SGreg Roach 'w' => $width, 3568f5f5da8SGreg Roach 'h' => $height, 3578f5f5da8SGreg Roach 'fit' => $fit, 3588f5f5da8SGreg Roach 'mark' => $mark, 3598f5f5da8SGreg Roach 'markh' => '100h', 3608f5f5da8SGreg Roach 'markw' => '100w', 3618f5f5da8SGreg Roach 'markalpha' => 25, 3628f5f5da8SGreg Roach 'or' => 0, // Intervention uses exif_read_data() which is very buggy. 3638f5f5da8SGreg Roach ]); 3648f5f5da8SGreg Roach 3658f5f5da8SGreg Roach return $url; 3668f5f5da8SGreg Roach } 3678f5f5da8SGreg Roach 3688f5f5da8SGreg Roach /** 3698f5f5da8SGreg Roach * What file extension is used by this file? 3708f5f5da8SGreg Roach * 3718f5f5da8SGreg Roach * @return string 3728f5f5da8SGreg Roach */ 3738f5f5da8SGreg Roach public function extension() { 374a99c6938SGreg Roach if (preg_match('/\.([a-zA-Z0-9]+)$/', $this->multimedia_file_refn, $match)) { 3758f5f5da8SGreg Roach return strtolower($match[1]); 3768f5f5da8SGreg Roach } else { 3778f5f5da8SGreg Roach return ''; 3788f5f5da8SGreg Roach } 3798f5f5da8SGreg Roach } 3808f5f5da8SGreg Roach 3818f5f5da8SGreg Roach /** 3828f5f5da8SGreg Roach * What is the mime-type of this object? 3838f5f5da8SGreg Roach * For simplicity and efficiency, use the extension, rather than the contents. 3848f5f5da8SGreg Roach * 3858f5f5da8SGreg Roach * @return string 3868f5f5da8SGreg Roach */ 3878f5f5da8SGreg Roach public function mimeType() { 3888f5f5da8SGreg Roach // Themes contain icon definitions for some/all of these mime-types 3898f5f5da8SGreg Roach switch ($this->extension()) { 3908f5f5da8SGreg Roach case 'bmp': 3918f5f5da8SGreg Roach return 'image/bmp'; 3928f5f5da8SGreg Roach case 'doc': 3938f5f5da8SGreg Roach return 'application/msword'; 3948f5f5da8SGreg Roach case 'docx': 3958f5f5da8SGreg Roach return 'application/msword'; 3968f5f5da8SGreg Roach case 'ged': 3978f5f5da8SGreg Roach return 'text/x-gedcom'; 3988f5f5da8SGreg Roach case 'gif': 3998f5f5da8SGreg Roach return 'image/gif'; 4008f5f5da8SGreg Roach case 'htm': 4018f5f5da8SGreg Roach return 'text/html'; 4028f5f5da8SGreg Roach case 'html': 4038f5f5da8SGreg Roach return 'text/html'; 4048f5f5da8SGreg Roach case 'jpeg': 4058f5f5da8SGreg Roach return 'image/jpeg'; 4068f5f5da8SGreg Roach case 'jpg': 4078f5f5da8SGreg Roach return 'image/jpeg'; 4088f5f5da8SGreg Roach case 'mov': 4098f5f5da8SGreg Roach return 'video/quicktime'; 4108f5f5da8SGreg Roach case 'mp3': 4118f5f5da8SGreg Roach return 'audio/mpeg'; 4128f5f5da8SGreg Roach case 'mp4': 4138f5f5da8SGreg Roach return 'video/mp4'; 4148f5f5da8SGreg Roach case 'ogv': 4158f5f5da8SGreg Roach return 'video/ogg'; 4168f5f5da8SGreg Roach case 'pdf': 4178f5f5da8SGreg Roach return 'application/pdf'; 4188f5f5da8SGreg Roach case 'png': 4198f5f5da8SGreg Roach return 'image/png'; 4208f5f5da8SGreg Roach case 'rar': 4218f5f5da8SGreg Roach return 'application/x-rar-compressed'; 4228f5f5da8SGreg Roach case 'swf': 4238f5f5da8SGreg Roach return 'application/x-shockwave-flash'; 4248f5f5da8SGreg Roach case 'svg': 4258f5f5da8SGreg Roach return 'image/svg'; 4268f5f5da8SGreg Roach case 'tif': 4278f5f5da8SGreg Roach return 'image/tiff'; 4288f5f5da8SGreg Roach case 'tiff': 4298f5f5da8SGreg Roach return 'image/tiff'; 4308f5f5da8SGreg Roach case 'xls': 4318f5f5da8SGreg Roach return 'application/vnd-ms-excel'; 4328f5f5da8SGreg Roach case 'xlsx': 4338f5f5da8SGreg Roach return 'application/vnd-ms-excel'; 4348f5f5da8SGreg Roach case 'wmv': 4358f5f5da8SGreg Roach return 'video/x-ms-wmv'; 4368f5f5da8SGreg Roach case 'zip': 4378f5f5da8SGreg Roach return 'application/zip'; 4388f5f5da8SGreg Roach default: 4398f5f5da8SGreg Roach return 'application/octet-stream'; 4408f5f5da8SGreg Roach } 4418f5f5da8SGreg Roach } 4428f5f5da8SGreg Roach} 443