- WordPress core y plugins - Tema Twenty Twenty-Four configurado - Plugin allow-unfiltered-html.php simplificado - .gitignore configurado para excluir wp-config.php y uploads 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
455 lines
13 KiB
PHP
Executable File
455 lines
13 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* Secure Uploader
|
|
*
|
|
* @since 1.0.0
|
|
*
|
|
* @package WP-PDF-Secure
|
|
*/
|
|
|
|
// Exit if accessed directly.
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Uploader class
|
|
*/
|
|
class WP_PDF_Secure_Uploader {
|
|
|
|
/**
|
|
* Undocumented variable
|
|
*
|
|
* @var bool
|
|
*/
|
|
protected $pdfemb_secure = true;
|
|
|
|
/**
|
|
* Undocumented variable
|
|
*
|
|
* @var bool
|
|
*/
|
|
protected $pdfemb_cacheencrypted = true;
|
|
|
|
/**
|
|
* Undocumented function
|
|
*
|
|
* @param object $pdfemb_secure PDF secure object.
|
|
* @param bool $pdfemb_cacheencrypted Encrypted cache.
|
|
*/
|
|
public function __construct( $pdfemb_secure = true, $pdfemb_cacheencrypted = true ) {
|
|
$this->pdfemb_secure = $pdfemb_secure;
|
|
$this->pdfemb_cacheencrypted = $pdfemb_cacheencrypted;
|
|
}
|
|
|
|
/**
|
|
* Undocumented function
|
|
*
|
|
* @return void
|
|
*/
|
|
public function intercept_uploads() {
|
|
// Called in admin_init.
|
|
add_filter( 'wp_handle_upload_prefilter', array( $this, 'custom_upload_filter' ) );
|
|
}
|
|
|
|
/**
|
|
* Undocumented function
|
|
*
|
|
* @return void
|
|
*/
|
|
public function handle_downloads() {
|
|
// Called in init.
|
|
if ( ! isset( $_GET['pdfemb-serveurl'] ) && ! isset( $_GET['pdfemb-nonce'] ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( 'POST' !== $_SERVER['REQUEST_METHOD'] && ! isset( $_GET['pdfemb-nonce'] ) ) {
|
|
return;
|
|
}
|
|
|
|
$direct_download = false;
|
|
if ( isset( $_GET['pdfemb-nonce'] ) ) {
|
|
if ( ! wp_verify_nonce( $_GET['pdfemb-nonce'], 'pdfemb-secure-download-' . $_GET['pdfemb-serveurl'] ) ) {
|
|
return;
|
|
} else {
|
|
$direct_download = true;
|
|
}
|
|
}
|
|
|
|
$pdfurl = $_GET['pdfemb-serveurl'];
|
|
|
|
$filepath = $this->get_secure_path( $pdfurl );
|
|
if ( '' !== $filepath && file_exists( $filepath ) ) {
|
|
$filename = basename( $filepath );
|
|
|
|
if ( ! $this->is_func_disabled( 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) { // @codingStandardsIgnoreLine
|
|
@set_time_limit( 0 ); // @codingStandardsIgnoreLine
|
|
}
|
|
if ( function_exists( 'get_magic_quotes_runtime' ) && get_magic_quotes_runtime() && version_compare( phpversion(), '5.4', '<' ) ) { // @codingStandardsIgnoreLine
|
|
set_magic_quotes_runtime( 0 ); // @codingStandardsIgnoreLine
|
|
}
|
|
|
|
@session_write_close(); // @codingStandardsIgnoreLine
|
|
if ( function_exists( 'apache_setenv' ) ) {
|
|
@apache_setenv( 'no-gzip', 1 ); // @codingStandardsIgnoreLine
|
|
}
|
|
@ini_set( 'zlib.output_compression', 'Off' ); // @codingStandardsIgnoreLine
|
|
|
|
if ( ob_get_length() ) {
|
|
ob_clean(); // Clear output buffer in case Unicode BOM was added by a PHP file saved in wrong encoding.
|
|
}
|
|
|
|
nocache_headers();
|
|
header( 'Robots: none' );
|
|
header( 'Content-Type: application/octet-stream' );
|
|
header( 'Content-Description: File Transfer' );
|
|
header( 'Content-Disposition: attachment; filename="' . $filename . ( $direct_download ? '' : '.binary' ) . '"' );
|
|
header( 'Content-Transfer-Encoding: binary' );
|
|
|
|
if ( ! class_exists( 'WP_PDF_SimpleRC4' ) ) {
|
|
include_once dirname( __FILE__ ) . '/rc4_simple.php';
|
|
}
|
|
|
|
$sk = $this->get_secret_key();
|
|
|
|
$filetime = filemtime( $filepath );
|
|
$cache_to_file = ( false !== $filetime && ! $direct_download && $this->pdfemb_cacheencrypted
|
|
? $filepath . '.encrypted-cache.' . md5( $filetime . '-' . $sk ) . '.pdf'
|
|
: '' );
|
|
|
|
$myrc4 = $direct_download
|
|
? new WP_PDF_DirectRC4( '' )
|
|
: new WP_PDF_SimpleRC4( $sk );
|
|
|
|
$this->readfile_chunked( $filepath, true, $myrc4, $cache_to_file );
|
|
|
|
} else {
|
|
header( 'Location: ' . $pdfurl );
|
|
}
|
|
|
|
exit();
|
|
}
|
|
|
|
/**
|
|
* Helper Method to get secure
|
|
*
|
|
* @param string $pdfurl PDF Url.
|
|
* @return string
|
|
*/
|
|
public function get_secure_path( $pdfurl ) {
|
|
$upload_dir = wp_upload_dir();
|
|
|
|
$basedir = trailingslashit( $upload_dir['basedir'] ) . 'securepdfs/';
|
|
$baseurl = trailingslashit( set_url_scheme( $upload_dir['baseurl'] ) ) . 'securepdfs/';
|
|
|
|
$regex = '|^' . $baseurl . '(([^/]+/)*([^/]+\.pdf))$|';
|
|
|
|
$matches = array();
|
|
if ( preg_match( $regex, set_url_scheme( $pdfurl ), $matches ) ) {
|
|
// Check for .. tricks.
|
|
|
|
if ( ! preg_match( '|[\\\/]\.\.[\\\/]|', $pdfurl ) && strpos( $pdfurl, '\\' ) === false ) {
|
|
return $basedir . $matches[1];
|
|
}
|
|
}
|
|
return ''; // Wasn't a secure PDF.
|
|
}
|
|
|
|
/**
|
|
* Hash
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $myrc4;
|
|
|
|
/**
|
|
* Read Chunk file
|
|
*
|
|
* @param string $file File.
|
|
* @param bool $retbytes Retbytes.
|
|
* @param bool $myrc4 RC4.
|
|
* @param string $cache_to_file file to cache.
|
|
* @return mixed
|
|
*/
|
|
protected function readfile_chunked( $file, $retbytes = true, $myrc4 = false, $cache_to_file = '' ) {
|
|
|
|
$chunksize = 1024 * 1024;
|
|
$buffer = '';
|
|
$cnt = 0;
|
|
|
|
$reading_from_cache = false;
|
|
$handle = false;
|
|
$cache_handle = false;
|
|
|
|
if ( '' !== $cache_to_file ) {
|
|
$cache_handle = @fopen( $cache_to_file, 'r' ); // @codingStandardsIgnoreLine
|
|
if ( false === $cache_handle ) {
|
|
$cache_handle = @fopen( $cache_to_file, 'w' ); // @codingStandardsIgnoreLine
|
|
} else {
|
|
// Read from cache.
|
|
$reading_from_cache = true;
|
|
$handle = $cache_handle;
|
|
$cache_handle = false;
|
|
}
|
|
}
|
|
|
|
$size = @filesize( $file ); // @codingStandardsIgnoreLine
|
|
|
|
if ( $reading_from_cache && @filesize( $cache_to_file ) != $size ) { // @codingStandardsIgnoreLine
|
|
// Revert back to non-cached version since can't be trusted.
|
|
error_log( 'Cached encrypted PDF was not of correct filesize: ' . $cache_to_file );
|
|
$handle = false;
|
|
$reading_from_cache = false;
|
|
$cache_handle = false;
|
|
}
|
|
|
|
if ( false === $handle ) {
|
|
$handle = @fopen( $file, 'r' ); // @codingStandardsIgnoreLine
|
|
}
|
|
|
|
if ( false === $handle ) {
|
|
return false;
|
|
}
|
|
|
|
if ( $size = @filesize( $file ) ) { // @codingStandardsIgnoreLine
|
|
header( 'Content-Length: ' . $size );
|
|
}
|
|
|
|
while ( ! @feof( $handle ) ) { // @codingStandardsIgnoreLine
|
|
$buffer = @fread( $handle, $chunksize ); // @codingStandardsIgnoreLine
|
|
|
|
if ( ! $reading_from_cache ) {
|
|
$buffer = $myrc4->rc4_encrypt_block( $buffer );
|
|
}
|
|
|
|
echo $buffer; // @codingStandardsIgnoreLine
|
|
|
|
if ( ! $reading_from_cache && false !== $cache_handle ) {
|
|
// Write to cache.
|
|
@fwrite( $cache_handle, $buffer ); // @codingStandardsIgnoreLine
|
|
}
|
|
|
|
if ( $retbytes ) {
|
|
$cnt += strlen( $buffer );
|
|
}
|
|
}
|
|
|
|
$status = @fclose( $handle ); // @codingStandardsIgnoreLine
|
|
|
|
if ( false !== $cache_handle ) {
|
|
@fclose( $cache_handle ); // @codingStandardsIgnoreLine
|
|
}
|
|
|
|
if ( $retbytes && $status ) {
|
|
return $cnt;
|
|
}
|
|
|
|
return $status;
|
|
}
|
|
|
|
/**
|
|
* Helper method for Secret Key
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_secret_key() {
|
|
$sk = get_site_option( 'pdfemb-sk' );
|
|
|
|
if ( false === $sk ) {
|
|
$sk = md5( sprintf( 'pdfemb-%d-%d', wp_rand(), time() ) );
|
|
update_site_option( 'pdfemb-sk', $sk, 60 * 60 * 12 );
|
|
}
|
|
|
|
return $sk;
|
|
}
|
|
|
|
/**
|
|
* Helper Method for Custom Upload Filter.
|
|
*
|
|
* @param string $file Upload File.
|
|
* @return string
|
|
*/
|
|
public function custom_upload_filter( $file ) {
|
|
|
|
if ( 'application/pdf' === $file['type'] && $this->pdfemb_secure ) {
|
|
add_filter( 'upload_dir', array( $this, 'custom_upload_dir' ) );
|
|
}
|
|
|
|
return $file;
|
|
}
|
|
|
|
/**
|
|
* Undocumented function
|
|
*
|
|
* @param string $function Function.
|
|
* @return bool
|
|
*/
|
|
protected function is_func_disabled( $function ) {
|
|
$disabled = explode( ',', ini_get( 'disable_functions' ) );
|
|
|
|
return in_array( $function, $disabled, true );
|
|
}
|
|
|
|
/**
|
|
* Undocumented function
|
|
*
|
|
* @param array $upload Upload.
|
|
* @return array
|
|
*/
|
|
public function custom_upload_dir( $upload ) {
|
|
|
|
// Override the year / month being based on the post publication date, if year/month organization is enabled.
|
|
if ( get_option( 'uploads_use_yearmonth_folders' ) ) {
|
|
// Generate the yearly and monthly dirs.
|
|
$time = current_time( 'mysql' );
|
|
$y = substr( $time, 0, 4 );
|
|
$m = substr( $time, 5, 2 );
|
|
$upload['subdir'] = "/$y/$m";
|
|
}
|
|
|
|
$upload['subdir'] = '/securepdfs' . $upload['subdir'];
|
|
$upload['path'] = $upload['basedir'] . $upload['subdir'];
|
|
$upload['url'] = $upload['baseurl'] . $upload['subdir'];
|
|
|
|
return $upload;
|
|
}
|
|
|
|
/**
|
|
* Helper Method to create protected files
|
|
*
|
|
* @param bool $force Force Upload.
|
|
* @param string $method Protected method.
|
|
* @return void
|
|
*/
|
|
public function create_protection_files( $force = false, $method = false ) {
|
|
if ( false === get_transient( 'pdfemb_check_protection_files' ) || $force ) {
|
|
$wp_upload_dir = wp_upload_dir();
|
|
$upload_path = $wp_upload_dir['basedir'] . '/securepdfs';
|
|
wp_mkdir_p( $upload_path );
|
|
|
|
// Make sure the /edd folder is created.
|
|
wp_mkdir_p( $upload_path );
|
|
|
|
// Top level .htaccess file.
|
|
$rules = $this->get_htaccess_rules( $method );
|
|
|
|
if ( file_exists( $upload_path . '/.htaccess' ) ) {
|
|
$contents = @file_get_contents( $upload_path . '/.htaccess' ); // @codingStandardsIgnoreLine
|
|
if ( $contents !== $rules ) {
|
|
// Update the .htaccess rules if they don't match.
|
|
@file_put_contents( $upload_path . '/.htaccess', $rules ); // @codingStandardsIgnoreLine
|
|
}
|
|
} elseif ( wp_is_writable( $upload_path ) ) {
|
|
// Create the file if it doesn't exist.
|
|
@file_put_contents( $upload_path . '/.htaccess', $rules ); // @codingStandardsIgnoreLine
|
|
}
|
|
|
|
// Top level blank index.php.
|
|
if ( ! file_exists( $upload_path . '/index.php' ) && wp_is_writable( $upload_path ) ) {
|
|
@file_put_contents( $upload_path . '/index.php', '<?php' . PHP_EOL . '// This file is intentionally blank.' ); // @codingStandardsIgnoreLine
|
|
}
|
|
|
|
// Now place index.php files in all sub folders.
|
|
$folders = array();
|
|
$this->scan_folders( $upload_path, $folders );
|
|
|
|
foreach ( $folders as $folder ) {
|
|
// Create index.php, if it doesn't exist.
|
|
if ( ! file_exists( $folder . 'index.php' ) && wp_is_writable( $folder ) ) {
|
|
@file_put_contents( $folder . 'index.php', '<?php' . PHP_EOL . '// This file is intentionally blank.' ); // @codingStandardsIgnoreLine
|
|
}
|
|
}
|
|
// Check for the files every eight days.
|
|
set_transient( 'pdfemb_check_protection_files', true, 3600 * 24 * 8 );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper method to get htacess rules.
|
|
*
|
|
* @param string $method Protected Method.
|
|
* @return string
|
|
*/
|
|
protected function get_htaccess_rules( $method = false ) {
|
|
|
|
if ( empty( $method ) ) {
|
|
$method = 'direct';
|
|
}
|
|
|
|
switch ( $method ) {
|
|
|
|
case 'redirect':
|
|
// Prevent directory browsing.
|
|
$rules = 'Options -Indexes';
|
|
break;
|
|
|
|
case 'direct':
|
|
default:
|
|
$rules = "Options -Indexes\n";
|
|
$rules .= "<IfModule mod_version.c>\n";
|
|
$rules .= " <IfVersion < 2.4>\n";
|
|
$rules .= " Order Deny,Allow\n";
|
|
$rules .= " Deny from all\n";
|
|
$rules .= " <FilesMatch '\.(jpg|png|gif|mp3|ogg)$'>\n";
|
|
$rules .= " Order Allow,Deny\n";
|
|
$rules .= " Allow from all\n";
|
|
$rules .= " </FilesMatch>\n";
|
|
$rules .= " </IfVersion>\n";
|
|
$rules .= " <IfVersion >= 2.4>\n";
|
|
$rules .= " Require all denied\n";
|
|
$rules .= " <FilesMatch '\.(jpg|png|gif|mp3|ogg)$'>\n";
|
|
$rules .= " Require all granted\n";
|
|
$rules .= " </FilesMatch>\n";
|
|
$rules .= " </IfVersion>\n";
|
|
$rules .= "</IfModule>\n";
|
|
$rules .= "<IfModule !mod_version.c>\n";
|
|
$rules .= " <IfModule !mod_authz_core.c>\n";
|
|
$rules .= " Order Deny,Allow\n";
|
|
$rules .= " Deny from all\n";
|
|
$rules .= " <FilesMatch '\.(jpg|png|gif|mp3|ogg)$'>\n";
|
|
$rules .= " Order Allow,Deny\n";
|
|
$rules .= " Allow from all\n";
|
|
$rules .= " </FilesMatch>\n";
|
|
$rules .= " </IfModule>\n";
|
|
$rules .= " <IfModule mod_authz_core.c>\n";
|
|
$rules .= " Require all denied\n";
|
|
$rules .= " <FilesMatch '\.(jpg|png|gif|mp3|ogg)$'>\n";
|
|
$rules .= " Require all granted\n";
|
|
$rules .= " </FilesMatch>\n";
|
|
$rules .= " </IfModule>\n";
|
|
$rules .= "</IfModule>\n";
|
|
break;
|
|
|
|
}
|
|
return $rules;
|
|
}
|
|
|
|
/**
|
|
* Helper Method to scan folders
|
|
*
|
|
* @param string $path Path.
|
|
* @param string $return Return Path.
|
|
* @return void
|
|
*/
|
|
protected function scan_folders( $path, &$return ) {
|
|
$lists = @scandir( $path ); // @codingStandardsIgnoreLine
|
|
|
|
if ( ! empty( $lists ) ) {
|
|
foreach ( $lists as $f ) {
|
|
if ( is_dir( $path . DIRECTORY_SEPARATOR . $f ) && '.' !== $f && '..' !== $f ) {
|
|
$finaldirpath = trailingslashit( $path . DIRECTORY_SEPARATOR . $f );
|
|
if ( ! in_array( $finaldirpath, $return, true ) ) {
|
|
$return[] = $finaldirpath;
|
|
}
|
|
|
|
$this->scan_folders( $path . DIRECTORY_SEPARATOR . $f, $return );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|