PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 2.1.7
Backup Migration v2.1.7
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 / backup-process.php
backup-backup / includes Last commit date
banner 2 weeks ago bodies 2 weeks ago check 2 weeks ago cli 2 weeks ago cron 2 weeks ago dashboard 2 weeks ago database 2 weeks ago external 2 weeks ago extracter 2 weeks ago htaccess 2 weeks ago notices 2 weeks ago progress 2 weeks ago scanner 2 weeks ago services 2 weeks ago staging 2 weeks ago traits 2 weeks ago uploader 2 weeks ago vendor 2 weeks ago zipper 2 weeks ago .htaccess 2 weeks ago activation.php 2 weeks ago ajax.php 2 weeks ago ajax_offline.php 2 weeks ago analyst.php 2 weeks ago backup-process.php 2 weeks ago class-backup-method-mananger.php 2 weeks ago cli-handler.php 2 weeks ago compatibility.php 2 weeks ago config.php 2 weeks ago config_v2.php 2 weeks ago constants.php 2 weeks ago file-explorer.php 2 weeks ago initializer.php 2 weeks ago logger.php 2 weeks ago offline.php 2 weeks ago
backup-process.php
1438 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_Logger 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\Database\Export\DatabaseExportProcessor;
15 use BMI\Plugin\BMI_Pro_Core as Pro_Core;
16 use BMI\Plugin AS BMI;
17
18 // Exit on direct access
19 if (!defined('ABSPATH')) exit;
20
21 // Fixes for some cases
22 require_once BMI_INCLUDES . '/compatibility.php';
23
24 /**
25 * Main class to handle heartbeat of the backup
26 */
27 class BMI_Backup_Heart {
28
29 public $it;
30 public $dbit;
31 public $abs;
32 public $dir;
33 public $url;
34 public $curl;
35 public $config;
36 public $content;
37 public $backups;
38 public $dblast;
39 public $output;
40 public $useragent;
41 public $remote_settings;
42
43 public $identy;
44 public $manifest;
45 public $backupname;
46 public $safelimit;
47 public $total_files;
48 public $rev;
49 public $backupstart;
50 public $filessofar;
51 public $identyfile;
52 public $browserSide;
53
54 public $identyFolder;
55 public $fileList;
56 public $dbfile;
57 public $db_dir_v2;
58 public $db_v2_engine;
59
60 public $final_made;
61 public $final_batch;
62 public $dbitJustFinished;
63 public $lock_cli;
64 public $startOfBatch;
65 public $errorSent = false;
66 public $statusSent = false;
67 public $backupSize = 0;
68 public $isStreamed = false;
69
70 public $_zip;
71 public $_lib;
72 public $batches_left;
73 public $res = [ 'status' => 'success', 'default' => true ];
74
75 // Prepare the request details
76 function __construct($curlIdenty = false, $config = false, $content = false, $backups = false, $abs = false, $dir = false, $remote_settings = []) {
77
78 if (!defined('BMI_CLI_REQUEST')) {
79 define('BMI_CLI_REQUEST', false);
80 }
81
82 $curl = false;
83 if ($curlIdenty != false) $curl = true;
84
85 $remote_settings = $this->getRemoteSettings($curlIdenty, $curl);
86 $this->remote_settings = $remote_settings;
87 if (sizeof($remote_settings) === 0) return;
88
89 $this->setupConstants();
90
91 $this->it = $remote_settings['it'];
92 $this->dbit = $remote_settings['dbit'];
93 $this->abs = $abs;
94 $this->dir = $dir;
95 $this->curl = $curl;
96 $this->config = $config;
97 $this->content = $content;
98 $this->backups = $backups;
99 $this->dblast = $remote_settings['dblast'];
100 $this->useragent = $remote_settings['useragent'];
101
102 $this->identy = $remote_settings['identy'];
103 $this->manifest = $remote_settings['manifest'];
104 $this->backupname = $remote_settings['backupname'];
105 $this->safelimit = intval($remote_settings['safelimit']);
106 $this->total_files = $remote_settings['total_files'];
107 $this->rev = intval($remote_settings['rev']);
108 $this->backupstart = $remote_settings['start'];
109 $this->filessofar = intval($remote_settings['filessofar']);
110 $this->identyfile = BMI_TMP . DIRECTORY_SEPARATOR . '.' . $this->identy;
111 $this->browserSide = (isset($remote_settings['browser']) && ($remote_settings['browser'] === true || $remote_settings['browser'] === 'true')) ? true : false;
112
113 if ($curl) {
114 // Here we could use nonces, but well, WordPress can't handle nonces in such scenario due to the way its generated
115 // We still use "nonce" here to bypass some security plugins as they may block the URL if the nonce string does not exist in such URL
116 $this->url = get_home_url(null, sprintf('/?backup-migration=CURL_BACKUP&bmi-id=%s&_wpnonce=%s&t=%s&sk=%s', $this->identy, 'Wn19dnWuq', time(), Dashboard\bmi_get_config('REQUEST:SECRET')));
117 } else {
118 $this->url = null;
119 }
120
121 $this->identyFolder = BMI_TMP . DIRECTORY_SEPARATOR . 'bg-' . $this->identy;
122 $this->fileList = BMI_TMP . DIRECTORY_SEPARATOR . 'files_latest.list';
123 $this->dbfile = BMI_TMP . DIRECTORY_SEPARATOR . 'bmi_database_backup.sql';
124 $this->db_dir_v2 = BMI_TMP . DIRECTORY_SEPARATOR . 'db_tables';
125 $this->db_v2_engine = false;
126
127 $this->final_made = $remote_settings['final_made'];
128 $this->final_batch = $remote_settings['final_batch'];
129 $this->dbitJustFinished = $remote_settings['dbitJustFinished'];
130 $this->backupSize = 0;
131 if (isset($remote_settings['backupSize']))
132 $this->backupSize = $remote_settings['backupSize'];
133
134 if (isset($remote_settings['is_streamed']))
135 $this->isStreamed = $remote_settings['is_streamed'];
136
137 $this->startOfBatch = time();
138 if (isset($remote_settings['startOfBatch']))
139 $this->startOfBatch = $remote_settings['startOfBatch'];
140
141 $this->lock_cli = BMI_BACKUPS . '/.backup_cli_lock';
142 if ($this->it > 1) @touch($this->lock_cli);
143
144 if ($this->isFunctionEnabled('ini_set') && $this->isFunctionEnabled('session_status') && session_status() != PHP_SESSION_ACTIVE) {
145 ini_set('display_errors', 1);
146 ini_set('error_reporting', E_ALL);
147 ini_set('log_errors', 1);
148 ini_set('error_log', BMI_CONFIG_DIR . '/background-errors.' . BMI_LOGS_SUFFIX . '.log');
149 }
150
151 }
152
153 // Get "remote_settings" from file created by the server
154 public function getRemoteSettings($curlIdenty, $curl = false) : array {
155 $settings_name = 'currentBackupConfig.' . 'php';
156 $settings_path = BMP::fixSlashes(BMI_TMP . DIRECTORY_SEPARATOR . $settings_name);
157
158 if (!file_exists($settings_path) && $curl) {
159 Logger::error('Settings path does not exist for backup-process.php');
160 return [];
161 }
162
163 if (!file_exists($settings_path)) {
164 Logger::error('Config file does not exist, please try to run the backup process once again.');
165 return $this->send_error('Config file does not exist, please try to run the backup process once again.', true);
166 }
167
168 $remote_settings = file_get_contents($settings_path);
169 $remote_settings = (array) json_decode(substr($remote_settings, 8));
170
171 if (!isset($remote_settings['identy'])) {
172 Logger::error('Identy is not set in the config, which prevents backup-process.php from running.');
173 return [];
174 }
175
176 if ($curl && $curlIdenty != $remote_settings['identy']) {
177 Logger::error('backup-process.php runs via CURL but the identy provided by CURL does not match config.');
178 return [];
179 }
180
181 return $remote_settings;
182 }
183
184 // Save remote setting for next batch
185 public function saveRemoteSettings() {
186 $settings_name = 'currentBackupConfig.' . 'php';
187 $settings_path = BMP::fixSlashes(BMI_TMP . DIRECTORY_SEPARATOR . $settings_name);
188
189 $this->remote_settings['identy'] = $this->identy;
190 $this->remote_settings['manifest'] = $this->manifest;
191 $this->remote_settings['backupname'] = $this->backupname;
192 $this->remote_settings['safelimit'] = $this->safelimit;
193 $this->remote_settings['total_files'] = $this->total_files;
194 $this->remote_settings['rev'] = $this->rev;
195 $this->remote_settings['start'] = $this->backupstart;
196 $this->remote_settings['filessofar'] = $this->filessofar;
197 $this->remote_settings['browser'] = $this->browserSide;
198 $this->remote_settings['it'] = $this->it;
199 $this->remote_settings['dbit'] = $this->dbit;
200 $this->remote_settings['dblast'] = $this->dblast;
201 $this->remote_settings['final_made'] = $this->final_made;
202 $this->remote_settings['final_batch'] = $this->final_batch;
203 $this->remote_settings['dbitJustFinished'] = $this->dbitJustFinished;
204 $this->remote_settings['startOfBatch'] = $this->startOfBatch;
205 $this->remote_settings['backupSize'] = $this->backupSize;
206 $this->remote_settings['is_streamed'] = $this->isStreamed;
207
208 if (file_exists($settings_path)) @unlink($settings_path);
209 file_put_contents($settings_path, '<?php //' . json_encode($this->remote_settings));
210 }
211
212 // Setup constants and handle request
213 public function setupConstants() {
214
215 if (!defined('BMI_CURL_REQUEST')) define('BMI_CURL_REQUEST', true);
216 if (!defined('BMI_CLI_REQUEST')) define('BMI_CLI_REQUEST', false);
217 if (!defined('BMI_SAFELIMIT')) define('BMI_SAFELIMIT', intval($this->remote_settings['safelimit']));
218
219 if ($this->isFunctionEnabled('ignore_user_abort')) @ignore_user_abort(true);
220 if ($this->isFunctionEnabled('set_time_limit')) @set_time_limit(259200);
221 if ($this->isFunctionEnabled('ini_set') && $this->isFunctionEnabled('session_status') && session_status() != PHP_SESSION_ACTIVE) {
222 @ini_set('max_input_time', '259200');
223 @ini_set('max_execution_time', '259200');
224 }
225
226 if (!isset($this->remote_settings['browser'])) $this->remote_settings['browser'] = false;
227
228 // Don't block server handler
229 // if ($this->isFunctionEnabled('session_write_close')) @session_write_close();
230
231 }
232
233 // Make sure it's impossible to unlink some files
234 public function unlinksafe($path) {
235
236 if (substr($path, 0, 7) == 'file://') {
237 $path = substr($path, 7);
238 }
239
240 $path = realpath($path);
241 if ($path === false) return;
242 if (strpos($path, 'wp-config.php') !== false) return;
243 if (substr($path, -8) == '/backups') return;
244
245 @unlink('file://' . $path);
246
247 }
248
249 // Human size from bytes
250 public static function humanSize($bytes) {
251 if (is_int($bytes)) {
252 $label = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
253 for ($i = 0; $bytes >= 1024 && $i < (count($label) - 1); $bytes /= 1024, $i++);
254
255 return (round($bytes, 2) . " " . $label[$i]);
256 } else return $bytes;
257 }
258
259 // Create new process
260 public function send_beat($manual = false, &$logger = null) {
261
262 $globalLockFile = BMI_TMP . DIRECTORY_SEPARATOR . '.beat_lock_' . $this->identy;
263 $maxLockAge = 10;
264
265 if (is_null($logger)) $this->load_logger();
266 else if ($logger instanceof Output) $this->output = $logger;
267
268 if (file_exists($globalLockFile)) {
269 $lockAge = time() - filemtime($globalLockFile);
270 if ($lockAge < $maxLockAge) {
271 sleep($maxLockAge - $lockAge + 1);
272 @unlink($globalLockFile);
273 } else {
274 @unlink($globalLockFile);
275 }
276 }
277
278 $fp = fopen($globalLockFile, 'c');
279 if (!$fp) {
280 $this->output->log('Could not open lock file for writing, skipping this beat.', 'WARN');
281 return;
282 }
283 if (!flock($fp, LOCK_EX | LOCK_NB)) {
284 $this->output->log('Another process is already running, skipping this beat.', 'WARN');
285 fclose($fp);
286 return;
287 }
288
289 try {
290
291 $header = array(
292 'Content-Accept:*/*',
293 'Access-Control-Allow-Origin:*'
294 );
295
296 $this->saveRemoteSettings();
297 $c = curl_init();
298 curl_setopt($c, CURLOPT_POST, 1);
299 curl_setopt($c, CURLOPT_COOKIESESSION, false);
300 curl_setopt($c, CURLOPT_TIMEOUT, 20);
301 curl_setopt($c, CURLOPT_VERBOSE, false);
302 curl_setopt($c, CURLOPT_HEADER, false);
303 curl_setopt($c, CURLOPT_URL, $this->url);
304 curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
305 curl_setopt($c, CURLOPT_MAXREDIRS, 10);
306 curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
307 curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 0);
308 curl_setopt($c, CURLOPT_SSL_VERIFYPEER, 0);
309 curl_setopt($c, CURLOPT_HTTPHEADER, $header);
310 curl_setopt($c, CURLOPT_CUSTOMREQUEST, 'POST');
311 curl_setopt($c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
312 curl_setopt($c, CURLOPT_USERAGENT, $this->useragent);
313
314 $r = curl_exec($c);
315
316 $statusCode = curl_getinfo($c, CURLINFO_HTTP_CODE);
317 if ($statusCode > 299) {
318 Logger::error(print_r(curl_getinfo($c), true));
319 $this->output->log('Response: ' . $r, 'ERROR');
320 $this->send_error('There was something wrong with the request, the response code was: ' . $statusCode, true);
321 }
322
323 if ($manual === true && $logger !== null) {
324 if ($r === false) {
325 if (intval(curl_errno($c)) === CURLE_OPERATION_TIMEDOUT || $statusCode === 429) {
326 Logger::error(print_r(curl_getinfo($c), true));
327 Logger::error(curl_errno($c) . ': ' . curl_error($c));
328 $this->output->log('There was something wrong with the request:', 'WARN');
329 $this->output->log(curl_errno($c) . ': ' . curl_error($c), 'WARN');
330 }
331 } else {
332 $this->output->log('Request sent successfully, without error returned.', 'SUCCESS');
333 }
334 }
335
336 if (is_resource($c)) {
337 curl_close($c);
338 }
339 if (isset($this->output)) $this->output->end();
340
341 } catch (\Exception $e) {
342
343 error_log($e->getMessage());
344 $this->output->log($e->getMessage(), 'ERROR');
345 if (isset($this->output)) $this->output->end();
346
347 } catch (\Throwable $e) {
348
349 error_log($e->getMessage());
350 $this->output->log($e->getMessage(), 'ERROR');
351 if (isset($this->output)) $this->output->end();
352
353 } finally {
354 flock($fp, LOCK_UN);
355 fclose($fp);
356 @unlink($globalLockFile);
357 }
358
359 }
360
361 // Load backup logger
362 public function load_logger() {
363
364 if ($this->output instanceof Output) return;
365
366 require_once BMI_INCLUDES . '/logger.php';
367 require_once BMI_INCLUDES . '/progress/logger-only.php';
368
369 $this->output = new Output();
370 $this->output->start();
371
372 }
373
374 // Remove common files
375 public function remove_commons() {
376
377 if (is_null($this->fileList)) return;
378
379 // Remove list if exists
380 $identyfile = $this->identyfile;
381 $logfile = BMI_TMP . DIRECTORY_SEPARATOR . 'bmi_logs_this_backup.log';
382 $clidata = BMI_TMP . DIRECTORY_SEPARATOR . 'bmi_cli_data.json';
383 $settings_path = BMI_TMP . DIRECTORY_SEPARATOR . 'currentBackupConfig.php';
384 $backupNameFile = BMI_TMP . DIRECTORY_SEPARATOR . '.backup_name';
385 if (file_exists($this->fileList)) $this->unlinksafe($this->fileList);
386 if (file_exists($this->dbfile)) $this->unlinksafe($this->dbfile);
387 if (file_exists($this->manifest)) $this->unlinksafe($this->manifest);
388 if (file_exists($logfile)) $this->unlinksafe($logfile);
389 if (file_exists($backupNameFile)) $this->unlinksafe($backupNameFile);
390 if (file_exists($clidata)) $this->unlinksafe($clidata);
391 if (file_exists($identyfile)) $this->unlinksafe($identyfile);
392 if (file_exists($identyfile . '-running')) $this->unlinksafe($identyfile . '-running');
393 if (file_exists($this->lock_cli)) $this->unlinksafe($this->lock_cli);
394 if (file_exists($settings_path)) $this->unlinksafe($settings_path);
395
396 // Remove backup
397 if (file_exists(BMI_BACKUPS . '/.running')) $this->unlinksafe(BMI_BACKUPS . '/.running');
398 if (file_exists(BMI_BACKUPS . '/.cron')) $this->unlinksafe(BMI_BACKUPS . '/.cron');
399 if (file_exists(BMI_BACKUPS . '/.space_check')) $this->unlinksafe(BMI_BACKUPS . '/.space_check');
400 if (file_exists(BMI_BACKUPS . '/.abort')) $this->unlinksafe(BMI_BACKUPS . '/.abort');
401 if (file_exists(BMI_BACKUPS . '/.last_triggered')) $this->unlinksafe(BMI_BACKUPS . '/.last_triggered');
402
403 // Remove group folder
404 if (file_exists($this->identyFolder)) {
405 $files = glob($this->identyFolder . '/*');
406 foreach ($files as $file) if (is_file($file)) $this->unlinksafe($file);
407 @rmdir($this->identyFolder);
408 }
409
410 // Remove tmp database files
411 if (file_exists($this->db_dir_v2) && is_dir($this->db_dir_v2)) {
412 $files = glob($this->db_dir_v2 . '/*');
413 foreach ($files as $file) if (is_file($file)) $this->unlinksafe($file);
414 if (is_dir($this->db_dir_v2)) @rmdir($this->db_dir_v2);
415 }
416
417 }
418
419 // Make success
420 public function send_success() {
421
422 $this->load_logger();
423
424 // Display the success
425 $this->output->log('Backup completed successfully!', 'SUCCESS');
426 $this->output->log('#001', 'END-CODE');
427
428 // Remove common files
429 $this->remove_commons();
430
431 // End logger
432 if (isset($this->output)) @$this->output->end();
433
434 $this->actionsAfterProcess(true);
435 $this->it += 1;
436
437 // Set header for browser
438 if ($this->browserSide) {
439
440 // Content finished
441 $this->sendResponse(true);
442
443 }
444
445 // End the process
446 exit;
447
448 }
449
450 // Make error
451 public function send_error($reason = false, $abort = false, $customEndCode = false) {
452
453 if ($this->errorSent) return;
454 $this->errorSent = true;
455
456 $this->load_logger();
457
458 // Log error
459 $this->output->log('Something went wrong with background process... ' . '(part: ' . $this->it . ')', 'ERROR');
460 if ($reason !== false) $this->output->log('Reason: ' . $reason, 'ERROR');
461 $this->output->log('Removing backup files... ', 'ERROR');
462
463 // Remove common files
464 $this->remove_commons();
465
466 // Remove backup
467 if (file_exists(BMI_BACKUPS . DIRECTORY_SEPARATOR . $this->backupname)) $this->unlinksafe(BMI_BACKUPS . DIRECTORY_SEPARATOR . $this->backupname);
468
469 // Abort step
470 $this->output->log('Aborting backup... ', 'STEP');
471 if ($customEndCode !== false) {
472 if ($customEndCode === "#003") {
473 $this->output->log('manually_aborted', 'verbose');
474 }
475 $this->output->log($customEndCode, 'END-CODE');
476 }
477 else if ($abort === false) $this->output->log('#002', 'END-CODE');
478 else $this->output->log('#004', 'END-CODE');
479 if (isset($this->output)) @$this->output->end();
480
481 $this->actionsAfterProcess();
482 $this->it += 1;
483
484 // Set header for browser
485 if ($this->browserSide) {
486
487 // Content finished
488 $this->sendResponse(false, true);
489
490 }
491 exit;
492
493 }
494
495 // Group files for batches
496 public function make_file_groups() {
497
498 if (!(file_exists($this->fileList) && is_readable($this->fileList))) {
499 return $this->send_error('File list is not accessible or does not exist, try to run your backup process once again.', true);
500 }
501
502 $this->output->log('Making batches for each process...', 'STEP');
503 $list_path = $this->fileList;
504
505 $file = fopen($list_path, 'r');
506 $this->output->log('Reading list file...', 'INFO');
507 $first_line = explode('_', fgets($file));
508 $files = intval($first_line[0]);
509 $firstmax = intval($first_line[1]);
510
511 if ($files > 0) {
512 $batches = 100;
513 if ($files <= 200) $batches = 100;
514 if ($files > 200) $batches = 400;
515 if ($files > 1600) $batches = 600;
516 if ($files > 3200) $batches = 1000;
517 if ($files > 6400) $batches = 2000;
518 if ($files > 12800) $batches = 4000;
519 if ($files > 25600) $batches = 5000;
520 if ($files > 30500) $batches = 10000;
521 if ($files > 60500) $batches = 20000;
522 if ($files > 90500) $batches = 40000;
523 if ($files > 100000) $batches = 60000;
524 if ($files > 150000) $batches = 80000;
525
526 $this->output->log('Each batch will contain up to ' . $batches . ' files.', 'INFO');
527 $this->output->log('Large files takes more time, you will be notified about those.', 'INFO');
528
529 $folder = $this->identyFolder;
530 if (!(file_exists($folder) && is_dir($file))) {
531 @mkdir($folder, 0755, true);
532 }
533
534 $limitcrl = 96;
535 if (BMI_CLI_REQUEST === true) {
536 $limitcrl = 512;
537 if ($files > 30000) $limitcrl = 1024;
538 }
539
540 $i = 0; $bigs = 0; $prev = 0; $currsize = 0;
541 while (($line = fgets($file)) !== false) {
542
543 $line = explode(',', $line);
544 $last = sizeof($line) - 1;
545 $size = intval($line[$last]);
546 unset($line[$last]);
547 $line = implode(',', $line);
548
549 $i++;
550 if ($firstmax != -1 && $i > $firstmax) $bigs++;
551 $suffix = intval(ceil(abs($i / $batches))) + $bigs;
552
553 if ($prev == $suffix) {
554 $currsize += $size;
555 } else {
556 $currsize = $size;
557 $prev = $suffix;
558 }
559
560 $skip = false;
561 if ($currsize > ($limitcrl * (1024 * 1024))) $skip = true;
562
563 $groupFile = $folder . DIRECTORY_SEPARATOR . $this->identy . '-' . $suffix . '.files';
564 $group = fopen($groupFile, 'a');
565 fwrite($group, $line . ',' . $size . "\r\n");
566 fclose($group);
567
568 if ($skip === true) $bigs++;
569 unset($line);
570
571 }
572
573 fclose($file);
574 if (file_exists($this->fileList)) $this->unlinksafe($this->fileList);
575
576 } else {
577
578 $this->output->log('No file found to be backed up, omitting files.', 'INFO');
579
580 }
581
582 if (file_exists($this->fileList)) $this->unlinksafe($this->fileList);
583 $this->output->log('Batches completed...', 'SUCCESS');
584
585 }
586
587 // Final batch
588 public function get_final_batch() {
589
590 $db_root_dir = BMI_TMP . DIRECTORY_SEPARATOR;
591 $logs = $db_root_dir . 'bmi_logs_this_backup.log';
592 $_manifest = $this->manifest;
593 $backupName = $db_root_dir . '.backup_name';
594
595 if (strpos($logs, 'file://') !== false) $logs = substr($logs, 7);
596 if (strpos($_manifest, 'file://') !== false) $_manifest = substr($_manifest, 7);
597 if (strpos($backupName, 'file://') !== false) $backupName = substr($backupName, 7);
598
599 $log_file = fopen($logs, 'w');
600 fwrite($log_file, file_get_contents(BMI_BACKUPS . DIRECTORY_SEPARATOR . 'latest.' . BMI_LOGS_SUFFIX . '.log'));
601 fclose($log_file);
602
603 $backup_name_file = fopen($backupName, 'w');
604 fwrite($backup_name_file, $this->backupname);
605 fclose($backup_name_file);
606
607 $files = [$logs, $_manifest, $backupName];
608
609 return $files;
610
611 }
612
613 // Final logs
614 public function log_final_batch() {
615
616 $this->output->log('Finalizing backup', 'STEP');
617 $this->output->log('Closing files and archives', 'STEP');
618 $this->output->log('Archiving of ' . $this->total_files . ' files took: ' . number_format(microtime(true) - floatval($this->backupstart), 2) . 's', 'INFO');
619
620 if (!BMI_CLI_REQUEST) {
621 if (!$this->browserSide) sleep(1);
622 }
623
624 if (file_exists(BMI_BACKUPS . '/.abort')) {
625 $this->send_error('Backup aborted manually by user.', true, '#003');
626 return;
627 }
628
629 $this->send_success();
630
631 }
632
633 // Load batch
634 public function load_batch() {
635
636 if (!(file_exists($this->identyFolder) && is_dir($this->identyFolder))) {
637 return $this->send_error('Temporary directory does not exist, please start the backup once again.', true);
638 }
639
640 $allFiles = scandir($this->identyFolder);
641 $files = array_slice((array) $allFiles, 2);
642 if (sizeof($files) > 0) {
643
644 $largest = $files[0]; $prev_size = 0;
645 for ($i = 0; $i < sizeof($files); ++$i) {
646 $curr_size = filesize($this->identyFolder . DIRECTORY_SEPARATOR . $files[$i]);
647 if ($curr_size > $prev_size) {
648 $largest = $files[$i];
649 $prev_size = $curr_size;
650 }
651 }
652 $this->batches_left = sizeof($files);
653
654 if (sizeof($files) == 1) {
655 $this->final_batch = true;
656 }
657
658 return $this->identyFolder . DIRECTORY_SEPARATOR . $largest;
659
660 } else {
661
662 $this->final_batch = true;
663 return false;
664
665 }
666
667 }
668
669 // Cut Path for ZIP structure
670 public function cutDir($file) {
671
672 if (substr($file, -4) === '.sql') {
673
674 if ($this->db_v2_engine == true) {
675
676 $path = 'db_tables' . DIRECTORY_SEPARATOR . basename($file);
677
678 } else {
679
680 $path = basename($file);
681
682 }
683
684 } else {
685
686 $path = basename($file);
687
688 }
689
690 $path = str_replace('\\', '/', $path);
691 return $path;
692
693 }
694
695 // Add files to ZIP – The Backup
696 public function add_files($files = [], $file_list = false, $final = false, $dbLog = false) {
697
698 try {
699 $encryptionEnabled = defined('BMI_BACKUP_PRO') && BMI_BACKUP_PRO === 1 && Dashboard\bmi_get_config('ENCRYPTION:ENABLED');
700 $encryptionPassword = $encryptionEnabled ? Dashboard\bmi_get_config('ENCRYPTION:PASSWORD') : false;
701 $zipArchive = false;
702 if (class_exists('\ZipArchive') || class_exists('ZipArchive')) {
703 if (!isset($this->_zip)) {
704 $this->_zip = new \ZipArchive();
705 }
706
707 if ($this->_zip) {
708 $zipArchive = true;
709 } else {
710 $zipArchive = false;
711 }
712 }
713
714 // Check if PclZip exists
715 if ($zipArchive === false) {
716 if (!class_exists('PclZip')) {
717 if (!defined('PCLZIP_TEMPORARY_DIR')) {
718 $bmi_tmp_dir = BMI_TMP;
719 if (!file_exists($bmi_tmp_dir)) {
720 @mkdir($bmi_tmp_dir, 0775, true);
721 }
722
723 define('PCLZIP_TEMPORARY_DIR', $bmi_tmp_dir . DIRECTORY_SEPARATOR . 'bmi-');
724 }
725
726 if (!defined('PCLZIP_READ_BLOCK_SIZE')) {
727 define('PCLZIP_READ_BLOCK_SIZE', 32768);
728 }
729 }
730
731 // Require the LIB and check if it's compatible
732 $alternative = dirname($this->dir) . '/backup-backup-pro/includes/pcl.php';
733 if ($this->rev === 2 || !file_exists($alternative)) {
734 require_once ABSPATH . 'wp-admin/includes/class-pclzip.php';
735 } else {
736 require_once $alternative;
737 if ($this->it === 1) {
738 $this->output->log('Using dedicated PclZIP for Premium Users.', 'INFO');
739 if ($dbLog == true) {
740 $this->output->log('Adding database SQL file(s) to the backup file.', 'STEP');
741 }
742 }
743 }
744
745 // Get/Create the Archive
746 if (!isset($this->_lib)) {
747 $this->_lib = new \PclZip(BMI_BACKUPS . DIRECTORY_SEPARATOR . $this->backupname);
748 }
749
750 if (!$this->_lib) {
751 $this->send_error('PHP-ZIP: Permission Denied or zlib cannot be found');
752 return;
753 }
754
755 } else {
756
757 $backupPath = BMI_BACKUPS . DIRECTORY_SEPARATOR . $this->backupname;
758 $isNewArchive = !file_exists($backupPath);
759 $needsInit = false;
760
761 if (BMI_CLI_REQUEST) {
762 if (!$isNewArchive) {
763 $this->_zip->open($backupPath);
764 } else {
765 $this->_zip->open($backupPath, \ZipArchive::CREATE);
766 $needsInit = true;
767 }
768 if ($encryptionEnabled && $encryptionPassword !== false) {
769
770 if ($needsInit) {
771 $this->_zip->addFromString('bmi_init.txt', 'init');
772 $this->_zip->close();
773 $this->_zip->open($backupPath);
774 }
775
776 $this->_zip->setPassword($encryptionPassword);
777 }
778 } else {
779 if (!$isNewArchive) {
780 $this->_zip->open($backupPath);
781 } else {
782 $this->_zip->open($backupPath, \ZipArchive::CREATE);
783 $needsInit = true;
784 }
785 if ($encryptionEnabled && $encryptionPassword !== false) {
786 if ($needsInit) {
787 $this->_zip->addFromString('bmi_init.txt', 'init');
788 $this->_zip->close();
789 $this->_zip->open($backupPath);
790 }
791
792 $this->_zip->setPassword($encryptionPassword);
793 }
794 }
795
796 if ($this->it === 1) {
797 $this->output->log('Using ZipArchive extension for this backup process.', 'INFO');
798 if ($encryptionEnabled && $encryptionPassword !== false) {
799 $this->output->log('Encryption is enabled, setting password for the archive.', 'INFO');
800 }
801 if ($dbLog == true) {
802 $this->output->log('Adding database SQL file(s) to the backup file.', 'STEP');
803 }
804 }
805
806 }
807
808 if (sizeof($files) <= 0) {
809 return false;
810 }
811
812 $back = 0;
813 $files = array_filter($files, function ($path) {
814 if (is_readable($path) && file_exists($path) && !is_link($path)) return true;
815 else {
816 $this->output->log("Excluding file that cannot be read: " . $path, 'warn');
817 return false;
818 }
819 });
820
821 $_abspath = ABSPATH;
822 $_bmi_tmp = BMI_TMP;
823 $_wp_content = WP_CONTENT_DIR;
824 if (strpos($_abspath, 'file://') !== false) $_abspath = substr($_abspath, 7);
825 if (strpos($_bmi_tmp, 'file://') !== false) $_bmi_tmp = substr($_bmi_tmp, 7);
826 if (strpos($_wp_content, 'file://') !== false) $_wp_content = substr($_wp_content, 7);
827
828 $needManipulation = false;
829 if ( strpos($_wp_content, $_abspath) === false ) {
830 $needManipulation = true;
831 }
832
833 // Add files
834 if ($final || $dbLog) {
835
836 if ($zipArchive) {
837 for ($i = 0; $i < sizeof($files); ++$i) {
838
839 // Add the file
840 if (is_dir($files[$i])) {
841 $this->_zip->addEmptyDir($this->cutDir($files[$i]));
842 } else {
843 $this->_zip->addFile($files[$i], $this->cutDir($files[$i]));
844 if ($encryptionEnabled && $encryptionPassword !== false) {
845 if ($final && basename($files[$i]) === '.backup_name') {
846 $this->_zip->setEncryptionName($this->cutDir($files[$i]), \ZipArchive::EM_AES_256);
847 } elseif (!$final) {
848 $this->_zip->setEncryptionName($this->cutDir($files[$i]), \ZipArchive::EM_AES_256);
849 }
850 }
851 }
852
853 }
854 } else {
855
856 // Final configuration
857 if (sizeof($files) > 0) {
858 $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);
859 }
860
861 }
862
863 if ($dbLog === false) {
864 $this->final_made = true;
865 }
866
867 } else {
868
869 if ($zipArchive) {
870
871 for ($i = 0; $i < sizeof($files); ++$i) {
872
873 if ($needManipulation) {
874 if (strpos($files[$i], $_wp_content) !== false) {
875 $path = 'wordpress' . DIRECTORY_SEPARATOR . 'wp-content' . DIRECTORY_SEPARATOR . substr($files[$i], strlen($_wp_content));
876 } else {
877 $path = 'wordpress' . DIRECTORY_SEPARATOR . substr($files[$i], strlen($_abspath));
878 }
879 } else {
880 $path = 'wordpress' . DIRECTORY_SEPARATOR . substr($files[$i], strlen($_abspath));
881 }
882
883 $path = BMP::fixSlashes($path);
884
885 // Add the file
886 if (is_dir($files[$i])) {
887 $this->_zip->addEmptyDir($path);
888 } else {
889 $this->_zip->addFile($files[$i], $path);
890 if ($encryptionEnabled && $encryptionPassword !== false) {
891 $this->_zip->setEncryptionName($path, \ZipArchive::EM_AES_256);
892 }
893 }
894
895 }
896
897 } else {
898
899 if ($needManipulation) {
900 $coreFiles = [];
901 $contentFiles = [];
902 foreach ($files as $file) {
903 if (strpos($file, $_wp_content) !== false) {
904 $contentFiles[] = $file;
905 } else {
906 $coreFiles[] = $file;
907 }
908 }
909
910 $back_1 = $this->_lib->add($coreFiles, PCLZIP_OPT_REMOVE_PATH, $_abspath, PCLZIP_OPT_ADD_PATH, 'wordpress' . DIRECTORY_SEPARATOR, PCLZIP_OPT_ADD_TEMP_FILE_ON, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $this->safelimit);
911 $back_2 = $this->_lib->add($contentFiles, PCLZIP_OPT_REMOVE_PATH, $_wp_content, PCLZIP_OPT_ADD_PATH, 'wordpress' . DIRECTORY_SEPARATOR . 'wp-content' . DIRECTORY_SEPARATOR, PCLZIP_OPT_ADD_TEMP_FILE_ON, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $this->safelimit);
912 $back = $back_1 && $back_2;
913
914 } else {
915 $back = $this->_lib->add($files, PCLZIP_OPT_REMOVE_PATH, $_abspath, PCLZIP_OPT_ADD_PATH, 'wordpress' . DIRECTORY_SEPARATOR, PCLZIP_OPT_ADD_TEMP_FILE_ON, PCLZIP_OPT_TEMP_FILE_THRESHOLD, $this->safelimit);
916 }
917
918 }
919
920 }
921
922 // Check if there was any error
923 touch(BMI_BACKUPS . '/.running');
924 if ($zipArchive) {
925 if (!BMI_CLI_REQUEST || $final) {
926 $result = $this->_zip->close();
927
928 if ($result === true) {
929
930 // Remove batch
931 if ($file_list && file_exists($file_list)) {
932 $this->unlinksafe($file_list);
933 }
934
935 } else {
936 $this->output->log('not_enough_space', 'verbose');
937
938 $this->send_error('Error, there is most likely not enough space for the backup.');
939 return false;
940
941 }
942 } else {
943
944 // Remove batch
945 if ($file_list && file_exists($file_list)) {
946 $this->unlinksafe($file_list);
947 }
948
949 }
950 } else {
951
952 if ($back == 0) {
953
954 $this->send_error($this->_lib->errorInfo(true));
955 return false;
956
957 } else {
958
959 if ($file_list && file_exists($file_list)) {
960 $this->unlinksafe($file_list);
961 }
962
963 }
964
965 }
966
967 } catch (\Exception $e) {
968
969 error_log($e->getMessage());
970 $this->send_error($e->getMessage());
971 return false;
972
973 } catch (\Throwable $e) {
974
975 error_log($e->getMessage());
976 $this->send_error($e->getMessage());
977 return false;
978
979 }
980
981 }
982
983 // ZIP one of the grouped files
984 public function zip_batch() {
985
986 if ($this->final_batch === true) {
987 $this->output->log('Adding final files to this batch...', 'STEP');
988 $this->output->log('Adding manifest as addition...', 'INFO');
989
990 $additionalFiles = $this->get_final_batch();
991 $this->add_files($additionalFiles, false, true);
992 $this->log_final_batch();
993 return true;
994 }
995
996 $_dbfile = $this->dbfile;
997 $_db_dir_v2 = $this->db_dir_v2;
998 if (strpos($_dbfile, 'file://') !== false) $_dbfile = substr($_dbfile, 7);
999 if (strpos($_db_dir_v2, 'file://') !== false) $_db_dir_v2 = substr($_db_dir_v2, 7);
1000
1001 if ($this->it === 1) {
1002
1003 $files = [];
1004 if (file_exists($this->dbfile)) {
1005 $files[] = $_dbfile;
1006 } elseif (file_exists($this->db_dir_v2) && is_dir($this->db_dir_v2)) {
1007 $this->db_v2_engine = true;
1008 $db_files = scandir($this->db_dir_v2);
1009 foreach ($db_files as $i => $name) {
1010 if (!($name == '.' || $name == '..')) {
1011 $files[] = $_db_dir_v2 . DIRECTORY_SEPARATOR . $name;
1012 }
1013 }
1014 }
1015
1016 if (sizeof($files) > 0) {
1017 $this->add_files($files, false, false, true);
1018 $this->output->log('Database added to the backup file.', 'SUCCESS');
1019 $this->output->log('Performing site files backup...', 'STEP');
1020 return true;
1021 }
1022
1023 $this->output->log('Performing site files backup...', 'STEP');
1024
1025 }
1026
1027 $list_file = $this->load_batch();
1028 if ($list_file === false) return true;
1029 $files = explode("\r\n", file_get_contents($list_file));
1030
1031 $total_size = 0;
1032 $parsed_files = [];
1033
1034 $absWo = ABSPATH;
1035 $wpcDirWo = WP_CONTENT_DIR;
1036
1037 if (strpos($absWo, 'file://') !== false) $absWo = substr(ABSPATH, 7);
1038 if (strpos($wpcDirWo, 'file://') !== false) $wpcDirWo = substr(WP_CONTENT_DIR, 7);
1039
1040 for ($i = 0; $i < sizeof($files); ++$i) {
1041 if (strlen(trim($files[$i])) <= 1) {
1042 $this->total_files--;
1043 continue;
1044 }
1045
1046 $files[$i] = explode(',', $files[$i]);
1047 $last = sizeof($files[$i]) - 1;
1048 $size = intval($files[$i][$last]);
1049 unset($files[$i][$last]);
1050 $files[$i] = implode(',', $files[$i]);
1051
1052 $file = null;
1053 if ($files[$i][0] . $files[$i][1] . $files[$i][2] === '@1@') {
1054 $file = $wpcDirWo . DIRECTORY_SEPARATOR . substr($files[$i], 3);
1055 } else if ($files[$i][0] . $files[$i][1] . $files[$i][2] === '@2@') {
1056 $file = $absWo . DIRECTORY_SEPARATOR . substr($files[$i], 3);
1057 } else {
1058 $file = $files[$i];
1059 }
1060
1061 if (!file_exists($file)) {
1062 $this->output->log('Removing this file from backup (it does not exist anymore): ' . $file, 'WARN');
1063 $this->total_files--;
1064 continue;
1065 }
1066
1067 // if (filesize($file) === 0) {
1068 // $this->output->log('Removing this file from backup (file size is equal to 0 bytes): ' . $file, 'WARN');
1069 // $this->total_files--;
1070 // continue;
1071 // }
1072
1073 $parsed_files[] = $file;
1074 $total_size += $size;
1075 unset($file);
1076 }
1077
1078 unset($files);
1079 if (sizeof($parsed_files) === 1) {
1080 $this->output->log('Adding: ' . sizeof($parsed_files) . ' file...' . ' [Size: ' . $this->humanSize($total_size) . ']', 'INFO');
1081 $this->output->log('Alone-file mode for: ' . $parsed_files[0] . ' file...', 'INFO');
1082 } else $this->output->log('Adding: ' . sizeof($parsed_files) . ' files...' . ' [Size: ' . $this->humanSize($total_size) . ']', 'INFO');
1083
1084 if ((60 * (1024 * 1024)) < $total_size) $this->output->log('Current batch is quite large, it may take some time...', 'WARN');
1085
1086 $this->add_files($parsed_files, $list_file);
1087 $this->filessofar += sizeof($parsed_files);
1088
1089 $currentBackupSize = filesize(BMI_BACKUPS . DIRECTORY_SEPARATOR . $this->backupname);
1090 $this->output->progress($this->filessofar . '/' . $this->total_files);
1091 $this->output->log('Milestone: ' . $this->filessofar . '/' . $this->total_files . ' [' . $this->batches_left . ' batches left]', 'SUCCESS');
1092 $this->output->log('Backup file size: ' . $this->humanSize($currentBackupSize), 'VERBOSE');
1093 $this->output->log('Time since last batch: ' . (time() - $this->startOfBatch) . ' seconds.', 'VERBOSE');
1094 $this->startOfBatch = time();
1095
1096 if ($this->backupSize > $currentBackupSize)
1097 return $this->send_error('Backup size is smaller than it should be, it has been damaged, it may not be restorable, abort recommended.', true);
1098
1099 $this->backupSize = $currentBackupSize;
1100
1101 }
1102
1103 // Shutdown callback
1104 public function shutdown() {
1105 if (file_exists($this->identyfile)) {
1106
1107 // Set header for browser
1108 if ($this->browserSide) {
1109
1110 $this->saveRemoteSettings();
1111
1112 // Content finished
1113 $this->sendResponse(false);
1114
1115 } else {
1116 if (apply_filters('bmip_should_schedule_next_beat', false)) {
1117 $this->res = [ 'status' => 'success' ];
1118 $this->saveRemoteSettings();
1119 } else {
1120 $this->send_beat();
1121 }
1122
1123 }
1124
1125 } else {
1126 if (!$this->statusSent && !isset($GLOBALS['BMI::RESPONSE::SENT'])) {
1127 $this->statusSent = true;
1128 return BMP::res([ 'status' => 'success' ]);
1129 }
1130 }
1131
1132 }
1133
1134 public function sendResponse($finish = false, $error = false) {
1135 $res = [
1136 'status' => 'success',
1137 'backup_completed' => (($finish == true) ? 'true' : 'false'),
1138 'backup_process_error' => (($error == true) ? 'true' : 'false')
1139 ];
1140
1141 if ($error == true)
1142 $res['status'] = 'error';
1143
1144 $this->res = $res;
1145
1146 if (!$this->statusSent && !isset($GLOBALS['BMI::RESPONSE::SENT'])) {
1147 $this->statusSent = true;
1148 BMP::res($res);
1149 }
1150
1151 return $res;
1152 }
1153
1154 // Handle received batch
1155 public function handle_batch() {
1156
1157 // Check if aborted
1158 if (file_exists(BMI_BACKUPS . '/.abort')) {
1159 if (!isset($this->output)) $this->load_logger();
1160 $this->res = [ 'status' => 'error' ];
1161 $this->send_error('Backup aborted manually by user.', true, '#003');
1162 return;
1163 }
1164
1165 // Check if it was triggered by verified user
1166 if (!file_exists($this->identyfile)) {
1167 $this->res = [ 'status' => 'error' ];
1168 return;
1169 }
1170
1171 // Register shutdown
1172 register_shutdown_function([$this, 'shutdown']);
1173
1174 // Load logger
1175 $this->load_logger();
1176
1177 set_error_handler(function ($errno, $errstr, $errfile, $errline) {
1178 Logger::error('Bypasser error:');
1179 Logger::error($errno . ' - ' . $errstr);
1180 Logger::error($errfile . ' - ' . $errline);
1181
1182 $this->load_logger();
1183 $this->output->log('Bypasser error:', 'ERROR');
1184 $this->output->log($errno . ' - ' . $errstr, 'ERROR');
1185 $this->output->log($errfile . ' - ' . $errline, 'ERROR');
1186
1187 $this->res = [ 'status' => 'error' ];
1188 return;
1189 }, E_ERROR);
1190
1191 // Notice parent script
1192 touch($this->identyfile . '-running');
1193 touch(BMI_BACKUPS . '/.running');
1194
1195 // CLI case
1196 if (BMI_CLI_REQUEST) {
1197
1198 $this->output->log('Starting database backup exporter', 'STEP');
1199 $this->output->log('Database exporter started via CLI', 'VERBOSE');
1200 while ($this->dbit !== -1) {
1201 $this->databaseBackupMaker();
1202 }
1203
1204 // Log
1205 $this->output->log("PHP CLI initialized - process ran successfully", 'SUCCESS');
1206 $this->make_file_groups();
1207
1208 // Make ZIP
1209 $this->output->log('Making archive...', 'STEP');
1210 while (!$this->final_made) {
1211 touch($this->identyfile . '-running');
1212 touch(BMI_BACKUPS . '/.running');
1213 $this->it += 1;
1214 if ($this->isStreamed) {
1215 do_action('bmip_streaming_backup_process_batch', $this);
1216 } else {
1217 $this->zip_batch();
1218 }
1219 }
1220
1221 } else {
1222
1223 // Background
1224 if ($this->dbit !== -1) {
1225
1226 if ($this->dbit === 0) {
1227 $this->output->log('Background process initialized', 'SUCCESS');
1228 $this->output->log('Starting database backup exporter', 'STEP');
1229 $this->output->log('Database exporter started via WEB REQUESTS', 'VERBOSE');
1230 }
1231
1232 $this->databaseBackupMaker();
1233 return $this->res;
1234
1235 } else {
1236
1237 if ($this->it === 0) {
1238
1239 $this->make_file_groups();
1240 $this->it += 1;
1241 $this->output->log('Making archive...', 'STEP');
1242
1243 } else {
1244 if ($this->isStreamed) {
1245 do_action('bmip_streaming_backup_process_batch', $this);
1246 } else {
1247 $this->zip_batch();
1248 }
1249 $this->it += 1;
1250
1251 }
1252
1253 }
1254
1255 }
1256
1257 }
1258
1259 public function fixSlashes($str, $slash = false) {
1260 // Old version
1261 // $str = str_replace('\\\\', DIRECTORY_SEPARATOR, $str);
1262 // $str = str_replace('\\', DIRECTORY_SEPARATOR, $str);
1263 // $str = str_replace('\/', DIRECTORY_SEPARATOR, $str);
1264 // $str = str_replace('/', DIRECTORY_SEPARATOR, $str);
1265
1266 // if ($str[strlen($str) - 1] == DIRECTORY_SEPARATOR) {
1267 // $str = substr($str, 0, -1);
1268 // }
1269
1270 // Since 1.3.2
1271 $protocol = '';
1272 if ($slash == false) $slash = DIRECTORY_SEPARATOR;
1273 if (substr($str, 0, 7) == 'http://') $protocol = 'http://';
1274 else if (substr($str, 0, 8) == 'https://') $protocol = 'https://';
1275
1276 $str = substr($str, strlen($protocol));
1277 $str = preg_replace('/[\\\\\/]+/', $slash, $str);
1278 $str = rtrim($str, '/\\' );
1279
1280 return $protocol . $str;
1281 }
1282
1283 public function isFunctionEnabled($func) {
1284 $disabled = explode(',', ini_get('disable_functions'));
1285 $isDisabled = in_array($func, $disabled);
1286 if (!$isDisabled && function_exists($func)) return true;
1287 else return false;
1288 }
1289
1290 // Database batch maker and dumper
1291 // We need WP instance for that to get access to wpdb
1292 public function databaseBackupMaker() {
1293
1294 if ($this->dbit === -1) {
1295 $this->res = [ 'status' => 'error' ];
1296 return;
1297 }
1298
1299 // DB File Name for that type of backup
1300 $dbbackupname = 'bmi_database_backup.sql';
1301 $database_file = $this->fixSlashes(BMI_TMP . DIRECTORY_SEPARATOR . $dbbackupname);
1302 $shouldBackupDB = apply_filters('bmip_database_backup', Dashboard\bmi_get_config('BACKUP:DATABASE') == 'true');
1303 if ( $shouldBackupDB ) {
1304
1305 if (Dashboard\bmi_get_config('OTHER:BACKUP:DB:SINGLE:FILE') == 'true') {
1306
1307 // Require Database Manager
1308 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'manager.php';
1309
1310 // Log what's going on
1311 $this->output->log('Making single-file database backup (using deprecated engine, due to used settings)', 'STEP');
1312
1313 // Get database dump
1314 $databaser = new Database(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
1315 $databaser->exportDatabase($dbbackupname);
1316 $this->output->log("Database size: " . $this->humanSize(filesize($database_file)), 'INFO');
1317 $this->output->log('Database (single-file) backup finished.', 'SUCCESS');
1318
1319 $this->dbitJustFinished = true;
1320 $this->dbit = -1;
1321
1322 $this->res = [ 'status' => 'success' ];
1323 return true;
1324
1325 } else {
1326
1327 // Log what's going on
1328 if ($this->dbit === 0) {
1329 $this->output->log("Making database backup (using v3 engine, requires at least v1.2.2 to restore)", 'STEP');
1330 $this->output->log("Iterating database...", 'INFO');
1331 }
1332
1333
1334 $database_file_dir = $this->fixSlashes((dirname($database_file))) . DIRECTORY_SEPARATOR;
1335 $better_database_files_dir = $database_file_dir . 'db_tables';
1336 $better_database_files_dir = str_replace('file:', 'file://', $better_database_files_dir);
1337
1338 if (!is_dir($better_database_files_dir)) @mkdir($better_database_files_dir, 0755, true);
1339 $dbEngine = 3;
1340 if (Dashboard\bmi_get_config('OTHER::NEW_DATABASE_EXPORT_ENGINE')) {
1341 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'export' . DIRECTORY_SEPARATOR . 'class-database-export-processor.php';
1342 $dbEngine = 4;
1343 $db_exporter = new DatabaseExportProcessor($better_database_files_dir, $this->output, intval($this->backupstart));
1344 } else {
1345 // Require Database Manager
1346 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'better-backup-v3.php';
1347 $db_exporter = new BetterDatabaseExport($better_database_files_dir, $this->output, $this->dbit, intval($this->backupstart));
1348 }
1349
1350 $dbBatchingEnabled = false;
1351 if (Dashboard\bmi_get_config('OTHER:BACKUP:DB:BATCHING') == 'true') {
1352 $dbBatchingEnabled = true;
1353 } else {
1354 if ($this->dbit === 0) {
1355 $this->output->log("Database batching is disabled in options, consider to use this option if your database backup fails.", 'WARN');
1356 }
1357 }
1358
1359 if (BMI_CLI_REQUEST === true || $dbBatchingEnabled === false) {
1360
1361 if ($dbEngine === 4) {
1362 $results = $db_exporter->exportAll();
1363 } else {
1364 $results = $db_exporter->export();
1365 }
1366
1367 $this->output->log("Database backup finished", 'SUCCESS');
1368 $this->dbitJustFinished = true;
1369 $this->dbit = -1;
1370 $this->dblast = 0;
1371
1372 } else {
1373
1374 if ($dbEngine === 4) {
1375 $results = $db_exporter->exportBatch();
1376 // Backward compatibility for v3 engine
1377 $this->dbit += 1;
1378 $results['batchingStep'] = $this->dbit;
1379 $results['finishedQuery'] = $this->dblast;
1380 $results['dumpCompleted'] = $results['status'] === DatabaseExportProcessor::STATUS_COMPLETED;
1381 } else {
1382 $results = $db_exporter->export($this->dbit, $this->dblast);
1383 }
1384
1385 $this->dbit = intval($results['batchingStep']);
1386 $this->dblast = intval($results['finishedQuery']);
1387 $dbFinished = $results['dumpCompleted'];
1388
1389 if ($dbFinished == true) {
1390 $this->output->log("Database backup finished", 'SUCCESS');
1391 $this->dbitJustFinished = true;
1392 $this->dbit = -1;
1393 }
1394
1395 }
1396
1397 $this->res = [ 'status' => 'success' ];
1398 return true;
1399
1400 }
1401
1402 } else {
1403
1404 $this->output->log('Database will not be dumped due to user settings.', 'INFO');
1405 $this->dbitJustFinished = true;
1406 $this->dbit = -1;
1407
1408 $this->res = [ 'status' => 'success' ];
1409 return true;
1410
1411 }
1412
1413 }
1414
1415 public function actionsAfterProcess($success = false) {
1416 if ($success == true) {
1417 Logger::log("Backup file created successfully via backup-process.php");
1418 set_transient('bmi_latest_backup_file', base64_encode($this->backupname), 30);
1419 BMP::handle_after_cron();
1420
1421 if (!wp_next_scheduled('bmip_keepalive_cron')) {
1422 wp_schedule_single_event(time() + 15, 'bmip_keepalive_cron');
1423 }
1424 } else {
1425 Logger::log("Backup file creation failed via backup-process.php");
1426 }
1427
1428 if (has_action('bmi_premium_after_process') || (defined('BACKUP_TRIGGERED_BY_URL') && BACKUP_TRIGGERED_BY_URL === true)){
1429 do_action('bmi_premium_after_process', $success, 'backup', defined('BACKUP_TRIGGERED_BY_URL') && BACKUP_TRIGGERED_BY_URL === true);
1430 }
1431
1432 return null;
1433
1434 }
1435
1436
1437 }
1438