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