| 1 |
<?php |
| 2 |
if (!defined('ABSPATH')) die('Access denied.'); |
| 3 |
|
| 4 |
if (!class_exists('UpdraftCentral_Uploader')) : |
| 5 |
|
| 6 |
/** |
| 7 |
* Handles file upload process initiated by plupload which sends the data directly to the |
| 8 |
* remote website. Intended to be shared and used by the "plugin" and "theme" modules. |
| 9 |
*/ |
| 10 |
class UpdraftCentral_Uploader { |
| 11 |
|
| 12 |
protected static $_instance = null; |
| 13 |
|
| 14 |
/** |
| 15 |
* Creates an instance of this class. Singleton Pattern |
| 16 |
* |
| 17 |
* @return object Instance of this class |
| 18 |
*/ |
| 19 |
public static function instance() { |
| 20 |
if (empty(self::$_instance)) { |
| 21 |
self::$_instance = new self(); |
| 22 |
} |
| 23 |
|
| 24 |
return self::$_instance; |
| 25 |
} |
| 26 |
|
| 27 |
/** |
| 28 |
* Sends the uploaded data to the remote website |
| 29 |
* |
| 30 |
* @param integer $site_id The ID of the remote site where the data is to be sent |
| 31 |
* @param array $data The data to send |
| 32 |
* @param string $module Indicates whether this request is intended for the 'plugin' or 'theme' module |
| 33 |
* @return array |
| 34 |
*/ |
| 35 |
private function send_upload_request($site_id, $data, $module) { |
| 36 |
|
| 37 |
$user = UpdraftCentral()->user; |
| 38 |
|
| 39 |
$user_id = get_current_user_id(); |
| 40 |
if (empty($user)) $user = UpdraftCentral()->get_user_object($user_id); |
| 41 |
|
| 42 |
if (!empty($user) && is_a($user, 'UpdraftCentral_User')) { |
| 43 |
$remote_params = array( |
| 44 |
'site_id' => $site_id, |
| 45 |
'data' => array( |
| 46 |
'command' => $module.'.upload_'.$module, |
| 47 |
'data' => $data |
| 48 |
) |
| 49 |
); |
| 50 |
|
| 51 |
$remote_response = $user->send_remote_command($remote_params); |
| 52 |
if (!empty($remote_response) && 'ok' == $remote_response['responsetype']) { |
| 53 |
$response = $remote_response['rpc_response']['response']; |
| 54 |
$data = $remote_response['rpc_response']['data']; |
| 55 |
|
| 56 |
if ('rpcok' === $response) { |
| 57 |
return $data; |
| 58 |
} else { |
| 59 |
$data['error'] = true; |
| 60 |
if (is_null($data['data'])) $data['data'] = array(); |
| 61 |
|
| 62 |
return $data; |
| 63 |
} |
| 64 |
} |
| 65 |
|
| 66 |
// If proccess gets to this line it would mean that we have encountered |
| 67 |
// an issue other than the expected response (like fatal error, etc.). So, it would be |
| 68 |
// helpful if we pass back the original response to the caller for easier debugging if need be. |
| 69 |
return $remote_response; |
| 70 |
} |
| 71 |
} |
| 72 |
|
| 73 |
/** |
| 74 |
* Retrieves a PHP error message associated with the error code. These error messages |
| 75 |
* are the ones raised when an upload error has occured using PHP. |
| 76 |
* |
| 77 |
* @param integer $code The error code returned by $_FILES |
| 78 |
* @return string |
| 79 |
*/ |
| 80 |
private function get_upload_error_message($code) { |
| 81 |
|
| 82 |
// $_FILES error code may return as string. Thus, we're going to |
| 83 |
// cast it into int before proceeding with the check. |
| 84 |
switch ((int) $code) { |
| 85 |
case 1: |
| 86 |
$message = __('The uploaded file exceeds the upload_max_filesize directive set in your UpdraftCentral dashboard\'s php.ini file.', 'updraftcentral'); |
| 87 |
break; |
| 88 |
case 2: |
| 89 |
$message = __('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.', 'updraftcentral'); |
| 90 |
break; |
| 91 |
case 3: |
| 92 |
$message = __('The uploaded file was only partially uploaded.', 'updraftcentral'); |
| 93 |
break; |
| 94 |
case 4: |
| 95 |
$message = __('No file was uploaded.', 'updraftcentral'); |
| 96 |
break; |
| 97 |
case 6: |
| 98 |
$message = __('Missing a temporary folder that is needed to upload files to your UpdraftCentral dashboard.', 'updraftcentral'); |
| 99 |
break; |
| 100 |
case 7: |
| 101 |
$message = __('Failed to write file to disk.', 'updraftcentral').' '.__("Make sure you have sufficient permission to upload files to your UpdraftCentral dashboard's file system.", 'updraftcentral'); |
| 102 |
break; |
| 103 |
case 8: |
| 104 |
$message = __('Your UpdraftCentral dashboard\'s PHP extension stopped the file upload.', 'updraftcentral'); |
| 105 |
break; |
| 106 |
default: |
| 107 |
$message = __('The file was not uploaded successfully.', 'updraftcentral').' '.__('Please try again.', 'updraftcentral'); |
| 108 |
break; |
| 109 |
} |
| 110 |
|
| 111 |
return $message; |
| 112 |
} |
| 113 |
|
| 114 |
/** |
| 115 |
* Retrieves the Plupload configuration |
| 116 |
* |
| 117 |
* @param string $module Indicates whether the current request is intended for the 'plugin' or 'theme' module |
| 118 |
* @return string |
| 119 |
*/ |
| 120 |
public function get_plupload_config($module) { |
| 121 |
|
| 122 |
$chunk_size = min(wp_max_upload_size()-1024, 1024*1024*2-1024); |
| 123 |
$plupload_init = array( |
| 124 |
'runtimes' => 'html5,flash,silverlight,html4', |
| 125 |
'browse_button' => 'plupload-browse-button', |
| 126 |
'container' => 'plupload-upload-ui', |
| 127 |
'drop_element' => 'drag-drop-area', |
| 128 |
'file_data_name' => 'async-upload', |
| 129 |
'multiple_queues' => false, |
| 130 |
'max_file_count' => 1, |
| 131 |
'max_file_size' => '100Gb', |
| 132 |
'chunk_size' => $chunk_size.'b', |
| 133 |
'url' => admin_url('admin-ajax.php'), |
| 134 |
'filters' => array(array('title' => __('Allowed Files'), 'extensions' => 'zip')), |
| 135 |
'multipart' => true, |
| 136 |
'multi_selection' => false, |
| 137 |
'urlstream_upload' => true, |
| 138 |
// additional post data to send to our ajax hook |
| 139 |
'multipart_params' => array( |
| 140 |
'_ajax_nonce' => wp_create_nonce('updraftcentral-uploader-'.$module), |
| 141 |
'action' => $module.'_uploader_action' |
| 142 |
) |
| 143 |
); |
| 144 |
|
| 145 |
// WP 3.9 updated to plupload 2.0 - https://core.trac.wordpress.org/ticket/25663 |
| 146 |
if (is_file(ABSPATH.WPINC.'/js/plupload/Moxie.swf')) { |
| 147 |
$plupload_init['flash_swf_url'] = includes_url('js/plupload/Moxie.swf'); |
| 148 |
} else { |
| 149 |
$plupload_init['flash_swf_url'] = includes_url('js/plupload/plupload.flash.swf'); |
| 150 |
} |
| 151 |
|
| 152 |
if (is_file(ABSPATH.WPINC.'/js/plupload/Moxie.xap')) { |
| 153 |
$plupload_init['silverlight_xap_url'] = includes_url('js/plupload/Moxie.xap'); |
| 154 |
} else { |
| 155 |
$plupload_init['silverlight_xap_url'] = includes_url('js/plupload/plupload.silverlight.swf'); |
| 156 |
} |
| 157 |
|
| 158 |
// plupload_config |
| 159 |
return json_encode($plupload_init); |
| 160 |
} |
| 161 |
|
| 162 |
/** |
| 163 |
* Process the upload request originating from the plupload client and send |
| 164 |
* the uploaded data directly to the remote website. |
| 165 |
* |
| 166 |
* @param string $module Indicates whether the current request is intended for the 'plugin' or 'theme' module |
| 167 |
* @return string |
| 168 |
*/ |
| 169 |
public function plupload_action($module) { |
| 170 |
|
| 171 |
// Verify the nonce submitted |
| 172 |
$post = wp_unslash($_POST); |
| 173 |
$ajax_nonce = !empty($post['_ajax_nonce']) ? sanitize_text_field($post['_ajax_nonce']) : ''; |
| 174 |
if (empty($ajax_nonce) || !wp_verify_nonce($ajax_nonce, 'updraftcentral-uploader-'.$module)) { |
| 175 |
echo json_encode(array('e' => sprintf(__('Error: %s', 'updraftcentral'), __('Nonce verification failed.', 'updraftcentral')))); |
| 176 |
exit; |
| 177 |
} |
| 178 |
|
| 179 |
// "tmp_name" field of the $_FILES array is auto-generated by PHP, not a user input. |
| 180 |
// It contains the temporary location/path of the uploaded file. Adding "wp_unslash" will |
| 181 |
// break this upload feature on a Windows System thus, we added the phpcs:ignore |
| 182 |
// annotation here. |
| 183 |
$tmp_name = isset($_FILES['async-upload']['tmp_name']) ? sanitize_text_field($_FILES['async-upload']['tmp_name']) : '';// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash - this value is not slashed |
| 184 |
$filename = isset($post['name']) ? sanitize_file_name(basename($post['name'])) : ''; |
| 185 |
|
| 186 |
if (isset($_FILES['async-upload']['error']) && UPLOAD_ERR_OK == sanitize_text_field(wp_unslash($_FILES['async-upload']['error'])) && is_uploaded_file($tmp_name)) { |
| 187 |
if (isset($post['chunk']) && 0 === (int) $post['chunk']) { |
| 188 |
$validate = wp_check_filetype_and_ext($tmp_name, $filename); |
| 189 |
} else { |
| 190 |
$validate = wp_check_filetype($filename); |
| 191 |
} |
| 192 |
|
| 193 |
if (!empty($validate['ext']) && 'zip' === $validate['ext']) { |
| 194 |
$filename = (isset($validate['proper_filename']) && false !== $validate['proper_filename']) ? $validate['proper_filename'] : $filename; |
| 195 |
$args = array('filename' => $filename); |
| 196 |
|
| 197 |
// Check to see if the current upload was split in chunks |
| 198 |
if (isset($post['chunks'])) { |
| 199 |
// Handling upload in chunks |
| 200 |
$chunks = sanitize_text_field($post['chunks']); |
| 201 |
if (1 < (int) $chunks) { |
| 202 |
$args['chunks'] = $chunks; |
| 203 |
if (isset($post['chunk'])) { |
| 204 |
$args['chunk'] = sanitize_text_field($post['chunk']); |
| 205 |
} |
| 206 |
} |
| 207 |
} |
| 208 |
|
| 209 |
$args['data'] = base64_encode(file_get_contents($tmp_name)); |
| 210 |
$args['activate'] = isset($post['activate']) ? sanitize_text_field($post['activate']) : ''; |
| 211 |
|
| 212 |
$response_data = array(); |
| 213 |
if (isset($post['sites'])) { |
| 214 |
$sites = json_decode(base64_decode(sanitize_text_field($post['sites'])), true); |
| 215 |
|
| 216 |
foreach ($sites as $site) { |
| 217 |
$args['filesystem_credentials'] = $site['filesystem_credentials']; |
| 218 |
$response_data[] = array( |
| 219 |
'site_id' => $site['id'], |
| 220 |
'site_description' => $site['description'], |
| 221 |
'response' => $this->send_upload_request($site['id'], $args, $module) |
| 222 |
); |
| 223 |
} |
| 224 |
} |
| 225 |
|
| 226 |
echo json_encode($response_data); |
| 227 |
exit; |
| 228 |
} else { |
| 229 |
// Not a valid zip file... |
| 230 |
echo json_encode(array('e' => sprintf(__('Error: %s', 'updraftcentral'), __('This file does not appear to be a zip file.', 'updraftcentral')))); |
| 231 |
exit; |
| 232 |
} |
| 233 |
} else { |
| 234 |
// An error has occured while processing the upload request |
| 235 |
$error_message = $this->get_upload_error_message(sanitize_text_field(wp_unslash($_FILES['async-upload']['error']))); |
| 236 |
echo json_encode(array('e' => sprintf(__('Error: %s', 'updraftcentral'), $error_message))); |
| 237 |
exit; |
| 238 |
} |
| 239 |
} |
| 240 |
} |
| 241 |
|
| 242 |
endif; |
| 243 |
|