I'm doing a CTF security challenge, I found a "downloader.php" page with a possible vulnerability.
it lets me download any file from the server, including "downloader.php" itself, the problem is running through all known php files, I couldn't find an access point.
I'm thinking of entering through the downloader.php itself, but it has a filename treatment with preg_replace, and I need to work around that.
Here is the code of the file "download.php":
<?php
$dnpath = $_GET['name'];
$dnpath = str_replace("..",".", $dnpath);
$dnpath = str_replace("..",".", $dnpath);
$dnpath = str_replace("..",".", $dnpath);
$dnpath = str_replace("..",".", $dnpath);
$dnpath = str_replace("..",".", $dnpath);
$dnpath = str_replace("..",".", $dnpath);
$dnpath = str_replace("..",".", $dnpath);
$dnpath = str_replace("..",".", $dnpath);
$dnpath = str_replace("..",".", $dnpath);
$dnpath = str_replace("..",".", $dnpath);
$dnpath = preg_replace("/[ #\&\+\%#=\\\:;,'\"\^`~|\!\?\*$#<>()\[\]\{\}]/i", "", $dnpath);
$lastDate = '0';
$file = "C:/test_root/".$dnpath;
if (!file_exists($file))
{
echo "File not exist : ";
echo $file;
exit;
}
Header("Content-type: application/octet-stream");
Header("Content-Length: ".filesize($file));
Header("Content-Disposition: attachment; filename=\"$dnpath\"");
Header("Pragma: no-cache");
ob_clean();
flush();
readfile($file);
return true;
?>
i tried something like:
downloader.php?name=alert(%22XSS%22)
but without success because of preg_replace
Related
Every time i make the xlsx file, i can't open it without error: "content that could not be read was found in the book".
Code:
function getNameFromNumber($num) {
$numeric = ($num - 1) % 26;
$letter = chr(65 + $numeric);
$num2 = intval(($num - 1) / 26);
if ($num2 > 0) {
return getNameFromNumber($num2) . $letter;
} else {
return $letter;
}
}
require_once $_SERVER['DOCUMENT_ROOT'] . '/local/vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
$objPHPExcel = new Spreadsheet();
$date = date("Y-m-d H:i:s");
// Set document properties
$objPHPExcel->getProperties()
->setTitle("Выгрузка заявлений от" . $date)
->setSubject("Выгрузка заявлений от" . $date)
->setDescription("Выгрузка заявлений по фильтру от" . $date);
// Rename worksheet
$objPHPExcel->getActiveSheet()->setTitle('Выгрузка заявлений по фильтру');
// Set active sheet index to the first sheet, so Excel opens this as the first sheet
$objPHPExcel->setActiveSheetIndex(0);
$headers = ['ID', 'Фамилия абитуриента', 'Имя абитуриента', 'Отчество абитуриента', 'EMAIL', 'Статус', 'Условния приема', 'Уровень образования', 'Дата отправки заявления'];
if($arParams['GET_DOCS'] == 'Y')
$headers[] = 'Ссылки на файлы';
$activeSheet = $objPHPExcel->setActiveSheetIndex(0);
foreach( $headers as $key => $header ){
$activeSheet->setCellValue(getNameFromNumber($key + 1) . '1' , $header);
}
foreach($arResult["ITEMS"] as $rowIndex => $arItem){
//Here i prepare $row to set values
$c = 0;
foreach($row as $columnIndex => $item ){
$c++;
$activeSheet->setCellValue(getNameFromNumber($c) . (2+$rowIndex) , $item);
}
$c = 0;
foreach($row as $columnIndex => $item ){
$c++;
$activeSheet->getColumnDimension(getNameFromNumber($c))->setAutoSize(true);
}
}
$APPLICATION->RestartBuffer();
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="' . $date . '.xlsx"');
header('Cache-Control: max-age=0');
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($objPHPExcel, 'Xlsx');
$writer->save('php://output');
I tried to make empty file -
$objPHPExcel = new Spreadsheet();
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="123.xlsx"');
header('Cache-Control: max-age=0');
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($objPHPExcel, 'Xlsx');
$writer->save('php://output');
it doesn't help. Error doesn't disappear
My MS Excel can recovery this file correctly, but it wouldn't open in OpenOffice or something else, so my client can't open it on MAC. How can i resolve this problem?
mb_internal_encoding('latin1');
That helps
sorry if this is not a new question but i'm really stuck in this problem. I'm using script below for downloading a large file like a movie, I can not let user to access direct link of the file, also I need to let user download the file in a resume and section support manner. Using this code I just have resume support, not section. I'm using Yii framework.
Please Help me on this by any solution and suggestion.
public static function downloadFile($fileLocation, $saveName = null, $maxSpeed = 100, $doStream = false){
$start = 0;
$end = -1;
$section = false;
$extension = CFileHelper::getExtension($fileLocation);
$fileName = is_null($saveName) ? basename($fileLocation) : $saveName . '.' . $extension;
/* #var $contentType string mime type for the file, if is null, it will be octet-stream */
$contentType = CFileHelper::getMimeType($fileLocation);
$contentType = is_null($contentType) ? 'application/octet-stream' : $contentType;
if(isset($_SERVER['HTTP_RANGE']))
{
$range2 = substr($_SERVER['HTTP_RANGE'], strlen('bytes='));
$range = explode('-', $range2);
if($range[0] > 0)
$start = intval($range[0]);
if($range[1] > 0)
$end = intval($range[1]);
$section = true;
}
ob_end_clean();
$old_status = ignore_user_abort(true);
set_time_limit(0);
$size = filesize($fileLocation);
if($start > ($size -1)) $start = 0;
$fp = fopen($fileLocation, 'rb');
if($start) fseek($fp, $start);
if($end < $start) $end = $size -1;
header('Content-Type: '.$contentType);
$contentDisposition = 'attachment';
if($doStream == true){
$array_listen = array('mp3','m3u','m4a','mid','ogg','ra','ram','wm',
'wav','wma','aac','3gp','avi','mov','mp4','mpeg','mpg','swf','wmv','divx','asf');
if(in_array($extension,$array_listen)){
$contentDisposition = 'inline';
}
}
if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE")) {
$fileName= preg_replace('/\./', '%2e', $fileName, substr_count($fileName, '.') - 1);
header("Content-Disposition: $contentDisposition; filename=\"$fileName\"");
} else {
header("Content-Disposition: $contentDisposition; filename=\"$fileName\"");
}
header('Content-Disposition: ' . $contentDisposition . '; filename="' . $fileName . '"');
header('Last-Modified: ' . date('D, d M Y H:i:s \G\M\T', filemtime($fileLocation)));
if($section)
{
header("HTTP/1.0 206 Partial Content");
header("Status: 206 Partial Content");
header('Accept-Ranges: bytes');
header("Content-Range: bytes $start-$end/$size");
header("Content-Length: " . ($end - $start + 1));
}else
header('Content-Length: '.$size);
$size = $end - $start + 1;
while(!(connection_aborted() || connection_status() == 1) && !feof($fp))
{
print(fread($fp,1024*$maxSpeed));
flush();
ob_flush();
sleep(1);
}
fclose($fp);
ignore_user_abort($old_status);
set_time_limit(ini_get('max_execution_time'));
}
If you are using apache and able to install mods to it, you can use mod_xsendfile module. It supports partial downloads, cashing and other cool things.
Simple demo: Send Files Faster & Better with PHP & X-Sendfile
I wrote this code to download an image from instagram. the image downloaded but it have some errors. that's mean I can download the image but I cant open it
<?php
$linktopage = 'http://instagram.com/p/jTh1cBHG36/';
$sourcecode = file_get_contents( $linktopage );
$sourcecode = substr($sourcecode , strpos($sourcecode, 'og:image') + 19, strlen($sourcecode));
$sourcecode = substr($sourcecode , 0 , strpos($sourcecode, '"'));
$name= substr($sourcecode , strpos($sourcecode, 'com/') + 4, strlen($sourcecode));
$fileToSend = $sourcecode;
header('Content-type: image/jpeg');
header('Content-Disposition: attachment; filename='.$name.'"');
header("Content-Length: ". file_size($fileToSend));
readfile($fileToSend);
?>
Comment the Content-Length line and it will work fine:
//header("Content-Length: ". file_size($fileToSend));
The correct function name is filesize (and I'm not sure if it will work on your URL).
I also see that you are missing a quote after filename=. It should be:
header('Content-Disposition: attachment; filename="'.$name.'"');
So, the working version of your code will be:
$linktopage = 'http://instagram.com/p/jTh1cBHG36/';
$sourcecode = file_get_contents( $linktopage );
$sourcecode = substr($sourcecode , strpos($sourcecode, 'og:image') + 19, strlen($sourcecode));
$sourcecode = substr($sourcecode , 0 , strpos($sourcecode, '"'));
$name= substr($sourcecode , strpos($sourcecode, 'com/') + 4, strlen($sourcecode));
$fileToSend = $sourcecode;
header('Content-type: image/jpeg');
header('Content-Disposition: attachment; filename="'.$name.'"');
//header("Content-Length: ". filesize($fileToSend));
readfile($fileToSend);
The problem is happening because of a typo error.
Typo error was in the following line:
header('Content-Disposition: attachment; filename='.$name.'"');
Due to the wrong name format '_' was getting added to the file extension.
One more addition in the code is for content length which is 2 line only:
$head = array_change_key_case(get_headers($sourcecode, TRUE));
$filesize = $head['content-length'];
Final Working code with typo error fixed:
<?php
$linktopage = 'http://instagram.com/p/jTh1cBHG36/';
$sourcecode = file_get_contents( $linktopage );
$sourcecode = substr($sourcecode , strpos($sourcecode, 'og:image') + 19, strlen($sourcecode));
$sourcecode = substr($sourcecode , 0 , strpos($sourcecode, '"'));
$name= substr($sourcecode , strpos($sourcecode, 'com/') + 4, strlen($sourcecode));
$fileToSend = $sourcecode;
$head = array_change_key_case(get_headers($sourcecode, TRUE));
$filesize = $head['content-length'];
header("Content-type: image/jpeg");
header("Content-Disposition: attachment; filename=".$name);
header("Content-Length: ". $filesize);
readfile($fileToSend);
?>
I have attached outlook msg file in php application. I am storing that file in sql server database.
Now i want to open and display it from browser.
I tried this code :
if($ext=="msg")
{
header('ContentType : application/octet-stream');
header('Content-Disposition: attachment; filename='. basename($filename));
echo base64_decode($file);
}
$filename and $file are coming from database.
Its opening msg file in outlook from IE and chrome but its not openning from firefox.
Is there any way to make it working in all the browser ?
Or Am i wrong somewhere or is there any setting in browser ?
I had an almost similar situation and was able to solve it.
Include the headers below and it should work just fine.
Regards
header("Pragma: public");
header("Expires: 0");
header('Content-Encoding: UTF-8');
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-type: application/vnd.ms-outlook;charset=UTF-8");
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");;
header("Content-Disposition: attachment;filename=test.msg");
header("Content-Transfer-Encoding: binary ");
#composer require hfig/mapi
# needed if you want to convert to MIME format
#composer require swiftmailer/swiftmailer
require 'vendor/autoload.php';
use Hfig\MAPI;
use Hfig\MAPI\OLE\Pear;
$decodelocation = '/var/html/tmp/';
$baseurl = 'http://example.com/tmp/';
$uniquefolder = uniqid();
// message parsing and file IO are kept separate
$messageFactory = new MAPI\MapiMessageFactory();
$documentFactory = new Pear\DocumentFactory();
$ole = $documentFactory->createFromFile('source-file.msg');
$message = $messageFactory->parseMessage($ole);
$html = preg_replace_callback($this->regex, "utf8replacer", $message->getBodyHTML());
if (count($message->getAttachments()) > 0) {
foreach ($message->getAttachments() as $attach) {
$filename = $attach->getFilename();
$temploc = $decodelocation . '/' . $uniquefolder . '/' . $filename;
$fileurl = $baseurl . '/' . $uniquefolder . '/' . $filename;
$replace_string = get_string_between($html, 'cid:' . $filename, '"');
if ($replace_string) {
file_put_contents($temploc, $attach->getData());
$html = str_replace('cid:' . $filename . $replace_string, base_url($temploc), $html);
} else {
$geturl = array(
'filename' => $filename,
'path' => cencode($temploc),
);
$attachments[] = '<a target="_blank" href="' . $fileurl . '">' . $filename . '</a>';
}
}
}
foreach ($message->getRecipients() as $recipient) {
$email = $recipient->getEmail();
$name = $recipient->getName();
if ($recipient->getType() == 'From') {
$From[] = ($name) ? '' . $name . '' : '' . $email . '';
} elseif ($recipient->getType() == 'To') {
$To[] = ($name) ? '' . $name . '' : '' . $email . '';
} elseif ($recipient->getType() == 'Cc') {
$Cc[] = ($name) ? '' . $name . '' : '' . $email . '';
} elseif ($recipient->getType() == 'Bcc') {
$Bcc[] = ($name) ? '' . $name . '' : '' . $email . '';
}
}
$data = array(
'From' => '' . $message->properties['sender_name'] . '',
'To' => ($To) ? implode('; ', $To) : '',
'Cc' => ($Cc) ? implode('; ', $Cc) : '',
'Bcc' => ($Bcc) ? implode('; ', $Bcc) : '',
'Subject' => $message->properties['subject'],
'hasAttachment' => $message->properties['hasattach'],
'attachments' => ($attachments) ? implode('; ', $attachments) : false,
'html' => $html,
);
#customize your html page using data array. It is take long time for greater than 5 MB.
if($ext=="msg")
{
header("Content-Type: text/Calendar");
header('Content-Disposition: inline; filename='. basename($filename));
echo base64_decode($file);
}
I've searched many topics and posts and made my own downloading snippet with 2 functions as below!
Download starts with no problem, custom errors are shown when user hasn't enough credit, all things are right but the resume! I thought it maybe Headers, but I didn't miss them.
I read many posts on stackoverflow too.
Helps needed!
function readfile_chunked($filename, $size = '', $uid, $pid, $retbytes = TRUE)
{
global $each_download;
$chunk_size = 1024*1024;
$buffer = '';
$cnt =0;
$handle = fopen($filename, 'rb');
if ($handle === false)
{
return false;
}
//check if http_range is sent by browser (or download manager)
if(isset($_SERVER['HTTP_RANGE']))
{
list($a, $range)=explode("=",$_SERVER['HTTP_RANGE']);
str_replace($range, "-", $range);
$size2=$size-1;
$new_length=$size2-$range;
header("HTTP/1.1 206 Partial Content");
header("Content-Length: $new_length");
header("Content-Range: bytes $range$size2/$size");
fseek($fp,$range);
}
else
{
if(get_user_meta($uid, 'revo_dropped', true) != $pid)
{
update_user_meta($uid, 'dropped', $pid);
update_user_meta($uid, 'credits', get_user_meta($uid, 'credits', true)-$each_download);
}
$size2 = $size-1;
header("Content-Range: bytes 0-$size2/$size");
header("Content-Length: ".$size);
}
while (!feof($handle) && connection_status()==0)
{
#set_time_limit(0);
$buffer = fread($handle, $chunk_size);
echo $buffer;
ob_flush();
flush();
if ($retbytes)
{
$cnt += strlen($buffer);
}
}
$status = fclose($handle);
if ($retbytes && $status)
{
return $cnt;
}
return $status;
}
function save_file()
{
global $wpdb;
global $uid;
global $each_download;
$hash = #$_GET["download"];
ini_set('zlib.output_compression', 'Off');
set_time_limit(0);
session_cache_limiter('none');
set_magic_quotes_runtime(0);
if(preg_match('/^[a-z0-9]{32}$/i',$hash))
{
if($row = $wpdb->get_row("SELECT * FROM wp_dlurl WHERE hash = '{$hash}'",ARRAY_A))
{
if(is_user_logged_in())
{
if($row['price'] != 0)
$each_download = $row['price'];
if(get_user_meta($uid, 'credits', true) >= $each_download)
{
$parts = pathinfo($row['url']);
$url = $parts['dirname'] . '/' . $parts['basename'];//urlencode($parts['basename']);
$file = pathinfo($row['filename']);
$ext = $file['extension'];
/* List of File Types */
$fileTypes['swf'] = 'application/x-shockwave-flash';
$fileTypes['pdf'] = 'application/pdf';
$fileTypes['exe'] = 'application/octet-stream';
$fileTypes['zip'] = 'application/zip';
$fileTypes['doc'] = 'application/msword';
$fileTypes['xls'] = 'application/vnd.ms-excel';
$fileTypes['ppt'] = 'application/vnd.ms-powerpoint';
$fileTypes['gif'] = 'image/gif';
$fileTypes['png'] = 'image/png';
$fileTypes['jpeg'] = 'image/jpg';
$fileTypes['jpg'] = 'image/jpg';
$fileTypes['rar'] = 'application/rar';
$fileTypes['ra'] = 'audio/x-pn-realaudio';
$fileTypes['ram'] = 'audio/x-pn-realaudio';
$fileTypes['ogg'] = 'audio/x-pn-realaudio';
$fileTypes['wav'] = 'video/x-msvideo';
$fileTypes['wmv'] = 'video/x-msvideo';
$fileTypes['avi'] = 'video/x-msvideo';
$fileTypes['asf'] = 'video/x-msvideo';
$fileTypes['divx'] = 'video/x-msvideo';
$fileTypes['mp3'] = 'audio/mpeg';
$fileTypes['mp4'] = 'audio/mpeg';
$fileTypes['mpeg'] = 'video/mpeg';
$fileTypes['mpg'] = 'video/mpeg';
$fileTypes['mpe'] = 'video/mpeg';
$fileTypes['mov'] = 'video/quicktime';
$fileTypes['swf'] = 'video/quicktime';
$fileTypes['3gp'] = 'video/quicktime';
$fileTypes['m4a'] = 'video/quicktime';
$fileTypes['aac'] = 'video/quicktime';
$fileTypes['m3u'] = 'video/quicktime';
$contentType = $fileTypes[$ext];
//ob_end_clean();
ob_start();
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Robots: none");
header("Content-Type: ".$contentType."");
header("Content-Description: File Transfer");
$new_name = $row['filename'];//rand(1000,999999).".".$ext;
$contentDisposition = 'attachment';
if(strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))
{
$new_name = preg_replace('/\./', '%2e', $new_name, substr_count($new_name,'.') - 1);
}
$new_name = urlencode($new_name);
//#ob_end_clean();
header("Content-Disposition: ".$contentDisposition."; filename=\"".$new_name."\";");
header("Content-Transfer-Encoding: binary");
header("Accept-Ranges: bytes");
$size = $row['size'];
$thefile = $url;
$urlparsed = parse_url($thefile); // urlllllllllllll
$isURI = array_key_exists('scheme', $urlparsed);
$localURI = (bool) strstr($thefile, get_bloginfo('wpurl'));
$patterns = array( '|^'. get_bloginfo('wpurl') . '/' . '|');
$path = preg_replace( $patterns, '', $thefile );
// this is joining the ABSPATH constant, changing any slashes to local filesystem slashes, and then finally getting the real path.
$thefile = str_replace( '/', DIRECTORY_SEPARATOR, path_join( ABSPATH, $path ) );
///////////////////////////////////
if($size == 0)
{
showMessage('Size 0');
}
if (isset($size) && $size > 0)
{
//header("Content-Length: ".$size);
#readfile_chunked($thefile, $size, $uid, $pid);
}
///////////////////////////////////
exit;
}
else
showMessage("buy credit");
}
else
showMessage("login");
}
else
{
wp_redirect(get_bloginfo('url'));
exit;
}
}
else
{
wp_redirect(get_bloginfo('url'));
exit;
}
}
This script might help you out:
<?php
/**
* Copyright 2012 Armand Niculescu - media-division.com
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// get the file request, throw error if nothing supplied
// hide notices
#ini_set('error_reporting', E_ALL & ~ E_NOTICE);
//- turn off compression on the server
#apache_setenv('no-gzip', 1);
#ini_set('zlib.output_compression', 'Off');
if(!isset($_REQUEST['file']) || empty($_REQUEST['file']))
{
header("HTTP/1.0 400 Bad Request");
exit;
}
// sanitize the file request, keep just the name and extension
// also, replaces the file location with a preset one ('./myfiles/' in this example)
$file_path = $_REQUEST['file'];
$path_parts = pathinfo($file_path);
$file_name = $path_parts['basename'];
$file_ext = $path_parts['extension'];
$file_path = './myfiles/' . $file_name;
// allow a file to be streamed instead of sent as an attachment
$is_attachment = isset($_REQUEST['stream']) ? false : true;
// make sure the file exists
if (is_file($file_path))
{
$file_size = filesize($file_path);
$file = #fopen($file_path,"rb");
if ($file)
{
// set the headers, prevent caching
header("Pragma: public");
header("Expires: -1");
header("Cache-Control: public, must-revalidate, post-check=0, pre-check=0");
header("Content-Disposition: attachment; filename=\"$file_name\"");
// set appropriate headers for attachment or streamed file
if ($is_attachment)
header("Content-Disposition: attachment; filename=\"$file_name\"");
else
header('Content-Disposition: inline;');
// set the mime type based on extension, add yours if needed.
$ctype_default = "application/octet-stream";
$content_types = array(
"exe" =--> "application/octet-stream",
"zip" => "application/zip",
"mp3" => "audio/mpeg",
"mpg" => "video/mpeg",
"avi" => "video/x-msvideo",
);
$ctype = isset($content_types[$file_ext]) ? $content_types[$file_ext] : $ctype_default;
header("Content-Type: " . $ctype);
//check if http_range is sent by browser (or download manager)
if(isset($_SERVER['HTTP_RANGE']))
{
list($size_unit, $range_orig) = explode('=', $_SERVER['HTTP_RANGE'], 2);
if ($size_unit == 'bytes')
{
//multiple ranges could be specified at the same time, but for simplicity only serve the first range
//http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt
list($range, $extra_ranges) = explode(',', $range_orig, 2);
}
else
{
$range = '';
header('HTTP/1.1 416 Requested Range Not Satisfiable');
exit;
}
}
else
{
$range = '';
}
//figure out download piece from range (if set)
list($seek_start, $seek_end) = explode('-', $range, 2);
//set start and end based on range (if set), else set defaults
//also check for invalid ranges.
$seek_end = (empty($seek_end)) ? ($file_size - 1) : min(abs(intval($seek_end)),($file_size - 1));
$seek_start = (empty($seek_start) || $seek_end < abs(intval($seek_start))) ? 0 : max(abs(intval($seek_start)),0);
//Only send partial content header if downloading a piece of the file (IE workaround)
if ($seek_start > 0 || $seek_end < ($file_size - 1))
{
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes '.$seek_start.'-'.$seek_end.'/'.$file_size);
header('Content-Length: '.($seek_end - $seek_start + 1));
}
else
header("Content-Length: $file_size");
header('Accept-Ranges: bytes');
set_time_limit(0);
fseek($file, $seek_start);
while(!feof($file))
{
print(#fread($file, 1024*8));
ob_flush();
flush();
if (connection_status()!=0)
{
#fclose($file);
exit;
}
}
// file save was a success
#fclose($file);
exit;
}
else
{
// file couldn't be opened
header("HTTP/1.0 500 Internal Server Error");
exit;
}
}
else
{
// file does not exist
header("HTTP/1.0 404 Not Found");
exit;
}
Taken from:
http://www.richnetapps.com/php-download-script-with-resume-option/