0 && ($download_temp_key !== '' || $override_temp_key !== '')) { $valid_key = validate_temp_download_key($ref, trim($download_temp_key === '' ? $override_temp_key : $download_temp_key), $size); } // External access support (authenticate only if no key provided, or if invalid access key provided) if (!$valid_key && ('' == $k || !check_access_key(getval('ref', '', true), $k)) && getval("slideshow", 0, true) <= 0) { include __DIR__ . '/../include/authenticate.php'; } // Set a flag for logged in users if $external_share_view_as_internal is set and logged on user is accessing an external share $internal_share_access = internal_share_access(); $log_download = true; // Ensure terms have been accepted and usage has been supplied when required. Not for slideshow files etc. $checktermsusage = !in_array($size, $sizes_always_allowed) && $tempfile == "" && $slideshow == 0 && $userfiledownload == "" && (!$video_preview_original && get_resource_access($ref)); if ( $terms_download && $checktermsusage && $iaccept != 'on' ) { exit($lang["mustaccept"]); } if ($download_usage && $checktermsusage) { if (!(is_numeric($usage) && $usage >= 0)) { exit($lang["termsmustindicateusage"]); } if ($usagecomment == '' && !$usage_comment_blank) { exit($lang["termsmustspecifyusagecomment"]); } } if (is_banned_extension($ext)) { $ext = 'jpg'; } $override_key = false; if ($ref > 0 && $override_temp_key !== '') { // Check if the download should be allowed. Permissions have already been considered elsewhere. // Used to display edit page resource preview image after upload where search filter has not yet been set. $override_key = validate_temp_download_key($ref, trim($override_temp_key), $size, 2, false); } // Is this a user specific download? if ('' != $userfiledownload) { $noattach = false; $log_download = true; // Provide a way of overriding $exiftool_write = false depending on download source e.g. from format chooser if ($exiftool_write && $write_exif_data) { $exiftool_write = true; } else { $exiftool_write = false; } $filedetails = explode('_', $userfiledownload); $ref = (int)$filedetails[0]; $downloadkey = strip_extension($filedetails[1]); $ext = safe_file_name(substr($filedetails[1], strlen($downloadkey) + 1)); $path = get_temp_dir(false, 'user_downloads') . '/' . $ref . '_' . md5($username . $downloadkey . $scramble_key) . '.' . $ext; $rqstname = getval("filename", ""); if ($rqstname != "") { $filename = $rqstname . "." . $ext; $filename_prefix = explode('_', $filename); if (($filename_prefix[0] == 'Col' && $ext == 'zip') || ($filename_prefix[0] == 'metadata' && isset($filename_prefix[1]) && $filename_prefix[1] == 'export' && $ext == 'csv')) { // For offline collection or offline csv download, $ref will be user ref not resource ref. $log_download = false; } } hook('modifydownloadpath'); } elseif ($slideshow != 0) { $noattach = true; $log_download = false; $path = __DIR__ . DIRECTORY_SEPARATOR . ".." . DIRECTORY_SEPARATOR . $homeanim_folder . DIRECTORY_SEPARATOR . getval("slideshow", 0, true) . ".jpg"; } elseif ($tempfile != "") { $noattach = true; $log_download = false; $exiftool_write = false; $filedetails = explode('_', $tempfile); if (count($filedetails) >= 3) { $code = safe_file_name($filedetails[0]); $ref = (int)$filedetails[1]; $downloadkey = strip_extension($filedetails[2]); $ext = safe_file_name(substr($filedetails[2], strlen($downloadkey) + 1)); $path = get_temp_dir(false, "") . '/' . $code . '_' . $ref . "_" . md5($username . $downloadkey . $scramble_key) . '.' . $ext; } else { $error = $lang['downloadfile_nofile']; if (getval('ajax', '') != '') { error_alert($error, true, 200); } else { include "../include/header.php"; $onload_message = ['title' => $lang["error"],'text' => $error]; include "../include/footer.php"; } exit(); } } else { $resource_data = get_resource_data($ref); if (!is_array($resource_data)) { $error = $lang["resourcenotfound"]; if (getval("ajax", "") != "") { error_alert($error, true, 200); } else { include "../include/header.php"; $onload_message = array("title" => $lang["error"],"text" => $error); include "../include/footer.php"; } exit(); } if ((int)$resource_data['has_image'] === 0 && $size != "") { // If configured, try and use the preview from a related resource $pullresource = related_resource_pull($resource_data); if ($pullresource !== false) { $resource_data = $pullresource; $ref = $pullresource["ref"]; } } resource_type_config_override($resource_data['resource_type']); // Check permissions $allowed = $override_key ? true : resource_download_allowed($ref, $size, $resource_data['resource_type'], $alternative); debug("PAGES/DOWNLOAD.PHP: \$allowed = " . ($allowed ? 'TRUE' : 'FALSE')); if (!$allowed || $ref <= 0) { $error = $lang['error-permissiondenied']; if (getval("ajax", "") != "") { error_alert($error, true, 200); } else { include "../include/header.php"; $onload_message = array("title" => $lang["error"],"text" => $error); include "../include/footer.php"; } exit(); } // additional access check, as the resource download may be allowed, but access restriction should force watermark. $access = get_resource_access($ref); $use_watermark = check_use_watermark(getval("dl_key", ""), $ref) || $watermarked; // If no extension was provided, we fallback to JPG. if ('' == $ext) { $ext = 'jpg'; } // Where we are getting mp3 preview for videojs, clear size as we want to get the auto generated mp3 file rather than a custom size. // Also allow mp4 files for videojs if $video_preview_original is enabled if ( $size == 'videojs' && ($ext == 'mp3' || ($ext == 'mp4' && $video_preview_original)) ) { $size = ""; $log_download = false; } elseif ($preview_tiles && $allowed && $size == '' && getval('tile_region', 0, true) == 1) { // Provide a tile region if enabled and requested for the main resource. $tile_scale = (int) getval('tile_scale', 1, true); $tile_row = (int) getval('tile_row', 0, true); $tile_col = (int) getval('tile_col', 0, true); $fulljpgsize = strtolower($ext) != "jpg" ? "hpr" : ""; $fullpath = get_resource_path($ref, true, $fulljpgsize, false, "jpg"); $image_size = get_original_imagesize($ref, $fullpath, "jpg"); if ($image_size === false) { debug("PAGES/DOWNLOAD.PHP: File does not exist!"); http_response_code(404); exit($lang['downloadfile_nofile']); } else { $image_width = (int) $image_size[1]; $image_height = (int) $image_size[2]; debug(sprintf('PAGES/DOWNLOAD.PHP: Requesting a tile region with scale=%s, row=%s, col=%s', $tile_scale, $tile_row, $tile_col)); $tiles = compute_tiles_at_scale_factor($tile_scale, $image_width, $image_height); foreach ($tiles as $tile) { if ($tile['column'] == $tile_col && $tile['row'] == $tile_row) { $size = $tile['id']; $ext = 'jpg'; break; } } } } // Establish nonwatermarked path for use when returning snapshot frames $nowmpath = get_resource_path($ref, true, $size, false, $ext, -1, $page, false, '', $alternative, true); $path = get_resource_path($ref, true, $size, false, $ext, -1, $page, $use_watermark && $alternative == -1, '', $alternative, true); $download_extra = hook('download_resource_extra', '', array($path)); // Process depending on whether snapshot frame is to be returned if ($snapshot_frame > 0 && $ffmpeg_snapshot_frames > 1) { // Snapshot frame is to be returned, so adjust the path to be the actual frame requested $path = str_replace('snapshot', "snapshot_{$snapshot_frame}", $nowmpath); } if ($snapshot_frame == 0 && $size == "pre" && $use_watermark && $ext == "mp4") { // Video stream preview size is to be returned and watermark is specified // In this case there is no such thing as a watermarked video preview size, so use the unwatermarked path $path = $nowmpath; } hook('modifydownloadpath'); // Hook to modify the download path. $path_modified = hook('modifydownloadpath2', '', array($download_extra)); if (isset($path_modified) && $path_modified != '' && is_string($path_modified)) { $path = $path_modified; } if (!file_exists($path) && $noattach) { # Return icon for file (for previews) if(in_array($size,["thm","col"])) { $path = '../gfx/no_preview/default_thm.png'; } else { $path = '../gfx/no_preview/default.png'; } } if ($noattach) { if (!allow_in_browser($path)) { // Override the noattach request if file is not valid for display in browser $noattach = false; } if (!($size == "" && $resource_data["file_extension"] == $ext)) { // Do not log downloads for these unless accessing original resource files $log_download = false; } } // Process metadata // Note: only for downloads (not previews) if (!$noattach && -1 == $alternative) { // Strip existing metadata only if we do not plan on writing metadata, otherwise this will be done twice if ($exiftool_remove_existing && !$exiftool_write) { $temp_file_stripped_metadata = createTempFile($path, '', ''); if ($temp_file_stripped_metadata !== false && stripMetadata($temp_file_stripped_metadata)) { $path = $temp_file_stripped_metadata; } } // writing RS metadata to files: exiftool if ($exiftool_write) { $tmpfile = write_metadata($path, $ref); if (false !== $tmpfile && file_exists($tmpfile)) { $path = $tmpfile; } } } } debug("PAGES/DOWNLOAD.PHP: Preparing to download/ stream file '{$path}'"); if (!file_exists($path)) { debug("PAGES/DOWNLOAD.PHP: File does not exist!"); http_response_code(404); exit($lang['downloadfile_nofile']); } hook('modifydownloadfile'); $file_size = filesize_unlimited($path); $file_handle = fopen($path, 'rb'); debug("PAGES/DOWNLOAD.PHP: \$file_size = {$file_size}"); if (!$file_handle) { // File could not be opened debug("PAGES/DOWNLOAD.PHP: File could not be opened!"); http_response_code(500); exit(); } if ( !$noattach && (!isset($filename) || $filename == "" || $alternative != -1) ) { // Compute a file name for the download. $filename = get_download_filename($ref, $size, $alternative, $ext); } if ($log_download) { // Log this activity (download only, not preview) daily_stat('Resource download', $ref); $email_add_to_log = ($email != "") ? ' Downloaded by ' . $email : ""; resource_log($ref, LOG_CODE_DOWNLOADED, 0, $usagecomment . $email_add_to_log, '', '', $usage, $alternative); hook('moredlactions'); // Update hit count if tracking downloads only if ($resource_hit_count_on_downloads) { // greatest() is used so the value is taken from the hit_count column in the event that new_hit_count // is zero to support installations that did not previously have a new_hit_count column (i.e. upgrade compatability). ps_query("UPDATE resource SET new_hit_count = greatest(hit_count, new_hit_count) + 1 WHERE ref = ?", ['i', $ref]); } } // Set appropriate headers for attachment or streamed file if (isset($filename)) { header("Content-Disposition: attachment; filename=\"{$filename}\""); debug("PAGES/DOWNLOAD.PHP: Set header for attachment file"); } else { header("Content-Disposition: inline; filename=\"download.{$ext}\""); header('Content-Transfer-Encoding: binary'); debug("PAGES/DOWNLOAD.PHP: Set header for streamed file"); } // We declare the downloaded content mime type $mime = get_mime_type($path)[0]; header("Content-Type: {$mime}"); header('X-Content-Type-Options: nosniff'); debug("PAGES/DOWNLOAD.PHP: Set MIME type to '{$mime}'"); // Check if http_range is sent by browser (or download manager) $range_requested = false; if (isset($_SERVER['HTTP_RANGE']) && strpos($_SERVER['HTTP_RANGE'], "=") !== false) { # Check it's set and also contains the expected = delimiter debug("PAGES/DOWNLOAD.PHP: HTTP_RANGE is set to '{$_SERVER['HTTP_RANGE']}'"); list($size_unit, $range_orig) = explode('=', $_SERVER['HTTP_RANGE'], 2); if ('bytes' == $size_unit) { /* 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 IMPORTANT: If multiple ranges are not specified, PHP can return an error for "Undefined offset: 1", so we pad the array with an empty string */ list($range, $extra_ranges) = array_pad(explode(',', $range_orig, 2), 2, ''); $range_requested = true; } else { debug("PAGES/DOWNLOAD.PHP: Requested range was not valid"); header('HTTP/1.1 416 Requested Range Not Satisfiable'); header("Content-Range: */{$file_size}"); exit(); } } else { debug("PAGES/DOWNLOAD.PHP: HTTP_RANGE is not set!"); $range = ''; } debug("PAGES/DOWNLOAD.PHP: \$range = {$range}"); // Figure out download piece from range (if set) list($seek_start, $seek_end) = array_pad(explode('-', $range, 2), 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); debug("PAGES/DOWNLOAD.PHP: \$seek_start = {$seek_start}"); debug("PAGES/DOWNLOAD.PHP: \$seek_end = {$seek_end}"); // Only send partial content header if downloading a piece of the file (IE workaround) if (0 < $seek_start || $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)); debug("PAGES/DOWNLOAD.PHP: Content-Range: bytes {$seek_start}-{$seek_end}/{$file_size}"); debug('PAGES/DOWNLOAD.PHP: Content-Length: ' . ($seek_end - $seek_start + 1)); $total_to_send = $seek_end - $seek_start + 1; } else { header("Content-Length: {$file_size}"); if ($range_requested) { // Safari seems to require this header("Content-Range: bytes {$seek_start}-{$seek_end}/{$file_size}"); } debug("PAGES/DOWNLOAD.PHP: Content-Length: {$file_size}"); $total_to_send = $file_size; } header('Accept-Ranges: bytes'); set_time_limit(0); $sent = (0 == fseek($file_handle, $seek_start) ? $seek_start : 0); while ($sent < $file_size) { echo fread($file_handle, $download_chunk_size); ob_flush(); flush(); $sent += $download_chunk_size; if (0 != connection_status()) { break; } } fclose($file_handle); // File send complete, log to daily stat daily_stat('Downloaded KB', $ref, floor($total_to_send / 1024)); // Deleting Exiftool temp File: // Note: Only for downloads (not previews) if (!$noattach && -1 == $alternative && $exiftool_write && isset($tmpfile) && file_exists($tmpfile)) { delete_exif_tmpfile($tmpfile); } if (isset($download_extra)) { hook('beforedownloadresourceexit', '', array($download_extra)); } exit();