<?php
ini_set( 'log_errors', 1 );
ini_set( 'error_log', '/tmp/php-errors.log' );
set_time_limit( 0 );
while( ob_get_level() > 0 ) {
ob_end_flush();
}
if( session_id() === "" ) {
session_start();
}
ignore_user_abort( true );
$filename = get_sanitized_filename();
$valid_filename = !empty( $filename );
$expiry = 24 * 60 * 60;
if( $valid_filename && download( $filename ) && token_expired( $expiry ) ) {
increment_count( "$filename-count.txt" );
}
function get_sanitized_filename() {
$filepath = isset( $_GET[ 'filename' ] ) ? $_GET[ 'filename' ] : '';
$fileinfo = pathinfo( $filepath );
$basename = $fileinfo[ 'basename' ];
if( isset( $_SERVER[ 'HTTP_USER_AGENT' ] ) ) {
$periods = substr_count( $basename, '.' );
$basename = strstr( $_SERVER[ 'HTTP_USER_AGENT' ], 'MSIE' )
? mb_ereg_replace( '/\./', '%2e', $basename, $periods - 1 )
: $basename;
}
$basename = mb_ereg_replace( '/\s+/', '', $basename );
$basename = mb_ereg_replace( '([^\w\d\-_~,;\[\]\(\).])', '', $basename );
return $basename;
}
function token_expired( $lifetime ) {
$TOKEN_NAME = 'LAST_DOWNLOAD';
$now = time();
$expired = !isset( $_SESSION[ $TOKEN_NAME ] );
if( !$expired && ($now - $_SESSION[ $TOKEN_NAME ] > $lifetime) ) {
$expired = true;
$_SESSION = array();
session_destroy();
}
$_SESSION[ $TOKEN_NAME ] = $now;
$TOKEN_CREATE = 'CREATED';
if( !isset( $_SESSION[ $TOKEN_CREATE ] ) ) {
$_SESSION[ $TOKEN_CREATE ] = $now;
}
else if( $now - $_SESSION[ $TOKEN_CREATE ] > $lifetime ) {
session_regenerate_id( true );
$_SESSION[ $TOKEN_CREATE ] = $now;
}
return $expired;
}
function download( $filename ) {
clearstatcache();
$size = @filesize( $filename );
$size = $size === false || empty( $size ) ? 0 : $size;
$content_type = mime_content_type( $filename );
list( $seek_start, $content_length ) = parse_range( $size );
header_remove( 'x-powered-by' );
header( 'Expires: 0' );
header( 'Cache-Control: public, must-revalidate, post-check=0, pre-check=0' );
header( 'Cache-Control: private', false );
header( "Content-Disposition: attachment; filename=\"$filename\"" );
header( 'Accept-Ranges: bytes' );
header( "Content-Length: $content_length" );
header( "Content-Type: $content_type" );
$method = isset( $_SERVER[ 'REQUEST_METHOD' ] )
? $_SERVER[ 'REQUEST_METHOD' ]
: 'GET';
return $method === 'HEAD'
? false
: transmit( $filename, $seek_start, $size );
}
function parse_range( $size ) {
$seek_start = 0;
$content_length = $size;
if( isset( $_SERVER[ 'HTTP_RANGE' ] ) ) {
$range_format = '/^bytes=\d*-\d*(,\d*-\d*)*$/';
$request_range = $_SERVER[ 'HTTP_RANGE' ];
if( !preg_match( $range_format, $request_range, $matches ) ) {
header( 'HTTP/1.1 416 Requested Range Not Satisfiable' );
header( "Content-Range: bytes */$size" );
exit;
}
$seek_start = isset( $matches[ 1 ] ) ? $matches[ 1 ] + 0 : 0;
$seek_end = isset( $matches[ 2 ] ) ? $matches[ 2 ] + 0 : $size - 1;
$range_bytes = $seek_start . '-' . $seek_end . '/' . $size;
$content_length = $seek_end - $seek_start + 1;
header( 'HTTP/1.1 206 Partial Content' );
header( "Content-Range: bytes $range_bytes" );
}
return array( $seek_start, $content_length );
}
function transmit( $filename, $seek_start, $size ) {
if( ob_get_level() == 0 ) {
ob_start();
}
$bytes_sent = -1;
$fp = @fopen( $filename, 'rb' );
if( $fp !== false ) {
@fseek( $fp, $seek_start );
$aborted = false;
$bytes_sent = $seek_start;
$chunk_size = 1024 * 16;
while( !feof( $fp ) && !$aborted ) {
print( @fread( $fp, $chunk_size ) );
$bytes_sent += $chunk_size;
if( ob_get_level() > 0 ) {
ob_flush();
}
flush();
$aborted = connection_aborted() || connection_status() != 0;
}
if( ob_get_level() > 0 ) {
ob_end_flush();
}
fclose( $fp );
}
return $bytes_sent >= $size;
}
function increment_count( $filename ) {
try {
lock_open( $filename );
$count = @file_get_contents( $filename ) + 0;
file_put_contents( $filename, $count + 1 );
}
finally {
lock_close( $filename );
}
}
function lock_open( $filename ) {
$lockdir = create_lock_filename( $filename );
$iterations = 0;
do {
if( @mkdir( $lockdir, 0777 ) ) {
$iterations = 0;
}
else {
$iterations++;
$lifetime = time() - filemtime( $lockdir );
if( $lifetime > 10 ) {
@rmdir( $lockdir );
}
else {
usleep( rand( 1000, 10000 ) );
}
}
}
while( $iterations > 0 && $iterations < 10 );
return $iterations == 0;
}
function lock_close( $filename ) {
@rmdir( create_lock_filename( $filename ) );
}
function create_lock_filename( $filename ) {
return $filename .'.lock';
}
?>