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