1<?php 2/** 3 * DokuWiki media passthrough file 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Andreas Gohr <andi@splitbrain.org> 7 */ 8 9 if(!defined('DOKU_INC')) define('DOKU_INC',dirname(__FILE__).'/../../'); 10 define('DOKU_DISABLE_GZIP_OUTPUT', 1); 11 require_once(DOKU_INC.'inc/init.php'); 12 13 //close session 14 session_write_close(); 15 16 $mimetypes = getMimeTypes(); 17 18 //get input 19 $MEDIA = stripctl(getID('media',false)); // no cleaning except control chars - maybe external 20 $CACHE = calc_cache($INPUT->str('cache')); 21 $WIDTH = $INPUT->int('w'); 22 $HEIGHT = $INPUT->int('h'); 23 $REV = &$INPUT->ref('rev'); 24 //sanitize revision 25 $REV = preg_replace('/[^0-9]/','',$REV); 26 27 list($EXT,$MIME,$DL) = mimetype($MEDIA,false); 28 if($EXT === false){ 29 $EXT = 'unknown'; 30 $MIME = 'application/octet-stream'; 31 $DL = true; 32 } 33 34 // check for permissions, preconditions and cache external files 35 list($STATUS, $STATUSMESSAGE) = checkFileStatus($MEDIA, $FILE, $REV); 36 37 // prepare data for plugin events 38 $data = array('media' => $MEDIA, 39 'file' => $FILE, 40 'orig' => $FILE, 41 'mime' => $MIME, 42 'download' => $DL, 43 'cache' => $CACHE, 44 'ext' => $EXT, 45 'width' => $WIDTH, 46 'height' => $HEIGHT, 47 'status' => $STATUS, 48 'statusmessage' => $STATUSMESSAGE, 49 ); 50 51 // handle the file status 52 $evt = new Doku_Event('FETCH_MEDIA_STATUS', $data); 53 if ( $evt->advise_before() ) { 54 // redirects 55 if($data['status'] > 300 && $data['status'] <= 304){ 56 send_redirect($data['statusmessage']); 57 } 58 // send any non 200 status 59 if($data['status'] != 200){ 60 header('HTTP/1.0 ' . $data['status'] . ' ' . $data['statusmessage']); 61 } 62 // die on errors 63 if($data['status'] > 203){ 64 print $data['statusmessage']; 65 exit; 66 } 67 } 68 $evt->advise_after(); 69 unset($evt); 70 71 //handle image resizing/cropping 72 if((substr($MIME,0,5) == 'image') && $WIDTH){ 73 if($HEIGHT){ 74 $data['file'] = $FILE = media_crop_image($data['file'],$EXT,$WIDTH,$HEIGHT); 75 }else{ 76 $data['file'] = $FILE = media_resize_image($data['file'],$EXT,$WIDTH,$HEIGHT); 77 } 78 } 79 80 // finally send the file to the client 81 $evt = new Doku_Event('MEDIA_SENDFILE', $data); 82 if ($evt->advise_before()) { 83 sendFile($data['file'],$data['mime'],$data['download'],$data['cache']); 84 } 85 // Do something after the download finished. 86 $evt->advise_after(); 87 88/* ------------------------------------------------------------------------ */ 89 90/** 91 * Set headers and send the file to the client 92 * 93 * @author Andreas Gohr <andi@splitbrain.org> 94 * @author Ben Coburn <btcoburn@silicodon.net> 95 */ 96function sendFile($file,$mime,$dl,$cache){ 97 global $conf; 98 $fmtime = @filemtime($file); 99 // send headers 100 header("Content-Type: $mime"); 101 // smart http caching headers 102 if ($cache==-1) { 103 // cache 104 // cachetime or one hour 105 header('Expires: '.gmdate("D, d M Y H:i:s", time()+max($conf['cachetime'], 3600)).' GMT'); 106 header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.max($conf['cachetime'], 3600)); 107 header('Pragma: public'); 108 } else if ($cache>0) { 109 // recache 110 // remaining cachetime + 10 seconds so the newly recached media is used 111 header('Expires: '.gmdate("D, d M Y H:i:s", $fmtime+$conf['cachetime']+10).' GMT'); 112 header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.max($fmtime-time()+$conf['cachetime']+10, 0)); 113 header('Pragma: public'); 114 } else if ($cache==0) { 115 // nocache 116 header('Cache-Control: must-revalidate, no-transform, post-check=0, pre-check=0'); 117 header('Pragma: public'); 118 } 119 //send important headers first, script stops here if '304 Not Modified' response 120 http_conditionalRequest($fmtime); 121 122 123 //download or display? 124 if($dl){ 125 header('Content-Disposition: attachment; filename="'.utf8_basename($file).'";'); 126 }else{ 127 header('Content-Disposition: inline; filename="'.utf8_basename($file).'";'); 128 } 129 130 //use x-sendfile header to pass the delivery to compatible webservers 131 if (http_sendfile($file)) exit; 132 133 // send file contents 134 $fp = @fopen($file,"rb"); 135 if($fp){ 136 http_rangeRequest($fp,filesize($file),$mime); 137 }else{ 138 header("HTTP/1.0 500 Internal Server Error"); 139 print "Could not read $file - bad permissions?"; 140 } 141} 142 143/** 144 * Check for media for preconditions and return correct status code 145 * 146 * READ: MEDIA, MIME, EXT, CACHE 147 * WRITE: MEDIA, FILE, array( STATUS, STATUSMESSAGE ) 148 * 149 * @author Gerry Weissbach <gerry.w@gammaproduction.de> 150 * @param $media reference to the media id 151 * @param $file reference to the file variable 152 * @returns array(STATUS, STATUSMESSAGE) 153 */ 154function checkFileStatus(&$media, &$file, $rev='') { 155 global $MIME, $EXT, $CACHE, $INPUT; 156 157 //media to local file 158 if(preg_match('#^(https?)://#i',$media)){ 159 //check hash 160 if(substr(md5(auth_cookiesalt().$media),0,6) !== $INPUT->str('hash')){ 161 return array( 412, 'Precondition Failed'); 162 } 163 //handle external images 164 if(strncmp($MIME,'image/',6) == 0) $file = media_get_from_URL($media,$EXT,$CACHE); 165 if(!$file){ 166 //download failed - redirect to original URL 167 return array( 302, $media ); 168 } 169 }else{ 170 $media = cleanID($media); 171 if(empty($media)){ 172 return array( 400, 'Bad request' ); 173 } 174 175 //check permissions (namespace only) 176 if(auth_quickaclcheck(getNS($media).':X') < AUTH_READ){ 177 return array( 403, 'Forbidden' ); 178 } 179 $file = mediaFN($media, $rev); 180 } 181 182 //check file existance 183 if(!@file_exists($file)){ 184 return array( 404, 'Not Found' ); 185 } 186 187 return array(200, null); 188} 189 190/** 191 * Returns the wanted cachetime in seconds 192 * 193 * Resolves named constants 194 * 195 * @author Andreas Gohr <andi@splitbrain.org> 196 */ 197function calc_cache($cache){ 198 global $conf; 199 200 if(strtolower($cache) == 'nocache') return 0; //never cache 201 if(strtolower($cache) == 'recache') return $conf['cachetime']; //use standard cache 202 return -1; //cache endless 203} 204 205//Setup VIM: ex: et ts=2 : 206