GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Branch dev (231e41)
by Liuta
03:38
created

Xcloner_File_System::get_diff_timestamp_start()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * XCloner - Backup and Restore backup plugin for Wordpress
4
 *
5
 * class-xcloner-file-system.php
6
 * @author Liuta Ovidiu <[email protected]>
7
 *
8
 *        This program is free software; you can redistribute it and/or modify
9
 *        it under the terms of the GNU General Public License as published by
10
 *        the Free Software Foundation; either version 2 of the License, or
11
 *        (at your option) any later version.
12
 *
13
 *        This program is distributed in the hope that it will be useful,
14
 *        but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *        GNU General Public License for more details.
17
 *
18
 *        You should have received a copy of the GNU General Public License
19
 *        along with this program; if not, write to the Free Software
20
 *        Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21
 *        MA 02110-1301, USA.
22
 *
23
 * @link https://github.com/ovidiul/XCloner-Wordpress
24
 *
25
 * @modified 7/31/18 3:46 PM
26
 *
27
 */
28
29
use League\Flysystem\Config;
30
use League\Flysystem\Filesystem;
31
use League\Flysystem\Util;
32
use League\Flysystem\Adapter\Local;
33
34
/**
35
 * Class Xcloner_File_System
36
 */
37
class Xcloner_File_System
38
{
39
40
    private $excluded_files = "";
41
    private $additional_regex_patterns = array();
42
    private $excluded_files_by_default = array("administrator/backups", "wp-content/backups");
43
    private $included_files_handler = "backup_files.csv";
44
    private $temp_dir_handler = ".dir";
45
    public $filesystem;
46
    public $tmp_filesystem;
47
    public $storage_filesystem;
48
    private $xcloner_container;
49
    private $diff_timestamp_start = "";
50
51
    private $logger;
52
    private $start_adapter;
53
    private $tmp_adapter;
54
    private $storage_adapter;
55
    private $xcloner_settings;
56
    private $start_filesystem;
57
    private $tmp_filesystem_append;
58
    private $storage_filesystem_append;
59
60
    private $files_counter;
61
    private $files_size;
62
    private $last_logged_file;
63
    private $folders_to_process_per_session = 25;
64
    private $backup_archive_extensions = array("tar", "tgz", "tar.gz", "gz", "csv", "encrypted", "decrypted");
65
    private $backup_name_tags = array('[time]', '[hostname]', '[domain]');
66
67
    /**
68
     * Xcloner_File_System constructor.
69
     * @param Xcloner $xcloner_container
70
     * @param string $hash
71
     */
72
    public function __construct(Xcloner $xcloner_container, $hash = "")
73
    {
74
        $this->xcloner_container = $xcloner_container;
75
76
        $this->logger = $xcloner_container->get_xcloner_logger()->withName("xcloner_file_system");
77
        $this->xcloner_settings = $xcloner_container->get_xcloner_settings();
78
79
        try {
80
81
            $this->start_adapter = new Local($this->xcloner_settings->get_xcloner_start_path(), LOCK_EX, 'SKIP_LINKS');
0 ignored issues
show
Bug introduced by
'SKIP_LINKS' of type string is incompatible with the type integer expected by parameter $linkHandling of League\Flysystem\Adapter\Local::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

81
            $this->start_adapter = new Local($this->xcloner_settings->get_xcloner_start_path(), LOCK_EX, /** @scrutinizer ignore-type */ 'SKIP_LINKS');
Loading history...
82
            $this->start_filesystem = new Filesystem($this->start_adapter, new Config([
83
                'disable_asserts' => true,
84
            ]));
85
86
            $this->tmp_adapter = new Local($this->xcloner_settings->get_xcloner_tmp_path(), LOCK_EX, 'SKIP_LINKS');
87
            $this->tmp_filesystem = new Filesystem($this->tmp_adapter, new Config([
88
                'disable_asserts' => true,
89
            ]));
90
            $adapter = new Local($this->xcloner_settings->get_xcloner_tmp_path(), LOCK_EX | FILE_APPEND, 'SKIP_LINKS');
91
            $this->tmp_filesystem_append = new Filesystem($adapter, new Config([
92
                'disable_asserts' => true,
93
            ]));
94
95
            $adapter = new Local($this->xcloner_settings->get_xcloner_store_path(), LOCK_EX, 'SKIP_LINKS');
96
            $this->storage_filesystem = new Filesystem($adapter, new Config([
97
                'disable_asserts' => true,
98
            ]));
99
100
            $this->storage_adapter = new Local($this->xcloner_settings->get_xcloner_store_path(), FILE_APPEND,
101
                'SKIP_LINKS');
102
            $this->storage_filesystem_append = new Filesystem($this->storage_adapter, new Config([
103
                'disable_asserts' => true,
104
            ]));
105
        }catch (Exception $e) {
106
            $this->logger->error("Filesystem Initialization Error: ".$e->getMessage());
107
        }
108
109
110
        if ($value = get_option('xcloner_directories_to_scan_per_request')) {
0 ignored issues
show
Bug introduced by
The function get_option was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

110
        if ($value = /** @scrutinizer ignore-call */ get_option('xcloner_directories_to_scan_per_request')) {
Loading history...
111
            $this->folders_to_process_per_session = $value;
112
        }
113
114
    }
115
116
    /**
117
     * Set differential timestamp date
118
     * @param string $timestamp
119
     */
120
    public function set_diff_timestamp_start($timestamp = "")
121
    {
122
        if ($timestamp) {
123
            $this->logger->info(sprintf("Setting Differential Timestamp To %s", date("Y-m-d", $timestamp)), array(
0 ignored issues
show
Bug introduced by
$timestamp of type string is incompatible with the type integer expected by parameter $timestamp of date(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

123
            $this->logger->info(sprintf("Setting Differential Timestamp To %s", date("Y-m-d", /** @scrutinizer ignore-type */ $timestamp)), array(
Loading history...
124
                "FILESYSTEM",
125
                "DIFF"
126
            ));
127
            $this->diff_timestamp_start = $timestamp;
128
        }
129
    }
130
131
    /**
132
     * Gets the differential timestamp date
133
     * @return string
134
     */
135
    public function get_diff_timestamp_start()
136
    {
137
        return $this->diff_timestamp_start;
138
    }
139
140
    private function get_xcloner_container()
141
    {
142
        return $this->xcloner_container;
143
    }
144
145
    public function set_hash($hash)
146
    {
147
        $this->xcloner_settings->set_hash($hash);
148
    }
149
150
    public function get_hash($hash)
151
    {
152
        $this->xcloner_settings->get_hash();
153
    }
154
155
    public function get_tmp_filesystem()
156
    {
157
        return $this->tmp_filesystem;
158
    }
159
160
    public function get_storage_filesystem($remote_storage_selection = "")
161
    {
162
        if ($remote_storage_selection != "") {
163
            $remote_storage = $this->get_xcloner_container()->get_xcloner_remote_storage();
164
            $method = "get_".$remote_storage_selection."_filesystem";
165
166
            if (!method_exists($remote_storage, $method)) {
167
                return false;
168
            }
169
170
            list($adapter, $filesystem) = $remote_storage->$method();
171
172
            return $filesystem;
173
        }
174
175
        return $this->storage_filesystem;
176
    }
177
178
    public function get_tmp_filesystem_adapter()
179
    {
180
        return $this->tmp_adapter;
181
    }
182
183
    public function get_tmp_filesystem_append()
184
    {
185
        return $this->tmp_filesystem_append;
186
    }
187
188
    public function get_start_adapter()
189
    {
190
        return $this->start_adapter;
191
    }
192
193
    public function get_start_filesystem()
194
    {
195
        return $this->start_filesystem;
196
    }
197
198
    public function get_logger()
199
    {
200
        return $this->logger;
201
    }
202
203
    public function get_start_path_file_info($file)
204
    {
205
        $info = $this->getMetadataFull('start_adapter', $file);
206
207
        return $this->start_filesystem->normalizeFileInfo($info);
208
    }
209
210
    /**
211
     * @param string $file
212
     */
213
    public function get_storage_path_file_info($file)
214
    {
215
        return $this->getMetadataFull('storage_adapter', $file);
216
    }
217
218
    public function get_included_files_handler($metadata = 0)
219
    {
220
        $path = $this->included_files_handler;
221
        if (!$metadata) {
222
            return $path;
223
        }
224
225
        $spl_info = $this->getMetadataFull('tmp_adapter', $path);
226
227
        return $spl_info;
228
229
    }
230
231
    public function get_temp_dir_handler()
232
    {
233
        return $this->temp_dir_handler;
234
    }
235
236
    public function get_latest_backup()
237
    {
238
        $files = $this->get_backup_archives_list();
239
240
        if (is_array($files)) {
0 ignored issues
show
introduced by
The condition is_array($files) is always true.
Loading history...
241
            $this->sort_by($files, "timestamp", "desc");
242
        }
243
244
        $new_list = array();
245
246
        foreach ($files as $key => $file) {
247
            if (!isset($file['parent'])) {
248
                $new_list[] = ($files[$key]);
249
            }
250
        }
251
252
        if (isset($new_list[0])) {
253
            return $new_list[0];
254
        }
255
    }
256
257
    public function get_latest_backups()
258
    {
259
        $files = $this->get_backup_archives_list();
260
261
        if (is_array($files)) {
0 ignored issues
show
introduced by
The condition is_array($files) is always true.
Loading history...
262
            $this->sort_by($files, "timestamp", "desc");
263
        }
264
265
        $new_list = array();
266
267
        foreach ($files as $key => $file) {
268
            if (!isset($file['parent'])) {
269
                $new_list[] = ($files[$key]);
270
            }
271
        }
272
273
        return $new_list;
274
    }
275
276
    public function get_storage_usage()
277
    {
278
        $files = $this->get_backup_archives_list();
279
        $total = 0;
280
281
        if (is_array($files)) {
0 ignored issues
show
introduced by
The condition is_array($files) is always true.
Loading history...
282
            foreach ($files as $file) {
283
                $total += $file['size'];
284
            }
285
        }
286
287
        return $total;
288
    }
289
290
    public function is_part($backup_name)
291
    {
292
        if (stristr($backup_name, "-part")) {
293
            return true;
294
        }
295
296
        return false;
297
    }
298
299
    public function is_multipart($backup_name)
300
    {
301
        if (stristr($backup_name, "-multipart")) {
302
            return true;
303
        }
304
305
        return false;
306
    }
307
308
    public function get_backup_size($backup_name)
309
    {
310
        $backup_size = $this->get_storage_filesystem()->getSize($backup_name);
311
        if ($this->is_multipart($backup_name)) {
312
            $backup_parts = $this->get_multipart_files($backup_name);
313
            foreach ($backup_parts as $part_file) {
314
                $backup_size += $this->get_storage_filesystem()->getSize($part_file);
315
            }
316
        }
317
318
        return $backup_size;
319
    }
320
321
    public function get_multipart_files($backup_name, $storage_selection = "")
322
    {
323
        $files = array();
324
325
        if ($this->is_multipart($backup_name)) {
326
            $lines = explode(PHP_EOL, $this->get_storage_filesystem($storage_selection)->read($backup_name));
327
            foreach ($lines as $line) {
328
                if ($line) {
329
                    $data = str_getcsv($line);
330
                    $files[] = $data[0];
331
                }
332
            }
333
        }
334
335
        return $files;
336
    }
337
338
    public function delete_backup_by_name($backup_name, $storage_selection = "")
339
    {
340
        if ($this->is_multipart($backup_name)) {
341
            $lines = explode(PHP_EOL, $this->get_storage_filesystem($storage_selection)->read($backup_name));
342
            foreach ($lines as $line) {
343
                if ($line) {
344
                    $data = str_getcsv($line);
345
                    $this->get_storage_filesystem($storage_selection)->delete($data[0]);
346
                }
347
            }
348
        }
349
350
        if ($this->get_storage_filesystem($storage_selection)->delete($backup_name)) {
351
            $return = true;
352
        } else {
353
            $return = false;
354
        }
355
356
        return $return;
357
    }
358
359
    public function getMetadataFull($adapter = "storage_adapter", $path)
360
    {
361
        $location = $this->$adapter->applyPathPrefix($path);
362
        $spl_info = new SplFileInfo($location);
363
364
        return ($spl_info);
365
    }
366
367
368
    public function get_backup_archives_list($storage_selection = "")
369
    {
370
        $list = array();
371
372
373
        if (method_exists($this->get_storage_filesystem($storage_selection), "listContents")) {
374
            $list = $this->get_storage_filesystem($storage_selection)->listContents();
375
        }
376
377
        $backup_files = array();
378
        $parents = array();
379
380
        foreach ($list as $file_info) {
381
            if (isset($file_info['extension']) and $file_info['extension'] == "csv") {
382
                $data = array();
383
384
                $lines = explode(PHP_EOL, $this->get_storage_filesystem($storage_selection)->read($file_info['path']));
385
                foreach ($lines as $line) {
386
                    if ($line) {
387
                        $data = str_getcsv($line);
388
                        if (is_array($data)) {
389
                            $parents[$data[0]] = $file_info['basename'];
390
                            $file_info['childs'][] = $data;
391
                            $file_info['size'] += $data[2];
392
                        }
393
                    }
394
                }
395
396
            }
397
398
            if ($file_info['type'] == 'file' and isset($file_info['extension']) and in_array($file_info['extension'],
399
                    $this->backup_archive_extensions)) {
400
                $backup_files[$file_info['path']] = $file_info;
401
            }
402
        }
403
404
        foreach ($backup_files as $key => $file_info) {
405
            if (!isset($backup_files[$key]['timestamp'])) {
406
                //$backup_files[$key]['timestamp'] = $this->get_storage_filesystem($storage_selection)->getTimestamp($file_info['path']);
407
            }
408
409
            if (isset($parents[$file_info['basename']])) {
410
                $backup_files[$key]['parent'] = $parents[$file_info['basename']];
411
            }
412
        }
413
414
        return $backup_files;
415
    }
416
417
    public function start_file_recursion($init = 0)
418
    {
419
        if ($init) {
420
            $this->logger->info(sprintf(__("Starting the filesystem scanner on root folder %s"),
0 ignored issues
show
Bug introduced by
The function __ was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

420
            $this->logger->info(sprintf(/** @scrutinizer ignore-call */ __("Starting the filesystem scanner on root folder %s"),
Loading history...
421
                $this->xcloner_settings->get_xcloner_start_path()));
422
            $this->do_system_init();
423
        }
424
425
        if ($this->tmp_filesystem->has($this->get_temp_dir_handler())) {
426
            //.dir exists, we presume we have files to iterate
427
            $content = $this->tmp_filesystem->read($this->get_temp_dir_handler());
428
            $files = array_filter(explode("\n", $content));
429
            $this->tmp_filesystem->delete($this->get_temp_dir_handler());
430
431
            $counter = 0;
432
            foreach ($files as $file) {
433
                if ($counter < $this->folders_to_process_per_session) {
434
                    $this->build_files_list($file);
435
                    $counter++;
436
                } else {
437
                    $this->tmp_filesystem_append->write($this->get_temp_dir_handler(), $file."\n");
438
                }
439
            }
440
        } else {
441
            $this->build_files_list();
442
        }
443
444
        if ($this->scan_finished()) {
445
            $metadata_dumpfile = $this->get_tmp_filesystem()->getMetadata("index.html");
446
            $this->store_file($metadata_dumpfile, 'tmp_filesystem');
447
            $this->files_counter++;
448
449
            //adding included dump file to the included files list
450
            if ($this->get_tmp_filesystem()->has($this->get_included_files_handler())) {
451
                $metadata_dumpfile = $this->get_tmp_filesystem()->getMetadata($this->get_included_files_handler());
452
                $this->store_file($metadata_dumpfile, 'tmp_filesystem');
453
                $this->files_counter++;
454
            }
455
456
            //adding a default index.html to the temp xcloner folder
457
            if (!$this->get_tmp_filesystem()->has("index.html")) {
458
                $this->get_tmp_filesystem()->write("index.html", "");
459
            }
460
461
            //adding the default log file
462
            if ($this->get_tmp_filesystem()->has($this->xcloner_settings->get_logger_filename(1))) {
463
                $metadata_dumpfile = $this->get_tmp_filesystem()->getMetadata($this->xcloner_settings->get_logger_filename(1));
464
                $this->store_file($metadata_dumpfile, 'tmp_filesystem');
465
                $this->files_counter++;
466
            }
467
468
            return false;
469
        }
470
471
        return true;
472
    }
473
474
    public function get_backup_attachments()
475
    {
476
        $return = array();
477
478
        $files_list_file = $this->xcloner_settings->get_xcloner_tmp_path().DS.$this->get_included_files_handler();
479
        if (file_exists($files_list_file)) {
480
            $return[] = $files_list_file;
481
        }
482
483
        if ($this->xcloner_settings->get_xcloner_option('xcloner_enable_log')) {
484
            $log_file = $this->xcloner_settings->get_xcloner_tmp_path().DS.$this->xcloner_settings->get_logger_filename(1);
485
            if (!file_exists($log_file)) {
486
                $log_file = $this->xcloner_settings->get_xcloner_store_path().DS.$this->xcloner_settings->get_logger_filename();
487
            }
488
489
            if (file_exists($log_file)) {
490
                $return[] = $log_file;
491
            }
492
        }
493
494
        return $return;
495
    }
496
497
    public function remove_tmp_filesystem()
498
    {
499
        //delete the temporary folder
500
        $this->logger->debug(sprintf("Deleting the temporary storage folder %s",
501
            $this->xcloner_settings->get_xcloner_tmp_path()));
502
503
        $contents = $this->get_tmp_filesystem()->listContents();
504
505
        if (is_array($contents)) {
506
            foreach ($contents as $file_info) {
507
                $this->get_tmp_filesystem()->delete($file_info['path']);
508
            }
509
        }
510
511
        @rmdir($this->xcloner_settings->get_xcloner_tmp_path());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rmdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

511
        /** @scrutinizer ignore-unhandled */ @rmdir($this->xcloner_settings->get_xcloner_tmp_path());

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
512
513
        return;
514
    }
515
516
    public function cleanup_tmp_directories()
517
    {
518
        $adapter = new Local($this->xcloner_settings->get_xcloner_tmp_path(false), LOCK_EX | FILE_APPEND, 'SKIP_LINKS');
0 ignored issues
show
Bug introduced by
'SKIP_LINKS' of type string is incompatible with the type integer expected by parameter $linkHandling of League\Flysystem\Adapter\Local::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

518
        $adapter = new Local($this->xcloner_settings->get_xcloner_tmp_path(false), LOCK_EX | FILE_APPEND, /** @scrutinizer ignore-type */ 'SKIP_LINKS');
Loading history...
519
        $tmp_filesystem = new Filesystem($adapter, new Config([
520
            'disable_asserts' => true,
521
        ]));
522
523
        $contents = $tmp_filesystem->listContents();
524
525
        foreach ($contents as $file) {
526
527
            if (preg_match("/.xcloner-(.*)/", $file['path'])) {
528
                if ($file['timestamp'] < strtotime("-1days")) {
529
                    $tmp_filesystem->deleteDir($file['path']);
530
                    $this->logger->debug(sprintf("Delete temporary directory %s", $file['path']));
531
                }
532
            }
533
        }
534
535
        return true;
536
    }
537
538
    private function do_system_init()
539
    {
540
        $this->files_counter = 0;
541
542
        if (!$this->storage_filesystem->has("index.html")) {
543
            $this->storage_filesystem->write("index.html", "");
544
        }
545
546
        if (!$this->tmp_filesystem->has("index.html")) {
547
            $this->tmp_filesystem->write("index.html", "");
548
        }
549
550
        if ($this->tmp_filesystem->has($this->get_included_files_handler())) {
551
            $this->tmp_filesystem->delete($this->get_included_files_handler());
552
        }
553
554
        if ($this->tmp_filesystem->has($this->get_temp_dir_handler())) {
555
            $this->tmp_filesystem->delete($this->get_temp_dir_handler());
556
        }
557
    }
558
559
    public function get_scanned_files_num()
560
    {
561
        return $this->files_counter;
562
    }
563
564
    public function get_scanned_files_total_size()
565
    {
566
        return $this->files_size;
567
    }
568
569
    public function last_logged_file()
570
    {
571
        return $this->last_logged_file;
572
    }
573
574
    public static function is_regex($regex)
575
    {
576
        return preg_match("/^\^(.*)\$$/i", $regex);
577
    }
578
579
    public function set_excluded_files($excluded_files = array())
580
    {
581
        if (!is_array($excluded_files)) {
582
            $excluded_files = array();
583
        }
584
585
        foreach ($excluded_files as $excl) {
586
587
            if ($this->is_regex($excl)) {
588
                $this->additional_regex_patterns[] = $excl;
589
            }
590
        }
591
592
        $this->excluded_files = array_merge($excluded_files, $this->excluded_files_by_default);
593
594
        return $this->excluded_files;
595
    }
596
597
    public function get_excluded_files()
598
    {
599
        return $this->excluded_files_by_default;
600
    }
601
602
    public function list_directory($path)
603
    {
604
        return $this->start_filesystem->listContents($path);
605
    }
606
607
    public function build_files_list($folder = "")
608
    {
609
        $this->logger->debug(sprintf(("Building the files system list")));
610
611
        //if we start with the root folder(empty value), we initializa the file system
612
        if (!$folder) {
613
614
        }
615
616
        try {
617
618
            $files = $this->start_filesystem->listContents($folder);
619
            foreach ($files as $file) {
620
                if (!is_readable($this->xcloner_settings->get_xcloner_start_path().DS.$file['path'])) {
621
                    $this->logger->info(sprintf(__("Excluding %s from the filesystem list, file not readable"),
0 ignored issues
show
Bug introduced by
The function __ was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

621
                    $this->logger->info(sprintf(/** @scrutinizer ignore-call */ __("Excluding %s from the filesystem list, file not readable"),
Loading history...
622
                        $file['path']), array(
623
                        "FILESYSTEM SCAN",
624
                        "NOT READABLE"
625
                    ));
626
                } elseif (!$matching_pattern = $this->is_excluded($file)) {
627
                    $this->logger->info(sprintf(__("Adding %s to the filesystem list"), $file['path']), array(
628
                        "FILESYSTEM SCAN",
629
                        "INCLUDE"
630
                    ));
631
                    $file['visibility'] = $this->start_filesystem->getVisibility($file['path']);
632
                    if ($this->store_file($file)) {
633
                        $this->files_counter++;
634
                    }
635
                    if (isset($file['size'])) {
636
                        $this->files_size += $file['size'];
637
                    }
638
639
                } else {
640
                    $this->logger->info(sprintf(__("Excluding %s from the filesystem list, matching pattern %s"),
641
                        $file['path'], $matching_pattern), array(
642
                        "FILESYSTEM SCAN",
643
                        "EXCLUDE"
644
                    ));
645
                }
646
            }
647
648
        }catch (Exception $e) {
649
650
            $this->logger->error($e->getMessage());
651
652
        }
653
654
    }
655
656
    public function estimate_read_write_time()
657
    {
658
        $tmp_file = ".xcloner".substr(md5(time()), 0, 5);
659
660
        $start_time = microtime(true);
661
662
        $data = str_repeat(rand(0, 9), 1024 * 1024); //write 1MB data
663
664
        try {
665
            $this->tmp_filesystem->write($tmp_file, $data);
666
667
            $end_time = microtime(true) - $start_time;
668
669
            $return['writing_time'] = $end_time;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$return was never initialized. Although not strictly required by PHP, it is generally a good practice to add $return = array(); before regardless.
Loading history...
670
671
            $return['reading_time'] = $this->estimate_reading_time($tmp_file);
672
673
            $this->tmp_filesystem->delete($tmp_file);
674
675
        }catch (Exception $e) {
676
677
            $this->logger->error($e->getMessage());
678
679
        }
680
681
        return $return;
682
    }
683
684
    public function backup_storage_cleanup()
685
    {
686
        $this->logger->info(sprintf(("Cleaning the backup storage on matching rules")));
687
688
        $_storage_size = 0;
689
        $_backup_files_list = array();
690
691
        //rule date limit
692
        $current_timestamp = strtotime("-".$this->xcloner_settings->get_xcloner_option('xcloner_cleanup_retention_limit_days')." days");
693
694
        $files = $this->storage_filesystem->listContents();
695
696
        if (is_array($files)) {
697
            foreach ($files as $file) {
698
                if (isset($file['extension']) and in_array($file['extension'], $this->backup_archive_extensions)) {
699
                    $_storage_size += $file['size']; //bytes
700
                    $_backup_files_list[] = $file;
701
                }
702
            }
703
        }
704
705
706
        $this->sort_by($_backup_files_list, "timestamp", "asc");
707
708
        $_backups_counter = sizeof($_backup_files_list);
709
710
        foreach ($_backup_files_list as $file) {
711
            //processing rule folder capacity
712
            if ($this->xcloner_settings->get_xcloner_option('xcloner_cleanup_capacity_limit') &&
713
                $_storage_size >= ($set_storage_limit = 1024 * 1024 * $this->xcloner_settings->get_xcloner_option('xcloner_cleanup_capacity_limit')))    //bytes
714
            {
715
                $this->storage_filesystem->delete($file['path']);
716
                $_storage_size -= $file['size'];
717
                $this->logger->info("Deleting backup ".$file['path']." matching rule", array(
718
                    "STORAGE SIZE LIMIT",
719
                    $_storage_size." >= ".$set_storage_limit
720
                ));
721
            }
722
723
            //processing rule days limit
724
            if ($this->xcloner_settings->get_xcloner_option('xcloner_cleanup_retention_limit_days') && $current_timestamp >= $file['timestamp']) {
725
                $this->storage_filesystem->delete($file['path']);
726
                $this->logger->info("Deleting backup ".$file['path']." matching rule", array(
727
                    "RETENTION LIMIT TIMESTAMP",
728
                    $file['timestamp']." =< ".$this->xcloner_settings->get_xcloner_option('xcloner_cleanup_retention_limit_days')
729
                ));
730
            }
731
732
            //processing backup countert limit
733
            if ($this->xcloner_settings->get_xcloner_option('xcloner_cleanup_retention_limit_archives') && $_backups_counter >= $this->xcloner_settings->get_xcloner_option('xcloner_cleanup_retention_limit_archives')) {
734
                $this->storage_filesystem->delete($file['path']);
735
                $_backups_counter--;
736
                $this->logger->info("Deleting backup ".$file['path']." matching rule", array(
737
                    "BACKUP QUANTITY LIMIT",
738
                    $_backups_counter." >= ".$this->xcloner_settings->get_xcloner_option('xcloner_cleanup_retention_limit_archives')
739
                ));
740
            }
741
742
743
        }
744
745
    }
746
747
    /**
748
     * @param string $tmp_file
749
     */
750
    public function estimate_reading_time($tmp_file)
751
    {
752
        $this->logger->debug(sprintf(("Estimating file system reading time")));
753
754
        $start_time = microtime(true);
755
756
        if ($this->tmp_filesystem->has($tmp_file)) {
757
            $this->tmp_filesystem->read($tmp_file);
758
        }
759
760
        $end_time = microtime(true) - $start_time;
761
762
        return $end_time;
763
764
    }
765
766
    public function process_backup_name($name = "", $max_length = 100)
767
    {
768
        if (!$name) {
769
            $name = $this->xcloner_settings->get_default_backup_name();
770
        }
771
772
        foreach ($this->backup_name_tags as $tag) {
773
            if ($tag == '[time]') {
774
                $name = str_replace($tag, date("Y-m-d_H-i"), $name);
775
            } elseif ($tag == '[hostname]') {
776
                $name = str_replace($tag, gethostname(), $name);
777
            } elseif ($tag == '[domain]') {
778
                $domain = parse_url(admin_url(), PHP_URL_HOST);
0 ignored issues
show
Bug introduced by
The function admin_url was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

778
                $domain = parse_url(/** @scrutinizer ignore-call */ admin_url(), PHP_URL_HOST);
Loading history...
779
                $name = str_replace($tag, $domain, $name);
780
            }
781
        }
782
783
        if ($max_length) {
784
            $name = substr($name, 0, $max_length);
785
        }
786
787
        return $name;
788
    }
789
790
    /**
791
     * @param string $field
792
     */
793
    public function sort_by(&$array, $field, $direction = 'asc')
794
    {
795
        if (strtolower($direction) == "desc" || $direction == SORT_DESC) {
796
            $direction = SORT_DESC;
797
        } else {
798
            $direction = SORT_ASC;
799
        }
800
801
        $array = $this->array_orderby($array, $field, $direction);
802
803
        return true;
804
    }
805
806
    private function array_orderby()
807
    {
808
        $args = func_get_args();
809
        $data = array_shift($args);
810
811
        foreach ($args as $n => $field) {
812
            if (is_string($field)) {
813
                $tmp = array();
814
                foreach ($data as $key => $row) {
815
                    if (is_array($row)) {
816
                        $tmp[$key] = $row[$field];
817
                    } else {
818
                        $tmp[$key] = $row->$field;
819
                    }
820
                }
821
                $args[$n] = $tmp;
822
            }
823
        }
824
        $args[] = &$data;
825
826
        call_user_func_array('array_multisort', $args);
827
828
        return array_pop($args);
829
    }
830
831
    private function check_file_diff_time($file)
832
    {
833
        if ($this->get_diff_timestamp_start() != "") {
834
            $fileMeta = $this->getMetadataFull("start_adapter", $file['path']);
835
            $timestamp = $fileMeta->getMTime();
836
            if ($timestamp < $fileMeta->getCTime()) {
837
                $timestamp = $fileMeta->getCTime();
838
            }
839
840
            if ($timestamp <= $this->get_diff_timestamp_start()) {
841
                return " file DIFF timestamp ".$timestamp." < ".$this->diff_timestamp_start;
842
            }
843
        }
844
845
        return false;
846
    }
847
848
    public function is_excluded($file)
849
    {
850
        $this->logger->debug(sprintf(("Checking if %s is excluded"), $file['path']));
851
852
        if ($xcloner_exclude_files_larger_than_mb = $this->xcloner_settings->get_xcloner_option('xcloner_exclude_files_larger_than_mb')) {
853
            if (isset($file['size']) && $file['size'] > $this->calc_to_bytes($xcloner_exclude_files_larger_than_mb)) {
854
                return "> ".$xcloner_exclude_files_larger_than_mb."MB";
855
            }
856
        }
857
858
        if (!is_array($this->excluded_files) || !sizeof($this->excluded_files)) {
0 ignored issues
show
introduced by
The condition is_array($this->excluded_files) is always false.
Loading history...
859
            $this->set_excluded_files();
860
        }
861
862
        if (is_array($this->excluded_files)) {
0 ignored issues
show
introduced by
The condition is_array($this->excluded_files) is always true.
Loading history...
863
            foreach ($this->excluded_files as $excluded_file_pattern) {
864
                if ($excluded_file_pattern == "/") {
865
                    $needle = "$";
866
                } else {
867
                    $needle = "$".$excluded_file_pattern;
868
                }
869
870
                if (strstr("$".$file['path'], $needle)) {
871
                    return $excluded_file_pattern;
872
                }
873
            }
874
        }
875
876
        if ($regex = $this->is_excluded_regex($file)) {
877
            return $regex;
878
        }
879
880
        if ($file['type'] == "file") {
881
            $check_file_diff_timestamp = $this->check_file_diff_time($file);
882
            if ($check_file_diff_timestamp) {
883
                return $check_file_diff_timestamp;
884
            }
885
        }
886
887
        return false;
888
    }
889
890
    /*REGEX examples
891
     *
892
    * exclude all except .php file
893
    * PATTERN: ^(.*)\.(.+)$(?<!(php))
894
    *
895
    * exclude all except .php and .txt
896
    * PATTERN: ^(.*)\.(.+)$(?<!(php|txt))";
897
    *
898
    * exclude all .svn and .git
899
    * PATTERN: ^(.*)\.(svn|git)(.*)$";
900
    *
901
    * exclude root directory /test
902
    * PATTERN: "^\/test(.*)$";
903
    *
904
    * exclude the wp-admin folder
905
    * PATTERN: ^(\/wp-admin)(.*)$";
906
    *
907
    * exclude the wp-admin, wp-includes and wp-config.php
908
    * PATTERN: ^\/(wp-admin|wp-includes|wp-config.php)(.*)$";
909
    *
910
    * exclude all .avi files
911
    * PATTERN: ^(.*)$(?<=(avi))";
912
    *
913
    * exclude all .jpg and gif files
914
    * PATTERN: ^(.*)$(?<=(gif|jpg))";
915
    *
916
    * exclude all cache folders from wp-content/
917
    * PATTERN: ^\/wp-content(.*)\/cache($|\/)(.*)";
918
    *
919
    * exclude the backup folders
920
    * PATTERN: (^|^\/)(wp-content\/backups|administrator\/backups)(.*)$";
921
    */
922
    private function is_excluded_regex($file)
923
    {
924
        //$this->logger->debug(sprintf(("Checking if %s is excluded"), $file['path']));
925
926
        $regex_patterns = explode(PHP_EOL, $this->xcloner_settings->get_xcloner_option('xcloner_regex_exclude'));
927
928
        if (is_array($this->additional_regex_patterns)) {
0 ignored issues
show
introduced by
The condition is_array($this->additional_regex_patterns) is always true.
Loading history...
929
            $regex_patterns = array_merge($regex_patterns, $this->additional_regex_patterns);
930
        }
931
932
        //print_r($regex_patterns);exit;
933
934
        if (is_array($regex_patterns)) {
0 ignored issues
show
introduced by
The condition is_array($regex_patterns) is always true.
Loading history...
935
            //$this->excluded_files = array();
936
            //$this->excluded_files[] ="(.*)\.(git)(.*)$";
937
            //$this->excluded_files[] ="wp-content\/backups(.*)$";
938
939
            foreach ($regex_patterns as $excluded_file_pattern) {
940
941
                if (substr($excluded_file_pattern, strlen($excluded_file_pattern) - 1,
942
                        strlen($excluded_file_pattern)) == "\r") {
943
                    $excluded_file_pattern = substr($excluded_file_pattern, 0, strlen($excluded_file_pattern) - 1);
944
                }
945
946
                if ($file['path'] == "/") {
947
                    $needle = "/";
948
                } else {
949
                    $needle = "/".$file['path'];
950
                }
951
                //echo $needle."---".$excluded_file_pattern."---\n";
952
953
                if (@preg_match("/(^|^\/)".$excluded_file_pattern."/i", $needle)) {
954
                    return $excluded_file_pattern;
955
                }
956
            }
957
        }
958
959
        return false;
960
    }
961
962
    public function store_file($file, $storage = 'start_filesystem')
963
    {
964
        $this->logger->debug(sprintf("Storing %s in the backup list", $file['path']));
965
966
        if (!isset($file['size'])) {
967
            $file['size'] = 0;
968
        }
969
        if (!isset($file['visibility'])) {
970
            $file['visibility'] = "private";
971
        }
972
973
        $csv_filename = str_replace('"', '""', $file['path']);
974
975
        $line = '"'.($csv_filename).'","'.$file['timestamp'].'","'.$file['size'].'","'.$file['visibility'].'","'.$storage.'"'.PHP_EOL;
976
977
        $this->last_logged_file = $file['path'];
978
979
        if ($file['type'] == "dir") {
980
            try {
981
                $this->tmp_filesystem_append->write($this->get_temp_dir_handler(), $file['path']."\n");
982
            }catch (Exception $e) {
983
                $this->logger->error($e->getMessage());
984
            }
985
        }
986
987
        if ($this->get_diff_timestamp_start()) {
988
            if ($file['type'] != "file" && $response = $this->check_file_diff_time($file)) {
989
                $this->logger->info(sprintf("Directory %s archiving skipped on differential backup %s", $file['path'],
990
                    $response), array(
991
                    "FILESYSTEM SCAN",
992
                    "DIR DIFF"
993
                ));
994
995
                return false;
996
            }
997
        }
998
999
        try {
1000
            if (!$this->tmp_filesystem_append->has($this->get_included_files_handler())) {
1001
                //adding fix for UTF-8 CSV preview
1002
                $start_line = "\xEF\xBB\xBF".'"Filename","Timestamp","Size","Visibility","Storage"'.PHP_EOL;
1003
                $this->tmp_filesystem_append->write($this->get_included_files_handler(), $start_line);
1004
            }
1005
1006
            $this->tmp_filesystem_append->write($this->get_included_files_handler(), $line);
1007
1008
        }catch (Exception $e) {
1009
1010
            $this->logger->error($e->getMessage());
1011
        }
1012
1013
        return true;
1014
    }
1015
1016
    public function get_fileystem_handler()
1017
    {
1018
        return $this;
1019
    }
1020
1021
    public function get_filesystem($system = "")
1022
    {
1023
        if ($system == "storage_filesystem_append") {
1024
            return $this->storage_filesystem_append;
1025
        } elseif ($system == "tmp_filesystem_append") {
1026
            return $this->tmp_filesystem_append;
1027
        } elseif ($system == "tmp_filesystem") {
1028
            return $this->tmp_filesystem;
1029
        } elseif ($system == "storage_filesystem") {
1030
            return $this->storage_filesystem;
1031
        } else {
1032
            return $this->start_filesystem;
1033
        }
1034
    }
1035
1036
    public function get_adapter($system)
1037
    {
1038
        if ($system == "tmp_filesystem") {
1039
            return $this->tmp_adapter;
1040
        } elseif ($system == "storage_filesystem") {
1041
            return $this->storage_adapter;
1042
        } else {
1043
            return $this->start_adapter;
1044
        }
1045
    }
1046
1047
    /**
1048
     * File scan finished
1049
     * Method called when file scan is finished
1050
     *
1051
     * @return bool
1052
     */
1053
    private function scan_finished()
1054
    {
1055
        if ($this->tmp_filesystem_append->has($this->get_temp_dir_handler()) &&
1056
            $this->tmp_filesystem_append->getSize($this->get_temp_dir_handler())) {
1057
            return false;
1058
        }
1059
1060
        if ($this->tmp_filesystem->has($this->get_temp_dir_handler())) {
1061
            $this->tmp_filesystem->delete($this->get_temp_dir_handler());
1062
        }
1063
1064
        $this->logger->debug(sprintf(("File scan finished")));
1065
1066
        return true;
1067
    }
1068
1069
    /**
1070
     * Calculate bytes from MB value
1071
     *
1072
     * @param int $mb_size
1073
     *
1074
     * @return float|int
1075
     */
1076
    private function calc_to_bytes($mb_size)
1077
    {
1078
        return $mb_size * (1024 * 1024);
1079
    }
1080
1081
}
1082