2013-08-09 16:11:15 +00:00
< ? php
2014-05-28 13:52:50 +00:00
if ( ! defined ( 'ABSPATH' ) ) {
exit ; // Exit if accessed directly
}
2013-08-09 16:11:15 +00:00
/**
* Download handler
*
* Handle digital downloads .
*
* @ class WC_Download_Handler
2014-05-28 13:52:50 +00:00
* @ version 2.2 . 0
2013-08-09 16:11:15 +00:00
* @ package WooCommerce / Classes
* @ category Class
* @ author WooThemes
*/
class WC_Download_Handler {
/**
2014-05-28 13:52:50 +00:00
* Hook in methods
2013-08-09 16:11:15 +00:00
*/
2014-05-28 13:52:50 +00:00
public static function init () {
add_action ( 'init' , array ( __CLASS__ , 'download_product' ) );
2013-08-09 16:11:15 +00:00
}
/**
* Check if we need to download a file and check validity
*/
2014-05-28 13:52:50 +00:00
public static function download_product () {
2013-08-09 16:11:15 +00:00
if ( isset ( $_GET [ 'download_file' ] ) && isset ( $_GET [ 'order' ] ) && isset ( $_GET [ 'email' ] ) ) {
2013-08-13 13:53:55 +00:00
global $wpdb ;
2013-08-09 16:11:15 +00:00
2014-01-26 09:19:17 +00:00
$product_id = ( int ) $_GET [ 'download_file' ];
$order_key = $_GET [ 'order' ];
$email = sanitize_email ( str_replace ( ' ' , '+' , $_GET [ 'email' ] ) );
$download_id = isset ( $_GET [ 'key' ] ) ? preg_replace ( '/\s+/' , ' ' , $_GET [ 'key' ] ) : '' ;
2014-08-19 10:09:29 +00:00
$_product = wc_get_product ( $product_id );
2013-08-09 16:11:15 +00:00
2014-02-26 11:54:16 +00:00
if ( ! is_email ( $email ) ) {
2014-06-11 20:24:27 +00:00
wp_die ( __ ( 'Invalid email address.' , 'woocommerce' ) . ' <a href="' . esc_url ( home_url () ) . '" class="wc-forward">' . __ ( 'Go to homepage' , 'woocommerce' ) . '</a>' , '' , array ( 'response' => 403 ) );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
$query = "
SELECT order_id , downloads_remaining , user_id , download_count , access_expires , download_id
FROM " . $wpdb->prefix . " woocommerce_downloadable_product_permissions
WHERE user_email = % s
AND order_key = % s
AND product_id = % s " ;
$args = array (
$email ,
$order_key ,
$product_id
);
if ( $download_id ) {
// backwards compatibility for existing download URLs
$query .= " AND download_id = %s " ;
$args [] = $download_id ;
}
$download_result = $wpdb -> get_row ( $wpdb -> prepare ( $query , $args ) );
2014-02-26 11:54:16 +00:00
if ( ! $download_result ) {
2014-06-11 20:24:27 +00:00
wp_die ( __ ( 'Invalid download.' , 'woocommerce' ) . ' <a href="' . esc_url ( home_url () ) . '" class="wc-forward">' . __ ( 'Go to homepage' , 'woocommerce' ) . '</a>' , '' , array ( 'response' => 404 ) );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
$download_id = $download_result -> download_id ;
$order_id = $download_result -> order_id ;
$downloads_remaining = $download_result -> downloads_remaining ;
$download_count = $download_result -> download_count ;
$user_id = $download_result -> user_id ;
$access_expires = $download_result -> access_expires ;
if ( $user_id && get_option ( 'woocommerce_downloads_require_login' ) == 'yes' ) {
2014-02-26 11:54:16 +00:00
if ( ! is_user_logged_in () ) {
2014-06-11 20:24:27 +00:00
wp_die ( __ ( 'You must be logged in to download files.' , 'woocommerce' ) . ' <a href="' . esc_url ( wp_login_url ( get_permalink ( wc_get_page_id ( 'myaccount' ) ) ) ) . '" class="wc-forward">' . __ ( 'Login' , 'woocommerce' ) . '</a>' , __ ( 'Log in to Download Files' , 'woocommerce' ), '' , array ( 'response' => 403 ) );
2014-02-26 11:54:16 +00:00
} elseif ( ! current_user_can ( 'download_file' , $download_result ) ) {
2014-06-11 20:24:27 +00:00
wp_die ( __ ( 'This is not your download link.' , 'woocommerce' ), '' , array ( 'response' => 403 ) );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
}
2014-02-26 11:54:16 +00:00
if ( ! get_post ( $product_id ) ) {
2014-06-11 20:24:27 +00:00
wp_die ( __ ( 'Product no longer exists.' , 'woocommerce' ) . ' <a href="' . esc_url ( home_url () ) . '" class="wc-forward">' . __ ( 'Go to homepage' , 'woocommerce' ) . '</a>' , '' , array ( 'response' => 404 ) );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
if ( $order_id ) {
2014-08-15 12:29:21 +00:00
$order = wc_get_order ( $order_id );
2013-08-09 16:11:15 +00:00
2014-09-04 16:31:38 +00:00
if ( ! $order -> is_download_permitted () ) {
2014-06-11 20:24:27 +00:00
wp_die ( __ ( 'Invalid order.' , 'woocommerce' ) . ' <a href="' . esc_url ( home_url () ) . '" class="wc-forward">' . __ ( 'Go to homepage' , 'woocommerce' ) . '</a>' , '' , array ( 'response' => 404 ) );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
}
2014-02-26 11:54:16 +00:00
if ( $downloads_remaining == '0' ) {
2014-06-11 20:24:27 +00:00
wp_die ( __ ( 'Sorry, you have reached your download limit for this file' , 'woocommerce' ) . ' <a href="' . esc_url ( home_url () ) . '" class="wc-forward">' . __ ( 'Go to homepage' , 'woocommerce' ) . '</a>' , '' , array ( 'response' => 403 ) );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
2014-02-26 11:54:16 +00:00
if ( $access_expires > 0 && strtotime ( $access_expires ) < current_time ( 'timestamp' ) ) {
2014-06-11 20:24:27 +00:00
wp_die ( __ ( 'Sorry, this download has expired' , 'woocommerce' ) . ' <a href="' . esc_url ( home_url () ) . '" class="wc-forward">' . __ ( 'Go to homepage' , 'woocommerce' ) . '</a>' , '' , array ( 'response' => 403 ) );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
if ( $downloads_remaining > 0 ) {
$wpdb -> update ( $wpdb -> prefix . " woocommerce_downloadable_product_permissions " , array (
'downloads_remaining' => $downloads_remaining - 1 ,
), array (
'user_email' => $email ,
'order_key' => $order_key ,
'product_id' => $product_id ,
'download_id' => $download_id
), array ( '%d' ), array ( '%s' , '%s' , '%d' , '%s' ) );
}
// Count the download
$wpdb -> update ( $wpdb -> prefix . " woocommerce_downloadable_product_permissions " , array (
'download_count' => $download_count + 1 ,
), array (
'user_email' => $email ,
'order_key' => $order_key ,
'product_id' => $product_id ,
'download_id' => $download_id
), array ( '%d' ), array ( '%s' , '%s' , '%d' , '%s' ) );
// Trigger action
do_action ( 'woocommerce_download_product' , $email , $order_key , $product_id , $user_id , $download_id , $order_id );
// Get the download URL and try to replace the url with a path
$file_path = $_product -> get_file_download_path ( $download_id );
// Download it!
2014-05-28 13:52:50 +00:00
self :: download ( $file_path , $product_id );
2013-08-09 16:11:15 +00:00
}
}
/**
* Download a file - hook into init function .
2014-09-07 23:37:55 +00:00
* @ param integer $product_id
2013-08-09 16:11:15 +00:00
*/
2014-05-28 13:52:50 +00:00
public static function download ( $file_path , $product_id ) {
2014-06-08 20:33:11 +00:00
global $is_IE ;
2013-08-09 16:11:15 +00:00
$file_download_method = apply_filters ( 'woocommerce_file_download_method' , get_option ( 'woocommerce_file_download_method' ), $product_id );
2014-02-26 11:54:16 +00:00
if ( ! $file_path ) {
2014-06-11 20:24:27 +00:00
wp_die ( __ ( 'No file defined' , 'woocommerce' ) . ' <a href="' . esc_url ( home_url () ) . '" class="wc-forward">' . __ ( 'Go to homepage' , 'woocommerce' ) . '</a>' , '' , array ( 'response' => 404 ) );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
// Redirect to the file...
if ( $file_download_method == " redirect " ) {
header ( 'Location: ' . $file_path );
exit ;
}
// ...or serve it
2014-04-07 14:09:11 +00:00
$remote_file = true ;
$parsed_file_path = parse_url ( $file_path );
2014-05-25 21:10:23 +00:00
2014-04-07 14:09:11 +00:00
$wp_uploads = wp_upload_dir ();
$wp_uploads_dir = $wp_uploads [ 'basedir' ];
$wp_uploads_url = $wp_uploads [ 'baseurl' ];
if ( ( ! isset ( $parsed_file_path [ 'scheme' ] ) || ! in_array ( $parsed_file_path [ 'scheme' ], array ( 'http' , 'https' , 'ftp' ) ) ) && isset ( $parsed_file_path [ 'path' ] ) && file_exists ( $parsed_file_path [ 'path' ] ) ) {
2013-08-09 16:11:15 +00:00
2014-04-07 14:09:11 +00:00
/** This is an absolute path */
$remote_file = false ;
2013-08-09 16:11:15 +00:00
2014-04-07 14:09:11 +00:00
} elseif ( strpos ( $file_path , $wp_uploads_url ) !== false ) {
2013-08-09 16:11:15 +00:00
2014-04-07 14:09:11 +00:00
/** This is a local file given by URL so we need to figure out the path */
$remote_file = false ;
$file_path = str_replace ( $wp_uploads_url , $wp_uploads_dir , $file_path );
2013-08-09 16:11:15 +00:00
2014-04-07 14:09:11 +00:00
} elseif ( is_multisite () && ( strpos ( $file_path , network_site_url ( '/' , 'http' ) ) !== false || strpos ( $file_path , network_site_url ( '/' , 'https' ) ) !== false ) ) {
/** This is a local file outside of wp-content so figure out the path */
$remote_file = false ;
2013-08-09 16:11:15 +00:00
// Try to replace network url
2014-04-07 14:09:11 +00:00
$file_path = str_replace ( network_site_url ( '/' , 'https' ), ABSPATH , $file_path );
$file_path = str_replace ( network_site_url ( '/' , 'http' ), ABSPATH , $file_path );
// Try to replace upload URL
$file_path = str_replace ( $wp_uploads_url , $wp_uploads_dir , $file_path );
2013-08-09 16:11:15 +00:00
2014-04-07 14:09:11 +00:00
} elseif ( strpos ( $file_path , site_url ( '/' , 'http' ) ) !== false || strpos ( $file_path , site_url ( '/' , 'https' ) ) !== false ) {
/** This is a local file outside of wp-content so figure out the path */
$remote_file = false ;
$file_path = str_replace ( site_url ( '/' , 'https' ), ABSPATH , $file_path );
$file_path = str_replace ( site_url ( '/' , 'http' ), ABSPATH , $file_path );
2013-08-09 16:11:15 +00:00
2014-04-07 14:09:11 +00:00
} elseif ( file_exists ( ABSPATH . $file_path ) ) {
2014-05-25 21:10:23 +00:00
2014-04-07 14:09:11 +00:00
/** Path needs an abspath to work */
2013-08-09 16:11:15 +00:00
$remote_file = false ;
2014-04-07 14:09:11 +00:00
$file_path = ABSPATH . $file_path ;
}
2013-08-09 16:11:15 +00:00
2014-04-07 14:09:11 +00:00
if ( ! $remote_file ) {
2013-08-09 16:11:15 +00:00
// Remove Query String
2014-02-26 11:54:16 +00:00
if ( strstr ( $file_path , '?' ) ) {
2013-08-09 16:11:15 +00:00
$file_path = current ( explode ( '?' , $file_path ) );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
2014-04-07 14:09:11 +00:00
// Run realpath
$file_path = realpath ( $file_path );
2013-08-09 16:11:15 +00:00
}
2014-04-07 14:09:11 +00:00
// Get extension and type
2013-08-09 16:11:15 +00:00
$file_extension = strtolower ( substr ( strrchr ( $file_path , " . " ), 1 ) );
$ctype = " application/force-download " ;
foreach ( get_allowed_mime_types () as $mime => $type ) {
$mimes = explode ( '|' , $mime );
if ( in_array ( $file_extension , $mimes ) ) {
$ctype = $type ;
break ;
}
}
// Start setting headers
2014-02-26 11:54:16 +00:00
if ( ! ini_get ( 'safe_mode' ) ) {
2013-08-09 16:11:15 +00:00
@ set_time_limit ( 0 );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
2014-02-26 11:54:16 +00:00
if ( function_exists ( 'get_magic_quotes_runtime' ) && get_magic_quotes_runtime () ) {
2013-08-09 16:11:15 +00:00
@ set_magic_quotes_runtime ( 0 );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
2014-02-26 11:54:16 +00:00
if ( function_exists ( 'apache_setenv' ) ) {
2013-08-09 16:11:15 +00:00
@ apache_setenv ( 'no-gzip' , 1 );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
@ session_write_close ();
@ ini_set ( 'zlib.output_compression' , 'Off' );
2013-12-19 09:14:39 +00:00
/**
* Prevents errors , for example : transfer closed with 3 bytes remaining to read
*/
2014-05-25 21:10:23 +00:00
if ( ob_get_length () ) {
2013-12-19 09:14:39 +00:00
2014-05-25 21:10:23 +00:00
if ( ob_get_level () ) {
2014-01-03 02:35:17 +00:00
2014-05-25 21:10:23 +00:00
$levels = ob_get_level ();
2014-01-03 02:35:17 +00:00
2014-05-25 21:10:23 +00:00
for ( $i = 0 ; $i < $levels ; $i ++ ) {
ob_end_clean (); // Zip corruption fix
}
2014-01-03 02:35:17 +00:00
2014-05-25 21:10:23 +00:00
} else {
ob_end_clean (); // Clear the output buffer
}
2013-12-19 09:14:39 +00:00
}
2013-08-09 16:11:15 +00:00
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 ();
}
2014-03-14 10:04:41 +00:00
$filename = basename ( $file_path );
2013-08-09 16:11:15 +00:00
2014-03-14 10:04:41 +00:00
if ( strstr ( $filename , '?' ) ) {
$filename = current ( explode ( '?' , $filename ) );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
2014-03-14 10:04:41 +00:00
$filename = apply_filters ( 'woocommerce_file_download_filename' , $filename , $product_id );
2013-11-14 12:40:33 +00:00
header ( " X-Robots-Tag: noindex, nofollow " , true );
2013-08-09 16:11:15 +00:00
header ( " Content-Type: " . $ctype );
header ( " Content-Description: File Transfer " );
2014-03-14 10:04:41 +00:00
header ( " Content-Disposition: attachment; filename= \" " . $filename . " \" ; " );
2013-08-09 16:11:15 +00:00
header ( " Content-Transfer-Encoding: binary " );
2014-02-26 11:54:16 +00:00
if ( $size = @ filesize ( $file_path ) ) {
2013-08-09 16:11:15 +00:00
header ( " Content-Length: " . $size );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
if ( $file_download_method == 'xsendfile' ) {
// Path fix - kudos to Jason Judge
2014-02-26 11:54:16 +00:00
if ( getcwd () ) {
2013-11-18 11:20:31 +00:00
$file_path = trim ( preg_replace ( '`^' . str_replace ( '\\' , '/' , getcwd () ) . '`' , '' , $file_path ), '/' );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
2014-03-14 10:04:41 +00:00
header ( " Content-Disposition: attachment; filename= \" " . $filename . " \" ; " );
2013-08-09 16:11:15 +00:00
if ( function_exists ( 'apache_get_modules' ) && in_array ( 'mod_xsendfile' , apache_get_modules () ) ) {
header ( " X-Sendfile: $file_path " );
exit ;
} elseif ( stristr ( getenv ( 'SERVER_SOFTWARE' ), 'lighttpd' ) ) {
header ( " X-Lighttpd-Sendfile: $file_path " );
exit ;
} elseif ( stristr ( getenv ( 'SERVER_SOFTWARE' ), 'nginx' ) || stristr ( getenv ( 'SERVER_SOFTWARE' ), 'cherokee' ) ) {
header ( " X-Accel-Redirect: / $file_path " );
exit ;
}
}
2014-02-26 11:54:16 +00:00
if ( $remote_file ) {
2014-06-26 10:30:09 +00:00
self :: readfile_chunked ( $file_path ) || header ( 'Location: ' . $file_path );
2014-02-26 11:54:16 +00:00
} else {
2014-06-26 10:30:09 +00:00
self :: readfile_chunked ( $file_path ) || wp_die ( __ ( 'File not found' , 'woocommerce' ) . ' <a href="' . esc_url ( home_url () ) . '" class="wc-forward">' . __ ( 'Go to homepage' , 'woocommerce' ) . '</a>' , '' , array ( 'response' => 404 ) );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
exit ;
}
/**
* readfile_chunked
* Reads file in chunks so big downloads are possible without changing PHP . INI - http :// codeigniter . com / wiki / Download_helper_for_large_files /
2013-11-28 16:49:30 +00:00
* @ param string $file
* @ param bool $retbytes return bytes of file
* @ return bool | int
* @ todo Meaning of the return value ? Last return is status of fclose ?
2013-08-09 16:11:15 +00:00
*/
public static function readfile_chunked ( $file , $retbytes = true ) {
$chunksize = 1 * ( 1024 * 1024 );
$buffer = '' ;
$cnt = 0 ;
2014-05-25 21:10:23 +00:00
if ( file_exists ( $file ) ) {
$handle = fopen ( $file , 'r' );
if ( $handle === FALSE ) {
return FALSE ;
}
} elseif ( version_compare ( PHP_VERSION , '5.4.0' , '<' ) && ini_get ( 'safe_mode' ) ) {
$handle = @ fopen ( $file , 'r' );
if ( $handle === FALSE ) {
return FALSE ;
}
} else {
2013-08-09 16:11:15 +00:00
return FALSE ;
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
while ( ! feof ( $handle ) ) {
$buffer = fread ( $handle , $chunksize );
echo $buffer ;
2014-05-25 21:10:23 +00:00
if ( ob_get_length () ) {
ob_flush ();
flush ();
}
2013-08-09 16:11:15 +00:00
2014-02-26 11:54:16 +00:00
if ( $retbytes ) {
2013-08-09 16:11:15 +00:00
$cnt += strlen ( $buffer );
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
}
$status = fclose ( $handle );
2014-02-26 11:54:16 +00:00
if ( $retbytes && $status ) {
2013-08-09 16:11:15 +00:00
return $cnt ;
2014-02-26 11:54:16 +00:00
}
2013-08-09 16:11:15 +00:00
return $status ;
}
}
2014-05-28 13:52:50 +00:00
WC_Download_Handler :: init ();