ho the download_id and version_id is. $this->dlm_logging->log( $download, $version, 'completed', false, $referrer ); // At this point the $correct_path should have a value of the file path as the verification was made prior to this check // 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( $correct_path ), '', $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' ) . ' ' . esc_html__( 'Go to homepage →', 'download-monitor' ) . '', 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' ) . ' ' . esc_html__( 'Go to homepage →', 'download-monitor' ) . '', 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'; } $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 */ private 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' ) ); } } }