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