From 3b399a1bd4eba22429d676bddd759ed762c9e80e Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sat, 16 Feb 2013 11:07:36 +0100 Subject: reformatted lib/exe/fetch.php --- lib/exe/fetch.php | 295 +++++++++++++++++++++++++++--------------------------- 1 file changed, 147 insertions(+), 148 deletions(-) (limited to 'lib/exe/fetch.php') diff --git a/lib/exe/fetch.php b/lib/exe/fetch.php index 52e7ebe1e..f86809320 100644 --- a/lib/exe/fetch.php +++ b/lib/exe/fetch.php @@ -6,84 +6,84 @@ * @author Andreas Gohr */ - if(!defined('DOKU_INC')) define('DOKU_INC',dirname(__FILE__).'/../../'); - define('DOKU_DISABLE_GZIP_OUTPUT', 1); - require_once(DOKU_INC.'inc/init.php'); - - //close session - session_write_close(); - - $mimetypes = getMimeTypes(); - - //get input - $MEDIA = stripctl(getID('media',false)); // no cleaning except control chars - maybe external - $CACHE = calc_cache($INPUT->str('cache')); - $WIDTH = $INPUT->int('w'); - $HEIGHT = $INPUT->int('h'); - $REV = &$INPUT->ref('rev'); - //sanitize revision - $REV = preg_replace('/[^0-9]/','',$REV); - - list($EXT,$MIME,$DL) = mimetype($MEDIA,false); - if($EXT === false){ +if(!defined('DOKU_INC')) define('DOKU_INC', dirname(__FILE__).'/../../'); +define('DOKU_DISABLE_GZIP_OUTPUT', 1); +require_once(DOKU_INC.'inc/init.php'); +//close session +session_write_close(); + +$mimetypes = getMimeTypes(); + +//get input +$MEDIA = stripctl(getID('media', false)); // no cleaning except control chars - maybe external +$CACHE = calc_cache($INPUT->str('cache')); +$WIDTH = $INPUT->int('w'); +$HEIGHT = $INPUT->int('h'); +$REV = & $INPUT->ref('rev'); +//sanitize revision +$REV = preg_replace('/[^0-9]/', '', $REV); + +list($EXT, $MIME, $DL) = mimetype($MEDIA, false); +if($EXT === false) { $EXT = 'unknown'; $MIME = 'application/octet-stream'; $DL = true; - } - - // check for permissions, preconditions and cache external files - list($STATUS, $STATUSMESSAGE) = checkFileStatus($MEDIA, $FILE, $REV); - - // prepare data for plugin events - $data = array('media' => $MEDIA, - 'file' => $FILE, - 'orig' => $FILE, - 'mime' => $MIME, - 'download' => $DL, - 'cache' => $CACHE, - 'ext' => $EXT, - 'width' => $WIDTH, - 'height' => $HEIGHT, - 'status' => $STATUS, - 'statusmessage' => $STATUSMESSAGE, - ); - - // handle the file status - $evt = new Doku_Event('FETCH_MEDIA_STATUS', $data); - if ( $evt->advise_before() ) { +} + +// check for permissions, preconditions and cache external files +list($STATUS, $STATUSMESSAGE) = checkFileStatus($MEDIA, $FILE, $REV); + +// prepare data for plugin events +$data = array( + 'media' => $MEDIA, + 'file' => $FILE, + 'orig' => $FILE, + 'mime' => $MIME, + 'download' => $DL, + 'cache' => $CACHE, + 'ext' => $EXT, + 'width' => $WIDTH, + 'height' => $HEIGHT, + 'status' => $STATUS, + 'statusmessage' => $STATUSMESSAGE, +); + +// handle the file status +$evt = new Doku_Event('FETCH_MEDIA_STATUS', $data); +if($evt->advise_before()) { // redirects - if($data['status'] > 300 && $data['status'] <= 304){ - send_redirect($data['statusmessage']); + if($data['status'] > 300 && $data['status'] <= 304) { + send_redirect($data['statusmessage']); } // send any non 200 status - if($data['status'] != 200){ - header('HTTP/1.0 ' . $data['status'] . ' ' . $data['statusmessage']); + if($data['status'] != 200) { + header('HTTP/1.0 '.$data['status'].' '.$data['statusmessage']); } // die on errors - if($data['status'] > 203){ - print $data['statusmessage']; - exit; + if($data['status'] > 203) { + print $data['statusmessage']; + exit; } - } - $evt->advise_after(); - unset($evt); - - //handle image resizing/cropping - if((substr($MIME,0,5) == 'image') && $WIDTH){ - if($HEIGHT){ - $data['file'] = $FILE = media_crop_image($data['file'],$EXT,$WIDTH,$HEIGHT); - }else{ - $data['file'] = $FILE = media_resize_image($data['file'],$EXT,$WIDTH,$HEIGHT); +} +$evt->advise_after(); +unset($evt); + +//handle image resizing/cropping +if((substr($MIME, 0, 5) == 'image') && $WIDTH) { + if($HEIGHT) { + $data['file'] = $FILE = media_crop_image($data['file'], $EXT, $WIDTH, $HEIGHT); + } else { + $data['file'] = $FILE = media_resize_image($data['file'], $EXT, $WIDTH, $HEIGHT); } - } +} - // finally send the file to the client - $evt = new Doku_Event('MEDIA_SENDFILE', $data); - if ($evt->advise_before()) { - sendFile($data['file'],$data['mime'],$data['download'],$data['cache']); - } - // Do something after the download finished. - $evt->advise_after(); +// finally send the file to the client +$evt = new Doku_Event('MEDIA_SENDFILE', $data); +if($evt->advise_before()) { + sendFile($data['file'], $data['mime'], $data['download'], $data['cache']); +} +// Do something after the download finished. +$evt->advise_after(); /* ------------------------------------------------------------------------ */ @@ -93,51 +93,50 @@ * @author Andreas Gohr * @author Ben Coburn */ -function sendFile($file,$mime,$dl,$cache){ - global $conf; - $fmtime = @filemtime($file); - // send headers - header("Content-Type: $mime"); - // smart http caching headers - if ($cache==-1) { - // cache - // cachetime or one hour - header('Expires: '.gmdate("D, d M Y H:i:s", time()+max($conf['cachetime'], 3600)).' GMT'); - header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.max($conf['cachetime'], 3600)); - header('Pragma: public'); - } else if ($cache>0) { - // recache - // remaining cachetime + 10 seconds so the newly recached media is used - header('Expires: '.gmdate("D, d M Y H:i:s", $fmtime+$conf['cachetime']+10).' GMT'); - header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.max($fmtime-time()+$conf['cachetime']+10, 0)); - header('Pragma: public'); - } else if ($cache==0) { - // nocache - header('Cache-Control: must-revalidate, no-transform, post-check=0, pre-check=0'); - header('Pragma: public'); - } - //send important headers first, script stops here if '304 Not Modified' response - http_conditionalRequest($fmtime); - - - //download or display? - if($dl){ - header('Content-Disposition: attachment; filename="'.utf8_basename($file).'";'); - }else{ - header('Content-Disposition: inline; filename="'.utf8_basename($file).'";'); - } - - //use x-sendfile header to pass the delivery to compatible webservers - if (http_sendfile($file)) exit; - - // send file contents - $fp = @fopen($file,"rb"); - if($fp){ - http_rangeRequest($fp,filesize($file),$mime); - }else{ - header("HTTP/1.0 500 Internal Server Error"); - print "Could not read $file - bad permissions?"; - } +function sendFile($file, $mime, $dl, $cache) { + global $conf; + $fmtime = @filemtime($file); + // send headers + header("Content-Type: $mime"); + // smart http caching headers + if($cache == -1) { + // cache + // cachetime or one hour + header('Expires: '.gmdate("D, d M Y H:i:s", time() + max($conf['cachetime'], 3600)).' GMT'); + header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.max($conf['cachetime'], 3600)); + header('Pragma: public'); + } else if($cache > 0) { + // recache + // remaining cachetime + 10 seconds so the newly recached media is used + header('Expires: '.gmdate("D, d M Y H:i:s", $fmtime + $conf['cachetime'] + 10).' GMT'); + header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.max($fmtime - time() + $conf['cachetime'] + 10, 0)); + header('Pragma: public'); + } else if($cache == 0) { + // nocache + header('Cache-Control: must-revalidate, no-transform, post-check=0, pre-check=0'); + header('Pragma: public'); + } + //send important headers first, script stops here if '304 Not Modified' response + http_conditionalRequest($fmtime); + + //download or display? + if($dl) { + header('Content-Disposition: attachment; filename="'.utf8_basename($file).'";'); + } else { + header('Content-Disposition: inline; filename="'.utf8_basename($file).'";'); + } + + //use x-sendfile header to pass the delivery to compatible webservers + if(http_sendfile($file)) exit; + + // send file contents + $fp = @fopen($file, "rb"); + if($fp) { + http_rangeRequest($fp, filesize($file), $mime); + } else { + header("HTTP/1.0 500 Internal Server Error"); + print "Could not read $file - bad permissions?"; + } } /** @@ -148,43 +147,43 @@ function sendFile($file,$mime,$dl,$cache){ * * @author Gerry Weissbach * @param $media reference to the media id - * @param $file reference to the file variable + * @param $file reference to the file variable * @returns array(STATUS, STATUSMESSAGE) */ -function checkFileStatus(&$media, &$file, $rev='') { - global $MIME, $EXT, $CACHE, $INPUT; - - //media to local file - if(preg_match('#^(https?)://#i',$media)){ - //check hash - if(substr(md5(auth_cookiesalt().$media),0,6) !== $INPUT->str('hash')){ - return array( 412, 'Precondition Failed'); - } - //handle external images - if(strncmp($MIME,'image/',6) == 0) $file = media_get_from_URL($media,$EXT,$CACHE); - if(!$file){ - //download failed - redirect to original URL - return array( 302, $media ); - } - }else{ - $media = cleanID($media); - if(empty($media)){ - return array( 400, 'Bad request' ); +function checkFileStatus(&$media, &$file, $rev = '') { + global $MIME, $EXT, $CACHE, $INPUT; + + //media to local file + if(preg_match('#^(https?)://#i', $media)) { + //check hash + if(substr(md5(auth_cookiesalt().$media), 0, 6) !== $INPUT->str('hash')) { + return array(412, 'Precondition Failed'); + } + //handle external images + if(strncmp($MIME, 'image/', 6) == 0) $file = media_get_from_URL($media, $EXT, $CACHE); + if(!$file) { + //download failed - redirect to original URL + return array(302, $media); + } + } else { + $media = cleanID($media); + if(empty($media)) { + return array(400, 'Bad request'); + } + + //check permissions (namespace only) + if(auth_quickaclcheck(getNS($media).':X') < AUTH_READ) { + return array(403, 'Forbidden'); + } + $file = mediaFN($media, $rev); } - //check permissions (namespace only) - if(auth_quickaclcheck(getNS($media).':X') < AUTH_READ){ - return array( 403, 'Forbidden' ); + //check file existance + if(!@file_exists($file)) { + return array(404, 'Not Found'); } - $file = mediaFN($media, $rev); - } - - //check file existance - if(!@file_exists($file)){ - return array( 404, 'Not Found' ); - } - return array(200, null); + return array(200, null); } /** @@ -194,12 +193,12 @@ function checkFileStatus(&$media, &$file, $rev='') { * * @author Andreas Gohr */ -function calc_cache($cache){ - global $conf; +function calc_cache($cache) { + global $conf; - if(strtolower($cache) == 'nocache') return 0; //never cache - if(strtolower($cache) == 'recache') return $conf['cachetime']; //use standard cache - return -1; //cache endless + if(strtolower($cache) == 'nocache') return 0; //never cache + if(strtolower($cache) == 'recache') return $conf['cachetime']; //use standard cache + return -1; //cache endless } //Setup VIM: ex: et ts=2 : -- cgit v1.2.3 From 36625b969932ea8641dc1fd0461d4302c600a2a1 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sat, 16 Feb 2013 11:10:40 +0100 Subject: run main code only for non-tests in lib/exe/fetch.php --- lib/exe/fetch.php | 141 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 72 insertions(+), 69 deletions(-) (limited to 'lib/exe/fetch.php') diff --git a/lib/exe/fetch.php b/lib/exe/fetch.php index f86809320..73e74af40 100644 --- a/lib/exe/fetch.php +++ b/lib/exe/fetch.php @@ -9,81 +9,84 @@ if(!defined('DOKU_INC')) define('DOKU_INC', dirname(__FILE__).'/../../'); define('DOKU_DISABLE_GZIP_OUTPUT', 1); require_once(DOKU_INC.'inc/init.php'); -//close session -session_write_close(); - -$mimetypes = getMimeTypes(); - -//get input -$MEDIA = stripctl(getID('media', false)); // no cleaning except control chars - maybe external -$CACHE = calc_cache($INPUT->str('cache')); -$WIDTH = $INPUT->int('w'); -$HEIGHT = $INPUT->int('h'); -$REV = & $INPUT->ref('rev'); -//sanitize revision -$REV = preg_replace('/[^0-9]/', '', $REV); - -list($EXT, $MIME, $DL) = mimetype($MEDIA, false); -if($EXT === false) { - $EXT = 'unknown'; - $MIME = 'application/octet-stream'; - $DL = true; -} - -// check for permissions, preconditions and cache external files -list($STATUS, $STATUSMESSAGE) = checkFileStatus($MEDIA, $FILE, $REV); - -// prepare data for plugin events -$data = array( - 'media' => $MEDIA, - 'file' => $FILE, - 'orig' => $FILE, - 'mime' => $MIME, - 'download' => $DL, - 'cache' => $CACHE, - 'ext' => $EXT, - 'width' => $WIDTH, - 'height' => $HEIGHT, - 'status' => $STATUS, - 'statusmessage' => $STATUSMESSAGE, -); - -// handle the file status -$evt = new Doku_Event('FETCH_MEDIA_STATUS', $data); -if($evt->advise_before()) { - // redirects - if($data['status'] > 300 && $data['status'] <= 304) { - send_redirect($data['statusmessage']); +session_write_close(); //close session + +// BEGIN main (if not testing) +if(!defined('SIMPLE_TEST')) { + $mimetypes = getMimeTypes(); + + //get input + $MEDIA = stripctl(getID('media', false)); // no cleaning except control chars - maybe external + $CACHE = calc_cache($INPUT->str('cache')); + $WIDTH = $INPUT->int('w'); + $HEIGHT = $INPUT->int('h'); + $REV = & $INPUT->ref('rev'); + //sanitize revision + $REV = preg_replace('/[^0-9]/', '', $REV); + + list($EXT, $MIME, $DL) = mimetype($MEDIA, false); + if($EXT === false) { + $EXT = 'unknown'; + $MIME = 'application/octet-stream'; + $DL = true; } - // send any non 200 status - if($data['status'] != 200) { - header('HTTP/1.0 '.$data['status'].' '.$data['statusmessage']); + + // check for permissions, preconditions and cache external files + list($STATUS, $STATUSMESSAGE) = checkFileStatus($MEDIA, $FILE, $REV); + + // prepare data for plugin events + $data = array( + 'media' => $MEDIA, + 'file' => $FILE, + 'orig' => $FILE, + 'mime' => $MIME, + 'download' => $DL, + 'cache' => $CACHE, + 'ext' => $EXT, + 'width' => $WIDTH, + 'height' => $HEIGHT, + 'status' => $STATUS, + 'statusmessage' => $STATUSMESSAGE, + ); + + // handle the file status + $evt = new Doku_Event('FETCH_MEDIA_STATUS', $data); + if($evt->advise_before()) { + // redirects + if($data['status'] > 300 && $data['status'] <= 304) { + send_redirect($data['statusmessage']); + } + // send any non 200 status + if($data['status'] != 200) { + header('HTTP/1.0 '.$data['status'].' '.$data['statusmessage']); + } + // die on errors + if($data['status'] > 203) { + print $data['statusmessage']; + exit; + } } - // die on errors - if($data['status'] > 203) { - print $data['statusmessage']; - exit; + $evt->advise_after(); + unset($evt); + + //handle image resizing/cropping + if((substr($MIME, 0, 5) == 'image') && $WIDTH) { + if($HEIGHT) { + $data['file'] = $FILE = media_crop_image($data['file'], $EXT, $WIDTH, $HEIGHT); + } else { + $data['file'] = $FILE = media_resize_image($data['file'], $EXT, $WIDTH, $HEIGHT); + } } -} -$evt->advise_after(); -unset($evt); -//handle image resizing/cropping -if((substr($MIME, 0, 5) == 'image') && $WIDTH) { - if($HEIGHT) { - $data['file'] = $FILE = media_crop_image($data['file'], $EXT, $WIDTH, $HEIGHT); - } else { - $data['file'] = $FILE = media_resize_image($data['file'], $EXT, $WIDTH, $HEIGHT); + // finally send the file to the client + $evt = new Doku_Event('MEDIA_SENDFILE', $data); + if($evt->advise_before()) { + sendFile($data['file'], $data['mime'], $data['download'], $data['cache']); } -} + // Do something after the download finished. + $evt->advise_after(); -// finally send the file to the client -$evt = new Doku_Event('MEDIA_SENDFILE', $data); -if($evt->advise_before()) { - sendFile($data['file'], $data['mime'], $data['download'], $data['cache']); -} -// Do something after the download finished. -$evt->advise_after(); +}// END DO main /* ------------------------------------------------------------------------ */ -- cgit v1.2.3 From 9d2e1be699d573eebda922cf67f030d3d2aa462d Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sat, 16 Feb 2013 18:29:20 +0100 Subject: introduced http_status() for sending HTTP status code FS#1698 It seems, some servers require a special Status: header for sending the HTTP status code from PHP (F)CGI to the server. This patch introduces a new function (adopted from CodeIgniter) for simplifying the status handling. --- lib/exe/fetch.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/exe/fetch.php') diff --git a/lib/exe/fetch.php b/lib/exe/fetch.php index 73e74af40..9bac4d272 100644 --- a/lib/exe/fetch.php +++ b/lib/exe/fetch.php @@ -58,7 +58,7 @@ if(!defined('SIMPLE_TEST')) { } // send any non 200 status if($data['status'] != 200) { - header('HTTP/1.0 '.$data['status'].' '.$data['statusmessage']); + http_status($data['status'], $data['statusmessage']); } // die on errors if($data['status'] > 203) { @@ -137,7 +137,7 @@ function sendFile($file, $mime, $dl, $cache) { if($fp) { http_rangeRequest($fp, filesize($file), $mime); } else { - header("HTTP/1.0 500 Internal Server Error"); + http_status(500); print "Could not read $file - bad permissions?"; } } -- cgit v1.2.3 From add8678f233ad74892a96444e3013e0465616200 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 1 Mar 2013 12:54:01 +0100 Subject: alternative fix for FS#2734 --- lib/exe/fetch.php | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'lib/exe/fetch.php') diff --git a/lib/exe/fetch.php b/lib/exe/fetch.php index 9bac4d272..a9147a6c0 100644 --- a/lib/exe/fetch.php +++ b/lib/exe/fetch.php @@ -47,6 +47,7 @@ if(!defined('SIMPLE_TEST')) { 'height' => $HEIGHT, 'status' => $STATUS, 'statusmessage' => $STATUSMESSAGE, + 'ispublic' => media_ispublic($MEDIA), ); // handle the file status @@ -81,10 +82,13 @@ if(!defined('SIMPLE_TEST')) { // finally send the file to the client $evt = new Doku_Event('MEDIA_SENDFILE', $data); if($evt->advise_before()) { - sendFile($data['file'], $data['mime'], $data['download'], $data['cache']); + $cache = $data['cache']; + if($cache != 0 && !$data['ispublic']) $cache = 0; // no cache headers for private files FS#2734 + + sendFile($data['file'], $data['mime'], $data['download'], $cache); } // Do something after the download finished. - $evt->advise_after(); + $evt->advise_after(); // will not be emitted on 304 or x-sendfile }// END DO main @@ -93,8 +97,18 @@ if(!defined('SIMPLE_TEST')) { /** * Set headers and send the file to the client * + * Unless $cache is set to 0, the data may end up in intermediate proxy servers. Therefor, + * if you're sending (ACL protected) private files, $cache should be 0. + * + * This function will abort the current script when a 304 is sent or file sending is handled + * through x-sendfile + * * @author Andreas Gohr * @author Ben Coburn + * @param string $file local file to send + * @param string $mime mime type of the file + * @param bool $dl set to true to force a browser download + * @param int $cache remaining cache time in seconds (-1 for $conf['cache'], 0 for off) */ function sendFile($file, $mime, $dl, $cache) { global $conf; @@ -115,9 +129,10 @@ function sendFile($file, $mime, $dl, $cache) { header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.max($fmtime - time() + $conf['cachetime'] + 10, 0)); header('Pragma: public'); } else if($cache == 0) { - // nocache - header('Cache-Control: must-revalidate, no-transform, post-check=0, pre-check=0'); - header('Pragma: public'); + // nocache, avoid resending files from intermediate caches without revalidation FS#2734 + header('Expires: Thu, 01 Jan 1970 00:00:00 GMT'); + header('Cache-Control: private, no-transform, max-age=0'); + header('Pragma: no-cache'); } //send important headers first, script stops here if '304 Not Modified' response http_conditionalRequest($fmtime); -- cgit v1.2.3 From 4a516840077e2d3bb26c9ffca8316b3c9968d018 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 3 Mar 2013 12:47:40 +0100 Subject: handle public vs. private ressource in sendFile() --- lib/exe/fetch.php | 66 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 27 deletions(-) (limited to 'lib/exe/fetch.php') diff --git a/lib/exe/fetch.php b/lib/exe/fetch.php index a9147a6c0..e8853dca7 100644 --- a/lib/exe/fetch.php +++ b/lib/exe/fetch.php @@ -82,10 +82,7 @@ if(!defined('SIMPLE_TEST')) { // finally send the file to the client $evt = new Doku_Event('MEDIA_SENDFILE', $data); if($evt->advise_before()) { - $cache = $data['cache']; - if($cache != 0 && !$data['ispublic']) $cache = 0; // no cache headers for private files FS#2734 - - sendFile($data['file'], $data['mime'], $data['download'], $cache); + sendFile($data['file'], $data['mime'], $data['download'], $cache, $data['ispublic']); } // Do something after the download finished. $evt->advise_after(); // will not be emitted on 304 or x-sendfile @@ -97,44 +94,59 @@ if(!defined('SIMPLE_TEST')) { /** * Set headers and send the file to the client * - * Unless $cache is set to 0, the data may end up in intermediate proxy servers. Therefor, - * if you're sending (ACL protected) private files, $cache should be 0. + * The $cache parameter influences how long files may be kept in caches, the $public parameter + * influences if this caching may happen in public proxis or in the browser cache only FS#2734 * * This function will abort the current script when a 304 is sent or file sending is handled * through x-sendfile * * @author Andreas Gohr * @author Ben Coburn - * @param string $file local file to send - * @param string $mime mime type of the file - * @param bool $dl set to true to force a browser download - * @param int $cache remaining cache time in seconds (-1 for $conf['cache'], 0 for off) + * @param string $file local file to send + * @param string $mime mime type of the file + * @param bool $dl set to true to force a browser download + * @param int $cache remaining cache time in seconds (-1 for $conf['cache'], 0 for no-cache) + * @param bool $public is this a public ressource or a private one? */ -function sendFile($file, $mime, $dl, $cache) { +function sendFile($file, $mime, $dl, $cache, $public = false) { global $conf; - $fmtime = @filemtime($file); - // send headers + // send mime headers header("Content-Type: $mime"); - // smart http caching headers + + // calculate cache times if($cache == -1) { - // cache - // cachetime or one hour - header('Expires: '.gmdate("D, d M Y H:i:s", time() + max($conf['cachetime'], 3600)).' GMT'); - header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.max($conf['cachetime'], 3600)); - header('Pragma: public'); + $maxage = max($conf['cachetime'], 3600); // cachetime or one hour + $expires = time() + $maxage; } else if($cache > 0) { - // recache - // remaining cachetime + 10 seconds so the newly recached media is used - header('Expires: '.gmdate("D, d M Y H:i:s", $fmtime + $conf['cachetime'] + 10).' GMT'); - header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.max($fmtime - time() + $conf['cachetime'] + 10, 0)); - header('Pragma: public'); - } else if($cache == 0) { - // nocache, avoid resending files from intermediate caches without revalidation FS#2734 + $maxage = $cache; // given time + $expires = time() + $maxage; + } else { // $cache == 0 + $maxage = 0; + $expires = 0; // 1970-01-01 + } + + // smart http caching headers + if($maxage) { + if($public) { + // cache publically + header('Expires: '.gmdate("D, d M Y H:i:s", $expires).' GMT'); + header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.$maxage); + header('Pragma: public'); + } else { + // cache in browser + header('Expires: '.gmdate("D, d M Y H:i:s", $expires).' GMT'); + header('Cache-Control: private, no-transform, max-age='.$maxage); + header('Pragma: private'); + } + } else { + // no cache at all header('Expires: Thu, 01 Jan 1970 00:00:00 GMT'); header('Cache-Control: private, no-transform, max-age=0'); - header('Pragma: no-cache'); + header('Pragma: no-store'); } + //send important headers first, script stops here if '304 Not Modified' response + $fmtime = @filemtime($file); http_conditionalRequest($fmtime); //download or display? -- cgit v1.2.3 From 1c7d84bee69b8965844a960fa91551634986b35f Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 3 Mar 2013 12:51:16 +0100 Subject: fixed passed cache parameter --- lib/exe/fetch.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/exe/fetch.php') diff --git a/lib/exe/fetch.php b/lib/exe/fetch.php index e8853dca7..28401ab39 100644 --- a/lib/exe/fetch.php +++ b/lib/exe/fetch.php @@ -82,7 +82,7 @@ if(!defined('SIMPLE_TEST')) { // finally send the file to the client $evt = new Doku_Event('MEDIA_SENDFILE', $data); if($evt->advise_before()) { - sendFile($data['file'], $data['mime'], $data['download'], $cache, $data['ispublic']); + sendFile($data['file'], $data['mime'], $data['download'], $data['cache'], $data['ispublic']); } // Do something after the download finished. $evt->advise_after(); // will not be emitted on 304 or x-sendfile -- cgit v1.2.3 From 3b6f95e62fc7049712b96aacd245be507f83d5ee Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 3 Mar 2013 20:03:57 +0100 Subject: adjusted cache=0 headers again --- lib/exe/fetch.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/exe/fetch.php') diff --git a/lib/exe/fetch.php b/lib/exe/fetch.php index 28401ab39..8b77fa0b2 100644 --- a/lib/exe/fetch.php +++ b/lib/exe/fetch.php @@ -141,8 +141,8 @@ function sendFile($file, $mime, $dl, $cache, $public = false) { } else { // no cache at all header('Expires: Thu, 01 Jan 1970 00:00:00 GMT'); - header('Cache-Control: private, no-transform, max-age=0'); - header('Pragma: no-store'); + header('Cache-Control: no-cache, no-transform, max-age=0'); + header('Pragma: no-cache'); } //send important headers first, script stops here if '304 Not Modified' response -- cgit v1.2.3 From a6c362b61d32c897d430e72356b4efe5a399c0ac Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 3 Mar 2013 20:08:57 +0100 Subject: max-age not allowed with no-cache --- lib/exe/fetch.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/exe/fetch.php') diff --git a/lib/exe/fetch.php b/lib/exe/fetch.php index 8b77fa0b2..b9270d277 100644 --- a/lib/exe/fetch.php +++ b/lib/exe/fetch.php @@ -141,7 +141,7 @@ function sendFile($file, $mime, $dl, $cache, $public = false) { } else { // no cache at all header('Expires: Thu, 01 Jan 1970 00:00:00 GMT'); - header('Cache-Control: no-cache, no-transform, max-age=0'); + header('Cache-Control: no-cache, no-transform'); header('Pragma: no-cache'); } -- cgit v1.2.3 From 1b8b28faf419d50137b455d4d9a39cfd0fff3f4c Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sun, 3 Mar 2013 21:01:21 +0100 Subject: there's no pragma: private --- lib/exe/fetch.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/exe/fetch.php') diff --git a/lib/exe/fetch.php b/lib/exe/fetch.php index b9270d277..a558a3db8 100644 --- a/lib/exe/fetch.php +++ b/lib/exe/fetch.php @@ -136,7 +136,7 @@ function sendFile($file, $mime, $dl, $cache, $public = false) { // cache in browser header('Expires: '.gmdate("D, d M Y H:i:s", $expires).' GMT'); header('Cache-Control: private, no-transform, max-age='.$maxage); - header('Pragma: private'); + header('Pragma: no-cache'); } } else { // no cache at all -- cgit v1.2.3 From 0f4e009215bfa3136d334fa557335266637a7585 Mon Sep 17 00:00:00 2001 From: Christopher Smith Date: Wed, 20 Mar 2013 00:06:07 +0000 Subject: add a token to fetch urls requiring image resize/crop to prevent external DDOS via fetch --- lib/exe/fetch.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib/exe/fetch.php') diff --git a/lib/exe/fetch.php b/lib/exe/fetch.php index a558a3db8..48aa22fe2 100644 --- a/lib/exe/fetch.php +++ b/lib/exe/fetch.php @@ -32,7 +32,7 @@ if(!defined('SIMPLE_TEST')) { } // check for permissions, preconditions and cache external files - list($STATUS, $STATUSMESSAGE) = checkFileStatus($MEDIA, $FILE, $REV); + list($STATUS, $STATUSMESSAGE) = checkFileStatus($MEDIA, $FILE, $REV, $WIDTH, $HEIGHT); // prepare data for plugin events $data = array( @@ -180,7 +180,7 @@ function sendFile($file, $mime, $dl, $cache, $public = false) { * @param $file reference to the file variable * @returns array(STATUS, STATUSMESSAGE) */ -function checkFileStatus(&$media, &$file, $rev = '') { +function checkFileStatus(&$media, &$file, $rev = '', $width=0, $height=0) { global $MIME, $EXT, $CACHE, $INPUT; //media to local file @@ -200,6 +200,10 @@ function checkFileStatus(&$media, &$file, $rev = '') { if(empty($media)) { return array(400, 'Bad request'); } + // check token for resized images + if (($width || $height) && media_get_token($media, $width, $height) !== $INPUT->str('tok')) { + return array(412, 'Precondition Failed'); + } //check permissions (namespace only) if(auth_quickaclcheck(getNS($media).':X') < AUTH_READ) { -- cgit v1.2.3 From 7fb7960f92047a9bcadf9d497ae79615979e9a6d Mon Sep 17 00:00:00 2001 From: Christopher Smith Date: Fri, 22 Mar 2013 17:45:59 +0000 Subject: refactor fetch to support unittesting --- lib/exe/fetch.php | 160 ++++-------------------------------------------------- 1 file changed, 10 insertions(+), 150 deletions(-) (limited to 'lib/exe/fetch.php') diff --git a/lib/exe/fetch.php b/lib/exe/fetch.php index 48aa22fe2..7a2250373 100644 --- a/lib/exe/fetch.php +++ b/lib/exe/fetch.php @@ -7,12 +7,17 @@ */ if(!defined('DOKU_INC')) define('DOKU_INC', dirname(__FILE__).'/../../'); -define('DOKU_DISABLE_GZIP_OUTPUT', 1); +if (!defined('DOKU_DISABLE_GZIP_OUTPUT')) define('DOKU_DISABLE_GZIP_OUTPUT', 1); require_once(DOKU_INC.'inc/init.php'); session_write_close(); //close session -// BEGIN main (if not testing) -if(!defined('SIMPLE_TEST')) { +require_once(DOKU_INC.'inc/fetch.functions.php'); + +if (defined('SIMPLE_TEST')) { + $INPUT = new Input(); +} + +// BEGIN main $mimetypes = getMimeTypes(); //get input @@ -64,6 +69,7 @@ if(!defined('SIMPLE_TEST')) { // die on errors if($data['status'] > 203) { print $data['statusmessage']; + if (defined('SIMPLE_TEST')) return; exit; } } @@ -87,152 +93,6 @@ if(!defined('SIMPLE_TEST')) { // Do something after the download finished. $evt->advise_after(); // will not be emitted on 304 or x-sendfile -}// END DO main - -/* ------------------------------------------------------------------------ */ - -/** - * Set headers and send the file to the client - * - * The $cache parameter influences how long files may be kept in caches, the $public parameter - * influences if this caching may happen in public proxis or in the browser cache only FS#2734 - * - * This function will abort the current script when a 304 is sent or file sending is handled - * through x-sendfile - * - * @author Andreas Gohr - * @author Ben Coburn - * @param string $file local file to send - * @param string $mime mime type of the file - * @param bool $dl set to true to force a browser download - * @param int $cache remaining cache time in seconds (-1 for $conf['cache'], 0 for no-cache) - * @param bool $public is this a public ressource or a private one? - */ -function sendFile($file, $mime, $dl, $cache, $public = false) { - global $conf; - // send mime headers - header("Content-Type: $mime"); - - // calculate cache times - if($cache == -1) { - $maxage = max($conf['cachetime'], 3600); // cachetime or one hour - $expires = time() + $maxage; - } else if($cache > 0) { - $maxage = $cache; // given time - $expires = time() + $maxage; - } else { // $cache == 0 - $maxage = 0; - $expires = 0; // 1970-01-01 - } - - // smart http caching headers - if($maxage) { - if($public) { - // cache publically - header('Expires: '.gmdate("D, d M Y H:i:s", $expires).' GMT'); - header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.$maxage); - header('Pragma: public'); - } else { - // cache in browser - header('Expires: '.gmdate("D, d M Y H:i:s", $expires).' GMT'); - header('Cache-Control: private, no-transform, max-age='.$maxage); - header('Pragma: no-cache'); - } - } else { - // no cache at all - header('Expires: Thu, 01 Jan 1970 00:00:00 GMT'); - header('Cache-Control: no-cache, no-transform'); - header('Pragma: no-cache'); - } - - //send important headers first, script stops here if '304 Not Modified' response - $fmtime = @filemtime($file); - http_conditionalRequest($fmtime); - - //download or display? - if($dl) { - header('Content-Disposition: attachment; filename="'.utf8_basename($file).'";'); - } else { - header('Content-Disposition: inline; filename="'.utf8_basename($file).'";'); - } - - //use x-sendfile header to pass the delivery to compatible webservers - if(http_sendfile($file)) exit; - - // send file contents - $fp = @fopen($file, "rb"); - if($fp) { - http_rangeRequest($fp, filesize($file), $mime); - } else { - http_status(500); - print "Could not read $file - bad permissions?"; - } -} - -/** - * Check for media for preconditions and return correct status code - * - * READ: MEDIA, MIME, EXT, CACHE - * WRITE: MEDIA, FILE, array( STATUS, STATUSMESSAGE ) - * - * @author Gerry Weissbach - * @param $media reference to the media id - * @param $file reference to the file variable - * @returns array(STATUS, STATUSMESSAGE) - */ -function checkFileStatus(&$media, &$file, $rev = '', $width=0, $height=0) { - global $MIME, $EXT, $CACHE, $INPUT; - - //media to local file - if(preg_match('#^(https?)://#i', $media)) { - //check hash - if(substr(md5(auth_cookiesalt().$media), 0, 6) !== $INPUT->str('hash')) { - return array(412, 'Precondition Failed'); - } - //handle external images - if(strncmp($MIME, 'image/', 6) == 0) $file = media_get_from_URL($media, $EXT, $CACHE); - if(!$file) { - //download failed - redirect to original URL - return array(302, $media); - } - } else { - $media = cleanID($media); - if(empty($media)) { - return array(400, 'Bad request'); - } - // check token for resized images - if (($width || $height) && media_get_token($media, $width, $height) !== $INPUT->str('tok')) { - return array(412, 'Precondition Failed'); - } - - //check permissions (namespace only) - if(auth_quickaclcheck(getNS($media).':X') < AUTH_READ) { - return array(403, 'Forbidden'); - } - $file = mediaFN($media, $rev); - } - - //check file existance - if(!@file_exists($file)) { - return array(404, 'Not Found'); - } - - return array(200, null); -} - -/** - * Returns the wanted cachetime in seconds - * - * Resolves named constants - * - * @author Andreas Gohr - */ -function calc_cache($cache) { - global $conf; - - if(strtolower($cache) == 'nocache') return 0; //never cache - if(strtolower($cache) == 'recache') return $conf['cachetime']; //use standard cache - return -1; //cache endless -} +// END DO main //Setup VIM: ex: et ts=2 : -- cgit v1.2.3