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