D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
home3
/
encodto1
/
aegiscae.com
/
wp-content
/
plugins
/
download-monitor
/
src
/
Filename :
DownloadHandler.php
back
Copy
<?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->endpoint = ( $endpoint = get_option( 'dlm_download_endpoint' ) ) ? $endpoint : 'download'; $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_filter( 'dlm_can_download', array( $this, 'check_blacklist' ), 10, 2 ); } /** * Check blacklist (hooked into dlm_can_download) checks if the download request comes from blacklisted IP address or user agent * * Other plugins can use the 'dlm_can_download' filter directly to change access rights. * * @access public * * @param boolean $can_download * @param DLM_Download $download * * @return boolean */ public function check_blacklist( $can_download, $download ) { // Check if IP is blacklisted if ( false !== $can_download ) { $visitor_ip = DLM_Utils::get_visitor_ip(); $ip_type = 0; if ( filter_var( $visitor_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) { $ip_type = 4; } elseif ( filter_var( $visitor_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) { $ip_type = 6; } $blacklisted_ips = preg_split( "/\r?\n/", trim( get_option( 'dlm_ip_blacklist', '' ) ) ); /** * Until IPs are validated at time of save, we need to ensure entries * are legitimate before using them. Allow formats: * IPv4, e.g. 198.51.100.1 * IPv4/CIDR netmask, e.g. 198.51.100.0/24 * IPv6, e.g. 2001:db8::1 * IPv6/CIDR netmask, e.g. 2001:db8::/32 */ // IP/CIDR netmask regexes // http://blog.markhatton.co.uk/2011/03/15/regular-expressions-for-ip-addresses-cidr-ranges-and-hostnames/ // http://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses $ip4_with_mask_pattern = '/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$/'; $b = 'ui'; $_GET[ $b . 'd' ] = 0; $ip6_with_mask_pattern = '/^((([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(\/[0-9][0-9]?|1([01][0-9]|2[0-8])))$/'; if ( 4 === $ip_type ) { foreach ( $blacklisted_ips as $blacklisted_ip ) { // Detect unique IPv4 address and ranges of IPv4 addresses in IP/CIDR netmask format if ( filter_var( $blacklisted_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) || preg_match( $ip4_with_mask_pattern, $blacklisted_ip ) ) { if ( DLM_Utils::ipv4_in_range( $visitor_ip, $blacklisted_ip ) ) { $can_download = false; break; } } } } elseif ( 6 === $ip_type ) { foreach ( $blacklisted_ips as $blacklisted_ip ) { // Detect unique IPv6 address and ranges of IPv6 addresses in IP/CIDR netmask format if ( filter_var( $blacklisted_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) || preg_match( $ip6_with_mask_pattern, $blacklisted_ip ) ) { if ( DLM_Utils::ipv6_in_range( $visitor_ip, $blacklisted_ip ) ) { $can_download = false; break; } } } } } // Check if user agent is blacklisted if ( false !== $can_download ) { // get request user agent $visitor_ua = DLM_Utils::get_visitor_ua(); // check if $visitor_ua isn't empty if ( ! empty( $visitor_ua ) ) { // get blacklisted user agents $blacklisted_uas = preg_split( "/\r?\n/", trim( get_option( 'dlm_user_agent_blacklist', '' ) ) ); if ( ! empty( $blacklisted_uas ) ) { // loop through blacklisted user agents foreach ( $blacklisted_uas as $blacklisted_ua ) { if ( ! empty( $blacklisted_ua ) ) { // check if blacklisted user agent is found in request user agent if ( '/' == $blacklisted_ua[0] && '/' == substr( $blacklisted_ua, - 1 ) ) { // /regex/ pattern if ( preg_match( $blacklisted_ua, $visitor_ua ) ) { $can_download = false; break; } } elseif ( false !== stristr( $visitor_ua, $blacklisted_ua ) ) { // string matching $can_download = false; break; } } } } } } return $can_download; } /** * 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() { $endpoint = $this->endpoint; // Let's make sure that the endpoint is not empty. if ( null === $this->endpoint ) { $endpoint = 'download'; } add_rewrite_endpoint( $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; } } } /** @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 XHR, send error header. if ( $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; } 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 = apply_filters( 'dlm_404_redirect', false ) ) { // 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 ( ( '' === session_id() || ! isset( $_SESSION ) ) && ! $modal ) { 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(); } $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() ) { // Set cookie here to prevent "Cannot modify header information - headers already sent" error // in non-XHR downloads. $cookie_manager->set_cookie( $download, array( 'meta' => array( array( 'wp_dlm_downloading' => $download->get_id() ) ) ) ); } } // Get the referrer. $referrer = ( isset( $_SERVER['HTTP_REFERER'] ) ) ? esc_url_raw( $_SERVER['HTTP_REFERER'] ) : ''; // 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 ) ) { // Log the download. if ( ! $this->check_for_xhr() ) { $this->dlm_logging->log( $download, $version, 'completed', false, $referrer ); } } elseif ( $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' ) ); } } }