/var/www/html_it/wp-content/plugins/woocommerce/src/Internal/Admin/ImportExport/CSVUploadHelper.php


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
<?php
declare( strict_types );

namespace 
Automattic\WooCommerce\Internal\Admin\ImportExport;

use 
Automattic\WooCommerce\Internal\Utilities\FilesystemUtil;

/**
 * Helper for CSV import functionality.
 *
 * @since 9.3.0
 */
class CSVUploadHelper {

    
/**
     * Name (inside the uploads folder) to use for the CSV import directory.
     *
     * @return string
     */
    
protected function get_import_subdir_name(): string {
        return 
'wc-imports';
    }

    
/**
     * Returns the full path to the CSV import directory within the uploads folder.
     * It will attempt to create the directory if it doesn't exist.
     *
     * @param bool $create TRUE to attempt to create the directory. FALSE otherwise.
     * @return string
     * @throws \Exception In case the upload directory doesn't exits or can't be created.
     */
    
public function get_import_dirbool $create true ): string {
        
$wp_upload_dir wp_upload_dirnull$create );
        if ( 
$wp_upload_dir['error'] ) {
            throw new 
\Exceptionesc_html$wp_upload_dir['error'] ) );
        }

        
$upload_dir trailingslashit$wp_upload_dir['basedir'] ) . $this->get_import_subdir_name();
        if ( 
$create ) {
            
FilesystemUtil::mkdir_p_not_indexable$upload_dir );
        }
        return 
$upload_dir;
    }

    
/**
     * Handles a CSV file upload.
     *
     * @param string     $import_type        Type of upload or context.
     * @param string     $files_index        $_FILES index that contains the file to upload.
     * @param array|null $allowed_mime_types List of allowed MIME types.
     * @return array {
     *     Details for the uploaded file.
     *
     *     @type int    $id   Attachment ID.
     *     @type string $file Full path to uploaded file.
     * }
     *
     * @throws \Exception In case of error.
     */
    
public function handle_csv_uploadstring $import_typestring $files_index 'import', ?array $allowed_mime_types null ): array {
        
$import_type sanitize_key$import_type );
        if ( ! 
$import_type ) {
            throw new 
\Exception'Import type is invalid.' );
        }

        if ( ! 
$allowed_mime_types ) {
            
$allowed_mime_types = array(
                
'csv' => 'text/csv',
                
'txt' => 'text/plain',
            );
        }

        
$file $_FILES$files_index ] ?? null// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.NonceVerification.Missing
        
if ( ! isset( $file['tmp_name'] ) || ! is_uploaded_file$file['tmp_name'] ) ) {
            throw new 
\Exceptionesc_html__'File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your php.ini or by post_max_size being defined as smaller than upload_max_filesize in php.ini.''woocommerce' ) );
        }

        if ( ! 
function_exists'wp_import_handle_upload' ) ) {
            require_once 
ABSPATH 'wp-admin/includes/import.php';
        }

        
// Make sure upload dir exists.
        
$this->get_import_dir();

        
// Add prefix.
        
$file['name'] = $import_type '-' $file['name'];

        
$overrides_callback = function ( $overrides_ ) use ( $allowed_mime_types ) {
            
$overrides_['test_form'] = false;
            
$overrides_['test_type'] = true;
            
$overrides_['mimes']     = $allowed_mime_types;
            return 
$overrides_;
        };

        
add_filter'upload_dir', array( $this'override_upload_dir' ) );
        
add_filter'wp_unique_filename', array( $this'override_unique_filename' ), 0);
        
add_filter'wp_handle_upload_overrides'$overrides_callback999 );
        
add_filter'wp_handle_upload_prefilter', array( $this'remove_txt_from_uploaded_file' ), );
        
add_filter'wp_check_filetype_and_ext', array( $this'filter_woocommerce_check_filetype_for_csv' ), 10);

        
$orig_files_import $_FILES['import'] ?? null// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.NonceVerification.Missing
        
$_FILES['import']  = $file;  // wp_import_handle_upload() expects the file to be in 'import'.

        
$upload wp_import_handle_upload();

        
remove_filter'upload_dir', array( $this'override_upload_dir' ) );
        
