PluginProbe
The WP Remote WordPress Plugin / 5.88
The WP Remote WordPress Plugin v5.88
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.php

fs.php in The WP Remote WordPress Plugin 5.88, at callback/wings/fs.php

419 lines 11.7 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('BVFSCallback')) :
4 require_once dirname( __FILE__ ) . '/../streams.php';
5
6 class BVFSCallback extends BVCallbackBase {
7 public $stream;
8 public $account;
9
10 public static $cwAllowedFiles = array(".htaccess", ".user.ini", "malcare-waf.php");
11 const FS_WING_VERSION = 1.3;
12
13 public function __construct($callback_handler) {
14 $this->account = $callback_handler->account;
15 }
16
17 function fileStat($relfile, $md5 = false) {
18 $absfile = ABSPATH.$relfile;
19 $fdata = array();
20 $fdata["filename"] = $relfile;
21 $stats = @stat($absfile);
22 if ($stats) {
23 foreach (preg_grep('#size|uid|gid|mode|mtime#i', array_keys($stats)) as $key ) {
24 $fdata[$key] = $stats[$key];
25 }
26 if (is_link($absfile)) {
27 $fdata["link"] = @readlink($absfile);
28 }
29 if ($md5 === true && !is_dir($absfile)) {
30 $fdata["md5"] = $this->calculateMd5($absfile, array(), 0, 0, 0);
31 }
32 } else {
33 $fdata["failed"] = true;
34 }
35 return $fdata;
36 }
37
38 function scanFilesUsingGlob($initdir = "./", $offset = 0, $limit = 0, $bsize = 512, $recurse = true, $regex = '{.??,}*') {
39 $i = 0;
40 $dirs = array();
41 $dirs[] = $initdir;
42 $bfc = 0;
43 $bfa = array();
44 $current = 0;
45 $abspath = realpath(ABSPATH).'/';
46 $abslen = strlen($abspath);
47 # XNOTE: $recurse cannot be used directly here
48 while ($i < count($dirs)) {
49 $dir = $dirs[$i];
50
51 foreach (glob($abspath.$dir.$regex, GLOB_NOSORT | GLOB_BRACE) as $absfile) {
52 $relfile = substr($absfile, $abslen);
53 if (is_dir($absfile) && !is_link($absfile)) {
54 $dirs[] = $relfile."/";
55 }
56 $current++;
57 if ($offset >= $current)
58 continue;
59 if (($limit != 0) && (($current - $offset) > $limit)) {
60 $i = count($dirs);
61 break;
62 }
63 $bfa[] = $this->fileStat($relfile);
64 $bfc++;
65 if ($bfc == $bsize) {
66 $str = serialize($bfa);
67 $this->stream->writeStream($str);
68 $bfc = 0;
69 $bfa = array();
70 }
71 }
72 $regex = '{.??,}*';
73 $i++;
74 if ($recurse == false)
75 break;
76 }
77 if ($bfc != 0) {
78 $str = serialize($bfa);
79 $this->stream->writeStream($str);
80 }
81 return array("status" => "done");
82 }
83
84 function scanFiles($initdir = "./", $offset = 0, $limit = 0, $bsize = 512, $recurse = true, $md5 = false) {
85 $i = 0;
86 $links = array();
87 $dirs = array();
88 $dirs[] = $initdir;
89 $bfc = 0;
90 $bfa = array();
91 $current = 0;
92 while ($i < count($dirs)) {
93 $dir = $dirs[$i];
94 $d = @opendir(ABSPATH.$dir);
95 if ($d) {
96 while (($file = readdir($d)) !== false) {
97 if ($file == '.' || $file == '..') { continue; }
98 $relfile = $dir.$file;
99 $absfile = ABSPATH.$relfile;
100 if (is_link($absfile)) {
101 $links[] = $relfile;
102 }
103 if (is_dir($absfile) && !is_link($absfile)) {
104 $dirs[] = $relfile."/";
105 }
106 $current++;
107 if ($offset >= $current)
108 continue;
109 if (($limit != 0) && (($current - $offset) > $limit)) {
110 $i = count($dirs);
111 break;
112 }
113 $bfa[] = $this->fileStat($relfile, $md5);
114 $bfc++;
115 if ($bfc == $bsize) {
116 $str = serialize($bfa);
117 $this->stream->writeStream($str);
118 $bfc = 0;
119 $bfa = array();
120 }
121 }
122 closedir($d);
123 }
124 $i++;
125 if ($recurse == false)
126 break;
127 }
128 if ($bfc != 0) {
129 $str = serialize($bfa);
130 $this->stream->writeStream($str);
131 }
132
133 return $links;
134 }
135
136 function calculateMd5($absfile, $fdata, $offset, $limit, $bsize) {
137 if ($offset == 0 && $limit == 0) {
138 $md5 = md5_file($absfile);
139 } else {
140 if ($limit == 0)
141 $limit = $fdata["size"];
142 if ($offset + $limit < $fdata["size"])
143 $limit = $fdata["size"] - $offset;
144 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
145 $handle = fopen($absfile, "rb");
146 $ctx = hash_init('md5');
147 fseek($handle, $offset, SEEK_SET);
148 $dlen = 1;
149 while (($limit > 0) && ($dlen > 0)) {
150 if ($bsize > $limit)
151 $bsize = $limit;
152 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread -- Required for handling partial file reads with offset and limit
153 $d = fread($handle, $bsize);
154 $dlen = strlen($d);
155 hash_update($ctx, $d);
156 $limit -= $dlen;
157 }
158 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
159 fclose($handle);
160 $md5 = hash_final($ctx);
161 }
162 return $md5;
163 }
164
165 function getFilesContent($files, $withContent = true) {
166 $result = array();
167 $filesystem = WPRHelper::get_direct_filesystem();
168
169 foreach ($files as $file) {
170 $fdata = $this->fileStat($file);
171 $absfile = ABSPATH . $file;
172
173 if ($filesystem->is_dir($absfile) && !is_link($absfile)) {
174 $fdata['is_dir'] = true;
175 } else {
176 if (!$filesystem->is_readable($absfile)) {
177 $fdata['error'] = 'file not readable';
178 } else {
179 if ($withContent === true) {
180 $content = $filesystem->get_contents($absfile);
181 if ($content !== false) {
182 $fdata['content'] = $content;
183 } else {
184 $fdata['error'] = 'unable to read file';
185 }
186 }
187 }
188 }
189
190 if (is_wp_error($filesystem->errors) && $filesystem->errors->has_errors()) {
191 $fdata['fs_error'] = $filesystem->errors->get_error_message();
192 }
193
194 $result[$file] = $fdata;
195 }
196
197 return $result;
198 }
199
200 function getFilesStats($files, $offset = 0, $limit = 0, $bsize = 102400, $md5 = false) {
201 $result = array();
202 foreach ($files as $file) {
203 $fdata = $this->fileStat($file);
204 $absfile = ABSPATH.$file;
205 if (!is_readable($absfile)) {
206 $result["missingfiles"][] = $file;
207 continue;
208 }
209 if ($md5 === true && !is_dir($absfile)) {
210 $fdata["md5"] = $this->calculateMd5($absfile, $fdata, $offset, $limit, $bsize);
211 }
212 $result["stats"][] = $fdata;
213 }
214 return $result;
215 }
216
217 function uploadFiles($files, $offset = 0, $limit = 0, $bsize = 102400) {
218 $result = array();
219 foreach ($files as $file) {
220 if (!is_readable(ABSPATH.$file)) {
221 $result["missingfiles"][] = $file;
222 continue;
223 }
224 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- Required for binary-safe chunked reading
225 $handle = fopen(ABSPATH.$file, "rb");
226 if (($handle != null) && is_resource($handle)) {
227 $fdata = $this->fileStat($file);
228 $_limit = $limit;
229 $_bsize = $bsize;
230 if ($_limit == 0)
231 $_limit = $fdata["size"];
232 if ($offset + $_limit > $fdata["size"])
233 $_limit = $fdata["size"] - $offset;
234 $fdata["limit"] = $_limit;
235 $sfdata = serialize($fdata);
236 $this->stream->writeStream($sfdata);
237 fseek($handle, $offset, SEEK_SET);
238 $dlen = 1;
239 while (($_limit > 0) && ($dlen > 0)) {
240 if ($_bsize > $_limit)
241 $_bsize = $_limit;
242 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread -- Required for binary-safe chunked reading
243 $d = fread($handle, $_bsize);
244 $dlen = strlen($d);
245 $this->stream->writeStream($d);
246 $_limit -= $dlen;
247 }
248 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Required for cleanup
249 fclose($handle);
250 } else {
251 $result["unreadablefiles"][] = $file;
252 }
253 }
254 $result["status"] = "done";
255 return $result;
256 }
257
258 function process($request) {
259 $params = $request->params;
260 $stream_init_info = BVStream::startStream($this->account, $request);
261
262 if (array_key_exists('stream', $stream_init_info)) {
263 $this->stream = $stream_init_info['stream'];
264 switch ($request->method) {
265 case "scanfilesglob":
266 $initdir = $params['initdir'];
267 $offset = intval($params['offset']);
268 $limit = intval($params['limit']);
269 $bsize = intval($params['bsize']);
270 $regex = $params['regex'];
271 $recurse = true;
272 if (array_key_exists('recurse', $params) && $params["recurse"] == "false") {
273 $recurse = false;
274 }
275 $resp = $this->scanFilesUsingGlob($initdir, $offset, $limit, $bsize, $recurse, $regex);
276 break;
277 case "scanfiles":
278 $links = array();
279 $dir_options = array();
280 if (array_key_exists('dir_options', $params)) {
281 $dir_options = $params['dir_options'];
282 }
283 $bsize = intval($params['bsize']);
284 foreach($dir_options as $option) {
285 $dir = $option['dir'];
286 $offset = intval($option['offset']);
287 $limit = intval($option['limit']);
288 $recurse = true;
289 if (array_key_exists('recurse', $option) && $option["recurse"] == "false") {
290 $recurse = false;
291 }
292 $md5 = true;
293 if (array_key_exists('md5', $option) && $option["md5"] == "false") {
294 $md5 = false;
295 }
296
297 $_links = $this->scanFiles($dir, $offset, $limit, $bsize, $recurse, $md5);
298 $links = array_merge($links, $_links);
299 }
300 $resp = array("status" => "done", "links" => $links);
301 break;
302 case "getfilesstats":
303 $files = $params['files'];
304 $offset = intval($params['offset']);
305 $limit = intval($params['limit']);
306 $bsize = intval($params['bsize']);
307 $md5 = false;
308 if (array_key_exists('md5', $params)) {
309 $md5 = true;
310 }
311 $resp = $this->getFilesStats($files, $offset, $limit, $bsize, $md5);
312 break;
313 case "sendmanyfiles":
314 $files = $params['files'];
315 $offset = intval($params['offset']);
316 $limit = intval($params['limit']);
317 $bsize = intval($params['bsize']);
318 $resp = $this->uploadFiles($files, $offset, $limit, $bsize);
319 break;
320 case "filelist":
321 $dir_options = array();
322 if (array_key_exists('dir_options', $params)) {
323 $dir_options = $params['dir_options'];
324 }
325 if (array_key_exists('chdir', $params)) {
326 chdir(ABSPATH);
327 }
328 $resp = array();
329 foreach($dir_options as $options) {
330 $glob_option = 0;
331 if (array_key_exists('onlydir', $options)) {
332 $glob_option = GLOB_ONLYDIR;
333 }
334
335 $regexes = array("*", ".*");
336 if (array_key_exists('regex', $options)) {
337 $regexes = array($options['regex']);
338 }
339
340 $md5 = false;
341 if (array_key_exists('md5', $options)) {
342 $md5 = $options['md5'];
343 }
344
345 $directoryList = array();
346
347 foreach($regexes as $regex) {
348 $directoryList = array_merge($directoryList, glob($options['dir'].$regex, $glob_option));
349 }
350 $resp[$options['dir']] = $this->getFilesStats($directoryList, 0, 0, 0, $md5);
351 }
352 break;
353 case "dirsexists":
354 $resp = array();
355 $dirs = $params['dirs'];
356
357 foreach ($dirs as $dir) {
358 $path = ABSPATH.$dir;
359 if (file_exists($path) && is_dir($path) && !is_link($path)) {
360 $resp[$dir] = true;
361 } else {
362 $resp[$dir] = false;
363 }
364 }
365
366 $resp["status"] = "Done";
367 break;
368 case "gtfilescntent":
369 $files = $params['files'];
370 $withContent = array_key_exists('withcontent', $params) ? $params['withcontent'] : true;
371 $resp = array("files_content" => $this->getFilesContent($files, $withContent));
372 break;
373 case "gtfls":
374 $resp = array();
375
376 if (array_key_exists('get_files_content', $params)) {
377 $args = $params['get_files_content'];
378 $with_content = array_key_exists('withcontent', $args) ? $args['withcontent'] : true;
379 $resp['get_files_content'] = $this->getFilesContent($args['files'], $with_content);
380 }
381
382 if (array_key_exists('get_files_stats', $params)) {
383 $args = $params['get_files_stats'];
384 $md5 = array_key_exists('md5', $args) ? $args['md5'] : false;
385 $stats = $this->getFilesStats(
386 $args['files'], $args['offset'], $args['limit'], $args['bsize'], $md5
387 );
388
389 $result = array();
390
391 if (array_key_exists('stats', $stats)) {
392 $result['stats'] = array();
393 foreach ($stats['stats'] as $stat) {
394 $result['stats'][$stat['filename']] = $stat;
395 }
396 }
397
398 if (array_key_exists('missingfiles', $stats)) {
399 $result['missingfiles'] = $stats['missingfiles'];
400 }
401
402 $resp['get_files_stats'] = $result;
403 }
404
405 break;
406 default:
407 $resp = false;
408 }
409 $end_stream_info = $this->stream->endStream();
410 if (!empty($end_stream_info) && is_array($resp)) {
411 $resp = array_merge($resp, $end_stream_info);
412 }
413 } else {
414 $resp = $stream_init_info;
415 }
416 return $resp;
417 }
418 }
419 endif;