PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 1.3.5
Backup Migration v1.3.5
2.1.7 2.1.6 2.1.5.2 trunk 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.6.1 1.4.7 1.4.8 1.4.9 1.4.9.1 2.0.0 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.5.1
backup-backup / includes / bypasser.php
backup-backup / includes Last commit date
banner 2 years ago check 2 years ago cli 2 years ago cron 2 years ago dashboard 2 years ago database 2 years ago extracter 2 years ago htaccess 2 years ago progress 2 years ago scanner 2 years ago staging 2 years ago uploader 2 years ago zipper 2 years ago .htaccess 2 years ago activation.php 2 years ago ajax.php 2 years ago analyst.php 2 years ago backup-cli.php 2 years ago backup-heart.php 2 years ago bypasser.php 2 years ago cli-handler.php 2 years ago compatibility.php 2 years ago config.php 2 years ago constants.php 2 years ago initializer.php 2 years ago logger.php 2 years ago restore-batching.php 2 years ago
bypasser.php
1233 lines
1 <?php
2
3 // Namespace
4 namespace BMI\Plugin\Heart;
5
6 // Usage
7 use BMI\Plugin\BMI_Logger AS Logger;
8 use BMI\Plugin\Progress\BMI_ZipProgress AS Output;
9 use BMI\Plugin\Checker\System_Info as SI;
10 use BMI\Plugin\Dashboard as Dashboard;
11 use BMI\Plugin\Database\BMI_Database as Database;
12 use BMI\Plugin\Database\BMI_Database_Exporter as BetterDatabaseExport;
13 use BMI\Plugin\BMI_Pro_Core as Pro_Core;
14 use BMI\Plugin AS BMI;
15
16 // Exit on direct access
17 if (!(defined('BMI_CURL_REQUEST') || defined('ABSPATH'))) exit;
18
19 // Fixes for some cases
20 require_once BMI_INCLUDES . '/compatibility.php';
21
22 /**
23 * Main class to handle heartbeat of the backup
24 */
25 class BMI_Backup_Heart {
26
27 public $it;
28 public $dbit;
29 public $abs;
30 public $dir;
31 public $url;
32 public $curl;
33 public $config;
34 public $content;
35 public $backups;
36 public $dblast;
37 public $output;
38
39 public $identy;
40 public $manifest;
41 public $backupname;
42 public $safelimit;
43 public $total_files;
44 public $rev;
45 public $backupstart;
46 public $filessofar;
47 public $identyfile;
48 public $browserSide;
49
50 public $identyFolder;
51 public $fileList;
52 public $dbfile;
53 public $db_dir_v2;
54 public $db_v2_engine;
55
56 public $headersSet;
57 public $final_made;
58 public $final_batch;
59 public $dbitJustFinished;
60 public $lock_cli;
61
62 public $_zip;
63 public $batches_left;
64
65 // Prepare the request details
66 function __construct($curl = false, $config = false, $content = false, $backups = false, $abs = false, $dir = false, $url = false, $remote_settings = [], $it = 0, $dbit = 0, $dblast = 0) {
67
68 $this->it = intval($it);
69 $this->dbit = intval($dbit);
70 $this->abs = $abs;
71 $this->dir = $dir;
72 $this->url = $url;
73 $this->curl = $curl;
74 $this->config = $config;
75 $this->content = $content;
76 $this->backups = $backups;
77 $this->dblast = $dblast;
78
79 $this->identy = $remote_settings['identy'];
80 $this->manifest = $remote_settings['manifest'];
81 $this->backupname = $remote_settings['backupname'];
82 $this->safelimit = intval($remote_settings['safelimit']);
83 $this->total_files = $remote_settings['total_files'];
84 $this->rev = intval($remote_settings['rev']);
85 $this->backupstart = $remote_settings['start'];
86 $this->filessofar = intval($remote_settings['filessofar']);
87 $this->identyfile = BMI_INCLUDES . '/htaccess' . '/.' . $this->identy;
88 $this->browserSide = ($remote_settings['browser'] === true || $remote_settings['browser'] === 'true') ? true : false;
89
90 // if (isset($remote_settings['shareallowed'])) {
91 // $this->shareallowed = $remote_settings['shareallowed'];
92 // } else {
93 // $this->shareallowed = 'ask';
94 // }
95
96 $this->identyFolder = BMI_INCLUDES . '/htaccess/bg-' . $this->identy;
97 $this->fileList = BMI_INCLUDES . '/htaccess/files_latest.list';
98 $this->dbfile = BMI_INCLUDES . '/htaccess/bmi_database_backup.sql';
99 $this->db_dir_v2 = BMI_INCLUDES . '/htaccess/db_tables';
100 $this->db_v2_engine = false;
101
102 $this->headersSet = false;
103 $this->final_made = false;
104 $this->final_batch = false;
105 $this->dbitJustFinished = false;
106
107 $this->lock_cli = BMI_BACKUPS . '/.backup_cli_lock';
108 if ($this->it > 1) @touch($this->lock_cli);
109
110 }
111
112 // Human size from bytes
113 public static function humanSize($bytes) {
114 if (is_int($bytes)) {
115 $label = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
116 for ($i = 0; $bytes >= 1024 && $i < (count($label) - 1); $bytes /= 1024, $i++);
117
118 return (round($bytes, 2) . " " . $label[$i]);
119 } else return $bytes;
120 }
121
122 // Create new process
123 public function send_beat($manual = false, &$logger = null) {
124
125 try {
126
127 $header = array(
128 // 'Content-Type:Application/x-www-form-urlencoded',
129 'Content-Accept:*/*',
130 'Access-Control-Allow-Origin:*',
131 'Content-ConfigDir:' . $this->config,
132 'Content-Content:' . $this->content,
133 'Content-Backups:' . $this->backups,
134 'Content-Identy:' . $this->identy,
135 'Content-Url:' . $this->url,
136 'Content-Abs:' . $this->abs,
137 'Content-Dir:' . $this->dir,
138 'Content-Manifest:' . $this->manifest,
139 'Content-Name:' . $this->backupname,
140 'Content-Safelimit:' . $this->safelimit,
141 'Content-Start:' . $this->backupstart,
142 'Content-Filessofar:' . $this->filessofar,
143 'Content-Total:' . $this->total_files,
144 // 'Content-Shareallowed:' . $this->shareallowed,
145 'Content-Rev:' . $this->rev,
146 'Content-It:' . $this->it,
147 'Content-Dbit:' . $this->dbit,
148 'Content-Dblast:' . $this->dblast,
149 'Content-Browser:' . $this->browserSide ? 'true' : 'false'
150 );
151
152 // if (!defined('CURL_HTTP_VERSION_2_0')) {
153 // define('CURL_HTTP_VERSION_2_0', CURL_HTTP_VERSION_1_0);
154 // }
155
156 // $ckfile = tempnam(BMI_INCLUDES . DIRECTORY_SEPARATOR . 'htaccess', "CURLCOOKIE");
157 $c = curl_init();
158 curl_setopt($c, CURLOPT_POST, 1);
159 curl_setopt($c, CURLOPT_TIMEOUT, 10);
160 // curl_setopt($c, CURLOPT_NOBODY, true);
161 curl_setopt($c, CURLOPT_VERBOSE, false);
162 curl_setopt($c, CURLOPT_HEADER, false);
163 // curl_setopt($c, CURLOPT_COOKIEJAR, $ckfile);
164 curl_setopt($c, CURLOPT_URL, $this->url);
165 curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
166 curl_setopt($c, CURLOPT_MAXREDIRS, 10);
167 curl_setopt($c, CURLOPT_COOKIESESSION, true);
168 // curl_setopt($c, CURLOPT_CONNECTTIMEOUT, 1);
169 curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
170 curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 0);
171 curl_setopt($c, CURLOPT_SSL_VERIFYPEER, 0);
172 curl_setopt($c, CURLOPT_HTTPHEADER, $header);
173 curl_setopt($c, CURLOPT_CUSTOMREQUEST, 'POST');
174 curl_setopt($c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
175 // curl_setopt($c, CURLOPT_USERAGENT, 'BMI_HEART_TIMEOUT_BYPASS_' . $this->it);
176 curl_setopt($c, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
177
178 $r = curl_exec($c);
179
180 if ($manual === true && $logger !== null) {
181 if ($r === false) {
182 if (intval(curl_errno($c)) !== 28) {
183 Logger::error(print_r(curl_getinfo($c), true));
184 Logger::error(curl_errno($c) . ': ' . curl_error($c));
185 $logger->log('There was something wrong with the request:', 'WARN');
186 $logger->log(curl_errno($c) . ': ' . curl_error($c), 'WARN');
187 }
188 } else {
189 $logger->log('Request sent successfully, without error returned.', 'SUCCESS');
190 }
191 }
192
193 curl_close($c);
194 // if (file_exists($ckfile)) @unlink($ckfile);
195 if (isset($this->output)) $this->output->end();
196
197 } catch (Exception $e) {
198
199 error_log($e->getMessage());
200 if (isset($this->output)) $this->output->end();
201
202 } catch (Throwable $e) {
203
204 error_log($e->getMessage());
205 if (isset($this->output)) $this->output->end();
206
207 }
208
209 }
210
211 // Load backup logger
212 public function load_logger() {
213
214 require_once BMI_INCLUDES . '/logger.php';
215 require_once BMI_INCLUDES . '/progress/logger-only.php';
216
217 $this->output = new Output();
218 $this->output->start();
219
220 }
221
222 // Remove common files
223 public function remove_commons() {
224
225 // Remove list if exists
226 $identyfile = $this->identyfile;
227 $logfile = BMI_INCLUDES . '/htaccess/bmi_logs_this_backup.log';
228 $clidata = BMI_INCLUDES . '/htaccess/bmi_cli_data.json';
229 if (file_exists($this->fileList)) @unlink($this->fileList);
230 if (file_exists($this->dbfile)) @unlink($this->dbfile);
231 if (file_exists($this->manifest)) @unlink($this->manifest);
232 if (file_exists($logfile)) @unlink($logfile);
233 if (file_exists($clidata)) @unlink($clidata);
234 if (file_exists($identyfile)) @unlink($identyfile);
235 if (file_exists($identyfile . '-running')) @unlink($identyfile . '-running');
236 if (file_exists($this->lock_cli)) @unlink($this->lock_cli);
237
238 // Remove backup
239 if (file_exists(BMI_BACKUPS . '/.running')) @unlink(BMI_BACKUPS . '/.running');
240 if (file_exists(BMI_BACKUPS . '/.abort')) @unlink(BMI_BACKUPS . '/.abort');
241
242 // Remove group folder
243 if (file_exists($this->identyFolder)) {
244 $files = glob($this->identyFolder . '/*');
245 foreach ($files as $file) if (is_file($file)) unlink($file);
246 @rmdir($this->identyFolder);
247 }
248
249 // Remove tmp database files
250 if (file_exists($this->db_dir_v2) && is_dir($this->db_dir_v2)) {
251 $files = glob($this->db_dir_v2 . '/*');
252 foreach ($files as $file) if (is_file($file)) unlink($file);
253 if (is_dir($this->db_dir_v2)) @rmdir($this->db_dir_v2);
254 }
255
256 // Remove cookie files
257 if (file_exists($this->dir . '/tmp')) {
258 $files = glob($this->dir . '/tmp/*');
259 foreach ($files as $file) if (is_file($file)) unlink($file);
260 }
261
262 // Remove temporary files
263 $files = glob(BMI_INCLUDES . '/htaccess/CURLCOOKIE*');
264 foreach ($files as $file) if (is_file($file)) @unlink($file);
265
266 }
267
268 // Make success
269 public function send_success() {
270
271 // Set header for browser
272 if ($this->browserSide && $this->headersSet === false) {
273
274 // Content finished
275 header('Content-Finished: true');
276 header('Content-It: ' . ($this->it + 1));
277 header('Content-Dbit: ' . $this->dbit);
278 header('Content-Dblast: ' . $this->dblast);
279 header('Content-Filessofar: ' . $this->filessofar);
280 http_response_code(200);
281 $this->headersSet = true;
282
283 }
284
285 // Display the success
286 $this->output->log('Backup completed successfully!', 'SUCCESS');
287 $this->output->log('#001', 'END-CODE');
288
289 // Remove common files
290 $this->remove_commons();
291
292 // End logger
293 if (isset($this->output)) @$this->output->end();
294
295 $this->actionsAfterProcess(true);
296
297 // End the process
298 exit;
299
300 }
301
302 // Make error
303 public function send_error($reason = false, $abort = false) {
304
305 // Set header for browser
306 if ($this->browserSide && $this->headersSet === false) {
307
308 // Content finished
309 header('Content-Finished: false');
310 header('Content-It: ' . ($this->it + 1));
311 header('Content-Dbit: ' . $this->dbit);
312 header('Content-Dblast: ' . $this->dblast);
313 header('Content-Filessofar: ' . $this->filessofar);
314 http_response_code(200);
315 $this->headersSet = true;
316
317 }
318
319 // Log error
320 $this->output->log('Something went wrong with background process... ' . '(part: ' . $this->it . ')', 'ERROR');
321 if ($reason !== false) $this->output->log('Reason: ' . $reason, 'ERROR');
322 $this->output->log('Removing backup files... ', 'ERROR');
323
324 // Remove common files
325 $this->remove_commons();
326
327 // Remove backup
328 if (file_exists(BMI_BACKUPS . '/' . $this->backupname)) @unlink(BMI_BACKUPS . '/' . $this->backupname);
329
330 // Abort step
331 $this->output->log('Aborting backup... ', 'STEP');
332 if ($abort === false) $this->output->log('#002', 'END-CODE');
333 else $this->output->log('#003', 'END-CODE');
334 if (isset($this->output)) @$this->output->end();
335
336 $this->actionsAfterProcess();
337 exit;
338
339 }
340
341 // Group files for batches
342 public function make_file_groups() {
343
344 if (!(file_exists($this->fileList) && is_readable($this->fileList))) {
345 return $this->send_error('File list is not accessible or does not exist, try to run your backup process once again.', true);
346 }
347
348 $this->output->log('Making batches for each process...', 'STEP');
349 $list_path = $this->fileList;
350
351 $file = fopen($list_path, 'r');
352 $this->output->log('Reading list file...', 'INFO');
353 $first_line = explode('_', fgets($file));
354 $files = intval($first_line[0]);
355 $firstmax = intval($first_line[1]);
356
357 if ($files > 0) {
358 $batches = 100;
359 if ($files <= 200) $batches = 100;
360 if ($files > 200) $batches = 200;
361 if ($files > 1600) $batches = 400;
362 if ($files > 3200) $batches = 800;
363 if ($files > 6400) $batches = 1600;
364 if ($files > 12800) $batches = 3200;
365 if ($files > 25600) $batches = 5000;
366 if ($files > 30500) $batches = 10000;
367 if ($files > 60500) $batches = 20000;
368 if ($files > 90500) $batches = 40000;
369
370 $this->output->log('Each batch will contain up to ' . $batches . ' files.', 'INFO');
371 $this->output->log('Large files takes more time, you will be notified about those.', 'INFO');
372
373 $folder = $this->identyFolder;
374 mkdir($folder, 0755, true);
375
376 $limitcrl = 96;
377 if (BMI_CLI_REQUEST === true) {
378 $limitcrl = 512;
379 if ($files > 30000) $limitcrl = 1024;
380 }
381
382 $i = 0; $bigs = 0; $prev = 0; $currsize = 0;
383 while (($line = fgets($file)) !== false) {
384
385 $line = explode(',', $line);
386 $last = sizeof($line) - 1;
387 $size = intval($line[$last]);
388 unset($line[$last]);
389 $line = implode(',', $line);
390
391 $i++;
392 if ($firstmax != -1 && $i > $firstmax) $bigs++;
393 $suffix = intval(ceil(abs($i / $batches))) + $bigs;
394
395 if ($prev == $suffix) {
396 $currsize += $size;
397 } else {
398 $currsize = $size;
399 $prev = $suffix;
400 }
401
402 $skip = false;
403 if ($currsize > ($limitcrl * (1024 * 1024))) $skip = true;
404
405 $groupFile = $folder . '/' . $this->identy . '-' . $suffix . '.files';
406 $group = fopen($groupFile, 'a');
407 fwrite($group, $line . ',' . $size . "\r\n");
408 fclose($group);
409
410 if ($skip === true) $bigs++;
411 unset($line);
412
413 }
414
415 fclose($file);
416 usleep(100);
417 if (file_exists($this->fileList)) @unlink($this->fileList);
418
419 } else {
420
421 $this->output->log('No file found to be backed up, omitting files.', 'INFO');
422
423 }
424
425 if (file_exists($this->fileList)) @unlink($this->fileList);
426 $this->output->log('Batches completed...', 'SUCCESS');
427
428 }
429
430 // Final batch
431 public function get_final_batch() {
432
433 $db_root_dir = BMI_INCLUDES . '/htaccess' . '/';
434 $logs = $db_root_dir . 'bmi_logs_this_backup.log';
435
436 $log_file = fopen($logs, 'w');
437 fwrite($log_file, file_get_contents(BMI_BACKUPS . DIRECTORY_SEPARATOR . 'latest.log'));
438 fclose($log_file);
439
440 $files = [$logs, $this->manifest];
441
442 return $files;
443
444 }
445
446 // Final logs
447 public function log_final_batch() {
448
449 $this->output->log('Finalizing backup', 'STEP');
450 $this->output->log('Closing files and archives', 'STEP');
451 $this->output->log('Archiving of ' . $this->total_files . ' files took: ' . number_format(microtime(true) - floatval($this->backupstart), 2) . 's', 'INFO');
452
453 if (!BMI_CLI_REQUEST) {
454 if (!$this->browserSide) sleep(1);
455 }
456
457 if (file_exists(BMI_BACKUPS . '/.abort')) {
458 $this->send_error('Backup aborted manually by user.', true);
459 return;
460 }
461
462 $this->send_success();
463
464 }
465
466 // Load batch
467 public function load_batch() {
468
469 if (!(file_exists($this->identyFolder) && is_dir($this->identyFolder))) {
470 return $this->send_error('Temporary directory does not exist, please start the backup once again.', true);
471 }
472
473 $allFiles = scandir($this->identyFolder);
474 $files = array_slice((array) $allFiles, 2);
475 if (sizeof($files) > 0) {
476
477 $largest = $files[0]; $prev_size = 0;
478 for ($i = 0; $i < sizeof($files); ++$i) {
479 $curr_size = filesize($this->identyFolder . '/' . $files[$i]);
480 if ($curr_size > $prev_size) {
481 $largest = $files[$i];
482 $prev_size = $curr_size;
483 }
484 }
485 $this->batches_left = sizeof($files);
486
487 if (sizeof($files) == 1) {
488 $this->final_batch = true;
489 }
490
491 return $this->identyFolder . '/' . $largest;
492
493 } else {
494
495 $this->log_final_batch();
496 return false;
497
498 }
499
500 }
501
502 // Cut Path for ZIP structure
503 public function cutDir($file) {
504
505 if (substr($file, -4) === '.sql') {
506
507 if ($this->db_v2_engine == true) {
508
509 return 'db_tables' . DIRECTORY_SEPARATOR . basename($file);
510
511 } else {
512
513 return basename($file);
514
515 }
516
517 } else {
518
519 return basename($file);
520
521 }
522
523 }
524
525 // Add files to ZIP – The Backup
526 public function add_files($files = [], $file_list = false, $final = false, $dbLog = false) {
527
528 try {
529
530 // TODO: Remove false && or replace with option in settings to switch
531 if (false && (class_exists('\ZipArchive') || class_exists('ZipArchive'))) {
532
533 // Initialize Zip
534 if (!isset($this->_zip)) {
535 $this->_zip = new \ZipArchive();
536 }
537
538 if ($this->_zip) {
539
540 // Show what's in use
541 if ($this->it === 1) {
542 $this->output->log('Using ZipArchive module to create the Archive.', 'INFO');
543 if ($dbLog == true) {
544 $this->output->log('Adding database SQL file(s) to the backup file.', 'STEP');
545 }
546 }
547
548 // Open / create ZIP file
549 $back = BMI_BACKUPS . DIRECTORY_SEPARATOR . $this->backupname;
550 if (BMI_CLI_REQUEST) {
551 if (!isset($this->zip_initialized)) {
552 $this->_zip->open($back, \ZipArchive::CREATE);
553 }
554 } else {
555 if (file_exists($back)) $this->_zip->open($back);
556 else $this->_zip->open($back, \ZipArchive::CREATE);
557 }
558
559 // Final operation
560 if ($final || $dbLog) {
561
562 // Add files
563 for ($i = 0; $i < sizeof($files); ++$i) {
564
565 if (file_exists($files[$i]) && is_readable($files[$i]) && !is_link($files[$i])) {
566
567 // Add the file
568 $this->_zip->addFile($files[$i], $this->cutDir($files[$i]));
569
570 } else {
571
572 $this->output->log('This file is not readable, it will not be included in the backup: ' . $files[$i], 'WARN');
573
574 }
575
576 }
577
578 if ($dbLog === false) {
579 $this->final_made = true;
580 }
581
582 } else {
583
584 // Add files
585 for ($i = 0; $i < sizeof($files); ++$i) {
586
587 if (file_exists($files[$i]) && is_readable($files[$i]) && !is_link($files[$i])) {
588
589 // Calculate Path in ZIP
590 $path = 'wordpress' . DIRECTORY_SEPARATOR . substr($files[$i], strlen(ABSPATH));
591
592 // Add the file
593 $this->_zip->addFile($files[$i], $path);
594
595 } else {
596
597 $this->output->log('This file is not readable, it will not be included in the backup: ' . $files[$i], 'WARN');
598
599 }
600
601 }
602
603 }
604
605 // Close archive and prepare next batch
606 touch(BMI_BACKUPS . '/.running');
607 if (!BMI_CLI_REQUEST || $final) {
608 $result = $this->_zip->close();
609
610 if ($result === true) {
611
612 // Remove batch
613 if ($file_list && file_exists($file_list)) {
614 @unlink($file_list);
615 }
616
617 } else {
618
619 $this->send_error('Error, there is most likely not enough space for the backup.');
620 return false;
621
622 }
623 } else {
624
625 // Remove batch
626 if ($file_list && file_exists($file_list)) {
627 @unlink($file_list);
628 }
629
630 }
631
632 } else {
633 $this->send_error('ZipArchive error, please contact support - your site may be special case.');
634 }
635
636 } else {
637
638 // Check if PclZip exists
639 if (!class_exists('PclZip')) {
640 if (!defined('PCLZIP_TEMPORARY_DIR')) {
641 $bmi_tmp_dir = BMI_ROOT_DIR . '/tmp';
642 if (!file_exists($bmi_tmp_dir)) {
643 @mkdir($bmi_tmp_dir, 0775, true);
644 }
645
646 define('PCLZIP_TEMPORARY_DIR', $bmi_tmp_dir . '/bmi-');
647 }
648 }
649
650 // Require the LIB and check if it's compatible
651 $alternative = dirname($this->dir) . '/backup-backup-pro/includes/pcl.php';
652 if ($this->rev === 1 || !file_exists($alternative)) {
653 require_once ABSPATH . 'wp-admin/includes/class-pclzip.php';
654 } else {
655 require_once $alternative;
656 if ($this->it === 1) {
657 $this->output->log('Using dedicated PclZIP for Pro', 'INFO');
658 }
659 }
660
661 // Get/Create the Archive
662 if (!isset($this->_lib)) {
663 $this->_lib = new \PclZip(BMI_BACKUPS . DIRECTORY_SEPARATOR . $this->backupname);
664 }
665
666 if (!$this->_lib) {
667 $this->send_error('PHP-ZIP: Permission Denied or zlib cannot be found');
668 return;
669 }
670
671 if (sizeof($files) <= 0) {
672 return false;
673 }
674
675 // Add files
676 if ($final) {
677
678 // Final configuration
679 $back = $this->_lib->add($files, PCLZIP_OPT_REMOVE_PATH, BMI_INCLUDES . '/htaccess' . '/', PCLZIP_OPT_TEMP_FILE_THRESHOLD, $this->safelimit);
680 $this->final_made = true;
681
682 } else {
683
684 // Additional path
685 $add_path = 'wordpress' . DIRECTORY_SEPARATOR;
686
687 // Casual configuration
688 $back = $this->_lib->add($files, PCLZIP_OPT_REMOVE_PATH, ABSPATH, PCLZIP_OPT_ADD_PATH, $add_path, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $this->safelimit);
689
690 }
691
692 // Check if there was any error
693 touch(BMI_BACKUPS . '/.running');
694 if ($back == 0) {
695
696 $this->send_error($this->_lib->errorInfo(true));
697 return false;
698
699 } else {
700
701 if ($file_list && file_exists($file_list)) {
702 @unlink($file_list);
703 }
704
705 }
706
707 }
708
709 } catch (\Exception $e) {
710
711 $this->send_error($e->getMessage());
712 return false;
713
714 } catch (\Throwable $e) {
715
716 $this->send_error($e->getMessage());
717 return false;
718
719 }
720
721 }
722
723 // ZIP one of the grouped files
724 public function zip_batch() {
725
726 if ($this->it === 1) {
727
728 $files = [];
729 if (file_exists($this->dbfile)) {
730 $files[] = $this->dbfile;
731 } elseif (file_exists($this->db_dir_v2) && is_dir($this->db_dir_v2)) {
732 $this->db_v2_engine = true;
733 $db_files = scandir($this->db_dir_v2);
734 foreach ($db_files as $i => $name) {
735 if (!($name == '.' || $name == '..')) {
736 $files[] = $this->db_dir_v2 . '/' . $name;
737 }
738 }
739 }
740
741 if (sizeof($files) > 0) {
742 $this->add_files($files, false, false, true);
743 $this->output->log('Database added to the backup file.', 'SUCCESS');
744 $this->output->log('Performing site files backup...', 'STEP');
745 return true;
746 }
747
748 $this->output->log('Performing site files backup...', 'STEP');
749
750 }
751
752 $list_file = $this->load_batch();
753 if ($list_file === false) return true;
754 $files = explode("\r\n", file_get_contents($list_file));
755
756 $total_size = 0;
757 $parsed_files = [];
758
759 for ($i = 0; $i < sizeof($files); ++$i) {
760 if (strlen(trim($files[$i])) <= 1) {
761 $this->total_files--;
762 continue;
763 }
764
765 $files[$i] = explode(',', $files[$i]);
766 $last = sizeof($files[$i]) - 1;
767 $size = intval($files[$i][$last]);
768 unset($files[$i][$last]);
769 $files[$i] = implode(',', $files[$i]);
770
771 $file = null;
772 if ($files[$i][0] . $files[$i][1] . $files[$i][2] === '@1@') {
773 $file = WP_CONTENT_DIR . '/' . substr($files[$i], 3);
774 } else if ($files[$i][0] . $files[$i][1] . $files[$i][2] === '@2@') {
775 $file = ABSPATH . '/' . substr($files[$i], 3);
776 } else {
777 $file = $files[$i];
778 }
779
780 if (!file_exists($file)) {
781 $this->output->log('Removing this file from backup (it does not exist anymore): ' . $file, 'WARN');
782 $this->total_files--;
783 continue;
784 }
785
786 if (filesize($file) === 0) {
787 $this->output->log('Removing this file from backup (file size is equal to 0 bytes): ' . $file, 'WARN');
788 $this->total_files--;
789 continue;
790 }
791
792 $parsed_files[] = $file;
793 $total_size += $size;
794 unset($file);
795 }
796
797 unset($files);
798 if (sizeof($parsed_files) === 1) {
799 $this->output->log('Adding: ' . sizeof($parsed_files) . ' file...' . ' [Size: ' . $this->humanSize($total_size) . ']', 'INFO');
800 $this->output->log('Alone-file mode for: ' . $parsed_files[0] . ' file...', 'INFO');
801 } else $this->output->log('Adding: ' . sizeof($parsed_files) . ' files...' . ' [Size: ' . $this->humanSize($total_size) . ']', 'INFO');
802
803 if ((60 * (1024 * 1024)) < $total_size) $this->output->log('Current batch is quite large, it may take some time...', 'WARN');
804
805 $this->add_files($parsed_files, $list_file);
806 $this->filessofar += sizeof($parsed_files);
807
808 $this->output->progress($this->filessofar . '/' . $this->total_files);
809 $this->output->log('Milestone: ' . $this->filessofar . '/' . $this->total_files . ' [' . $this->batches_left . ' batches left]', 'SUCCESS');
810
811 if ($this->final_batch === true) {
812 $this->output->log('Adding final files to this batch...', 'STEP');
813 $this->output->log('Adding manifest as addition...', 'INFO');
814
815 $additionalFiles = $this->get_final_batch();
816 $this->add_files($additionalFiles, false, true);
817 $this->log_final_batch();
818 return true;
819 }
820
821 }
822
823 // Shutdown callback
824 public function shutdown() {
825
826 // Check if there was any error
827 $err = error_get_last();
828 if ($err != null) {
829 Logger::error('Shuted down');
830 Logger::error(print_r($err, true));
831 $this->output->log('Background process had some issues, more details printed to global logs.', 'WARN');
832 }
833
834 // Remove lock
835 if (file_exists($this->lock_cli)) {
836 @unlink($this->lock_cli);
837 }
838
839 // Send next beat to handle next batch
840 if (BMI_CLI_REQUEST) return;
841 if (file_exists($this->identyfile)) {
842
843 if ($this->dbit === -1 && $this->dbitJustFinished == false) {
844 $this->it += 1;
845 }
846
847 // Set header for browser
848 if ($this->browserSide && $this->headersSet === false) {
849
850 // Content finished
851 header('Content-Finished: false');
852 header('Content-It: ' . $this->it);
853 header('Content-Dbit: ' . $this->dbit);
854 header('Content-Dblast: ' . $this->dblast);
855 header('Content-Filessofar: ' . $this->filessofar);
856 http_response_code(200);
857 $this->headersSet = true;
858
859 } else {
860
861 usleep(100);
862 $this->send_beat();
863
864 }
865
866 }
867
868 }
869
870 // Handle received batch
871 public function handle_batch() {
872
873 // Check if aborted
874 if (file_exists(BMI_BACKUPS . '/.abort')) {
875 if (!isset($this->output)) $this->load_logger();
876 $this->send_error('Backup aborted manually by user.', true);
877 return;
878 }
879
880 // Handle cURL
881 if ($this->curl == true) {
882
883 // Check if it was triggered by verified user
884 if (!file_exists($this->identyfile)) {
885 return;
886 }
887
888 // Register shutdown
889 register_shutdown_function([$this, 'shutdown']);
890
891 // Load logger
892 $this->load_logger();
893
894 set_error_handler(function ($errno, $errstr, $errfile, $errline) {
895 Logger::error('Bypasser error:');
896 Logger::error($errno . ' - ' . $errstr);
897 Logger::error($errfile . ' - ' . $errline);
898 });
899
900 // Notice parent script
901 touch($this->identyfile . '-running');
902 touch(BMI_BACKUPS . '/.running');
903
904 // CLI case
905 if (BMI_CLI_REQUEST) {
906
907 $this->output->log('Starting database backup exporter', 'STEP');
908 $this->output->log('Database exporter started via CLI', 'VERBOSE');
909 while ($this->dbit !== -1) {
910 $this->databaseBackupMaker();
911 }
912
913 // Log
914 $this->output->log("PHP CLI initialized - process ran successfully", 'SUCCESS');
915 $this->make_file_groups();
916
917 // Make ZIP
918 $this->output->log('Making archive...', 'STEP');
919 while (!$this->final_made) {
920 touch($this->identyfile . '-running');
921 touch(BMI_BACKUPS . '/.running');
922 $this->it++;
923 $this->zip_batch();
924 }
925
926 } else {
927
928 // Background
929 if ($this->dbit !== -1) {
930
931 if ($this->dbit === 0) {
932 $this->output->log('Background process initialized', 'SUCCESS');
933 $this->output->log('Starting database backup exporter', 'STEP');
934 $this->output->log('Database exporter started via WEB REQUESTS', 'VERBOSE');
935 }
936
937 $this->databaseBackupMaker();
938
939 } else {
940
941 if ($this->it === 0) {
942
943 $this->make_file_groups();
944 $this->output->log('Making archive...', 'STEP');
945
946 } else $this->zip_batch();
947
948 }
949
950 }
951
952 }
953
954 }
955
956 public function fixSlashes($str, $slash = false) {
957 // Old version
958 // $str = str_replace('\\\\', DIRECTORY_SEPARATOR, $str);
959 // $str = str_replace('\\', DIRECTORY_SEPARATOR, $str);
960 // $str = str_replace('\/', DIRECTORY_SEPARATOR, $str);
961 // $str = str_replace('/', DIRECTORY_SEPARATOR, $str);
962
963 // if ($str[strlen($str) - 1] == DIRECTORY_SEPARATOR) {
964 // $str = substr($str, 0, -1);
965 // }
966
967 // Since 1.3.2
968 $protocol = '';
969 if ($slash == false) $slash = DIRECTORY_SEPARATOR;
970 if (substr($str, 0, 7) == 'http://') $protocol = 'http://';
971 else if (substr($str, 0, 8) == 'https://') $protocol = 'https://';
972
973 $str = substr($str, strlen($protocol));
974 $str = preg_replace('/[\\\\\/]+/', $slash, $str);
975 $str = rtrim($str, '/\\' );
976
977 return $protocol . $str;
978 }
979
980 // Database batch maker and dumper
981 // We need WP instance for that to get access to wpdb
982 public function databaseBackupMaker() {
983
984 if ($this->dbit === -1) return;
985
986 $this->loadWordPressAndBackupPlugin();
987
988 // DB File Name for that type of backup
989 $dbbackupname = 'bmi_database_backup.sql';
990 $database_file = $this->fixSlashes(BMI_INCLUDES . DIRECTORY_SEPARATOR . 'htaccess' . DIRECTORY_SEPARATOR . $dbbackupname);
991
992 if (Dashboard\bmi_get_config('BACKUP:DATABASE') == 'true') {
993
994 if (Dashboard\bmi_get_config('OTHER:BACKUP:DB:SINGLE:FILE') == 'true') {
995
996 // Require Database Manager
997 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'manager.php';
998
999 // Log what's going on
1000 $this->output->log('Making single-file database backup (using deprecated engine, due to used settings)', 'STEP');
1001
1002 // Get database dump
1003 $databaser = new Database(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
1004 $databaser->exportDatabase($dbbackupname);
1005 $this->output->log("Database size: " . $this->humanSize(filesize($database_file)), 'INFO');
1006 $this->output->log('Database (single-file) backup finished.', 'SUCCESS');
1007
1008 $this->dbitJustFinished = true;
1009 $this->dbit = -1;
1010 return true;
1011
1012 } else {
1013
1014 // Log what's going on
1015 if ($this->dbit === 0) {
1016 $this->output->log("Making database backup (using v3 engine, requires at least v1.2.2 to restore)", 'STEP');
1017 $this->output->log("Iterating database...", 'INFO');
1018 }
1019
1020 // Require Database Manager
1021 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'better-backup-v3.php';
1022
1023 $database_file_dir = $this->fixSlashes((dirname($database_file))) . DIRECTORY_SEPARATOR;
1024 $better_database_files_dir = $database_file_dir . 'db_tables';
1025
1026 if (!is_dir($better_database_files_dir)) @mkdir($better_database_files_dir, 0755, true);
1027 $db_exporter = new BetterDatabaseExport($better_database_files_dir, $this->output, $this->dbit, intval($this->backupstart));
1028
1029 $dbBatchingEnabled = false;
1030 if (Dashboard\bmi_get_config('OTHER:BACKUP:DB:BATCHING') == 'true') {
1031 $dbBatchingEnabled = true;
1032 } else {
1033 if ($this->dbit === 0) {
1034 $this->output->log("Database batching is disabled in options, consider to use this option if your database backup fails.", 'WARN');
1035 }
1036 }
1037
1038 if (BMI_CLI_REQUEST === true || $dbBatchingEnabled === false) {
1039
1040 $this->output->log('Exporting database via bypasser.php @ CLI', 'VERBOSE');
1041 $results = $db_exporter->export();
1042
1043 $this->output->log("Database backup finished", 'SUCCESS');
1044 $this->dbitJustFinished = true;
1045 $this->dbit = -1;
1046 $this->dblast = 0;
1047
1048 } else {
1049
1050 $this->output->log('Exporting database via bypasser.php @ WEB REQUEST', 'VERBOSE');
1051 $results = $db_exporter->export($this->dbit, $this->dblast);
1052
1053 $this->dbit = intval($results['batchingStep']);
1054 $this->dblast = intval($results['finishedQuery']);
1055 $dbFinished = $results['dumpCompleted'];
1056
1057 if ($dbFinished == true) {
1058 $this->output->log("Database backup finished", 'SUCCESS');
1059 $this->dbitJustFinished = true;
1060 $this->dbit = -1;
1061 }
1062
1063 }
1064
1065 return true;
1066
1067 }
1068
1069 } else {
1070
1071 $this->output->log('Database will not be dumped due to user settings.', 'INFO');
1072 $this->dbitJustFinished = true;
1073 $this->dbit = -1;
1074 return true;
1075
1076 }
1077
1078 }
1079
1080 public function loadWordPressAndBackupPlugin() {
1081
1082 // Define how WP should load
1083 define('WP_USE_THEMES', false);
1084 define('SHORTINIT', true);
1085
1086 // Set path to our plugin's main file
1087 $bmiPluginPathToLoad = $this->fixSlashes(dirname(__DIR__) . '/backup-backup.php');
1088 $bmiPluginPathToLoadPro = $this->fixSlashes(dirname(dirname(__DIR__)) . '/backup-backup-pro/backup-backup-pro.php');
1089
1090 // Use WP Globals and load WordPress
1091 global $wp, $wp_query, $wp_the_query, $wp_rewrite, $wp_did_header;
1092 require_once $this->bmi_find_wordpress_base_path() . DIRECTORY_SEPARATOR . 'wp-load.php';
1093 global $wp_version;
1094
1095 // Load directory WordPress constants
1096 require_once ABSPATH . WPINC . '/formatting.php';
1097 require_once ABSPATH . WPINC . '/meta.php';
1098 wp_plugin_directory_constants();
1099
1100 // Allow to register activation hook and realpath
1101 $GLOBALS['wp_plugin_paths'] = array();
1102 $GLOBALS['shortcode_tags'] = array();
1103
1104 // Load all dependencies of WordPress for Backup plugin
1105 $dependencies = [
1106 ABSPATH . WPINC . '/l10n.php',
1107 ABSPATH . WPINC . '/plugin.php',
1108 ABSPATH . WPINC . '/link-template.php',
1109 ABSPATH . WPINC . '/class-wp-textdomain-registry.php',
1110 ABSPATH . WPINC . '/class-wp-locale.php',
1111 ABSPATH . WPINC . '/class-wp-locale-switcher.php',
1112 ABSPATH . WPINC . '/session.php',
1113 ABSPATH . WPINC . '/pluggable.php',
1114 ABSPATH . WPINC . '/class-wp-ajax-response.php',
1115 ABSPATH . WPINC . '/capabilities.php',
1116 ABSPATH . WPINC . '/class-wp-roles.php',
1117 ABSPATH . WPINC . '/class-wp-role.php',
1118 ABSPATH . WPINC . '/class-wp-user.php',
1119 ABSPATH . WPINC . '/class-wp-query.php',
1120 ABSPATH . WPINC . '/query.php',
1121 ABSPATH . WPINC . '/general-template.php',
1122 ABSPATH . WPINC . '/http.php',
1123 ABSPATH . WPINC . '/class-http.php',
1124 ABSPATH . WPINC . '/class-wp-http.php',
1125 ABSPATH . WPINC . '/class-wp-http-streams.php',
1126 ABSPATH . WPINC . '/class-wp-http-curl.php',
1127 ABSPATH . WPINC . '/class-wp-http-proxy.php',
1128 ABSPATH . WPINC . '/class-wp-http-cookie.php',
1129 ABSPATH . WPINC . '/class-wp-http-encoding.php',
1130 ABSPATH . WPINC . '/class-wp-http-response.php',
1131 ABSPATH . WPINC . '/class-wp-http-requests-response.php',
1132 ABSPATH . WPINC . '/class-wp-http-requests-hooks.php',
1133 ABSPATH . WPINC . '/class-wp-user-request.php',
1134 ABSPATH . WPINC . '/user.php',
1135 ABSPATH . WPINC . '/class-wp-user-query.php',
1136 ABSPATH . WPINC . '/class-wp-session-tokens.php',
1137 ABSPATH . WPINC . '/class-wp-user-meta-session-tokens.php',
1138 ABSPATH . WPINC . '/rest-api.php',
1139 ABSPATH . WPINC . '/kses.php',
1140 ABSPATH . WPINC . '/theme.php',
1141 ABSPATH . WPINC . '/rewrite.php',
1142 ABSPATH . WPINC . '/class-wp-block-editor-context.php',
1143 ABSPATH . WPINC . '/class-wp-block-type.php',
1144 ABSPATH . WPINC . '/class-wp-block-pattern-categories-registry.php',
1145 ABSPATH . WPINC . '/class-wp-block-patterns-registry.php',
1146 ABSPATH . WPINC . '/class-wp-block-styles-registry.php',
1147 ABSPATH . WPINC . '/class-wp-block-type-registry.php',
1148 ABSPATH . WPINC . '/class-wp-block.php',
1149 ABSPATH . WPINC . '/class-wp-block-list.php',
1150 ABSPATH . WPINC . '/class-wp-block-parser-block.php',
1151 ABSPATH . WPINC . '/class-wp-block-parser-frame.php',
1152 ABSPATH . WPINC . '/class-wp-block-parser.php',
1153 ABSPATH . WPINC . '/blocks.php',
1154 ABSPATH . WPINC . '/blocks/index.php',
1155 ];
1156
1157 for ($i = 0; $i < sizeof($dependencies); ++$i) {
1158 $dependency = $dependencies[$i];
1159 if (strpos($dependency, 'class-http.php') && version_compare($wp_version, '5.9.0', '>=')) {
1160 continue;
1161 }
1162 if (strpos($dependency, 'session.php') && version_compare($wp_version, '4.7.0', '>=')) {
1163 continue;
1164 }
1165 if (file_exists($dependency)) require_once $dependency;
1166 }
1167
1168 // Load Cookie Constants
1169 wp_cookie_constants();
1170
1171 // Load SSL Constants for DB export
1172 wp_ssl_constants();
1173
1174 // Register Translation
1175 if (class_exists('WP_Textdomain_Registry')) {
1176 $GLOBALS['wp_textdomain_registry'] = new \WP_Textdomain_Registry();
1177 }
1178
1179 if (is_readable($bmiPluginPathToLoadPro)) {
1180 wp_register_plugin_realpath($bmiPluginPathToLoadPro);
1181 include_once $bmiPluginPathToLoadPro;
1182
1183 require_once BMI_PRO_ROOT_DIR . '/classes/core' . ((BMI_PRO_DEBUG) ? '.to-enc' : '') . '.php';
1184 $bmi_pro_instance = new Pro_Core();
1185 $bmi_pro_instance->initialize();
1186 }
1187
1188 // Register our backup plugin and load its contents
1189 wp_register_plugin_realpath($bmiPluginPathToLoad);
1190 include_once $bmiPluginPathToLoad;
1191
1192 // Enable our plugin WITHOUT calling plugins_loaded hook – it's important
1193 require_once BMI_ROOT_DIR . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'constants.php';
1194
1195 // Initialize backup-migration
1196 if (!class_exists('Backup_Migration_Plugin')) {
1197
1198 // Require initializator
1199 require_once BMI_ROOT_DIR . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'initializer.php';
1200
1201 // Initialize entire plugin
1202 $bmi_instance = new BMI\Backup_Migration_Plugin();
1203 $bmi_instance->initialize();
1204
1205 }
1206
1207 }
1208
1209 public function actionsAfterProcess($success = false) {
1210
1211 return null;
1212
1213 }
1214
1215 public function bmi_find_wordpress_base_path() {
1216
1217 $dir = dirname(__FILE__);
1218 $previous = null;
1219
1220 do {
1221
1222 if (file_exists($dir . '/wp-load.php') && file_exists($dir . '/wp-config.php')) return $dir;
1223 if ($previous == $dir) break;
1224 $previous = $dir;
1225
1226 } while ($dir = dirname($dir));
1227
1228 return null;
1229
1230 }
1231
1232 }
1233