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
ajax_offline.php
409 lines
| 1 | <?php |
| 2 | |
| 3 | // Namespace |
| 4 | namespace BMI\Plugin; |
| 5 | |
| 6 | // Uses |
| 7 | use BMI\Plugin\Backup_Migration_Plugin as BMP; |
| 8 | use BMI\Plugin\BMI_Logger as Logger; |
| 9 | use BMI\Plugin\BMI_Pro_Core; |
| 10 | use BMI\Plugin\BMProAjax as BMProAjax; |
| 11 | use BMI\Plugin\Scanner\BMI_BackupsScanner as Backups; |
| 12 | use BMI\Plugin\External\BMI_External_BackupBliss as BackupBliss; |
| 13 | use BMI\Plugin\Dashboard as Dashboard; |
| 14 | |
| 15 | // Exit on direct access |
| 16 | if (!defined('ABSPATH')) exit; |
| 17 | |
| 18 | /** |
| 19 | * Ajax Offline (unauthorized) Handler for BMI |
| 20 | */ |
| 21 | class BMI_Ajax_Offline |
| 22 | { |
| 23 | |
| 24 | public $post; |
| 25 | public $backupbliss; |
| 26 | public $proajax; |
| 27 | |
| 28 | public function __construct($post) |
| 29 | { |
| 30 | |
| 31 | // $POST is sanitized by BMI Basic Version |
| 32 | // Do not call this class anywhere else [!] |
| 33 | $this->post = $post; |
| 34 | |
| 35 | // Active offline ajax premium side |
| 36 | if (defined('BMI_PRO_INC')) { |
| 37 | if (BMI_DEBUG) |
| 38 | Logger::error("PREMIUM CHECK"); |
| 39 | |
| 40 | require_once BMI_PRO_INC . 'ajax_offline.php'; |
| 41 | $this->proajax = new BMI_Ajax_Offline_Premium($post); |
| 42 | } |
| 43 | |
| 44 | require_once BMI_INCLUDES . '/external/backupbliss.php'; |
| 45 | $this->backupbliss = new BackupBliss(); |
| 46 | |
| 47 | if (is_user_logged_in() && current_user_can('manage_options')) { |
| 48 | if ($this->post['f'] == 'check-not-uploaded-backups') { |
| 49 | |
| 50 | $this->checkForBackupsToUpload(); |
| 51 | |
| 52 | if ($this->proajax) |
| 53 | $this->proajax->checkForBackupsToUpload(); |
| 54 | |
| 55 | BMP::res(['status' => 'success']); |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | if ($this->post['f'] == 'refresh') { |
| 60 | BMP::res($this->keepAliveUnAuthorizedRefresh()); |
| 61 | } |
| 62 | |
| 63 | } |
| 64 | |
| 65 | public function getBackupBlissConnectionStatus() |
| 66 | { |
| 67 | |
| 68 | $res = $this->backupbliss->getSecret(); |
| 69 | return $res !== false; |
| 70 | |
| 71 | } |
| 72 | |
| 73 | public function checkForBackupsToUpload() { |
| 74 | $toBeUploaded = $this->fetchToBeUploaded(); |
| 75 | |
| 76 | $task = $toBeUploaded['current_upload']; |
| 77 | $queue = $toBeUploaded['queue']; |
| 78 | |
| 79 | //If there's no task or queue present, then check for backups to upload |
| 80 | if (sizeof($task) == 0 && sizeof($queue) == 0) { |
| 81 | $this->backupbliss->checkForBackupsToUpload(); |
| 82 | |
| 83 | //Check for backups premium |
| 84 | if ($this->proajax) |
| 85 | $this->proajax->checkForBackupsToUpload(); |
| 86 | } |
| 87 | |
| 88 | //Remove failed tasks if the local backup is deleted |
| 89 | if (isset($toBeUploaded['failed'])) { |
| 90 | // Local Backups |
| 91 | require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'scanner' . DIRECTORY_SEPARATOR . 'backups.php'; |
| 92 | $backups = new Backups(); |
| 93 | $backupsAvailable = $backups->getAvailableBackups("local"); |
| 94 | $localBackups = $backupsAvailable['local']; |
| 95 | $localBackups = array_reverse($localBackups); |
| 96 | |
| 97 | $failed = $toBeUploaded['failed']; |
| 98 | foreach($failed as $failed_task => $failed_count) { |
| 99 | $data = explode("_", $failed_task); |
| 100 | |
| 101 | |
| 102 | if (count($data) == 2) { |
| 103 | $cloudtype = $data[0]; |
| 104 | $md5 = $data[1]; |
| 105 | |
| 106 | $md5s = array_map(function($backup) { return $backup[7]; }, $localBackups); |
| 107 | |
| 108 | if (!in_array($md5, $md5s)) { |
| 109 | unset($toBeUploaded["failed"][$failed_task]); |
| 110 | update_option('bmip_to_be_uploaded', $toBeUploaded); |
| 111 | } |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | /** |
| 118 | * keepAliveUnAuthorizedRefresh - Unauthorized Keep Alive Request |
| 119 | * DO NOT RESPONSE WITH ANY SENSITIVE DATA, ONLY SUCCESS OR FAIL |
| 120 | * THIS CAN BE ACCESSED BY ANYONE WITHOUT ANY AUTH |
| 121 | * |
| 122 | * @return string[] success/fail |
| 123 | */ |
| 124 | public function keepAliveUnAuthorizedRefresh() |
| 125 | { |
| 126 | //Atomic locking to prevent race conditions |
| 127 | $lock_file = BMI_CONFIG_DIR . DIRECTORY_SEPARATOR . '.keep_alive.lock'; |
| 128 | |
| 129 | // Open the lock file |
| 130 | $fp = fopen($lock_file, 'c'); |
| 131 | |
| 132 | // Try to acquire an exclusive lock |
| 133 | if (flock($fp, LOCK_EX | LOCK_NB)) { |
| 134 | if (BMI_DEBUG) |
| 135 | Logger::error("Lock acquired."); |
| 136 | |
| 137 | $ret = $this->keepAliveUnAuthorizedRefreshExec(); |
| 138 | |
| 139 | // Release the lock |
| 140 | flock($fp, LOCK_UN); |
| 141 | if (BMI_DEBUG) |
| 142 | Logger::error("Lock released."); |
| 143 | return $ret; |
| 144 | } else { |
| 145 | return ['status' => 'success']; // Lock is already held |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | private function removeCurrentTask($toBeUploaded) { |
| 150 | $toBeUploaded["current_upload"] = []; //Removes the current ttask |
| 151 | update_option("bmip_to_be_uploaded", $toBeUploaded); |
| 152 | |
| 153 | return ['status' => 'no_tasks']; |
| 154 | } |
| 155 | |
| 156 | private function fetchToBeUploaded() { |
| 157 | //Get the option without any caching when used with get_option which prevvents stale data from being retreived. |
| 158 | //This is implemented after observing and debugging the issue that sometimes the same batch is uploaded again causing issues. |
| 159 | global $wpdb; |
| 160 | $bmip_to_be_uploaded = $wpdb->get_var( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s", 'bmip_to_be_uploaded' ) ); |
| 161 | if ($bmip_to_be_uploaded !== null) { |
| 162 | $toBeUploaded = maybe_unserialize($bmip_to_be_uploaded); |
| 163 | if (!isset($toBeUploaded['current_upload'])) |
| 164 | $toBeUploaded['current_upload'] = []; |
| 165 | } else { |
| 166 | $toBeUploaded = [ |
| 167 | 'current_upload' => [], |
| 168 | 'queue' => [], |
| 169 | 'failed' => [] |
| 170 | ]; |
| 171 | } |
| 172 | |
| 173 | return $toBeUploaded; |
| 174 | } |
| 175 | |
| 176 | private function checkIfBackupCanBeUploaded($type, $taskname) { |
| 177 | |
| 178 | $backupPath = BMI_BACKUPS . DIRECTORY_SEPARATOR . $taskname; |
| 179 | $backupSize = file_exists($backupPath) ? filesize($backupPath) : -1; |
| 180 | |
| 181 | switch($type) { |
| 182 | |
| 183 | case "backupbliss": { |
| 184 | $storageInfo = $this->backupbliss->getStorageInfo(); |
| 185 | |
| 186 | if ($storageInfo["used_space_percent"] > 80 && $storageInfo["used_space_percent"] <= 100) { |
| 187 | $error_message_notice = 'It seems you already used more than 80% of your space. <a href="'.BMI_AUTHOR_URI . 'pricing'.'">Get more storage now.</a>'; |
| 188 | |
| 189 | $this->backupbliss->showNotice("storage_warn", $error_message_notice, 60 * 60); |
| 190 | } elseif($storageInfo["used_space_percent"] > 100) { |
| 191 | $error_message_notice = 'You’re using more space than allowed. No new backups will be moved to your storage and some of the <b>existing backups will be deleted very soon</b>. '; |
| 192 | |
| 193 | $this->backupbliss->showNotice("upload_issue_space", $error_message_notice, 60 * 60); |
| 194 | } else { |
| 195 | $this->backupbliss->removeNotice("storage_warn"); |
| 196 | $this->backupbliss->removeNotice("upload_issue_space"); |
| 197 | } |
| 198 | |
| 199 | if (!$this->getBackupBlissConnectionStatus()) { |
| 200 | return false; |
| 201 | } |
| 202 | |
| 203 | |
| 204 | |
| 205 | if (!$this->backupbliss->getNotice("upload_issue_space")) { |
| 206 | |
| 207 | if (isset($storageInfo["remaining_space"])) |
| 208 | { |
| 209 | $remaining = $storageInfo["remaining_space"]; |
| 210 | |
| 211 | |
| 212 | if ($backupSize != -1) |
| 213 | { |
| 214 | if ($remaining < $backupSize) |
| 215 | { |
| 216 | $error_message_notice = 'Moving backups to your storage is failing or will fail because you don’t have enough space.'; |
| 217 | |
| 218 | add_option("bmip_backupbliss_required_space", $backupPath); |
| 219 | $this->backupbliss->showNotice("upload_issue_space", $error_message_notice, 60 * 60); |
| 220 | //Triggering the server, so that an alert also get sent |
| 221 | $this->backupbliss->initiateUploadSession($backupPath); |
| 222 | return false; |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | } |
| 227 | else { |
| 228 | Logger::error("[BMI] Couldn't fetch quota from BackupBliss!"); |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | if ($this->backupbliss->getNotice("upload_issue")) { |
| 233 | return false; |
| 234 | } |
| 235 | |
| 236 | break; |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | if ($this->proajax) |
| 241 | return $this->proajax->checkIfBackupCanBeUploaded($type, $backupSize); |
| 242 | |
| 243 | return true; |
| 244 | } |
| 245 | |
| 246 | private function _removeTasksFromDeactivatedClouds($cltype, $toBeUploaded, $task, $queue, $failed) { |
| 247 | if (sizeof($task) > 0 && isset($task['task'])) { |
| 248 | $taskname = $task['task']; |
| 249 | $type = explode('_', $taskname)[0]; |
| 250 | if ($type == $cltype) |
| 251 | $task = []; |
| 252 | } |
| 253 | |
| 254 | if (sizeof($queue) > 0) { |
| 255 | $tasks = array_keys($queue); |
| 256 | foreach($tasks as $taskname) { |
| 257 | $type = explode('_', $taskname)[0]; |
| 258 | if ($type == $cltype) |
| 259 | unset($queue[$taskname]); |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | if (sizeof($failed) > 0) { |
| 264 | $tasks = array_keys($failed); |
| 265 | foreach($tasks as $taskname) { |
| 266 | $type = explode('_', $taskname)[0]; |
| 267 | if ($type == $cltype) |
| 268 | unset($failed[$taskname]); |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | $toBeUploaded['current_upload'] = $task; |
| 273 | $toBeUploaded['queue'] = $queue; |
| 274 | $toBeUploaded['failed'] = $failed; |
| 275 | update_option("bmip_to_be_uploaded", $toBeUploaded); |
| 276 | } |
| 277 | |
| 278 | public function getDeactivatedClouds() { |
| 279 | $deactivatedClouds = []; |
| 280 | if (!$this->getBackupBlissConnectionStatus()) $deactivatedClouds[] = "backupbliss"; |
| 281 | |
| 282 | if ($this->proajax) |
| 283 | $deactivatedClouds = array_merge($this->proajax->getDeactivatedClouds(), $deactivatedClouds); |
| 284 | |
| 285 | return $deactivatedClouds; |
| 286 | } |
| 287 | |
| 288 | public function keepAliveUnAuthorizedRefreshExec() { |
| 289 | |
| 290 | $isOnGoing = get_transient('bmip_upload_ongoing'); |
| 291 | if ($isOnGoing === '1') return ['status' => 'success']; //Returning success so that the auto pinger will keep on pinging |
| 292 | |
| 293 | |
| 294 | $toBeUploaded = $this->fetchToBeUploaded(); |
| 295 | |
| 296 | |
| 297 | |
| 298 | $task = $toBeUploaded['current_upload']; |
| 299 | $queue = $toBeUploaded['queue']; |
| 300 | $failed = isset($toBeUploaded['failed']) ? $toBeUploaded['failed'] : []; |
| 301 | |
| 302 | foreach ($this->getDeactivatedClouds() as $cloudType) |
| 303 | $this->_removeTasksFromDeactivatedClouds($cloudType, $toBeUploaded, $task, $queue, $failed); |
| 304 | |
| 305 | |
| 306 | //Check for uploads |
| 307 | if (get_transient('bmip_check_for_backups_to_upload') !== "wait") { |
| 308 | set_transient("bmip_check_for_backups_to_upload", "wait", 10); |
| 309 | $this->checkForBackupsToUpload(); |
| 310 | //Refresh variables after checking for backups to upload |
| 311 | $toBeUploaded = $this->fetchToBeUploaded(); |
| 312 | $task = $toBeUploaded['current_upload']; |
| 313 | $queue = $toBeUploaded['queue']; |
| 314 | } |
| 315 | |
| 316 | $shouldBeQueued = false; |
| 317 | |
| 318 | if (sizeof($task) > 0 && isset($task['task'])) { |
| 319 | $taskname = $task['task']; |
| 320 | $type = explode('_', $taskname)[0]; |
| 321 | |
| 322 | |
| 323 | if (!$this->checkIfBackupCanBeUploaded($type, $task['name'])) { |
| 324 | $this->removeCurrentTask($toBeUploaded); |
| 325 | $type = null; //Set type as null so that no actions will be taken |
| 326 | $shouldBeQueued = true; //Set it to queue the next task |
| 327 | } |
| 328 | |
| 329 | // BackupBliss |
| 330 | if ($type == 'backupbliss') { |
| 331 | |
| 332 | if (!isset($task['uploadSession'])) { |
| 333 | |
| 334 | $backupPath = BMI_BACKUPS . DIRECTORY_SEPARATOR . $task['name']; |
| 335 | $manifestPath = BMI_BACKUPS . DIRECTORY_SEPARATOR . $task['json']; |
| 336 | $uploadSession = $this->backupbliss->initiateUploadSession($backupPath); |
| 337 | if (!$uploadSession) |
| 338 | { |
| 339 | $this->removeCurrentTask($toBeUploaded); |
| 340 | return ['status' => 'success']; |
| 341 | } |
| 342 | |
| 343 | $availableMemory = BMP::getAvailableMemoryInBytes(); |
| 344 | $bytesPerRequest = intval($availableMemory / 4); |
| 345 | |
| 346 | $toBeUploaded['current_upload']['bytesPerRequest'] = $bytesPerRequest; |
| 347 | $toBeUploaded['current_upload']['uploadSession'] = $uploadSession; |
| 348 | $toBeUploaded['current_upload']['manifestPath'] = $manifestPath; |
| 349 | $toBeUploaded['current_upload']['backupPath'] = $backupPath; |
| 350 | $toBeUploaded['current_upload']['batch'] = 1; |
| 351 | |
| 352 | update_option('bmip_to_be_uploaded', $toBeUploaded); |
| 353 | |
| 354 | if (!file_exists($backupPath)) delete_option('bmip_to_be_uploaded'); |
| 355 | return ['status' => 'success']; |
| 356 | } else { |
| 357 | |
| 358 | if (!file_exists($task['backupPath'])) { |
| 359 | delete_option('bmip_to_be_uploaded'); |
| 360 | return ['status' => 'success']; |
| 361 | } |
| 362 | |
| 363 | $this->backupbliss->uploadFile($task['uploadSession'], $task['backupPath'], $task['manifestPath'], $task['md5'], $task['batch'], $task['bytesPerRequest']); |
| 364 | return ['status' => 'success']; |
| 365 | } |
| 366 | } elseif ($this->proajax) { |
| 367 | $ret = $this->proajax->processClouds($type, $task, $toBeUploaded, $taskname); |
| 368 | if ($ret["status"] !== "no_tasks") |
| 369 | return $ret; |
| 370 | } |
| 371 | |
| 372 | } else { |
| 373 | $shouldBeQueued = true; |
| 374 | } |
| 375 | |
| 376 | if ($shouldBeQueued && sizeof($queue) > 0) { |
| 377 | |
| 378 | $tasks = array_keys($queue); |
| 379 | if (sizeof($tasks) > 0) { |
| 380 | |
| 381 | $selectedTask = $tasks[0]; |
| 382 | $cloudType = explode("_", $selectedTask)[0]; |
| 383 | $toBeProcessed = $queue[$selectedTask]; |
| 384 | |
| 385 | if ($this->checkIfBackupCanBeUploaded($cloudType, $toBeProcessed['name'])) { |
| 386 | $toBeUploaded['current_upload'] = [ |
| 387 | 'task' => $selectedTask, |
| 388 | 'name' => $toBeProcessed['name'], |
| 389 | 'md5' => $toBeProcessed['md5'], |
| 390 | 'json' => $toBeProcessed['json'], |
| 391 | 'progress' => '0%' |
| 392 | ]; |
| 393 | } else { |
| 394 | if (isset($toBeUploaded['failed'])) |
| 395 | $toBeUploaded['failed'][$selectedTask] = 1; //Mark the task as failed |
| 396 | } |
| 397 | |
| 398 | unset($toBeUploaded['queue'][$selectedTask]); |
| 399 | update_option('bmip_to_be_uploaded', $toBeUploaded); |
| 400 | |
| 401 | //Return success if there are more tasks in the queue, so auto pinger can ping rapidly |
| 402 | return ['status' => sizeof($queue) > 0 ? 'success' : 'no_tasks']; |
| 403 | } else return ['status' => 'no_tasks']; |
| 404 | } else return ['status' => 'no_tasks']; |
| 405 | |
| 406 | return ['status' => 'no_tasks']; |
| 407 | } |
| 408 | } |
| 409 |