uploads.php 15.6 KB
<?php
    /**
	* @Description :Process Uploads
	* @Package : Drag & Drop Multiple File Upload - Contact Form 7
	* @Author : CodeDropz
	*/

    if ( ! defined( 'ABSPATH' ) || ! defined('dnd_upload_cf7') || ! defined('dnd_upload_cf7_PRO') ) {
		exit;
	}

    class CodeDropz_Uploader_Uploads {

        private static $instance = null;

        public $uploads_dir = array();

        public $helper = null;

        public $options = array(
            'zip_files'     =>  false,
            'save_to_media' =>  false,
            'tmp_folder'    =>  ''
        );

        public $debug = false;

        public static function get_instance() {
            if( null === self::$instance ) {
                self::$instance = new self();
            }
            return self::$instance;
        }

        public function __construct(){
            
            // Get directory uri & base dir
            $this->uploads_dir = CodeDropz_Uploader_Directory::get_instance()->get_dir();

            // Initialize helper instance
            $this->helper = CodeDropz_Uploader_Helpers::get_instance();

            // Tmp folder
            $this->options['tmp_folder'] = wp_dndcf7_tmp_folder;

            // Debug
            $this->debug = get_option('drag_n_drop_debug');

        }

        // Begin to process uploading files
        public function upload() {

            // Verify ajax none
            if( ! check_ajax_referer( 'dnd_cf7_ajax_upload', 'security', false ) ){
                wp_send_json_error('The security nonce is invalid or expired');
            }

            $upload_dir = '';

            // Make sure upload directory is set (Set Directory/Path - where the files being stored)
            if( ! empty( $_POST['upload_dir'] ) ) {
                $upload_dir = path_join( $this->uploads_dir['basedir'], trim( sanitize_text_field( $_POST['upload_dir'] ) ) );
            }

            // cf7 form id & upload name
            $cf7_id = sanitize_text_field( (int)$_POST['form_id']);

            // Get the name of upload field.
            $cf7_upload_name = sanitize_text_field( $_POST['upload_name'] );
            
            // input type file 'name'
            $name = 'upload-file';

            // Setup $_FILE name (from Ajax)
            $file = isset( $_FILES[$name] ) ? array_map( 'sanitize_text_field', $_FILES[ $name ] ) : null;
            
            // Custom name
            if( isset( $_POST['orig-name'] ) ) {
                $file['name'] = sanitize_text_field( $_POST['orig-name'] );
            }

            // Tells whether the file was uploaded via HTTP POST
            if ( ! is_uploaded_file( $file['tmp_name'] ) ) {
                $error_code = ( $file['error'] == 1 ? __('The uploaded file exceeds the upload_max_filesize limit.','dnd-upload-cf7') : $this->helper->get_error('failed_upload') );
                wp_send_json_error( dnmfu_cf7_option('drag_n_drop_error_failed_to_upload') ? dnmfu_cf7_option('drag_n_drop_error_failed_to_upload') : $error_code  );
            }

            // Get allowed ext list @expected : png|jpeg|jpg
            $allowed_types = $this->helper->allowed_types( $cf7_id );

            /* Get allowed extension */
            $supported_type = ( isset( $allowed_types["$cf7_upload_name"] ) ? $allowed_types["$cf7_upload_name"] : $this->helper->default_ext() );

            /* File type validation */
            $file_type_pattern = dndmfu_cf7_filetypes( $supported_type );

            // Get file extension
            $extension = strtolower( pathinfo( $file['name'], PATHINFO_EXTENSION ) );

            // validate file type
            if ( ! preg_match( $file_type_pattern, $file['name'] ) || ! dndmfu_cf7_validate_type( $extension, $supported_type ) ) {
                wp_send_json_error( dnmfu_cf7_option('drag_n_drop_error_invalid_file') ? dnmfu_cf7_option('drag_n_drop_error_invalid_file') : $this->helper->get_error('invalid_type') );
            }

            // validate file size limit
            if( $file['size'] > (int)$_POST['size_limit'] ) {
                wp_send_json_error( dnmfu_cf7_option('drag_n_drop_error_files_too_large') ? dnmfu_cf7_option('drag_n_drop_error_files_too_large') : $this->helper->get_error('large_file') );
            }

            // Get dir setup
            $dir = CodeDropz_Uploader_Folders::get_instance()->create_folder( $upload_dir, true );

            // Allow other plugin to change path
            $base_dir = apply_filters( 'dnd_cf7_base_upload_folder', $dir );

            // Security Check:: Preventing Directory Traversal
            if( strpos( $base_dir, $this->uploads_dir['basedir'] ) !== 0 ){
                $info = $this->debug ? '<span style="display:none;"> '. $base_dir .'|'. $this->uploads_dir['basedir'] .'</span>' : '';
                wp_send_json_error( '400 Bad Request: Traversal Attack!'. $info );
            }

            // Create file name
            $filename = $file['name'];
            $filename = wpcf7_canonicalize( $filename, 'as-is' );
            $filename = wpcf7_antiscript_file_name( $filename );

            // Add filter on upload file name
            $filename = apply_filters( 'wpcf7_upload_file_name', $filename,	$file['name'] );

            // Generate new filename
            if( ! get_option('drag_n_drop_overwrite_file') ) {
                $filename = wp_unique_filename( $base_dir, $filename );
            }

            $new_file = path_join( $base_dir, $filename );

            // Php manual files upload
            if ( false === move_uploaded_file( $file['tmp_name'], $new_file ) ) {
                $error_upload = dnmfu_cf7_option('drag_n_drop_error_failed_to_upload') ? dnmfu_cf7_option('drag_n_drop_error_failed_to_upload') : $this->helper->get_error('failed_upload');
                wp_send_json_error( $error_upload );
            }else{

                // Setup path and file name and add it to response.
                $path = trailingslashit( '/' . wp_basename( $base_dir ) );

                // Change file permission to 0400
                chmod( $new_file, 0644 );

                // Resize image
                if( get_option('drag_n_drop_image_resize') ) {
                    $filename = CodeDropz_Uploader_Resize::get_instance()->resize( $path, $filename );
                }

                // Get details of attachment from media_json_respons function
                $files = $this->helper->media_response( $path, wp_basename( $filename ) );

                // Send files to json response
                wp_send_json_success( $files );
            }

            die;
        }

        /**
        * Begin process ajax chunks upload.
        */

        public function upload_chunks() {

            // Verify ajax none
            if( ! check_ajax_referer( 'dnd_cf7_ajax_upload', 'security', false ) ){
                wp_send_json_error('The security nonce is invalid or expired');
            }

            $total_chunks = ( isset( $_POST['total_chunks'] ) ? (int)$_POST['total_chunks'] : '' );
            $num = ( isset( $_POST['chunk'] ) ? (int)$_POST['chunk'] + 1 : '' );
            $chunk_size = ( isset( $_POST['chunk_size'] ) ? sanitize_text_field( $_POST['chunk_size'] ) : '' );

            // Setup basedir `tmp_upload/chunks` folder for chunks files
            $tmp_folder = apply_filters('dndcf7_chunks_path', path_join( $this->uploads_dir['basedir'], $this->options['tmp_folder'] .'/chunks/' ) );

            // create tmp directory - if not exist
            if( ! is_dir( $tmp_folder ) ) {
                wp_mkdir_p( $tmp_folder );
            }

            // input type file 'name'
            $name = 'chunks-file';

            // Setup $_FILE name (from Ajax)
            $file = isset( $_FILES[$name] ) ? array_map( 'sanitize_text_field', $_FILES[ $name ] ) : null;

            // File Info
            $tmp_name = $file['tmp_name'];

            /**
            * File Types - Validation
            */

            $cf7_id 			= sanitize_text_field( (int)$_POST['form_id']); 	//cf7 form id & upload name
            $cf7_upload_name 	= sanitize_text_field( $_POST['upload_name'] ); 	//Get the name of upload field.
            $allowed_types 		= $this->helper->allowed_types( $cf7_id ); 			//Get allowed ext list @expected : png|jpeg|jpg
            $supported_type 	= ( isset( $allowed_types["$cf7_upload_name"] ) ? $allowed_types["$cf7_upload_name"] : $this->helper->default_ext() );
            $file_type_pattern 	= dndmfu_cf7_filetypes( $supported_type );

            // Get file extension
            $extension = strtolower( pathinfo( $file['name'], PATHINFO_EXTENSION ) );

            // validate file type
            if ( ! preg_match( $file_type_pattern, $file['name'] ) || ! dndmfu_cf7_validate_type( $extension, $supported_type ) ) {
                wp_send_json_error( dnmfu_cf7_option('drag_n_drop_error_invalid_file') ? dnmfu_cf7_option('drag_n_drop_error_invalid_file') : $this->helper->get_error('invalid_type') );
            }

            // File unique index
            $file_index = sanitize_text_field( wp_unslash( $_POST['unique'] ) );

            // Check if file exists then add increment number on filename.
            $filename = wp_unique_filename( $tmp_folder, $file['name'] );

            // New Filename for chunks
            $new_file_name = $file_index . '-chunk-' . $num .'-'. $filename;

            // Tells whether the file was uploaded via HTTP POST
            if ( ! is_uploaded_file( $tmp_name ) ) {
                $error_code = ( $file['error'] == 1 ? __('The uploaded file exceeds the upload_max_filesize limit.','dnd-upload-cf7') : $this->helper->get_error('failed_upload') );
                wp_send_json_error( dnmfu_cf7_option('drag_n_drop_error_failed_to_upload') ? dnmfu_cf7_option('drag_n_drop_error_failed_to_upload') : $error_code  );
            }

            // Make sure upload directory is set
            if( ! empty( $_POST['upload_dir'] ) ) {
                $upload_dir = trim( sanitize_text_field( $_POST['upload_dir'] ) );
            }

            // Final dir for merging files
            $final_chunks_path = apply_filters( 'dndcf7_final_chunks_path', path_join( $this->uploads_dir['basedir'], $upload_dir .'/' ) );

            // Create base dir if it's not yet created
            if( ! is_dir( $final_chunks_path ) ) {
                wp_mkdir_p( $final_chunks_path );
            }

            // Get real path
            $final_chunks_path = trailingslashit( realpath( $final_chunks_path ) );

            // Preventing Directory Traversal
            if( strpos( $final_chunks_path, $this->uploads_dir['basedir'] ) !== 0 ){
                $info = $this->debug ? '<span style="display:none;"> '. $final_chunks_path .'|'. $this->uploads_dir['basedir'] .'</span>' : '';
                wp_send_json_error( '400 Bad Request: Traversal Attack!'. $info );
            }

            //@todo - validate file size if chunks > server_max_upload limit

            if ( false === move_uploaded_file( $tmp_name, $tmp_folder . $new_file_name ) ) {
                $error_upload = dnmfu_cf7_option('drag_n_drop_error_failed_to_upload') ? dnmfu_cf7_option('drag_n_drop_error_failed_to_upload') : $this->helper->get_error('failed_upload');
                wp_send_json_error( $error_upload );
            }

            // Process merging files
            if( $num == $total_chunks ) {

                // Create unique filename
                $chunk_unique_name = $chunks_name = apply_filters( 'wpcf7_upload_file_name', $filename,	$file['name'] );

                // Final - name of chunk
                if( ! get_option('drag_n_drop_overwrite_file') ) {
                    $chunk_unique_name = wp_unique_filename( $final_chunks_path, $chunks_name );
                }

                // Begin merging files and loop chunks
                for( $i = 1; $i <= $total_chunks; $i++ ) {
                    $chunk_name = $tmp_folder . $file_index . '-chunk-' . $i .'-'. $filename;
                    if( file_exists( $chunk_name ) ) {

                        // Open chunks file ( rb - read binary )
                        $chunk_file = fopen( $chunk_name, 'rb');
                        if( $chunk_file ) {
                            $read_file_chunks = fread( $chunk_file, $chunk_size );
                            fclose( $chunk_file );
                        }

                        // Make sure path is writable - write final files
                        if( is_writable( $final_chunks_path ) && $read_file_chunks == true ) {
                            $final_file = fopen( $final_chunks_path . $chunk_unique_name, 'ab' );
                            if( $final_file ) {
                                $write = fwrite( $final_file, $read_file_chunks );
                                unset( $read_file_chunks );
                                fclose( $final_file );
                                @chmod( $final_chunks_path . $chunk_unique_name, 0644 );
                            }
                        }

                        // Remove chunks file
                        unlink( $chunk_name );
                    }
                }

                // Done merging - return files details
                if( file_exists( $final_chunks_path . $chunk_unique_name ) ) {

                    //Trim path
                    $path = trailingslashit( '/' . wp_basename( $final_chunks_path ) );

                    // Resize image
                    if( get_option('drag_n_drop_image_resize') ) {
                        $image = CodeDropz_Uploader_Resize::get_instance()->resize( $path, $chunk_unique_name );
                    }

                    // Get details of attachment from media_json_respons function
                    wp_send_json_success( $this->helper->media_response( $path, $chunk_unique_name ) );
                }
            }

            // Return part chunks
            if( file_exists( $tmp_folder . $new_file_name ) ) {
                wp_send_json_success( array('partial_chunks' => $new_file_name ) );
            }

            die;
        }

        /**
        * Delete specific files
        */

        public function delete() {

            // Verify ajax none
            if( ! check_ajax_referer( 'dnd_cf7_ajax_upload', 'security', false ) ){
                wp_send_json_error('The security nonce is invalid or expired');
            }

            // Sanitize Path
            $path = ( isset( $_POST['path'] ) ? sanitize_text_field( $_POST['path'] ) : null );

            // Make sure path is set
            if( ! is_null( $path ) ) {

                // Check valid filename & extensions
                if( preg_match_all('/wp-|(\.php|\.exe|\.js|\.phtml|\.cgi|\.aspx|\.asp|\.bat)/', $path ) ) {
                    die('File not safe');
                }

                // Concat path and upload directory	- prevent from transversal attack
                $_dir =  trailingslashit( wp_basename( dirname( $path ) ) );
                $file_path = realpath( trailingslashit( $this->uploads_dir['basedir'] ) . $_dir . wp_basename( $path ) );

                // Check if directory inside wp_content/uploads/
                $is_path_in_content_dir = strpos( $file_path, realpath( wp_normalize_path( $this->uploads_dir['basedir'] ) ) );

                // Check if is in the correct upload_dir
                if( ! preg_match("/". wp_dndcf7_upload_folder ."/i", $file_path ) || ( 0 !== $is_path_in_content_dir ) ) {
                    die('It\'s not a valid upload directory');
                }

                // Check if file exists
                if( file_exists( $file_path ) ){
                    $this->helper->delete_files( $file_path );
                    if( ! file_exists( $file_path ) ) {
                        wp_send_json_success('File Deleted!');
                    }
                }
            }

            die;
        }


    }