PluginProbe
The WP Remote WordPress Plugin / 6.69
The WP Remote WordPress Plugin v6.69
6.72 6.69 6.65 6.62 6.48 6.47 4.87 4.97 5.05 5.09 5.16 5.22 5.24 5.25 5.38 5.41 5.42 5.45 5.47 5.53 5.56 5.65 5.68 5.72 5.73 All 53 releases
wpremote / callback / wings / fs_write.php

fs_write.php in The WP Remote WordPress Plugin 6.69, at callback/wings/fs_write.php

547 lines 15.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) exit;
3 if (!class_exists('WPRFSWriteCallback')) :
4
5 class WPRFSWriteCallback extends WPRCallbackBase {
6
7 const MEGABYTE = 1048576;
8 const FS_WRITE_WING_VERSION = 1.2;
9
10 public function __construct() {
11 }
12
13 public function removeFiles($files) {
14 $result = array();
15
16 foreach($files as $file) {
17 $file_result = array();
18
19 if (file_exists($file)) {
20
21 $file_result['status'] = unlink($file);
22 if ($file_result['status'] === false) {
23 $file_result['error'] = "UNLINK_FAILED";
24 }
25
26 } else {
27 $file_result['status'] = true;
28 $file_result['error'] = "NOT_PRESENT";
29 }
30
31 $result[$file] = $file_result;
32 }
33
34 $result['status'] = true;
35 return $result;
36 }
37
38 public function makeDirs($dirs, $permissions = 0777, $recursive = true) {
39 $result = array();
40
41 foreach($dirs as $dir) {
42 $dir_result = array();
43
44 if (file_exists($dir)) {
45
46 if (is_dir($dir)) {
47 $dir_result['status'] = true;
48 $dir_result['message'] = "DIR_ALREADY_PRESENT";
49 } else {
50 $dir_result['status'] = false;
51 $dir_result['error'] = "FILE_PRESENT_IN_PLACE_OF_DIR";
52 }
53
54 } else {
55 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_mkdir -- Using mkdir() directly as there is no direct suport for recursion
56 $dir_result['status'] = mkdir($dir, $permissions, $recursive);
57 if ($dir_result['status'] === false) {
58 $dir_result['error'] = "MKDIR_FAILED";
59 }
60
61 }
62
63 $result[$dir] = $dir_result;
64 }
65
66 $result['status'] = true;
67 return $result;
68 }
69
70 public function removeDirs($dirs) {
71 $result = array();
72
73 foreach ($dirs as $dir) {
74 $dir_result = array();
75
76 if ((WPRWPFileSystem::getInstance()->isDir($dir) === true) && !is_link($dir)) {
77 if ($this->isEmptyDir($dir)) {
78 $dir_result['status'] = WPRWPFileSystem::getInstance()->rmdir($dir);
79 if ($dir_result['status'] === false) {
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 }
85 }
86 } else {
87 $dir_result['status'] = false;
88 $dir_result['error'] = "NOT_EMPTY";
89 }
90 } else {
91 $dir_result['status'] = false;
92 $dir_result['error'] = "NOT_DIR";
93 }
94
95 $result[$dir] = $dir_result;
96 }
97
98 $result['status'] = true;
99 return $result;
100 }
101
102 public function isEmptyDir($dir) {
103 $handle = opendir($dir);
104
105 while (false !== ($entry = readdir($handle))) {
106 if ($entry != "." && $entry != "..") {
107 closedir($handle);
108 return false;
109 }
110 }
111 closedir($handle);
112
113 return true;
114 }
115
116 public function doChmod($path_infos) {
117 $result = array();
118
119 foreach ($path_infos as $path => $mode) {
120 $path_result = array();
121
122 if (WPRWPFileSystem::getInstance()->exists($path) === true) {
123 $path_result['status'] = WPRWPFileSystem::getInstance()->chmod($path, $mode);
124 if ($path_result['status'] === false) {
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 }
130 }
131 } else {
132 $path_result['status'] = false;
133 $path_result['error'] = "NOT_FOUND";
134 }
135
136 $result[$path] = $path_result;
137 }
138
139 $result['status'] = true;
140 return $result;
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
147 public function concatFiles($ifiles, $ofile, $bsize, $offset) {
148 if (($offset !== 0) && (!file_exists($ofile))) {
149 return array(
150 'status' => false,
151 'error' => 'OFILE_NOT_FOUND_BEFORE_CONCAT'
152 );
153 }
154
155 if (file_exists($ofile) && ($offset !== 0)) {
156 $handle = fopen($ofile, 'rb+');
157 } else {
158 $handle = fopen($ofile, 'wb+');
159 }
160
161 if ($handle === false) {
162 return array(
163 'status' => false,
164 'error' => 'FOPEN_FAILED'
165 );
166 }
167
168 if ($offset !== 0) {
169 if (fseek($handle, $offset, SEEK_SET) === -1) {
170 return array(
171 'status' => false,
172 'error' => 'FSEEK_FAILED'
173 );
174 }
175 }
176
177 $total_written = 0;
178 foreach($ifiles as $file) {
179 $fp = fopen($file, 'rb');
180 if ($fp === false) {
181 return array(
182 'status' => false,
183 'error' => "UNABLE_TO_OPEN_TMP_OFILE_FOR_READING"
184 );
185 }
186
187 while (!feof($fp)) {
188 $content = fread($fp, $bsize);
189 if ($content === false) {
190 return array(
191 'status' => false,
192 'error' => "UNABLE_TO_READ_INFILE",
193 'filename' => $file
194 );
195 }
196
197 $written = fwrite($handle, $content);
198 if ($written === false) {
199 return array(
200 'status' => false,
201 'error' => "UNABLE_TO_WRITE_TO_OFILE",
202 'filename' => $file
203 );
204 }
205 $total_written += $written;
206 }
207
208 fclose($fp);
209 }
210
211 $result = array();
212 $result['fclose'] = fclose($handle);
213
214 if (file_exists($ofile) && ($total_written != 0)) {
215 $result['status'] = true;
216 $result['fsize'] = filesize($ofile);
217 $result['total_written'] = $total_written;
218 } else {
219 $result['status'] = false;
220 $result['error'] = 'CONCATINATED_FILE_FAILED';
221 }
222
223 return $result;
224 }
225 // phpcs:enable
226
227 public function renameFiles($path_infos) {
228 $result = array();
229
230 foreach ($path_infos as $oldpath => $newpath) {
231 $action_result = array();
232
233 if (WPRWPFileSystem::getInstance()->exists($oldpath)) {
234 $action_result['status'] = WPRWPFileSystem::getInstance()->move($oldpath, $newpath, true);
235 if ($action_result['status'] === false) {
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 }
245 }
246 } else {
247 $action_result['status'] = false;
248 $action_result['error'] = "NOT_FOUND";
249 }
250
251 $result[$oldpath] = $action_result;
252 }
253
254 $result['status'] = true;
255 return $result;
256 }
257
258 public function curlFile($ifile_url, $ofile, $timeout) {
259 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
260 $fp = fopen($ofile, "wb+");
261 if ($fp === false) {
262 return array(
263 'error' => 'FOPEN_FAILED_FOR_TEMP_OFILE'
264 );
265 }
266
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
270 $ch = curl_init($ifile_url);
271 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
272 curl_setopt($ch, CURLOPT_HEADER, 0);
273 curl_setopt($ch, CURLOPT_FILE, $fp);
274
275 if (!curl_exec($ch)) {
276 $result['error'] = curl_error($ch);
277 $result['errorno'] = curl_errno($ch);
278 }
279
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
285 fclose($fp);
286
287
288 return $result;
289 }
290
291 public function streamCopyFile($ifile_url, $ofile) {
292 $result = array();
293 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
294 $handle = fopen($ifile_url, "rb");
295
296 if ($handle === false) {
297 return array(
298 'error' => "UNABLE_TO_OPEN_REMOTE_FILE_STREAM"
299 );
300 }
301
302 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
303 $fp = fopen($ofile, "wb+");
304 if ($fp === false) {
305 fclose($handle); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
306
307 return array(
308 'error' => 'FOPEN_FAILED_FOR_OFILE'
309 );
310 }
311
312 if (stream_copy_to_stream($handle, $fp) === false) {
313 $result['error'] = "UNABLE_TO_WRITE_TO_TMP_OFILE";
314 }
315
316 fclose($handle); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
317 fclose($fp); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
318
319 return $result;
320 }
321
322 public function writeContentToFile($content, $ofile) {
323 $result = array();
324
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 }
331 }
332
333 return $result;
334 }
335
336 public function moveUploadedFile($ofile) {
337 $result = array();
338
339 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
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
342 $myfile = $_FILES['myfile'];
343 $is_upload_ok = false;
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
348 switch ($myfile['error']) {
349 case UPLOAD_ERR_OK:
350 $is_upload_ok = true;
351 break;
352 case UPLOAD_ERR_NO_FILE:
353 $result['error'] = "UPLOADERR_NO_FILE";
354 break;
355 case UPLOAD_ERR_INI_SIZE:
356 case UPLOAD_ERR_FORM_SIZE:
357 $result['error'] = "UPLOADERR_FORM_SIZE";
358 break;
359 default:
360 $result['error'] = "UPLOAD_ERR_UNKNOWN";
361 }
362
363 if ($is_upload_ok && !isset($myfile['tmp_name'])) {
364 $result['error'] = "MYFILE_TMP_NAME_NOT_FOUND";
365 $is_upload_ok = false;
366 }
367
368 if ($is_upload_ok) {
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) {
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 }
389 }
390 }
391
392 } else {
393 $result['error'] = "FILE_NOT_PRESENT_IN_FILES";
394 }
395
396 return $result;
397 }
398
399
400 public function uploadFile($params) {
401 $resp = array();
402 $ofile = $params['ofile'];
403
404 switch($params['protocol']) {
405 case "curl":
406 $timeout = isset($params['timeout']) ? $params['timeout'] : 60;
407 $ifile_url = isset($params['ifileurl']) ? $params['ifileurl'] : null;
408
409 $resp = $this->curlFile($ifile_url, $ofile, $timeout);
410 break;
411 case "streamcopy":
412 $ifile_url = isset($params['ifileurl']) ? $params['ifileurl'] : null;
413
414 $resp = $this->streamCopyFile($ifile_url, $ofile);
415 break;
416 case "httpcontenttransfer":
417 $resp = $this->writeContentToFile($params['content'], $ofile);
418 break;
419 case "httpfiletransfer":
420 $resp = $this->moveUploadedFile($ofile);
421 break;
422 default:
423 $resp['error'] = "INVALID_PROTOCOL";
424 }
425
426 if (isset($resp['error'])) {
427 $resp['status'] = false;
428 } else {
429
430 if (file_exists($ofile)) {
431 $resp['status'] = true;
432 $resp['fsize'] = filesize($ofile);
433 } else {
434 $resp['status'] = false;
435 $resp['error'] = "OFILE_NOT_FOUND";
436 }
437
438 }
439
440 return $resp;
441 }
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
509 public function process($request) {
510 $params = $request->params;
511
512 switch ($request->method) {
513 case "rmfle":
514 $resp = $this->removeFiles($params['files']);
515 break;
516 case "chmd":
517 $resp = $this->doChmod($params['pathinfos']);
518 break;
519 case "mkdr":
520 $resp = $this->makeDirs($params['dirs'], $params['permissions'], $params['recursive']);
521 break;
522 case "rmdr":
523 $resp = $this->removeDirs($params['dirs']);
524 break;
525 case "renmefle":
526 $resp = $this->renameFiles($params['pathinfos']);
527 break;
528 case "wrtfle":
529 $resp = $this->uploadFile($params);
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;
535 case "cncatfls":
536 $bsize = (isset($params['bsize'])) ? $params['bsize'] : (8 * WPRFSWriteCallback::MEGABYTE);
537 $offset = (isset($params['offset'])) ? $params['offset'] : 0;
538 $resp = $this->concatFiles($params['infiles'], $params['ofile'], $bsize, $offset);
539 break;
540 default:
541 $resp = false;
542 }
543
544 return $resp;
545 }
546 }
547 endif;