PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 1.4.9
Backup Migration v1.4.9
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 / ajax_offline.php
backup-backup / includes Last commit date
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