File: /home/assotraipi/www/wp-content/plugins/download-monitor/src/DownloadHandler.php
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
} // Exit if accessed directly
if ( ! class_exists( 'DLM_Download_Handler' ) ) {
/**
* DLM_Download_Handler class.
*
* Represents the class that handles download requests and download process.
*/
class DLM_Download_Handler {
/**
* Endpoint name
*
* @var string
*/
private $endpoint;
/**
* Endpoint value
*
* @var string
*/
private $ep_value;
/**
* DLM_Logging instance
*
* @var DLM_Logging
*/
public $dlm_logging;
/**
* Constructor
*/
public function __construct() {
$this->ep_value = ( $ep_value = get_option( 'dlm_download_endpoint_value' ) ) ? $ep_value : 'ID';
$this->dlm_logging = DLM_Logging::get_instance();
}
/**
* Setup Download Handler class
*/
public function setup() {
add_filter( 'query_vars', array( $this, 'add_query_vars' ), 0 );
add_action( 'init', array( $this, 'add_endpoint' ), 0 );
add_action( 'parse_request', array( $this, 'handler' ), 0 );
}
/**
* add_query_vars function.
*
* @access public
* @return array
*/
public function add_query_vars( $vars ) {
$vars[] = $this->endpoint;
return $vars;
}
/**
* add_endpoint function.
*
* @access public
* @return void
*/
public function add_endpoint() {
$this->endpoint = get_option( 'dlm_download_endpoint', 'download' );
if ( function_exists( 'icl_get_languages' ) && has_filter( 'wpml_translate_single_string' ) ) {
$languages = icl_get_languages( 'skip_missing=0' );
if ( ! empty( $languages ) ) {
foreach ( $languages as $lang_code => $language ) {
$translated_endpoint = apply_filters( 'wpml_translate_single_string', $this->endpoint, 'admin_texts_dlm_download_endpoint', 'dlm_download_endpoint', $lang_code );
if ( ! empty( $translated_endpoint ) ) {
$arr[] = $translated_endpoint;
add_rewrite_endpoint( $translated_endpoint, EP_ALL );
}
}
}
} else {
// Let's make sure that the endpoint is not empty.
if ( null === $this->endpoint ) {
$this->endpoint = 'download';
}
add_rewrite_endpoint( $this->endpoint, EP_ALL );
}
}
/**
* Listen for download requests and trigger downloading.
*
* @access public
* @return void
*/
public function handler() {
global $wp, $wpdb;
// Get error handler instance.
$error_handler = DLM_Download_Error_Handler::get_instance( $this );
// Don't do anything if in admin or REST API route.
if ( is_admin() || ! empty( $GLOBALS['wp']->query_vars['rest_route'] ) ) {
return;
}
// check HTTP method.
$request_method = ( ! empty( $_SERVER['REQUEST_METHOD'] )
? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) )
: 'GET' );
// Return if the request method is not GET or POST.
if ( ! in_array(
$request_method,
apply_filters(
'dlm_accepted_request_methods',
array( 'GET', 'POST' )
)
)
) {
return;
}
// GET to query_var.
if ( ! empty( $_GET[ $this->endpoint ] ) ) {
$wp->query_vars[ $this->endpoint ]
= sanitize_text_field( wp_unslash( $_GET[ $this->endpoint ] ) );
}
// Check and see if this is an XHR request or a classic request.
if ( isset( $_SERVER['HTTP_DLM_XHR_REQUEST'] ) && 'dlm_XMLHttpRequest' === $_SERVER['HTTP_DLM_XHR_REQUEST'] ) {
define( 'DLM_DOING_XHR', true );
}
// check if endpoint is set but is empty.
if ( apply_filters( 'dlm_empty_download_redirect_enabled', true )
&& isset( $wp->query_vars[ $this->endpoint ] )
&& empty( $wp->query_vars[ $this->endpoint ] )
) {
// IF XHR, send redirect header.
if ( $this->check_for_xhr() ) {
header(
'X-DLM-Redirect: '
. apply_filters(
'dlm_empty_download_redirect_url',
home_url()
)
);
exit;
}
wp_redirect(
apply_filters(
'dlm_empty_download_redirect_url',
home_url()
)
);
exit;
}
// check if need to handle an actual download.
if ( ! empty( $wp->query_vars[ $this->endpoint ] )
&& ( ( null === $wp->request )
|| ( '' === $wp->request )
|| ( strstr( $wp->request, $this->endpoint . '/' ) ) )
) {
// Prevent caching when endpoint is set
if ( ! defined( 'DONOTCACHEPAGE' ) ) {
define( 'DONOTCACHEPAGE', true );
}
// Get ID of download
$raw_id
= sanitize_title( stripslashes( $wp->query_vars[ $this->endpoint ] ) );
// Find real ID
switch ( $this->ep_value ) {
case 'slug':
$download_id
= absint(
$wpdb->get_var(
$wpdb->prepare(
"SELECT ID FROM {$wpdb->posts} WHERE post_name = '%s' AND post_type = 'dlm_download';",
$raw_id
)
)
);
break;
default:
$download_id = absint( $raw_id );
break;
}
// Prevent hotlinking
if ( WP_DLM::dlm_prevent_hotlinking() ) {
// Get referer
$referer = ! empty( $_SERVER['HTTP_REFERER'] )
? sanitize_text_field( wp_unslash( $_SERVER['HTTP_REFERER'] ) )
: '';
// Check if referer isn't empty or if referer is empty but empty referer isn't allowed
if ( ! empty( $referer )
|| ( empty( $referer )
&& apply_filters(
'dlm_hotlink_block_empty_referer',
false
) )
) {
$allowed_referers
= apply_filters(
'dlm_hotlink_allowed_referers',
array( home_url() )
);
$allowed = false;
// Loop allowed referers
foreach ( $allowed_referers as $allowed_referer ) {
if ( strstr( $referer, $allowed_referer ) ) {
$allowed = true;
break;
}
}
// Check if allowed
if ( false == $allowed ) {
// IF XHR, send redirect header.
if ( $this->check_for_xhr() ) {
header(
'X-DLM-Redirect: '
. apply_filters(
'dlm_hotlink_redirect',
home_url(),
$download_id
)
);
exit;
}
wp_redirect(
apply_filters(
'dlm_hotlink_redirect',
home_url(),
$download_id
)
);
exit;
}
}
}
$redirect = apply_filters( 'dlm_404_redirect', false );
/** @var DLM_Download $download */
$download = null;
$version = null;
if ( $download_id > 0 ) {
try {
$download = download_monitor()
->service( 'download_repository' )
->retrieve_single( $download_id );
} catch ( Exception $e ) {
if ( $redirect ) {
// IF XHR, send redirect header.
if ( $this->check_for_xhr() ) {
header( 'X-DLM-Redirect: ' . $redirect );
exit;
}
wp_safe_redirect( $redirect );
exit;
} elseif ( $this->check_for_xhr() ) {
header( 'X-DLM-Error: not_found' );
$restriction_type = 'not_found';
// Set no access modal.
$this->set_no_access_modal(
__(
'Download does not exist.',
'download-monitor'
),
$download,
$restriction_type
);
http_response_code( 404 );
exit;
} else {
wp_die(
esc_html__(
'Download does not exist.',
'download-monitor'
) . ' <a href="'
. esc_url( home_url() ) . '">'
. esc_html__(
'Go to homepage →',
'download-monitor'
) . '</a>',
esc_html__( 'Download Error', 'download-monitor' ),
array( 'response' => 404 )
);
}
}
}
if ( ! $download ) {
$error_handler->no_download_error( $download );
}
// Handle version (if set)
$version_id = '';
if ( ! empty( $_GET['version'] ) ) {
$version_id
= $download->get_version_id_version_name( sanitize_text_field( wp_unslash( $_GET['version'] ) ) );
}
if ( ! empty( $_GET['v'] ) ) {
$version_id = absint( $_GET['v'] );
}
// Check if version is set and if it exists
if ( $version_id ) {
try {
$version = download_monitor()
->service( 'version_repository' )
->retrieve_single( $version_id );
$download->set_version( $version );
} catch ( Exception $e ) {
}
}
// Action on found download
if ( $download->exists() ) {
// Check if download is restricted by password.
if ( post_password_required( $download_id ) ) {
// IF XHR, send redirect header.
if ( $this->check_for_xhr() ) {
header(
'X-DLM-Redirect: '
. $download->get_the_download_link()
);
exit;
}
wp_die(
get_the_password_form( $download_id ),
esc_html__(
'Password Required',
'download-monitor'
)
);
}
// Trigger download process.
$this->trigger( $download );
} elseif ( $redirect ) {
// IF XHR, send redirect header.
if ( $this->check_for_xhr() ) {
header( 'X-DLM-Redirect: ' . $redirect );
exit;
}
wp_redirect( $redirect );
} else {
$error_handler->no_download_error( $download );
}
die( '1' );
} else {
// Set the no-waypoints in case link/button has triggering class, and we don't want to do the download action. Ex.: Page Addon extension.
header( 'X-dlm-no-waypoints: true' );
}
}
/**
* Trigger function.
*
* @access private
*
* @param DLM_Download $download
*
* @return void
*/
private function trigger( $download ) {
// Download is triggered. First thing we do, send no cache headers.
$this->cache_headers();
// Get error handler instance.
$error_handler = DLM_Download_Error_Handler::get_instance( $this );
/** @var DLM_Download_Version $version */
$version = $download->get_version();
/** @var array $file_paths */
$file_paths = $version->get_mirrors();
if ( $this->check_for_xhr() ) {
// Set required headers used by XHR download
$this->set_required_xhr_headers( $download, $version );
}
// Check if we got files in this version.
if ( empty( $file_paths ) ) {
$error_handler->no_file_paths_error( $download );
}
// Get a random file (mirror).
$file_path = $file_paths[ array_rand( $file_paths ) ];
// Check if we actually got a path.
if ( ! $file_path ) {
$error_handler->no_file_path_error( $download );
}
/**
* Action used for extra checks before download.
*
* @hook dlm_extra_download_checks
*
* @param DLM_Download $download The download.
* @param string $file_path The download file path.
*
* @since 4.9.6
*
* @hooked $this->check_requirements() - 10
*
*/
do_action( 'dlm_extra_download_checks', $download, $file_path );
// Parse file path.
list( $file_path, $remote_file, $restriction ) = download_monitor()
->service( 'file_manager' )->get_secure_path( $file_path );
// Check if $file_path exists, as it can be false if the file was deleted or moved.
if ( ! $file_path ) {
$error_handler->no_secure_file_path( $download );
}
$is_redirect = $download->is_redirect_only() || apply_filters( 'dlm_do_not_force', false, $download, $version );
$file_path = apply_filters( 'dlm_file_path', $file_path, $remote_file, $download );
// Not a redirect, so we need to check the file type.
if ( ! $is_redirect ) {
// Defined restricted file types.
$def_restricted = array( 'php', 'html', 'htm', 'tmp' );
// User defined restricted file types.
$user_defined_restricted = apply_filters( 'dlm_restricted_file_types', array(), $download );
// Merge the two arrays.
$restricted_file_types = array_merge( $def_restricted, $user_defined_restricted );
// Do not allow the download of certain file types.
// If user wants, file type checks can be disabled for remote files.
$check_file_extension = ( $remote_file && apply_filters( 'dlm_check_remote_extension', true ) ) || ! $remote_file;
// Check if the file type is restricted.
if ( $check_file_extension && in_array( $download->get_version()->get_filetype(), $restricted_file_types ) ) {
$error_handler->restricted_file_type_error( $download );
}
}
// The return of the get_secure_path function is an array that consists of the path ( string ), remote file
// ( bool ) and restriction ( bool ).
// If the path is false it means that the file is restricted, so don't download it or redirect to it.
if ( $restriction ) {
$error_handler->restricted_file_error( $download );
}
if ( $this->check_for_xhr() ) {
// Set extra headers for XHR download.
$this->set_extra_xhr_headers( $file_path, $download, $version );
}
$modal = get_option( 'dlm_no_access_modal', false );
// Start session if not started
if ( apply_filters( 'dlm_set_no_access_download_session', true ) && ( '' === session_id() || ! isset( $_SESSION ) ) && ! $modal ) {
$is_https = ( ! empty( $_SERVER['HTTPS'] ) && 'off' !== $_SERVER['HTTPS'] ) || ( isset( $_SERVER['SERVER_PORT'] ) && 443 === $_SERVER['SERVER_PORT'] );
$params = array(
'secure' => $is_https,
'httponly' => true,
'samesite' => 'Lax',
);
session_set_cookie_params( apply_filters( 'dlm_set_session_params', $params ) );
session_start();
}
// Check Access.
if ( ! apply_filters( 'dlm_can_download', true, $download, $version, $_REQUEST, $this->check_for_xhr() ) ) {
// Check if we need to redirect if visitor don't have access to file.
if ( $redirect = apply_filters( 'dlm_access_denied_redirect', false ) ) {
// IF XHR, send redirect header.
if ( $this->check_for_xhr() ) {
header( 'X-DLM-Redirect: ' . $redirect );
header( 'X-DLM-No-Access: true' );
exit;
}
header( "Status: 301 redirect,$redirect" );
wp_redirect( $redirect );
exit;
} else {
// get 'no access' page id.
$no_access_page_id = get_option( 'dlm_no_access_page', 0 );
// check if a no access page is set.
if ( $no_access_page_id > 0 ) {
// Polylang plugin no access page compatibility.
if ( function_exists( 'pll_current_language' ) ) {
$polylang_lang = pll_current_language();
$translations = pll_get_post_translations( $no_access_page_id );
// If a translation for no access page exists, set it as the page id.
if ( isset( $translations[ $polylang_lang ] ) ) {
$no_access_page_id
= absint( $translations[ $polylang_lang ] );
}
}
// get permalink of no access page.
$no_access_permalink = get_permalink( $no_access_page_id );
// check if we can find a permalink.
if ( false !== $no_access_permalink ) {
// get WordPress permalink structure so we can build the url.
$structure = get_option( 'permalink_structure', 0 );
// append download id to no access URL.
if ( '' == $structure || 0 == $structure ) {
$no_access_permalink = add_query_arg( 'download-id', $download->get_id(), untrailingslashit( $no_access_permalink ) );
} else {
$no_access_permalink = untrailingslashit( $no_access_permalink ) . '/download-id/' . $download->get_id() . '/';
}
if ( ! $download->get_version()->is_latest() ) {
$no_access_permalink = add_query_arg( 'version', $download->get_version()->get_version(), $no_access_permalink );
}
// IF XHR, send redirect header.
if ( $this->check_for_xhr() ) {
header( 'X-DLM-Redirect: ' . $no_access_permalink );
// Set no access modal.
$this->set_no_access_modal( false, $download, 'no_access_page' );
exit;
}
// redirect to no access page.
header( "Status: 301 redirect,$no_access_permalink" );
wp_redirect( $no_access_permalink );
exit; // out.
}
}
$error_handler->no_access_error( $download );
}
}
if ( ! $modal ) {
// We made it so far, so we can unset the no access text.
unset( $_SESSION['dlm_no_access_text'] );
session_write_close();
}
// Get the referrer.
$referrer = ( isset( $_SERVER['HTTP_REFERER'] ) ) ? esc_url_raw( $_SERVER['HTTP_REFERER'] ) : '';
$cookie_manager = DLM_Cookie_Manager::get_instance();
// check if user downloaded this version in the past minute. This checks if the cookie exists and if it's
// value is the same as the download id.
if ( false === $cookie_manager->check_cookie_meta( 'wp_dlm_downloading', $download->get_id() ) ) {
// Trigger Download Action.
do_action( 'dlm_downloading', $download, $version, $file_path );
// Set the cookie to prevent multiple download logs in download window of 60 seconds.
// Do this only for non-XHR downloads as XHR downloads are logged through AJAX request
if ( WP_DLM::dlm_window_logging() && ! $this->check_for_xhr() && ! $is_redirect ) {
// Set cookie here to prevent "Cannot modify header information - headers already sent" error
// in non-XHR downloads.
$this->dlm_logging->log( $download, $version, 'completed', true, $referrer );
}
}
// Redirect to the file...
if ( $is_redirect ) {
if ( ! $this->check_for_xhr() ) {
$this->dlm_logging->log( $download, $version, 'redirected', false, $referrer );
}
// If it's not a remote file we need to create the correct URL.
if ( ! $remote_file ) {
// Let's check if the file is in the uploads' folder.
$uploads_dir = wp_upload_dir();
// Ge the file path.
$file_path = str_replace( DIRECTORY_SEPARATOR, '/', $file_path );
$basedir = str_replace( DIRECTORY_SEPARATOR, '/', $uploads_dir['basedir'] );
// Check if the path of the file is a symbolic link.
$sympath = ( is_link( $basedir ) ) ? str_replace( DIRECTORY_SEPARATOR, '/', readlink( $basedir ) ) : false;
if ( false !== strpos( $file_path, $basedir ) ) { // File is in the uploads' folder, so we need to create the correct URL.
// Set the URL for the uploads' folder.
$file_path
= str_replace(
str_replace(
DIRECTORY_SEPARATOR,
'/',
trailingslashit( $basedir )
),
str_replace(
DIRECTORY_SEPARATOR,
'/',
trailingslashit( $uploads_dir['baseurl'] )
),
$file_path
);
} elseif ( $sympath && false !== strpos( $file_path, $sympath ) ) { // File is in the uploads' folder but in symlinked directory, so we need to create the correct URL.
// Set the URL for the uploads' folder.
$file_path
= str_replace(
str_replace(
DIRECTORY_SEPARATOR,
'/',
trailingslashit( $sympath )
),
str_replace(
DIRECTORY_SEPARATOR,
'/',
trailingslashit( $uploads_dir['baseurl'] )
),
$file_path
);
} else { // This is the case if the file is not located in the uploads' folder.
// Ensure we have a valid URL, not a file path.
$scheme = wp_parse_url(
get_option( 'home' ),
PHP_URL_SCHEME
);
// If there are symbolik links the return of the function will be an URL, so the last replace will not be taken into consideration.
$file_path = download_monitor()
->service( 'file_manager' )
->check_symbolic_links( $file_path, true );
$file_path
= str_replace(
trailingslashit( ABSPATH ),
site_url( '/', $scheme ),
$file_path
);
}
// We need to rawurlencode in case there are unicode characters in the file name
// and to prevent white space from being converted to + sign. Only do this for non-remote files
// as remote files already have encoded url.
// Get file name.
$file_name = DLM_Utils::basename( $file_path );
if ( strstr( $file_name, '?' ) ) {
$file_name = current( explode( '?', $file_name ) );
}
$file_path = str_replace( $file_name, rawurlencode( $file_name ), $file_path );
}
// IF XHR, send redirect header.
if ( $this->check_for_xhr() ) {
header( 'X-DLM-Redirect: ' . $file_path );
exit;
}
header( 'X-Robots-Tag: noindex, nofollow', true );
header( 'Location: ' . $file_path );
exit;
}
$this->download_headers( $file_path, $download, $version, $remote_file );
do_action( 'dlm_start_download_process', $download, $version, $file_path, $remote_file );
if ( WP_DLM::dlm_x_sendfile() ) {
if ( function_exists( 'apache_get_modules' ) && in_array( 'mod_xsendfile', apache_get_modules() ) ) {
$this->dlm_logging->log( $download, $version, 'completed', false, $referrer );
header( "X-Sendfile: $file_path" );
exit;
} elseif ( stristr(
getenv( 'SERVER_SOFTWARE' ),
'lighttpd'
)
) {
$this->dlm_logging->log(
$download,
$version,
'completed',
false,
$referrer
);
header( "X-LIGHTTPD-send-file: $file_path" );
exit;
} elseif ( stristr( getenv( 'SERVER_SOFTWARE' ), 'nginx' )
|| stristr( getenv( 'SERVER_SOFTWARE' ), 'cherokee' )
) {
// Log this way as the js doesn't know who the download_id and version_id is.
$this->dlm_logging->log(
$download,
$version,
'completed',
false,
$referrer
);
// If there are symbolik links the return of the function will be an URL, so the last replace will not be taken into consideration.
$file_path = download_monitor()->service( 'file_manager' )
->check_symbolic_links(
$file_path,
true
);
$file_path = str_replace(
trailingslashit( ABSPATH ),
'',
$file_path
);
header( "X-Accel-Redirect: /$file_path" );
exit;
} elseif ( stristr(
getenv( 'SERVER_SOFTWARE' ),
'LiteSpeed'
)
) {
// Log this way as the js doesn't know who the download_id and version_id is.
$this->dlm_logging->log(
$download,
$version,
'completed',
false,
$referrer
);
header( "X-LiteSpeed-Location: $file_path" );
exit;
}
}
$safe_remote = wp_safe_remote_head( $file_path );
$safe = true;
if ( $remote_file && is_wp_error( $safe_remote ) ) {
$safe = false;
}
if ( ! $safe ) {
// IF XHR, send error header.
if ( $this->check_for_xhr() ) {
header( 'X-DLM-Error: security_error' );
$restriction_type = 'security_error';
$this->set_no_access_modal(
__(
'Something is wrong with the file path.',
'download-monitor'
),
$download,
$restriction_type
);
exit;
}
$this->dlm_logging->log(
$download,
$version,
'failed',
false,
$referrer
);
wp_die(
esc_html__(
'Something is wrong with the file path.',
'download-monitor'
) . ' <a href="'
. esc_url( home_url() ) . '">'
. esc_html__(
'Go to homepage →',
'download-monitor'
) . '</a>',
esc_html__( 'Download Error', 'download-monitor' ),
array( 'response' => 404 )
);
}
// multipart-download and download resuming support - http://www.phpgang.com/force-to-download-a-file-in-php_112.html.
if ( isset( $_SERVER['HTTP_RANGE'] ) && $version->get_filesize() ) {
// phpcs:ignore
list( $a, $range ) = explode( "=", $_SERVER['HTTP_RANGE'], 2 );
list( $range ) = explode( ',', $range, 2 );
list( $range, $range_end ) = explode( '-', $range );
$range = intval( $range );
$range_end_modified = false;
if ( ! $range_end || $range_end > $version->get_filesize() ) {
$range_end = $version->get_filesize() - 1;
$range_end_modified = true;
} else {
$range_end = intval( $range_end );
}
if ( $range_end_modified ) {
$new_length = ( $range_end - $range ) + 1;
} else {
$new_length = $range_end - $range;
}
header( $_SERVER['SERVER_PROTOCOL'] . ' 206 Partial Content' );
header( "Content-Length: $new_length" );
header( "Content-Range: bytes {$range}-{$range_end}/{$version->get_filesize()}" );
} else {
$range = false;
}
// Adding contents to an object will trigger error on big files.
if ( ! $this->readfile_chunked( $file_path, false, $range ) ) {
if ( $remote_file ) {
// Redirect - we can't track if this completes or not.
if ( $this->check_for_xhr() ) {
header( 'X-DLM-Redirect: ' . $file_path );
exit;
}
header( 'Location: ' . $file_path );
$this->dlm_logging->log(
$download,
$version,
'redirected',
false,
$referrer
);
} else {
// IF XHR, send error header.
if ( $this->check_for_xhr() ) {
header( 'X-DLM-Error: file_not_found' );
$restriction_type = 'file_not_found';
$this->set_no_access_modal(
__( 'File not found.', 'download-monitor' ),
$download,
$restriction_type
);
exit;
}
$this->dlm_logging->log(
$download,
$version,
'failed',
false,
$referrer
);
wp_die(
esc_html__( 'File not found.', 'download-monitor' )
. ' <a href="' . esc_url( home_url() ) . '">'
. esc_html__( 'Go to homepage →', 'download-monitor' )
. '</a>',
esc_html__( 'Download Error', 'download-monitor' ),
array( 'response' => 404 )
);
}
}
exit;
}
/**
* Send cache headers to browser. No cache pelase.
*/
private function cache_headers() {
global $is_IE;
if ( $is_IE && is_ssl() ) {
// IE bug prevents download via SSL when Cache Control and Pragma no-cache headers set.
header( 'Expires: Wed, 11 Jan 1984 05:00:00 GMT' );
header( 'Cache-Control: private' );
} else {
nocache_headers();
}
}
/**
* Output download headers
*
* @param string $file_path
* @param DLM_Download $download
* @param DLM_Download_Version $version
*/
private function download_headers( $file_path, $download, $version, $remote_file ) {
// Get Mime Type
$mime_type = 'application/octet-stream';
foreach ( get_allowed_mime_types() as $mime => $type ) {
$mimes = explode( '|', $mime );
if ( in_array( $version->get_filetype(), $mimes ) ) {
$mime_type = $type;
break;
}
}
// Get file name
$file_name = urldecode( DLM_Utils::basename( $file_path ) );
if ( strstr( $file_name, '?' ) ) {
$file_name = current( explode( '?', $file_name ) );
}
// Environment + headers
if ( ! ini_get( 'safe_mode' ) ) {
@set_time_limit( 0 );
}
if ( version_compare( PHP_VERSION, '7.4.0', '<' )
&& function_exists( 'get_magic_quotes_runtime' )
&& get_magic_quotes_runtime()
) {
@set_magic_quotes_runtime( 0 );
}
if ( function_exists( 'apache_setenv' ) ) {
@apache_setenv( 'no-gzip', 1 );
}
@session_write_close();
@ini_set( 'zlib.output_compression', 'Off' );
@error_reporting( 0 );
/**
* Prevents errors, for example: transfer closed with 3 bytes remaining to read
*/
@ob_end_clean(); // Clear the output buffer
// Zip corruption fix
while ( ob_get_level() > 0 ) {
@ob_end_clean();
}
$headers = array();
// We use this method to encode the filename so that file names with characters like
// chinese or persian can be named correctly after the download in Safari.
$file_name = rawurlencode( sanitize_file_name( $file_name ) );
if ( $this->check_for_xhr() ) {
$headers['Content-Disposition']
= "attachment; filename=\"{$file_name}\";";
$headers['X-DLM-File-Name'] = "{$file_name}";
} else {
$headers['Content-Disposition']
= "attachment; filename*=UTF-8''{$file_name};";
}
$headers['X-Robots-Tag'] = 'noindex, nofollow';
$headers['Content-Type'] = $mime_type;
$headers['Content-Description'] = 'File Transfer';
$headers['Content-Transfer-Encoding'] = 'binary';
$headers['Cache-Control']
= 'no-store, no-cache, must-revalidate, no-transform, max-age=0';
if ( $remote_file ) {
$file = wp_remote_head( $file_path );
if ( ! is_wp_error( $file )
&& ! empty( $file['headers']['content-length'] )
) {
$file_size = $file['headers']['content-length'];
}
} else {
$file_size = filesize( $file_path );
}
if ( isset( $file_size ) && $file_size ) {
// Replace the old way ( getting the filesize from the DB ) in case the user has replaced the file directly using cPanel,
// FTP or other File Manager, or sometimes using an optimization service it may cause unwanted results.
$headers['Content-Length'] = $file_size;
$headers['Accept-Ranges'] = 'bytes';
// Set custom filesize header.
$headers['X-DLM-Filesize'] = $file_size;
}
$headers = apply_filters(
'dlm_download_headers',
$headers,
$file_path,
$download,
$version
);
foreach ( $headers as $key => $value ) {
header( $key . ': ' . $value );
}
}
/**
* Set required XHR download headers
*
* @param DLM_Download $download DLM Download object.
* @param DLM_Download_Version $version DLN Version object.
*/
private function set_required_xhr_headers( $download, $version ) {
$headers = array();
$headers['X-DLM-Download-ID'] = $download->get_id();
$headers['X-DLM-Version-ID'] = $version->get_id();
$headers['X-DLM-Nonce'] = wp_create_nonce( 'dlm_ajax_nonce' );
foreach ( $headers as $key => $value ) {
header( $key . ': ' . $value );
}
}
/**
* Set extra XHR download headers
*
* @param DLM_Download $download DLM Download object.
* @param DLM_Download_Version $version DLN Version object.
* @param string $file_path The file path.
*/
private function set_extra_xhr_headers(
$file_path,
$download,
$version
) {
$headers = apply_filters(
'dlm_xhr_download_headers',
array(),
$file_path,
$download,
$version,
$_REQUEST
);
if ( ! empty( $headers ) ) {
foreach ( $headers as $key => $value ) {
header( $key . ': ' . $value );
}
}
}
/**
* readfile_chunked
*
* Reads file in chunks so big downloads are possible without changing PHP.INI - http://codeigniter.com/wiki/Download_helper_for_large_files/
*
* @access public
*
* @param string $file
* @param boolean $retbytes return bytes of file
* @param boolean $range if HTTP RANGE to seek
*
* @return mixed
*/
public function readfile_chunked(
$file,
$retbytes = true,
$range = false
) {
$chunksize = 1 * ( 1024 * 1024 );
$buffer = '';
$cnt = 0;
$handle = fopen( $file, 'rb' );
if ( $handle === false ) {
return false;
}
if ( $range ) {
fseek( $handle, $range );
}
while ( ! feof( $handle ) ) {
$buffer = fread( $handle, $chunksize );
// phpcs:ignore
echo $buffer;
if ( $retbytes ) {
$cnt += strlen( $buffer );
}
}
$status = fclose( $handle );
if ( $retbytes && $status ) {
return $cnt;
}
return $status;
}
/**
* Check if this is an XHR request or not
*
* @return bool
*/
public function check_for_xhr() {
return defined( 'DLM_DOING_XHR' ) && DLM_DOING_XHR;
}
/**
* Set headers for Modal opening
*
* @param string $text The text to be displayed.
* @param object $download The download object.
* @param string $restriction_type The restriction type.
*
* @return void
* @since 4.7.4
*/
public function set_no_access_modal(
$text,
$download,
$restriction_type
) {
$access_modal = absint( get_option( 'dlm_no_access_modal', 0 ) );
header( 'X-DLM-No-Access: true' );
header(
'X-DLM-No-Access-Modal: '
. apply_filters(
'do_dlm_xhr_access_modal',
$access_modal,
$download
)
);
header( 'X-DLM-No-Access-Restriction: ' . $restriction_type );
if ( ! empty( $text ) ) {
header(
'X-DLM-No-Access-Modal-Text: '
. apply_filters(
'do_dlm_xhr_access_modal_text',
$text,
$download,
$restriction_type
)
);
}
header( 'X-DLM-Nonce: ' . wp_create_nonce( 'dlm_ajax_nonce' ) );
}
}
}