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