function _file_download_transfer in Ubercart 5
Send the file's binary data to a user via HTTP and update the uc_file_users table.
Supports resume and download managers.
Parameters
$file_user: The file_user object from the uc_file_users
$ip: The string containing the ip address the download is going to
$fid: The file id of the file to transfer
1 call to _file_download_transfer()
- _file_download in uc_file/
uc_file.module - Perform first-pass authorization. Call authorization hooks afterwards.
File
- uc_file/
uc_file.module, line 1125 - Allows products to be associated with downloadable files.
Code
function _file_download_transfer($file_user, $ip, $fid) {
$file = db_result(db_query("SELECT filename FROM {uc_files} WHERE fid = %d", $fid));
$file_path = variable_get('uc_file_base_dir', NULL) . '/' . $file;
if (!is_file($file_path)) {
drupal_set_message(t('The file %filename could not be found. Please contact the site administrator.', array(
'%filename' => basename($file),
)), 'error');
watchdog('uc_file', t('%username failed to download the file %filename.', array(
'%username' => $message_user,
'%filename' => basename($file),
)), WATCHDOG_NOTICE);
drupal_not_found();
exit;
}
else {
//Check any if any hook_file_transfer_alter calls alter the download
foreach (module_implements('file_transfer_alter') as $module) {
$name = $module . '_file_transfer_alter';
$file_path = $name($file_user, $ip, $fid, $file_path);
}
//Gather relevent info about file
$size = filesize($file_path);
$fileinfo = pathinfo($file_path);
// Workaround for IE filename bug with multiple periods / multiple dots in filename
// that adds square brackets to filename - eg. setup.abc.exe becomes setup[1].abc.exe
if (strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
$filename = preg_replace('/\\./', '%2e', $fileinfo['basename'], substr_count($fileinfo['basename'], '.') - 1);
}
else {
$filename = $fileinfo['basename'];
}
// Compatibility workaround for older versions of Drupal 5
if (function_exists('file_get_mimetype')) {
$mimetype = file_get_mimetype($filename);
}
else {
// Set the Content-Type based on file extension.
$file_extension = strtolower($fileinfo['extension']);
switch ($file_extension) {
case 'exe':
$mimetype = 'application/octet-stream';
break;
case 'zip':
$mimetype = 'application/zip';
break;
case 'mp3':
$mimetype = 'audio/mpeg';
break;
case 'mpg':
$mimetype = 'video/mpeg';
break;
case 'avi':
$mimetype = 'video/x-msvideo';
break;
default:
$mimetype = 'application/force-download';
}
}
// 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
// See http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt
list($range, $extra_ranges) = explode(',', $range_orig, 2);
}
else {
$range = '';
}
}
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 and check for invalid ranges.
$seek_end = intval(empty($seek_end) ? $size - 1 : min(abs(intval($seek_end)), $size - 1));
$seek_start = intval(empty($seek_start) || $seek_end < abs(intval($seek_start)) ? 0 : max(abs(intval($seek_start)), 0));
ob_end_clean();
// Start building the array of headers
$http_headers = array();
//Only send partial content header if downloading a piece of the file (IE workaround)
if ($seek_start > 0 || $seek_end < $size - 1) {
drupal_set_header('HTTP/1.1 206 Partial Content');
}
// Standard headers, including content-range and length
drupal_set_header('Pragma: public');
drupal_set_header('Cache-Control: cache, must-revalidate');
drupal_set_header('Accept-Ranges: bytes');
drupal_set_header('Content-Range: bytes ' . $seek_start . '-' . $seek_end . '/' . $size);
drupal_set_header('Content-Type: ' . $mimetype);
drupal_set_header('Content-Disposition: attachment; filename="' . $filename . '"');
drupal_set_header('Content-Length: ' . ($seek_end - $seek_start + 1));
// Last-modified is required for content served dynamically
drupal_set_header('Last-modified: ' . format_date(filemtime($file_path), 'large'));
// Etag header is required for Firefox3 and other managers
drupal_set_header('ETag: ' . md5($file_path));
// Open the file and seek to starting byte
$fp = fopen($file_path, 'rb');
fseek($fp, $seek_start);
// Start buffered download
while (!feof($fp)) {
// Reset time limit for large files
set_time_limit(0);
print fread($fp, 1024 * 8);
flush();
ob_flush();
}
// Finished serving the file, close the stream and log the download to the user table
fclose($fp);
_user_table_action('download', $file_user, $ip);
exit;
}
}