PluginProbe
The WP Remote WordPress Plugin / trunk
The WP Remote WordPress Plugin vtrunk
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
← All changes | callback/wings/fs.php +155 -11 5.22trunk View file →
@@ -1,16 +1,15 @@
1 1 <?php
2 -
3 2 if (!defined('ABSPATH')) exit;
4 -if (!class_exists('BVFSCallback')) :
3 +if (!class_exists('WPRFSCallback')) :
5 4 require_once dirname( __FILE__ ) . '/../streams.php';
6 5
7 -class BVFSCallback extends BVCallbackBase {
6 +class WPRFSCallback extends WPRCallbackBase {
8 7 public $stream;
9 8 public $account;
10 9
11 10 public static $cwAllowedFiles = array(".htaccess", ".user.ini", "malcare-waf.php");
12 - const FS_WING_VERSION = 1.2;
11 + const FS_WING_VERSION = 1.4;
13 12
14 13 public function __construct($callback_handler) {
15 14 $this->account = $callback_handler->account;
16 15 }
@@ -18,8 +17,15 @@
18 17 function fileStat($relfile, $md5 = false) {
19 18 $absfile = ABSPATH.$relfile;
20 19 $fdata = array();
21 20 $fdata["filename"] = $relfile;
21 +
22 + if (@is_readable($absfile) === false) {
23 + $fdata["failed"] = true;
24 + $fdata["error"] = "NOT_READABLE";
25 + return $fdata;
26 + }
27 +
22 28 $stats = @stat($absfile);
23 29 if ($stats) {
24 30 foreach (preg_grep('#size|uid|gid|mode|mtime#i', array_keys($stats)) as $key ) {
25 31 $fdata[$key] = $stats[$key];
@@ -133,8 +139,105 @@
133 139
134 140 return $links;
135 141 }
136 142
143 + function getDirectoryPath($dir, $traversal_stack) {
144 + $base_path = rtrim($dir, '/');
145 + $sub_path = empty($traversal_stack) ? '' : '/' . implode('/', array_column($traversal_stack, 0));
146 + return $base_path . $sub_path . '/';
147 + }
148 +
149 + function seekDirectoryHandle($directory_handle, $offset) {
150 + while ($offset > 0 && ($file = @readdir($directory_handle)) !== false) {
151 + if ($file === "." || $file === "..") continue;
152 + $offset--;
153 + }
154 + }
155 +
156 + function scanFilesDfs($dir = "/", $traversal_stack = array(), $folder_offset = 0, $limit = 0, $traversal_stack_max_size = 100,
157 + $batch_size = 512, $is_recursive = true, $include_md5 = false) {
158 + $links = [];
159 + $batch_count = 0;
160 + $batch_files = [];
161 + $count = 0;
162 + $traversal_stack_max_size_reached_count = 0;
163 +
164 + $base_path = $this->getDirectoryPath($dir, $traversal_stack);
165 + $directory_handle = @opendir(ABSPATH . $base_path);
166 +
167 + $this->seekDirectoryHandle($directory_handle, $folder_offset);
168 +
169 + while ($limit == 0 || ($limit > 0 && $count < $limit)) {
170 + if (($file = @readdir($directory_handle)) !== false) {
171 + if ($file === "." || $file === "..") continue;
172 +
173 + $relative_path = $base_path . $file;
174 + $absolute_path = ABSPATH . $relative_path;
175 +
176 + $count++;
177 + $folder_offset++;
178 +
179 + $batch_files[] = $this->fileStat($relative_path, $include_md5);
180 + $batch_count++;
181 +
182 + if ($batch_count >= $batch_size) {
183 + $this->stream->writeStream(serialize($batch_files));
184 + $batch_count = 0;
185 + $batch_files = [];
186 + }
187 +
188 + if (is_link($absolute_path)) {
189 + $links[] = $relative_path;
190 + } elseif ($is_recursive && is_dir($absolute_path)) {
191 + if (count($traversal_stack) >= $traversal_stack_max_size) {
192 + $traversal_stack_max_size_reached_count += 1;
193 + continue;
194 + }
195 +
196 + closedir($directory_handle);
197 +
198 + array_push($traversal_stack, [$file, $folder_offset]);
199 + $base_path = $this->getDirectoryPath($dir, $traversal_stack);
200 +
201 + $directory_handle = @opendir(ABSPATH . $base_path);
202 + $folder_offset = 0;
203 + }
204 +
205 + continue;
206 + }
207 +
208 + if ($directory_handle !== false) {
209 + closedir($directory_handle);
210 + }
211 +
212 + if (empty($traversal_stack)) {
213 + break;
214 + }
215 + $current_info = array_pop($traversal_stack);
216 +
217 + $base_path = $this->getDirectoryPath($dir, $traversal_stack);
218 + $directory_handle = @opendir(ABSPATH . $base_path);
219 +
220 + if ($directory_handle === false) {
221 + continue;
222 + }
223 +
224 + $this->seekDirectoryHandle($directory_handle, $current_info[1]);
225 + $folder_offset = $current_info[1];
226 + }
227 +
228 + if ($batch_count > 0) {
229 + $this->stream->writeStream(serialize($batch_files));
230 + }
231 +
232 + return [
233 + 'links' => $links,
234 + 'traversal_stack' => $traversal_stack,
235 + 'folder_offset' => $folder_offset,
236 + 'traversal_stack_max_size_reached_count' => $traversal_stack_max_size_reached_count
237 + ];
238 + }
239 +
137 240 function calculateMd5($absfile, $fdata, $offset, $limit, $bsize) {
138 241 if ($offset == 0 && $limit == 0) {
139 242 $md5 = md5_file($absfile);
140 243 } else {
@@ -141,8 +244,9 @@
141 244 if ($limit == 0)
142 245 $limit = $fdata["size"];
143 246 if ($offset + $limit < $fdata["size"])
144 247 $limit = $fdata["size"] - $offset;
248 + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
145 249 $handle = fopen($absfile, "rb");
146 250 $ctx = hash_init('md5');
147 251 fseek($handle, $offset, SEEK_SET);
148 252 $dlen = 1;
@@ -148,13 +252,15 @@
148 252 $dlen = 1;
149 253 while (($limit > 0) && ($dlen > 0)) {
150 254 if ($bsize > $limit)
151 255 $bsize = $limit;
256 + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread -- Required for handling partial file reads with offset and limit
152 257 $d = fread($handle, $bsize);
153 258 $dlen = strlen($d);
154 259 hash_update($ctx, $d);
155 260 $limit -= $dlen;
156 261 }
262 + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
157 263 fclose($handle);
158 264 $md5 = hash_final($ctx);
159 265 }
160 266 return $md5;
@@ -161,20 +267,22 @@
161 267 }
162 268
163 269 function getFilesContent($files, $withContent = true) {
164 270 $result = array();
271 +
165 272 foreach ($files as $file) {
166 273 $fdata = $this->fileStat($file);
167 - $absfile = ABSPATH.$file;
274 + $absfile = ABSPATH . $file;
168 275
169 - if (is_dir($absfile) && !is_link($absfile)) {
276 + if ((WPRWPFileSystem::getInstance()->isDir($absfile) === true) && !is_link($absfile)) {
170 277 $fdata['is_dir'] = true;
171 278 } else {
172 - if (!is_readable($absfile)) {
279 + if (isset($fdata["error"]) && $fdata["error"] === "NOT_READABLE") {
173 280 $fdata['error'] = 'file not readable';
174 281 } else {
175 282 if ($withContent === true) {
176 - if ($content = file_get_contents($absfile)) {
283 + $content = WPRWPFileSystem::getInstance()->getContents($absfile);
284 + if ($content !== false) {
177 285 $fdata['content'] = $content;
178 286 } else {
179 287 $fdata['error'] = 'unable to read file';
180 288 }
@@ -181,8 +289,12 @@
181 289 }
182 290 }
183 291 }
184 292
293 + $fs_error = WPRWPFileSystem::getInstance()->checkForErrors();
294 + if (isset($fs_error)) {
295 + $fdata['fs_error'] = $fs_error;
296 + }
185 297 $result[$file] = $fdata;
186 298 }
187 299
188 300 return $result;
@@ -192,9 +304,9 @@
192 304 $result = array();
193 305 foreach ($files as $file) {
194 306 $fdata = $this->fileStat($file);
195 307 $absfile = ABSPATH.$file;
196 - if (!is_readable($absfile)) {
308 + if (isset($fdata["error"]) && $fdata["error"] === "NOT_READABLE") {
197 309 $result["missingfiles"][] = $file;
198 310 continue;
199 311 }
200 312 if ($md5 === true && !is_dir($absfile)) {
@@ -207,12 +319,13 @@
207 319
208 320 function uploadFiles($files, $offset = 0, $limit = 0, $bsize = 102400) {
209 321 $result = array();
210 322 foreach ($files as $file) {
211 - if (!is_readable(ABSPATH.$file)) {
323 + if (WPRWPFileSystem::getInstance()->isReadable(ABSPATH.$file) === false) {
212 324 $result["missingfiles"][] = $file;
213 325 continue;
214 326 }
327 + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- Required for binary-safe chunked reading
215 328 $handle = fopen(ABSPATH.$file, "rb");
216 329 if (($handle != null) && is_resource($handle)) {
217 330 $fdata = $this->fileStat($file);
218 331 $_limit = $limit;
@@ -228,13 +341,15 @@
228 341 $dlen = 1;
229 342 while (($_limit > 0) && ($dlen > 0)) {
230 343 if ($_bsize > $_limit)
231 344 $_bsize = $_limit;
345 + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread -- Required for binary-safe chunked reading
232 346 $d = fread($handle, $_bsize);
233 347 $dlen = strlen($d);
234 348 $this->stream->writeStream($d);
235 349 $_limit -= $dlen;
236 350 }
351 + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Required for cleanup
237 352 fclose($handle);
238 353 } else {
239 354 $result["unreadablefiles"][] = $file;
240 355 }
@@ -244,9 +359,9 @@
244 359 }
245 360
246 361 function process($request) {
247 362 $params = $request->params;
248 - $stream_init_info = BVStream::startStream($this->account, $request);
363 + $stream_init_info = WPRStream::startStream($this->account, $request);
249 364
250 365 if (array_key_exists('stream', $stream_init_info)) {
251 366 $this->stream = $stream_init_info['stream'];
252 367 switch ($request->method) {
@@ -285,8 +400,37 @@
285 400 $_links = $this->scanFiles($dir, $offset, $limit, $bsize, $recurse, $md5);
286 401 $links = array_merge($links, $_links);
287 402 }
288 403 $resp = array("status" => "done", "links" => $links);
404 + break;
405 + case "scanfilesdfs":
406 + $resp = array();
407 + $dir_options = array();
408 + if (array_key_exists('dir_options', $params)) {
409 + $dir_options = $params['dir_options'];
410 + }
411 + $bsize = intval($params['bsize']);
412 + $traversal_stack_max_size = intval($params['traversal_stack_max_size']);
413 + foreach($dir_options as $option) {
414 + $dir = $option['dir'];
415 + $traversal_stack = $option['traversal_stack'];
416 + $folder_offset = intval($option['folder_offset']);
417 + $limit = intval($option['limit']);
418 +
419 + $recurse = true;
420 + if (array_key_exists('recurse', $option) && $option["recurse"] == "false") {
421 + $recurse = false;
422 + }
423 +
424 + $md5 = true;
425 + if (array_key_exists('md5', $option) && $option["md5"] == "false") {
426 + $md5 = false;
427 + }
428 +
429 + $resp[$dir] = $this->scanFilesDfs($dir, $traversal_stack, $folder_offset, $limit,
430 + $traversal_stack_max_size, $bsize, $recurse, $md5);
431 + }
432 + $resp["status"] = "done";
289 433 break;
290 434 case "getfilesstats":
291 435 $files = $params['files'];
292 436 $offset = intval($params['offset']);