remove_filter'wp_unique_filename', array( $this'override_unique_filename' ), );
        
remove_filter'wp_handle_upload_overrides'$overrides_callback999 );
        
remove_filter'wp_handle_upload_prefilter', array( $this'remove_txt_from_uploaded_file' ), );
        
remove_filter'wp_check_filetype_and_ext', array( $this'filter_woocommerce_check_filetype_for_csv' ), 10 );

        if ( 
$orig_files_import ) {
            
$_FILES['import'] = $orig_files_import;
        } else {
            unset( 
$_FILES['import'] );
        }

        if ( ! empty( 
$upload['error'] ) ) {
            throw new 
\Exceptionesc_html$upload['error'] ) );
        }

        if ( ! 
wc_is_file_valid_csv$upload['file'], false ) ) {
            
wp_delete_attachment$file['id'], true );
            throw new 
\Exceptionesc_html__'Invalid file type for a CSV import.''woocommerce' ) );
        }

        return 
$upload;
    }

    
/**
     * Hooked onto 'upload_dir' to override the default upload directory for a CSV upload.
     *
     * @param array $uploads WP upload dir details.
     * @return array
     *
     * @internal For exclusive usage of WooCommerce core, backwards compatibility not guaranteed.
     */
    
public function override_upload_dir$uploads ): array {
        
$new_subdir '/' $this->get_import_subdir_name();

        
$uploads['path']   = $uploads['basedir'] . $new_subdir;
        
$uploads['url']    = $uploads['baseurl'] . $new_subdir;
        
$uploads['subdir'] = $new_subdir;

        return 
$uploads;
    }

    
/**
     * Adds a random string to the name of an uploaded CSV file to make it less discoverable. Hooked onto 'wp_unique_filename'.
     *
     * @param string $filename File name.
     * @param string $ext      File extension.
     * @return string
     *
     * @internal For exclusive usage of WooCommerce core, backwards compatibility not guaranteed.
     */
    
public function override_unique_filenamestring $filenamestring $ext ): string {
        
$length min10255 strlen$filename ) - );
        if ( 
$length ) {
            
$suffix   strtolowerwp_generate_password$lengthfalsefalse ) );
            
$filename substr$filename0strlen$filename ) - strlen$ext ) ) . '-' $suffix $ext;
        }

        return 
$filename;
    }

    
/**
     * `wp_import_handle_upload()` appends .txt to any file name. This function is hooked onto 'wp_handle_upload_prefilter'
     * to remove those extra characters.
     *
     * @param array $file File details in the form of a $_FILES entry.
     * @return array Modified file details.
     *
     * @internal For exclusive usage of WooCommerce core, backwards compatibility not guaranteed.
     */
    
public function remove_txt_from_uploaded_file( array $file ): array {
        
$file['name'] = substr$file['name'], 0, -);
        return 
$file;
    }

    
/**
     * Filters the WordPress determination of a file's type and extension, specifically to correct
     * CSV files that are misidentified as 'text/html'.
     *
     * @param array  $data      An array of file data: ['ext'] (string), ['type'] (string), ['proper_filename'] (string|false).
     * @param string $file      Full path to the file.
     * @param string $filename  The Mime type of the file.
     * @param array  $mimes     Array of mime types.
     * @param string $real_mime The actual mime type or empty string.
     * @return array Filtered file data.
     */
    
public function filter_woocommerce_check_filetype_for_csv$data$file$filename$mimes$real_mime ) {
        
// Check if the file was misidentified as 'text/html' by PHP.
        
if ( 'text/html' === $real_mime ) {
            
// Determine the expected file type based on the filename extension.
            // $mimes here is the context-specific list of mimes for the current upload.
            
$filename_check wp_check_filetype$filename$mimes );

            
$file_ext  $filename_check['ext'];
            
$file_type $filename_check['type'];

            if ( ( 
'csv' === $file_ext && 'text/csv' === $file_type ) ) {
                
$data['ext']  = 'csv';
                
$data['type'] = 'text/csv';
            }
        }

        return 
$data;
    }
}