| @@ -1,13 +1,12 @@ | ||
| 1 | 1 | <?php |
| 2 | - | |
| 3 | 2 | if (!defined('ABSPATH')) exit; |
| 4 | -if (!class_exists('BVFSWriteCallback')) : | |
| 3 | +if (!class_exists('WPRFSWriteCallback')) : | |
| 5 | 4 | |
| 6 | -class BVFSWriteCallback extends BVCallbackBase { | |
| 5 | +class WPRFSWriteCallback extends WPRCallbackBase { | |
| 7 | 6 | |
| 8 | 7 | const MEGABYTE = 1048576; |
| 9 | - const FS_WRITE_WING_VERSION = 1.0; | |
| 8 | + const FS_WRITE_WING_VERSION = 1.2; | |
| 10 | 9 | |
| 11 | 10 | public function __construct() { |
| 12 | 11 | } |
| 13 | 12 | |
| @@ -52,9 +51,9 @@ | ||
| 52 | 51 | $dir_result['error'] = "FILE_PRESENT_IN_PLACE_OF_DIR"; |
| 53 | 52 | } |
| 54 | 53 | |
| 55 | 54 | } else { |
| 56 | - | |
| 55 | + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_mkdir -- Using mkdir() directly as there is no direct suport for recursion | |
| 57 | 56 | $dir_result['status'] = mkdir($dir, $permissions, $recursive); |
| 58 | 57 | if ($dir_result['status'] === false) { |
| 59 | 58 | $dir_result['error'] = "MKDIR_FAILED"; |
| 60 | 59 | } |
| @@ -70,31 +69,31 @@ | ||
| 70 | 69 | |
| 71 | 70 | public function removeDirs($dirs) { |
| 72 | 71 | $result = array(); |
| 73 | 72 | |
| 74 | - foreach($dirs as $dir) { | |
| 73 | + foreach ($dirs as $dir) { | |
| 75 | 74 | $dir_result = array(); |
| 76 | 75 | |
| 77 | - if (is_dir($dir) && !is_link($dir)) { | |
| 78 | - | |
| 76 | + if ((WPRWPFileSystem::getInstance()->isDir($dir) === true) && !is_link($dir)) { | |
| 79 | 77 | if ($this->isEmptyDir($dir)) { |
| 80 | - | |
| 81 | - $dir_result['status'] = rmdir($dir); | |
| 78 | + $dir_result['status'] = WPRWPFileSystem::getInstance()->rmdir($dir); | |
| 82 | 79 | if ($dir_result['status'] === false) { |
| 83 | 80 | $dir_result['error'] = "RMDIR_FAILED"; |
| 81 | + $fs_error = WPRWPFileSystem::getInstance()->checkForErrors(); | |
| 82 | + if (isset($fs_error)) { | |
| 83 | + $dir_result['fs_error'] = $fs_error; | |
| 84 | + } | |
| 84 | 85 | } |
| 85 | - | |
| 86 | 86 | } else { |
| 87 | 87 | $dir_result['status'] = false; |
| 88 | 88 | $dir_result['error'] = "NOT_EMPTY"; |
| 89 | 89 | } |
| 90 | - | |
| 91 | 90 | } else { |
| 92 | 91 | $dir_result['status'] = false; |
| 93 | 92 | $dir_result['error'] = "NOT_DIR"; |
| 94 | 93 | } |
| 95 | 94 | |
| 96 | - $result[$dir] = $dir_result; | |
| 95 | + $result[$dir] = $dir_result; | |
| 97 | 96 | } |
| 98 | 97 | |
| 99 | 98 | $result['status'] = true; |
| 100 | 99 | return $result; |
| @@ -116,18 +115,20 @@ | ||
| 116 | 115 | |
| 117 | 116 | public function doChmod($path_infos) { |
| 118 | 117 | $result = array(); |
| 119 | 118 | |
| 120 | - foreach($path_infos as $path => $mode) { | |
| 119 | + foreach ($path_infos as $path => $mode) { | |
| 121 | 120 | $path_result = array(); |
| 122 | 121 | |
| 123 | - if (file_exists($path)) { | |
| 124 | - | |
| 125 | - $path_result['status'] = chmod($path, $mode); | |
| 122 | + if (WPRWPFileSystem::getInstance()->exists($path) === true) { | |
| 123 | + $path_result['status'] = WPRWPFileSystem::getInstance()->chmod($path, $mode); | |
| 126 | 124 | if ($path_result['status'] === false) { |
| 127 | 125 | $path_result['error'] = "CHMOD_FAILED"; |
| 126 | + $fs_error = WPRWPFileSystem::getInstance()->checkForErrors(); | |
| 127 | + if (isset($fs_error)) { | |
| 128 | + $path_result['fs_error'] = $fs_error; | |
| 129 | + } | |
| 128 | 130 | } |
| 129 | - | |
| 130 | 131 | } else { |
| 131 | 132 | $path_result['status'] = false; |
| 132 | 133 | $path_result['error'] = "NOT_FOUND"; |
| 133 | 134 | } |
| @@ -138,8 +139,12 @@ | ||
| 138 | 139 | $result['status'] = true; |
| 139 | 140 | return $result; |
| 140 | 141 | } |
| 141 | 142 | |
| 143 | + // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fopen | |
| 144 | + // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fread | |
| 145 | + // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fwrite | |
| 146 | + // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fclose | |
| 142 | 147 | public function concatFiles($ifiles, $ofile, $bsize, $offset) { |
| 143 | 148 | if (($offset !== 0) && (!file_exists($ofile))) { |
| 144 | 149 | return array( |
| 145 | 150 | 'status' => false, |
| @@ -216,23 +221,29 @@ | ||
| 216 | 221 | } |
| 217 | 222 | |
| 218 | 223 | return $result; |
| 219 | 224 | } |
| 225 | + // phpcs:enable | |
| 220 | 226 | |
| 221 | 227 | public function renameFiles($path_infos) { |
| 222 | 228 | $result = array(); |
| 223 | 229 | |
| 224 | - foreach($path_infos as $oldpath => $newpath) { | |
| 230 | + foreach ($path_infos as $oldpath => $newpath) { | |
| 225 | 231 | $action_result = array(); |
| 226 | - $failed = array(); | |
| 227 | 232 | |
| 228 | - if (file_exists($oldpath)) { | |
| 229 | - | |
| 230 | - $action_result['status'] = rename($oldpath, $newpath); | |
| 233 | + if (WPRWPFileSystem::getInstance()->exists($oldpath)) { | |
| 234 | + $action_result['status'] = WPRWPFileSystem::getInstance()->move($oldpath, $newpath, true); | |
| 231 | 235 | if ($action_result['status'] === false) { |
| 232 | 236 | $action_result['error'] = "RENAME_FAILED"; |
| 237 | + $fs_error = WPRWPFileSystem::getInstance()->checkForErrors(); | |
| 238 | + if (isset($fs_error)) { | |
| 239 | + $action_result['fs_error'] = $fs_error; | |
| 240 | + } | |
| 241 | + } else { | |
| 242 | + if (function_exists('opcache_invalidate')) { | |
| 243 | + $action_result['opcache'] = opcache_invalidate($newpath, true); | |
| 244 | + } | |
| 233 | 245 | } |
| 234 | - | |
| 235 | 246 | } else { |
| 236 | 247 | $action_result['status'] = false; |
| 237 | 248 | $action_result['error'] = "NOT_FOUND"; |
| 238 | 249 | } |
| @@ -244,8 +255,9 @@ | ||
| 244 | 255 | return $result; |
| 245 | 256 | } |
| 246 | 257 | |
| 247 | 258 | public function curlFile($ifile_url, $ofile, $timeout) { |
| 259 | + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen | |
| 248 | 260 | $fp = fopen($ofile, "wb+"); |
| 249 | 261 | if ($fp === false) { |
| 250 | 262 | return array( |
| 251 | 263 | 'error' => 'FOPEN_FAILED_FOR_TEMP_OFILE' |
| @@ -252,10 +264,11 @@ | ||
| 252 | 264 | ); |
| 253 | 265 | } |
| 254 | 266 | |
| 255 | 267 | $result = array(); |
| 268 | + | |
| 269 | + // phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_init, WordPress.WP.AlternativeFunctions.curl_curl_exec, WordPress.WP.AlternativeFunctions.curl_curl_setopt, WordPress.WP.AlternativeFunctions.curl_curl_close, WordPress.WP.AlternativeFunctions.curl_curl_error, WordPress.WP.AlternativeFunctions.curl_curl_errno | |
| 256 | 270 | $ch = curl_init($ifile_url); |
| 257 | - curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1); | |
| 258 | 271 | curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); |
| 259 | 272 | curl_setopt($ch, CURLOPT_HEADER, 0); |
| 260 | 273 | curl_setopt($ch, CURLOPT_FILE, $fp); |
| 261 | 274 | |
| @@ -264,15 +277,21 @@ | ||
| 264 | 277 | $result['errorno'] = curl_errno($ch); |
| 265 | 278 | } |
| 266 | 279 | |
| 267 | 280 | curl_close($ch); |
| 281 | + | |
| 282 | + // phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_init, WordPress.WP.AlternativeFunctions.curl_curl_exec, WordPress.WP.AlternativeFunctions.curl_curl_setopt, WordPress.WP.AlternativeFunctions.curl_curl_close, WordPress.WP.AlternativeFunctions.curl_curl_error, WordPress.WP.AlternativeFunctions.curl_curl_errno | |
| 283 | + | |
| 284 | + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose | |
| 268 | 285 | fclose($fp); |
| 269 | 286 | |
| 287 | + | |
| 270 | 288 | return $result; |
| 271 | 289 | } |
| 272 | 290 | |
| 273 | 291 | public function streamCopyFile($ifile_url, $ofile) { |
| 274 | 292 | $result = array(); |
| 293 | + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen | |
| 275 | 294 | $handle = fopen($ifile_url, "rb"); |
| 276 | 295 | |
| 277 | 296 | if ($handle === false) { |
| 278 | 297 | return array( |
| @@ -279,11 +298,12 @@ | ||
| 279 | 298 | 'error' => "UNABLE_TO_OPEN_REMOTE_FILE_STREAM" |
| 280 | 299 | ); |
| 281 | 300 | } |
| 282 | 301 | |
| 302 | + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen | |
| 283 | 303 | $fp = fopen($ofile, "wb+"); |
| 284 | 304 | if ($fp === false) { |
| 285 | - fclose($handle); | |
| 305 | + fclose($handle); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose | |
| 286 | 306 | |
| 287 | 307 | return array( |
| 288 | 308 | 'error' => 'FOPEN_FAILED_FOR_OFILE' |
| 289 | 309 | ); |
| @@ -292,10 +312,10 @@ | ||
| 292 | 312 | if (stream_copy_to_stream($handle, $fp) === false) { |
| 293 | 313 | $result['error'] = "UNABLE_TO_WRITE_TO_TMP_OFILE"; |
| 294 | 314 | } |
| 295 | 315 | |
| 296 | - fclose($handle); | |
| 297 | - fclose($fp); | |
| 316 | + fclose($handle); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose | |
| 317 | + fclose($fp); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose | |
| 298 | 318 | |
| 299 | 319 | return $result; |
| 300 | 320 | } |
| 301 | 321 | |
| @@ -301,20 +321,16 @@ | ||
| 301 | 321 | |
| 302 | 322 | public function writeContentToFile($content, $ofile) { |
| 303 | 323 | $result = array(); |
| 304 | 324 | |
| 305 | - $fp = fopen($ofile, "wb+"); | |
| 306 | - if ($fp === false) { | |
| 307 | - return array( | |
| 308 | - 'error' => 'FOPEN_FAILED_FOR_TEMP_OFILE' | |
| 309 | - ); | |
| 325 | + if (WPRWPFileSystem::getInstance()->putContents($ofile, $content) === false) { | |
| 326 | + $result['error'] = 'UNABLE_TO_WRITE_TO_TMP_OFILE'; | |
| 327 | + $fs_error = WPRWPFileSystem::getInstance()->checkForErrors(); | |
| 328 | + if (isset($fs_error)) { | |
| 329 | + $result['fs_error'] = $fs_error; | |
| 330 | + } | |
| 310 | 331 | } |
| 311 | 332 | |
| 312 | - if (fwrite($fp, $content) === false) { | |
| 313 | - $result['error'] = "UNABLE_TO_WRITE_TO_TMP_OFILE"; | |
| 314 | - } | |
| 315 | - fclose($fp); | |
| 316 | - | |
| 317 | 333 | return $result; |
| 318 | 334 | } |
| 319 | 335 | |
| 320 | 336 | public function moveUploadedFile($ofile) { |
| @@ -319,12 +335,17 @@ | ||
| 319 | 335 | |
| 320 | 336 | public function moveUploadedFile($ofile) { |
| 321 | 337 | $result = array(); |
| 322 | 338 | |
| 339 | + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing | |
| 323 | 340 | if (isset($_FILES['myfile'])) { |
| 341 | + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing -- tmp_name is a path and nonce is ignored here | |
| 324 | 342 | $myfile = $_FILES['myfile']; |
| 325 | 343 | $is_upload_ok = false; |
| 326 | 344 | |
| 345 | + // Validate PHP upload errors manually | |
| 346 | + // This approach handles any file type (PHP, ZIP, SQL, etc.) without MIME restrictions | |
| 347 | + // Uses WordPress Filesystem API instead of wp_handle_upload() which is designed for media uploads | |
| 327 | 348 | switch ($myfile['error']) { |
| 328 | 349 | case UPLOAD_ERR_OK: |
| 329 | 350 | $is_upload_ok = true; |
| 330 | 351 | break; |
| @@ -344,10 +365,28 @@ | ||
| 344 | 365 | $is_upload_ok = false; |
| 345 | 366 | } |
| 346 | 367 | |
| 347 | 368 | if ($is_upload_ok) { |
| 348 | - if (move_uploaded_file($myfile['tmp_name'], $ofile) === false) { | |
| 369 | + $tmp_name = $myfile['tmp_name']; | |
| 370 | + | |
| 371 | + // Ensure target directory exists | |
| 372 | + $target_dir = dirname($ofile); | |
| 373 | + if (!file_exists($target_dir)) { | |
| 374 | + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_mkdir -- Using mkdir() directly as there is no direct support for recursion | |
| 375 | + if (!mkdir($target_dir, 0777, true)) { | |
| 376 | + $result['error'] = 'MKDIR_FAILED_FOR_TARGET'; | |
| 377 | + return $result; | |
| 378 | + } | |
| 379 | + } | |
| 380 | + | |
| 381 | + // Use WordPress Filesystem API to move the uploaded file | |
| 382 | + // This is WordPress.org compliant and handles any file type | |
| 383 | + if (WPRWPFileSystem::getInstance()->move($tmp_name, $ofile, true) === false) { | |
| 349 | 384 | $result['error'] = 'MOVE_UPLOAD_FILE_FAILED'; |
| 385 | + $fs_error = WPRWPFileSystem::getInstance()->checkForErrors(); | |
| 386 | + if (isset($fs_error)) { | |
| 387 | + $result['fs_error'] = $fs_error; | |
| 388 | + } | |
| 350 | 389 | } |
| 351 | 390 | } |
| 352 | 391 | |
| 353 | 392 | } else { |
| @@ -400,8 +439,74 @@ | ||
| 400 | 439 | |
| 401 | 440 | return $resp; |
| 402 | 441 | } |
| 403 | 442 | |
| 443 | + public function runFileCmd($cmd_key, $cmd_params) { | |
| 444 | + switch ($cmd_key) { | |
| 445 | + case "wrtfle": | |
| 446 | + return $this->uploadFile($cmd_params); | |
| 447 | + case "renmefle": | |
| 448 | + $from = $cmd_params['from']; | |
| 449 | + $to = $cmd_params['to']; | |
| 450 | + $rename_result = $this->renameFiles(array($from => $to)); | |
| 451 | + return isset($rename_result[$from]) ? $rename_result[$from] : array('status' => false, 'error' => 'RENAME_NO_RESULT'); | |
| 452 | + case "chmd": | |
| 453 | + $path = $cmd_params['path']; | |
| 454 | + $chmod_result = $this->doChmod(array($path => $cmd_params['mode'])); | |
| 455 | + return isset($chmod_result[$path]) ? $chmod_result[$path] : array('status' => false, 'error' => 'CHMOD_NO_RESULT'); | |
| 456 | + case "mkdr": | |
| 457 | + $path = $cmd_params['path']; | |
| 458 | + $perms = isset($cmd_params['perms']) ? $cmd_params['perms'] : 0777; | |
| 459 | + $rec = isset($cmd_params['rec']) ? (bool) $cmd_params['rec'] : true; | |
| 460 | + $mkdir_result = $this->makeDirs(array($path), $perms, $rec); | |
| 461 | + return isset($mkdir_result[$path]) ? $mkdir_result[$path] : array('status' => false, 'error' => 'MKDIR_NO_RESULT'); | |
| 462 | + case "rmfle": | |
| 463 | + $files = $cmd_params['files']; | |
| 464 | + $rm_result = $this->removeFiles($files); | |
| 465 | + $first = reset($files); | |
| 466 | + return isset($rm_result[$first]) ? $rm_result[$first] : array('status' => false, 'error' => 'RMFLE_NO_RESULT'); | |
| 467 | + case "rmdr": | |
| 468 | + $dirs = $cmd_params['dirs']; | |
| 469 | + $rmdr_result = $this->removeDirs($dirs); | |
| 470 | + $first = reset($dirs); | |
| 471 | + return isset($rmdr_result[$first]) ? $rmdr_result[$first] : array('status' => false, 'error' => 'RMDR_NO_RESULT'); | |
| 472 | + default: | |
| 473 | + return array('status' => false, 'error' => 'UNKNOWN_CMD'); | |
| 474 | + } | |
| 475 | + } | |
| 476 | + | |
| 477 | + public function executeFileOps($ops, $all_required = false) { | |
| 478 | + $result = array(); | |
| 479 | + $all_success = true; | |
| 480 | + | |
| 481 | + foreach ($ops as $op) { | |
| 482 | + $identifier = $op['identifier']; | |
| 483 | + $cmds = $op['cmds']; | |
| 484 | + $op_result = array(); | |
| 485 | + | |
| 486 | + foreach ($cmds as $cmd) { | |
| 487 | + foreach ($cmd as $cmd_key => $cmd_params) { | |
| 488 | + $cmd_result = $this->runFileCmd($cmd_key, $cmd_params); | |
| 489 | + $op_result[$cmd_key] = $cmd_result; | |
| 490 | + | |
| 491 | + if (isset($cmd_result['status']) && $cmd_result['status'] === false) { | |
| 492 | + $all_success = false; | |
| 493 | + break 2; | |
| 494 | + } | |
| 495 | + } | |
| 496 | + } | |
| 497 | + | |
| 498 | + $result[$identifier] = $op_result; | |
| 499 | + | |
| 500 | + if ($all_required && !$all_success) { | |
| 501 | + break; | |
| 502 | + } | |
| 503 | + } | |
| 504 | + | |
| 505 | + $result['status'] = $all_success; | |
| 506 | + return $result; | |
| 507 | + } | |
| 508 | + | |
| 404 | 509 | public function process($request) { |
| 405 | 510 | $params = $request->params; |
| 406 | 511 | |
| 407 | 512 | switch ($request->method) { |
| @@ -422,10 +527,14 @@ | ||
| 422 | 527 | break; |
| 423 | 528 | case "wrtfle": |
| 424 | 529 | $resp = $this->uploadFile($params); |
| 425 | 530 | break; |
| 531 | + case "fleops": | |
| 532 | + $all_required = isset($params['all_required']) ? (bool) $params['all_required'] : false; | |
| 533 | + $resp = $this->executeFileOps($params['ops'], $all_required); | |
| 534 | + break; | |
| 426 | 535 | case "cncatfls": |
| 427 | - $bsize = (isset($params['bsize'])) ? $params['bsize'] : (8 * BVFSWriteCallback::MEGABYTE); | |
| 536 | + $bsize = (isset($params['bsize'])) ? $params['bsize'] : (8 * WPRFSWriteCallback::MEGABYTE); | |
| 428 | 537 | $offset = (isset($params['offset'])) ? $params['offset'] : 0; |
| 429 | 538 | $resp = $this->concatFiles($params['infiles'], $params['ofile'], $bsize, $offset); |
| 430 | 539 | break; |
| 431 | 540 | default: |