xref: /dokuwiki/lib/exe/fetch.php (revision 3b399a1bd4eba22429d676bddd759ed762c9e80e)
1f62ea8a1Sandi<?php
2f62ea8a1Sandi/**
3f62ea8a1Sandi * DokuWiki media passthrough file
4f62ea8a1Sandi *
5f62ea8a1Sandi * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6f62ea8a1Sandi * @author     Andreas Gohr <andi@splitbrain.org>
7f62ea8a1Sandi */
8f62ea8a1Sandi
9d0a27cb0SAndreas Gohrif(!defined('DOKU_INC')) define('DOKU_INC', dirname(__FILE__).'/../../');
103138b5c7SAndreas Gohrdefine('DOKU_DISABLE_GZIP_OUTPUT', 1);
11f62ea8a1Sandirequire_once(DOKU_INC.'inc/init.php');
1224b3cb1aSAndreas Gohr//close session
138746e727Sandisession_write_close();
148746e727Sandi
15f62ea8a1Sandi$mimetypes = getMimeTypes();
16f62ea8a1Sandi
17f62ea8a1Sandi//get input
1802b0b681SAndreas Gohr$MEDIA  = stripctl(getID('media', false)); // no cleaning except control chars - maybe external
19bfd0f597STom N Harris$CACHE  = calc_cache($INPUT->str('cache'));
20bfd0f597STom N Harris$WIDTH  = $INPUT->int('w');
21bfd0f597STom N Harris$HEIGHT = $INPUT->int('h');
22bfd0f597STom N Harris$REV    = & $INPUT->ref('rev');
23fc4aefb9SKate Arzamastseva//sanitize revision
24fc4aefb9SKate Arzamastseva$REV = preg_replace('/[^0-9]/', '', $REV);
25fc4aefb9SKate Arzamastseva
2627bf7924STom N Harrislist($EXT, $MIME, $DL) = mimetype($MEDIA, false);
27f62ea8a1Sandiif($EXT === false) {
28f62ea8a1Sandi    $EXT  = 'unknown';
29f62ea8a1Sandi    $MIME = 'application/octet-stream';
30ecebf3a8SAndreas Gohr    $DL   = true;
31f62ea8a1Sandi}
32f62ea8a1Sandi
3303293305SAndreas Gohr// check for permissions, preconditions and cache external files
34fc4aefb9SKate Arzamastsevalist($STATUS, $STATUSMESSAGE) = checkFileStatus($MEDIA, $FILE, $REV);
3503293305SAndreas Gohr
3603293305SAndreas Gohr// prepare data for plugin events
37*3b399a1bSAndreas Gohr$data = array(
38*3b399a1bSAndreas Gohr    'media'         => $MEDIA,
39cd98d9c3SGerry Weißbach    'file'          => $FILE,
40cd98d9c3SGerry Weißbach    'orig'          => $FILE,
41cd98d9c3SGerry Weißbach    'mime'          => $MIME,
42cd98d9c3SGerry Weißbach    'download'      => $DL,
43cd98d9c3SGerry Weißbach    'cache'         => $CACHE,
44cd98d9c3SGerry Weißbach    'ext'           => $EXT,
45cd98d9c3SGerry Weißbach    'width'         => $WIDTH,
46cd98d9c3SGerry Weißbach    'height'        => $HEIGHT,
47cd98d9c3SGerry Weißbach    'status'        => $STATUS,
48cd98d9c3SGerry Weißbach    'statusmessage' => $STATUSMESSAGE,
49cd98d9c3SGerry Weißbach);
50f62ea8a1Sandi
5103293305SAndreas Gohr// handle the file status
5203293305SAndreas Gohr$evt = new Doku_Event('FETCH_MEDIA_STATUS', $data);
53cd98d9c3SGerry Weißbachif($evt->advise_before()) {
5403293305SAndreas Gohr    // redirects
5503293305SAndreas Gohr    if($data['status'] > 300 && $data['status'] <= 304) {
5603293305SAndreas Gohr        send_redirect($data['statusmessage']);
5703293305SAndreas Gohr    }
5803293305SAndreas Gohr    // send any non 200 status
5903293305SAndreas Gohr    if($data['status'] != 200) {
60cd98d9c3SGerry Weißbach        header('HTTP/1.0 '.$data['status'].' '.$data['statusmessage']);
6103293305SAndreas Gohr    }
6203293305SAndreas Gohr    // die on errors
6303293305SAndreas Gohr    if($data['status'] > 203) {
64cd98d9c3SGerry Weißbach        print $data['statusmessage'];
65f62ea8a1Sandi        exit;
66f62ea8a1Sandi    }
67f62ea8a1Sandi}
6803293305SAndreas Gohr$evt->advise_after();
6903293305SAndreas Gohrunset($evt);
70f62ea8a1Sandi
7120bc86cfSAndreas Gohr//handle image resizing/cropping
72f62ea8a1Sandiif((substr($MIME, 0, 5) == 'image') && $WIDTH) {
73d52a56e1SAndreas Gohr    if($HEIGHT) {
74cd98d9c3SGerry Weißbach        $data['file'] = $FILE = media_crop_image($data['file'], $EXT, $WIDTH, $HEIGHT);
7520bc86cfSAndreas Gohr    } else {
76cd98d9c3SGerry Weißbach        $data['file'] = $FILE = media_resize_image($data['file'], $EXT, $WIDTH, $HEIGHT);
77f62ea8a1Sandi    }
7820bc86cfSAndreas Gohr}
79f62ea8a1Sandi
80e935fb4aSAndreas Gohr// finally send the file to the client
81b80bedd6SAndreas Gohr$evt = new Doku_Event('MEDIA_SENDFILE', $data);
82b80bedd6SAndreas Gohrif($evt->advise_before()) {
83ecebf3a8SAndreas Gohr    sendFile($data['file'], $data['mime'], $data['download'], $data['cache']);
84b80bedd6SAndreas Gohr}
85cd98d9c3SGerry Weißbach// Do something after the download finished.
86cd98d9c3SGerry Weißbach$evt->advise_after();
87f62ea8a1Sandi
88e935fb4aSAndreas Gohr/* ------------------------------------------------------------------------ */
89f62ea8a1Sandi
90e935fb4aSAndreas Gohr/**
91e935fb4aSAndreas Gohr * Set headers and send the file to the client
92e935fb4aSAndreas Gohr *
93e935fb4aSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
9483730152SBen Coburn * @author Ben Coburn <btcoburn@silicodon.net>
95e935fb4aSAndreas Gohr */
96ecebf3a8SAndreas Gohrfunction sendFile($file, $mime, $dl, $cache) {
9783730152SBen Coburn    global $conf;
9809a5b61fSgweissbach    $fmtime = @filemtime($file);
99e935fb4aSAndreas Gohr    // send headers
100e935fb4aSAndreas Gohr    header("Content-Type: $mime");
10183730152SBen Coburn    // smart http caching headers
10283730152SBen Coburn    if($cache == -1) {
10383730152SBen Coburn        // cache
10483730152SBen Coburn        // cachetime or one hour
10583730152SBen Coburn        header('Expires: '.gmdate("D, d M Y H:i:s", time() + max($conf['cachetime'], 3600)).' GMT');
10683730152SBen Coburn        header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.max($conf['cachetime'], 3600));
107e935fb4aSAndreas Gohr        header('Pragma: public');
10883730152SBen Coburn    } else if($cache > 0) {
10983730152SBen Coburn        // recache
11083730152SBen Coburn        // remaining cachetime + 10 seconds so the newly recached media is used
11183730152SBen Coburn        header('Expires: '.gmdate("D, d M Y H:i:s", $fmtime + $conf['cachetime'] + 10).' GMT');
11283730152SBen Coburn        header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.max($fmtime - time() + $conf['cachetime'] + 10, 0));
11383730152SBen Coburn        header('Pragma: public');
11483730152SBen Coburn    } else if($cache == 0) {
11583730152SBen Coburn        // nocache
11683730152SBen Coburn        header('Cache-Control: must-revalidate, no-transform, post-check=0, pre-check=0');
11783730152SBen Coburn        header('Pragma: public');
11883730152SBen Coburn    }
119ff4f5ee7SBen Coburn    //send important headers first, script stops here if '304 Not Modified' response
12083730152SBen Coburn    http_conditionalRequest($fmtime);
1219a87c72aSAndreas Gohr
122ecebf3a8SAndreas Gohr    //download or display?
123ecebf3a8SAndreas Gohr    if($dl) {
1243009a773SAndreas Gohr        header('Content-Disposition: attachment; filename="'.utf8_basename($file).'";');
125ecebf3a8SAndreas Gohr    } else {
1263009a773SAndreas Gohr        header('Content-Disposition: inline; filename="'.utf8_basename($file).'";');
127f62ea8a1Sandi    }
128f62ea8a1Sandi
1299a87c72aSAndreas Gohr    //use x-sendfile header to pass the delivery to compatible webservers
1306106ad89SChris Smith    if(http_sendfile($file)) exit;
1319a87c72aSAndreas Gohr
132e935fb4aSAndreas Gohr    // send file contents
133e935fb4aSAndreas Gohr    $fp = @fopen($file, "rb");
134f62ea8a1Sandi    if($fp) {
135758447cfSAndreas Gohr        http_rangeRequest($fp, filesize($file), $mime);
136f62ea8a1Sandi    } else {
137f62ea8a1Sandi        header("HTTP/1.0 500 Internal Server Error");
138e935fb4aSAndreas Gohr        print "Could not read $file - bad permissions?";
139e935fb4aSAndreas Gohr    }
140f62ea8a1Sandi}
141f62ea8a1Sandi
14203293305SAndreas Gohr/**
14303293305SAndreas Gohr * Check for media for preconditions and return correct status code
144cd98d9c3SGerry Weißbach *
145cd98d9c3SGerry Weißbach * READ: MEDIA, MIME, EXT, CACHE
146cd98d9c3SGerry Weißbach * WRITE: MEDIA, FILE, array( STATUS, STATUSMESSAGE )
147cd98d9c3SGerry Weißbach *
148cd98d9c3SGerry Weißbach * @author Gerry Weissbach <gerry.w@gammaproduction.de>
149cd98d9c3SGerry Weißbach * @param $media reference to the media id
150cd98d9c3SGerry Weißbach * @param $file  reference to the file variable
151cd98d9c3SGerry Weißbach * @returns array(STATUS, STATUSMESSAGE)
152cd98d9c3SGerry Weißbach */
153fc4aefb9SKate Arzamastsevafunction checkFileStatus(&$media, &$file, $rev = '') {
1545373d847SHakan Sandell    global $MIME, $EXT, $CACHE, $INPUT;
155cd98d9c3SGerry Weißbach
156cd98d9c3SGerry Weißbach    //media to local file
157cd98d9c3SGerry Weißbach    if(preg_match('#^(https?)://#i', $media)) {
158cd98d9c3SGerry Weißbach        //check hash
15958789954SAndreas Gohr        if(substr(md5(auth_cookiesalt().$media), 0, 6) !== $INPUT->str('hash')) {
160cd98d9c3SGerry Weißbach            return array(412, 'Precondition Failed');
161cd98d9c3SGerry Weißbach        }
162cd98d9c3SGerry Weißbach        //handle external images
163cd98d9c3SGerry Weißbach        if(strncmp($MIME, 'image/', 6) == 0) $file = media_get_from_URL($media, $EXT, $CACHE);
164cd98d9c3SGerry Weißbach        if(!$file) {
165cd98d9c3SGerry Weißbach            //download failed - redirect to original URL
16603293305SAndreas Gohr            return array(302, $media);
167cd98d9c3SGerry Weißbach        }
168cd98d9c3SGerry Weißbach    } else {
169cd98d9c3SGerry Weißbach        $media = cleanID($media);
170cd98d9c3SGerry Weißbach        if(empty($media)) {
171cd98d9c3SGerry Weißbach            return array(400, 'Bad request');
172cd98d9c3SGerry Weißbach        }
173cd98d9c3SGerry Weißbach
174cd98d9c3SGerry Weißbach        //check permissions (namespace only)
175cd98d9c3SGerry Weißbach        if(auth_quickaclcheck(getNS($media).':X') < AUTH_READ) {
176c6f610efSAndreas Gohr            return array(403, 'Forbidden');
177cd98d9c3SGerry Weißbach        }
178fc4aefb9SKate Arzamastseva        $file = mediaFN($media, $rev);
179cd98d9c3SGerry Weißbach    }
180cd98d9c3SGerry Weißbach
181cd98d9c3SGerry Weißbach    //check file existance
182cd98d9c3SGerry Weißbach    if(!@file_exists($file)) {
183cd98d9c3SGerry Weißbach        return array(404, 'Not Found');
184cd98d9c3SGerry Weißbach    }
185cd98d9c3SGerry Weißbach
18603293305SAndreas Gohr    return array(200, null);
187cd98d9c3SGerry Weißbach}
188cd98d9c3SGerry Weißbach
189e935fb4aSAndreas Gohr/**
190f62ea8a1Sandi * Returns the wanted cachetime in seconds
191f62ea8a1Sandi *
192f62ea8a1Sandi * Resolves named constants
193f62ea8a1Sandi *
194f62ea8a1Sandi * @author  Andreas Gohr <andi@splitbrain.org>
195f62ea8a1Sandi */
196f62ea8a1Sandifunction calc_cache($cache) {
197f62ea8a1Sandi    global $conf;
198f62ea8a1Sandi
199f62ea8a1Sandi    if(strtolower($cache) == 'nocache') return 0; //never cache
200f62ea8a1Sandi    if(strtolower($cache) == 'recache') return $conf['cachetime']; //use standard cache
201f62ea8a1Sandi    return -1; //cache endless
202f62ea8a1Sandi}
203f62ea8a1Sandi
204e3776c06SMichael Hamann//Setup VIM: ex: et ts=2 :
205