PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 2.1.2
Backup Migration v2.1.2
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 / external / backupbliss.php
backup-backup / includes / external Last commit date
backupbliss.php 3 months ago controller.php 3 months ago dropbox.php 3 months ago ftp.php 3 months ago google-drive.php 3 months ago s3.php 3 months ago
backupbliss.php
897 lines
1 <?php
2
3 // Namespace
4 namespace BMI\Plugin\External;
5
6 // Use
7 use BMI\Plugin\BMI_Logger as Logger;
8 use BMI\Plugin\Scanner\BMI_BackupsScanner as Backups;
9 use BMI\Plugin\Dashboard as Dashboard;
10
11 // Exit on direct access
12 if (!defined('ABSPATH')) {
13 exit;
14 }
15
16 class BMI_External_BackupBliss
17 {
18
19 public function __construct()
20 {
21
22 add_action('bmi_premium_remove_backup_file', [&$this, 'deleteBackup']);
23 add_action('bmi_premium_remove_backup_json_file', [&$this, 'deleteBackupManifest']);
24 }
25
26 public function process($action, $post) {
27
28 $uri = home_url();
29 if (substr($uri, 0, 4) != 'http') {
30 if (is_ssl()) $uri = 'https://' . home_url();
31 else $uri = 'http://' . home_url();
32 }
33
34 if ($action == "connect") {
35 $ret = $this->getSecret($post['api_key']);
36 if($ret[0] !== false) {
37 update_option("bmi_pro_backupbliss_key", $ret[1]);
38 return ["status"=>'success'];
39 } else {
40 if ($ret[1] != "")
41 return ["status"=>'fail', "message"=>$ret[1]];
42 }
43
44 return ["status"=>'fail', "message"=>"Invalid API Key provided!"];
45 }
46
47 if ($action == "disconnect") {
48 $res = $this->_makeApiCall("plugin/disconnect", "POST", ["site_url"=>$uri]);
49 if ($res["status"])
50 if ($res["response_data"]["status"]) {
51 delete_option("bmi_pro_backupbliss_key");
52 return ["status"=>"success"];
53 }
54
55 return ["status"=>"fail", "message"=> "Error disconnecting from the backupbliss server."];
56 }
57
58 if ($action == "storage-info") {
59 $res = $this->_makeApiCall("file/storage-info");
60 if ($res["status"])
61 if ($res["response_data"]["status"]) {
62 return ["status"=>"success", "data"=>$res["response_data"]];
63 }
64
65 return ["status"=>"fail", "message"=>"Error fetching storage info from the backupbliss server."];
66 }
67 }
68
69 /**
70 * checkForBackupsToUpload - Will check for backups that requires to be in sync with cloud
71 *
72 * @return string[]
73 */
74 public function checkForBackupsToUpload()
75 {
76 // Upload Object
77 $requiresUpload = get_option('bmip_to_be_uploaded', [
78 'current_upload' => [],
79 'queue' => [],
80 'failed' => []
81 ]);
82
83 // Local Backups
84 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'scanner' . DIRECTORY_SEPARATOR . 'backups.php';
85 $backups = new Backups();
86 $backupsAvailable = $backups->getAvailableBackups("local");
87 $localBackups = $backupsAvailable['local'];
88 $localBackups = array_reverse($localBackups);
89 $uploadedBackupStatus = get_option('bmi_uploaded_backups_status', []);
90
91 $files = $this->parseFiles($this->getAllFiles());
92 // if (BMI_DEBUG) {
93 // Logger::error( print_r($files, true));
94 // }
95
96
97 if (!$files) return;
98
99 foreach ($localBackups as $name => $details) {
100
101 $md5 = $details[7];
102 if (isset($uploadedBackupStatus[$md5]) && isset($uploadedBackupStatus[$md5]['backupbliss'])) {
103 continue;
104 }
105
106 if (!(isset($files['manifests'][$md5 . '.json']) && isset($files['backups'][$name]))) {
107
108 $task = 'backupbliss_' . $md5;
109
110 // File is not uploaded action required
111 if (!isset($requiresUpload['queue'][$task])) {
112 $isAnyTaskATM = isset($requiresUpload['current_upload']['task']);
113
114
115 // if (isset($requiresUpload['failed'][$task])) unset($requiresUpload['failed'][$task]);
116
117 if (($isAnyTaskATM && $requiresUpload['current_upload']['task'] != $task) || !$isAnyTaskATM) {
118 $requiresUpload['queue'][$task] = [
119 'name' => $name,
120 'md5' => $md5,
121 'json' => $md5 . '.json'
122 ];
123 }
124 }
125 }
126 }
127
128 update_option('bmip_to_be_uploaded', $requiresUpload);
129 return ['status' => 'success'];
130 }
131
132 public function parseFiles($files)
133 {
134
135 $parsedFiles = ['backups' => [], 'manifests' => []];
136
137 if ($files === false) return false;
138
139 foreach ($files as $index => $file) {
140 if (isset($file['folder'])) {
141 continue; //Skip directories
142 }
143
144 $extension = pathinfo($file['name'], PATHINFO_EXTENSION);
145
146 if (BMI_DEBUG) {
147 // Logger::error("parseFiles - " . $file['name'] . " - " . print_r($file['file'], true));
148 // Logger::error("parseFiles - ext - $extension");
149 }
150
151 if (in_array($extension, ["zip", "tar", "gz"])) {
152 $type = 'backups';
153 }
154
155 if ($extension === 'json') {
156 $type = 'manifests';
157 }
158
159 if (isset($type)) {
160 $parsedFiles[$type][$file['name']] = [
161 'size' => $file['size']
162 ];
163 unset($type);
164 }
165 }
166
167
168 if (BMI_DEBUG) {
169 // Logger::error("parseFiles - " . print_r($parsedFiles, true));
170 }
171
172 return $parsedFiles;
173 }
174
175 public function getSecret($api_key = false)
176 {
177 $tempKeyBackupBlissFiles = BMI_TMP . DIRECTORY_SEPARATOR . 'backupblissKeys.php';
178 if (file_exists($tempKeyBackupBlissFiles)) {
179 $backupblissKeys = file_get_contents($tempKeyBackupBlissFiles);
180 if (strpos($backupblissKeys, "\n") !== false) {
181 $lines = explode("\n", $backupblissKeys);
182 if (sizeof($lines) == 3) {
183 $backupbliss_key = substr($lines[1], 2);
184 if (function_exists('wp_load_alloptions')) {
185 wp_load_alloptions(true);
186 }
187 delete_option("bmi_pro_backupbliss_key");
188 if (function_exists('wp_load_alloptions')) {
189 wp_load_alloptions(true);
190 }
191 update_option("bmi_pro_backupbliss_key", $backupbliss_key);
192 }
193 }
194 if (strpos(site_url(), 'tastewp') !== false) {
195 if (function_exists('wp_load_alloptions')) {
196 wp_load_alloptions(true);
197 }
198
199 update_option('__tastewp_redirection_performed', true);
200 update_option('auto_smart_tastewp_redirect_performed', 1);
201 update_option('tastewp_auto_activated', true);
202 update_option('__tastewp_sub_requested', true);
203 }
204
205 unlink($tempKeyBackupBlissFiles);
206 }
207
208 $uri = home_url();
209 if (substr($uri, 0, 4) != 'http') {
210 if (is_ssl()) $uri = 'https://' . home_url();
211 else $uri = 'http://' . home_url();
212 }
213
214 if (!$api_key)
215 $key = get_option('bmi_pro_backupbliss_key', false);
216 else
217 $key = $api_key;
218
219 if ($key === false) {
220 return [false, ""];
221 } elseif($api_key === false) {
222 return [$key !== false, $key];
223 }
224
225 $url = BMI_BB_STORAGE_API_URI . '/plugin/verify';
226 $response = wp_remote_post($url, array(
227 'method' => 'POST',
228 'timeout' => 15,
229 'redirection' => 2,
230 'httpversion' => '1.0',
231 'blocking' => true,
232 'headers' => ["Content-Type"=>"application/json", "Authorization" => "Bearer ". $key],
233 'body' => json_encode([
234 "site_url" => $uri
235 ])
236 ));
237
238 if (is_wp_error($response)) {
239 $error_message = $response->get_error_message();
240 Logger::error('[BMI PRO] Something went wrong while authenticating the BackupBliss api key:' . $error_message);
241 return [false, $error_message];
242 } else {
243
244 $http_code = wp_remote_retrieve_response_code($response);
245 if (BMI_DEBUG) {
246 Logger::error("[BMI PRO] BackupBliss getSecret: $http_code - " . print_r($response['body'], true));
247 }
248 if ($http_code == 200) {
249 $this->removeNotice("invalid_key");
250 $result = json_decode($response['body']);
251
252
253 if ($result->status) {
254 return [true, $key];
255 }
256 } else if ($http_code == 401) {
257 if ($api_key) //Authentication request so no need to show notice.
258 return [false, ""];
259 $this->_keyDeactivatedNotice();
260 } else if ($http_code == 429) {
261 $result = json_decode($response['body']);
262 return [false, $result->message];
263 }
264 return [false, ""];
265 }
266 }
267
268 public function showNotice($type, $message, $time = 0)
269 {
270 if (BMI_DEBUG) {
271 Logger::error("showNotice($type, $message, $time)");
272 }
273
274 set_transient('bmip_backupbliss_notice_' . $type, $message, $time);
275 $transients = [];
276 $current_trasients = get_transient('bmip_backupbliss_notices');
277 if ($current_trasients) $transients = $current_trasients;
278 $transients[$type] = $type;
279 set_transient('bmip_backupbliss_notices', $transients);
280 }
281
282 public function hideFailureWarnNotice($exp) {
283 set_transient('bmip_backupbliss_hide_failure_notice', true, $exp);
284 }
285
286 public function showFailureWarnNotice() {
287 delete_transient('bmip_backupbliss_hide_failure_notice');
288 }
289
290 public function canShowFailureWarnNotice() {
291 return !get_transient("bmip_backupbliss_hide_failure_notice", false);
292 }
293
294 public function hasRequiredSpaceBeenFreed()
295 {
296 $required_space_file = get_option("bmip_backupbliss_required_space", false);
297 if ($required_space_file === false || !file_exists($required_space_file)) {
298 return true;
299 }
300
301 $required_space = @filesize($required_space_file);
302
303 if ($required_space === false) {
304 return false;
305 }
306
307 $storage_info = $this->getStorageInfo();
308 if (is_array($storage_info) && isset($storage_info["remaining_space"]) && $storage_info["remaining_space"] > $required_space) {
309 return true;
310 }
311 return false;
312 }
313
314 public function removeNotice($type)
315 {
316 // if (BMI_DEBUG) {
317 // Logger::error("removeNotice($type)");
318 // }
319
320 delete_transient('bmip_backupbliss_notice_' . $type);
321 $current_trasients = get_transient('bmip_backupbliss_notices');
322 if (isset($current_trasients[$type])) {
323 unset($current_trasients[$type]);
324 set_transient('bmip_backupbliss_notices', $current_trasients);
325 }
326 }
327
328 public function hideNotice($type, $time = 0)
329 {
330 if (BMI_DEBUG) {
331 Logger::error("hideNotice($type, $time)");
332 }
333
334 set_transient('bmip_backupbliss_notice_hide_' . $type, true, $time);
335 }
336
337 public function canShowNotice($type)
338 {
339 return !get_transient('bmip_backupbliss_notice_hide_' . $type, false);
340 }
341
342 public function getNotice($type)
343 {
344 // if (BMI_DEBUG) {
345 // Logger::error("getNotice($type)");
346 // }
347
348 return get_transient("bmip_backupbliss_notice_" . $type);
349 }
350
351 public function getNotices()
352 {
353 $bmip_backupbliss_notices = get_transient("bmip_backupbliss_notices");
354 $temp_notices = $bmip_backupbliss_notices;
355 $notices = [];
356 if ($bmip_backupbliss_notices) {
357 foreach ($bmip_backupbliss_notices as $notice) {
358 $noticemessage = get_transient("bmip_backupbliss_notice_" . $notice);
359 if ($noticemessage) {
360 $notices[$notice] = $noticemessage;
361 } else {
362 unset($temp_notices[$notice]);
363 }
364 }
365 }
366
367 if ($bmip_backupbliss_notices !== $temp_notices) {
368 set_transient("bmip_backupbliss_notices", $temp_notices);
369 }
370
371 return $notices;
372 }
373
374
375 private function _makeApiCall($url, $req_type = "GET", $body = [], $custom_headers = null)
376 {
377 $url = BMI_BB_STORAGE_API_URI . $url;
378
379 if (BMI_DEBUG) {
380 $backtrace = debug_backtrace();
381 // Get the caller's function name
382 $callerFunction = isset($backtrace[1]['function']) ? $backtrace[1]['function'] : 'unknown';
383 }
384 if (BMI_DEBUG) {
385 Logger::error("[BMI PRO][BackupBliss] REQUEST FROM $callerFunction() in _makeApiCall($url, $req_type, " . ($req_type != "PUT" ? print_r($body, true) : "BINARYDATA") . ", " . print_r($custom_headers, true) . ")");
386 }
387
388 $secret = $this->getSecret();
389 if (!$secret[0]) {
390 return ["status" => false];
391 }
392
393 $max_execution_time_pre_limit = ini_get('max_execution_time') - 2;
394
395 $headers = $custom_headers == null ? [
396 'Authorization: Bearer ' . $secret[1],
397 'Accept: application/json',
398 'Content-Type: application/json',
399 ] : $custom_headers;
400
401
402
403 $ch = curl_init();
404 curl_setopt($ch, CURLOPT_URL, $url);
405 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
406 curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
407 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $req_type);
408 curl_setopt($ch, CURLOPT_TIMEOUT, $max_execution_time_pre_limit);
409
410 if ($req_type == "POST") {
411 curl_setopt($ch, CURLOPT_POST, true);
412 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
413 } elseif ($req_type == "PUT") {
414 curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
415 }
416
417 $response = curl_exec($ch);
418 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
419
420 if ($req_type == "DELETE") {
421 return $http_code;
422 }
423
424 if (curl_errno($ch)) {
425 Logger::error("[BMI PRO][BackupBliss] Error in _makeApiCall request: Type: $req_type HTTP Code: $http_code. Error: " . curl_error($ch));
426 return ["status" => false, "http_code" => $http_code, "response_data" => $response];
427 }
428
429 curl_close($ch);
430
431 if ($http_code >= 200 && $http_code <= 299) {
432 $response_data = $custom_headers == null ? json_decode($response, true) : $response;
433 if (BMI_DEBUG) {
434 //Logger::error("[BMI PRO] RESPONSE _makeApiCall - " . print_r($response_data, true));
435 }
436 return ["status" => true, "http_code" => $http_code, "response_data" => $response_data];
437 } else {
438 if ($http_code == 401) {
439 $this->_keyDeactivatedNotice();
440 } elseif ($http_code == 403) {
441 $response_data = json_decode($response, true);
442 $this->_accessPermissionNotice($response_data['message']);
443 }
444 Logger::error("[BMI PRO][BackupBliss] Error in _makeApiCall request: Type: $req_type HTTP Code: $http_code. Response: $response");
445 return ["status" => false, "http_code" => $http_code, "response_data" => $response];
446 }
447 }
448
449 private function _accessPermissionNotice($message) {
450 Logger::error("[BMI] " . $message);
451 $this->showNotice("invalid_permission", $message, 0);
452 }
453
454 private function _keyDeactivatedNotice() {
455 Logger::error("[BMI] The API key is either invalid or deactivated.");
456 $message = 'There was an error while authenticating the BackupBliss API key.<br />';
457 $message .= 'Your BackupBliss API key got deactivated, hence moving backups to the BackupBliss storage is failing or will fail.<br>';
458 $message .= 'Please connect with a new API key by accessing your <a target="_blank" href="' . BMI_BB_STORAGE_URI . '">backupbliss storage</a> account.';
459
460 $this->showNotice("invalid_key", $message);
461 delete_option('bmi_pro_backupbliss_key');
462 $this->removeNotice("storage_warn");
463 $this->removeNotice("upload_issue");
464 $this->removeNotice("upload_issue_space");
465 }
466
467 public function getFileDetailByName($file_name) {
468 $response_data = $this->_makeApiCall("file/file-info/$file_name");
469
470 if($response_data["status"] && $response_data["response_data"]["status"]) {
471 return $response_data["response_data"]["file_info"];
472 }
473
474 return false;
475 }
476
477 private function deleteFile($file_name)
478 {
479 if (BMI_DEBUG) {
480 Logger::error("deleteFile($file_name)");
481 }
482
483 $url = "file/delete/$file_name";
484
485 $http_code = $this->_makeApiCall($url, "DELETE");
486
487 // Handle the response based on the status code
488 if ($http_code == 200) {
489 // 'File deleted successfully'
490 return True;
491 } else {
492 //Logger::error("[BMI PRO] Error in deleteFile. HTTP Code: $http_code.");
493 return False;
494 }
495 }
496
497 public function deleteBackup($md5)
498 {
499
500 if (BMI_DEBUG) {
501 Logger::error("deleteBackup($md5)");
502 }
503
504 $manifest = $this->getManifest($md5);
505 if ($manifest) {
506 $this->deleteFile($manifest->name);
507 }
508 }
509
510 public function getManifest($md5)
511 {
512 $manifest = false;
513 $localManifest = BMI_BACKUPS . DIRECTORY_SEPARATOR . $md5 . '.json';
514
515 if (file_exists($localManifest)) {
516
517 $manifestData = file_get_contents($localManifest);
518 $manifest = json_decode($manifestData);
519 } else {
520
521 $manifestData = $this->getFile($md5 . '.json');
522 if (is_array($manifestData) && $manifestData["file_data"]) {
523
524 $manifest = json_decode($manifestData["file_data"]);
525 }
526 }
527 return $manifest;
528 }
529
530 public function deleteBackupManifest($md5_json)
531 {
532 if (BMI_DEBUG) {
533 Logger::error("deleteBackupManifest($md5_json)");
534 }
535
536 $this->deleteFile($md5_json);
537 }
538
539 public function initiateUploadSession($file_path)
540 {
541
542 $uri = home_url();
543 if (substr($uri, 0, 4) != 'http') {
544 if (is_ssl()) $uri = 'https://' . home_url();
545 else $uri = 'http://' . home_url();
546 }
547
548 $file_name = basename($file_path);
549
550 if (BMI_DEBUG) {
551 Logger::error("[BMI PRO][BackupBliss] initiateUploadSession - $file_path - $file_name");
552 }
553
554
555 $url = 'file/initiate-upload-session';
556 $body = [
557 'filename' => $file_name,
558 'site_url' => $uri,
559 'file_size' => filesize($file_path)
560 ];
561
562 $response = $this->_makeApiCall($url, "POST", $body);
563 if ($response["status"]) {
564 $session = $response["response_data"];
565 if ($session["status"] && isset($session['upload_id']) && !empty($session['upload_id'])) {
566 return $session;
567 } else {
568 Logger::error("[BMI PRO][BackupBliss] Failed to create upload session: " . json_encode($session));
569 return false;
570 }
571 } else {
572 Logger::error("[BMI PRO][BackupBliss] Failed to create upload session: " . $response);
573 return false;
574 }
575 }
576
577 private function uploadChunkWithSession($upload_session, $chunk_data, $start_byte, $end_byte, $total_size)
578 {
579 if (BMI_DEBUG) {
580 // Logger::error("[BMI PRO] BEFORE UPLOAD uploadChunkWithSession(" . $upload_session['uploadUrl'] . ", $start_byte, $end_byte, $total_size" . ")\n" . print_r($headers, true));
581 }
582
583 $response = $this->_makeApiCall("file/upload-chunk/".$upload_session['upload_id'], "PUT", $chunk_data);
584
585 if ($response["status"]) {
586 if (BMI_DEBUG) {
587 // Logger::error("[BMI PRO] AFTER UPLOAD uploadChunkWithSession\n" . print_r($response, true));
588 }
589 return $response;
590 }
591
592 Logger::error("[BMI PRO] Failed to upload chunks. Start byte: $start_byte. End byte: $end_byte. Total Size: $total_size");
593 return $response;
594 }
595
596 public function getAllFiles()
597 {
598 $response = $this->_makeApiCall('file/backups');
599 if (BMI_DEBUG) {
600 // Logger::error("[BMI PRO] getAllBackups " . print_r($response, true));
601 }
602 if ($response["status"] && $response["response_data"]["status"]) {
603 $files = $response["response_data"];
604 return $files['backups'];
605 }
606
607 return false;
608 }
609
610 public function verifyConnection()
611 {
612 $res = $this->getSecret() ? 'connected' : 'disconnected';
613 return [ 'status' => 'success', 'result' => $res ];
614 }
615
616 private function downloadFile($file_details, $start_byte = 0, $end_byte = null)
617 {
618 if (BMI_DEBUG) {
619 //Logger::error("downloadFile(" . print_r($file_details, true) . ", $start_byte, $end_byte)");
620 }
621 if (!isset($file_details['download_hash'])) {
622 Logger::error('[BMI PRO] Download URL not available in the file details.');
623 return false;
624 }
625
626 $headers = [];
627
628 // Set the Range header for partial download
629 if ($end_byte !== null) {
630 $headers = [
631 'Range: bytes=' . $start_byte . '-' . $end_byte
632 ];
633 } else {
634 $headers = [
635 'Range: bytes=' . $start_byte . '-'
636 ];
637 }
638
639 $response = $this->_makeApiCall("file/download/".$file_details["download_hash"], "GET", [], $headers);
640
641 if ($response["status"]) {
642 return $response["response_data"];
643 }
644
645 return false;
646 }
647
648 public function getFile($file_name, $start_byte = 0, $end_byte = null)
649 {
650 if (BMI_DEBUG) {
651 Logger::error("getFile $file_name");
652 }
653 $file_detail = $this->getFileDetailByName($file_name);
654 if ($file_detail) {
655 return ["file_detail" => $file_detail, "file_data" => $this->downloadFile($file_detail, $start_byte, $end_byte)];
656 }
657 return false;
658 }
659
660 public function getStorageInfo()
661 {
662 $response = $this->_makeApiCall("file/storage-info");
663
664 if (BMI_DEBUG) {
665 // Logger::error("getStorageInfo - " . print_r($response, true));
666 }
667
668 if ($response["status"] && $response["response_data"]["status"]) {
669 return $response["response_data"]["storage_info"];
670 }
671
672 return false;
673 }
674
675
676 public function uploadFile($uploadSession, $filePath, $manifestPath, $md5, $batch, $bytesPerRequest)
677 {
678
679 if (!file_exists($filePath)) {
680
681 update_option('bmip_to_be_uploaded', [
682 'current_upload' => [],
683 'queue' => [],
684 'failed' => []
685 ]);
686
687 return ['status' => 'error'];
688 }
689
690 set_transient('bmip_upload_ongoing', '1', 31);
691
692 $batchNumber = intval($batch);
693 $maxLength = filesize($filePath);
694
695 # Microsoft recommended multiple value 320Kb
696 $chunkSize = 4 * 327680 * intval($bytesPerRequest / 1024 / 1024);
697
698 # Limit the chunk size to max of 50MB with multiple of recommended value
699 $maxChunkSize = 50 * 1024 * 1024;
700 $chunkSize = $chunkSize > $maxChunkSize ? $maxChunkSize : $chunkSize;
701
702 $chunkOffset = (($batchNumber - 1) * $chunkSize);
703 $rangeEnd = (($chunkSize * $batchNumber) - 1);
704
705 if ($rangeEnd >= $maxLength) $rangeEnd = $maxLength - 1;
706 if (($chunkSize + $chunkOffset) >= $maxLength) $chunkSize = $rangeEnd - $chunkOffset + 1;
707 $nextShouldStartAt = $rangeEnd + 1;
708
709
710 if ($stream = fopen($filePath, 'r')) {
711 $binaryData = stream_get_contents($stream, $chunkSize, $chunkOffset);
712 fclose($stream);
713 }
714
715
716 $toBeUploaded = get_option('bmip_to_be_uploaded', false);
717
718 if (isset($toBeUploaded['current_upload']['verifying'])) {
719 if (!$this->getFileDetailByName($md5 . '.json') || !$this->getFileDetailByName(basename($filePath))) {
720 $task = $toBeUploaded['current_upload']['task'];
721 $toBeUploaded['current_upload'] = [];
722 if (!isset($toBeUploaded['failed'])) $toBeUploaded['failed'] = [];
723 $toBeUploaded['failed'][$task] = 1;
724
725 update_option('bmip_to_be_uploaded', $toBeUploaded);
726 return ['status' => 'fail', 'data' => 'File not found on server during verification.'];
727 }
728 $uploadedBackupStatus = get_option('bmi_uploaded_backups_status', []);
729 if (!isset($uploadedBackupStatus[$md5])) {
730 $uploadedBackupStatus[$md5] = [];
731 }
732 $uploadedBackupStatus[$md5]['backupbliss'] = true;
733 update_option('bmi_uploaded_backups_status', $uploadedBackupStatus);
734
735 $task = $toBeUploaded['current_upload']['task'];
736 $toBeUploaded['current_upload'] = [];
737 if (!isset($toBeUploaded['failed'])) $toBeUploaded['failed'] = [];
738 if (isset($toBeUploaded['failed'][$task])) unset($toBeUploaded['failed'][$task]);
739 update_option('bmip_to_be_uploaded', $toBeUploaded);
740 return ['status' => 'success', 'data' => 'File verified successfully.'];
741 }
742
743 if (BMI_DEBUG)
744 {
745 Logger::error("Before uploadFile - " . print_r($uploadSession, true));
746 Logger::error("Before uploadFile - " . print_r($toBeUploaded['current_upload']['batch'], true));
747 }
748
749 $response = $this->uploadChunkWithSession($uploadSession, $binaryData, $chunkOffset, $rangeEnd, $maxLength);
750
751 $code = intval($response['http_code']);
752
753 if ($response["status"]) {
754
755 if ($rangeEnd == $maxLength - 1) //Last chunk already uploaded so complete upload and upload manifest
756 {
757 if ($code != 201) //If upload is not already completed
758 {
759 $response = $this->_makeApiCall('file/complete-upload', "POST", ['upload_id'=>$uploadSession['upload_id']]);
760 $code = intval($response['http_code']);
761 if ($code != 201) { //Something failed while completing the upload
762 $task = $toBeUploaded['current_upload']['task'];
763 $toBeUploaded['current_upload'] = [];
764 if (!isset($toBeUploaded['failed'])) $toBeUploaded['failed'] = [];
765 $toBeUploaded['failed'][$task] = 1;
766
767 update_option('bmip_to_be_uploaded', $toBeUploaded);
768 return ['status' => 'fail', 'data' => $response];
769 }
770 }
771
772 $manifestUploadSession = $this->initiateUploadSession($manifestPath);
773
774 if (!$manifestUploadSession) { //Something failed while completing the upload
775 $task = $toBeUploaded['current_upload']['task'];
776 $toBeUploaded['current_upload'] = [];
777 if (!isset($toBeUploaded['failed'])) $toBeUploaded['failed'] = [];
778 $toBeUploaded['failed'][$task] = 1;
779
780 update_option('bmip_to_be_uploaded', $toBeUploaded);
781 return ['status' => 'fail', 'data' => $response];
782 }
783
784 if ($stream = fopen($manifestPath, 'r')) {
785 $binaryData = stream_get_contents($stream);
786 fclose($stream);
787 }
788
789 $size = strlen($binaryData);
790 $manifestRes = $this->uploadChunkWithSession($manifestUploadSession, $binaryData, 0, $size - 1, $size);
791 if (
792 $manifestRes['status'] == true
793 && intval($manifestRes['http_code']) == 201
794 ) {
795 $toBeUploaded['current_upload']['verifying'] = true;
796 update_option('bmip_to_be_uploaded', $toBeUploaded);
797 return ['status' => 'success', 'data' => $response];
798 }
799
800 return ['status' => 'fail', 'data' => $manifestRes];
801 }
802
803
804 //Chunk accepted, let's continue uploading
805 if ($code == 202) {
806
807 $task = $toBeUploaded['current_upload']['task'];
808 if (!isset($toBeUploaded['failed'])) $toBeUploaded['failed'] = [];
809 if (isset($toBeUploaded['failed'][$task])) unset($toBeUploaded['failed'][$task]);
810
811 $toBeUploaded['current_upload']['batch'] = intval($batch) + 1;
812 $toBeUploaded['current_upload']['progress'] = number_format(($rangeEnd / $maxLength) * 100, 2) . '%';
813 update_option('bmip_to_be_uploaded', $toBeUploaded);
814
815 if (BMI_DEBUG)
816 Logger::error("After uploadFile - " . print_r($toBeUploaded['current_upload']['batch'], true));
817
818 $test = get_option('bmip_to_be_uploaded', false);
819
820 if (BMI_DEBUG)
821 Logger::error("After updating option uploadFile - " . print_r($test['current_upload']['batch'], true));
822 }
823 } elseif ($code == 507) {
824
825 $error_message_notice = 'Moving backups to your storage is failing or will fail because you don’t have enough space.';
826
827 add_option("bmip_backupbliss_required_space", $filePath);
828 $this->showNotice("upload_issue_space", $error_message_notice, 60 * 60);
829 } elseif ($code == 508) {
830
831 $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>. ';
832
833 $this->showNotice("upload_issue_space", $error_message_notice, 60 * 60);
834 } elseif ($code == 429) {
835
836 $error_message_notice = 'Upload to BackupBliss could not finish, due to rate limit error.<br />';
837 $error_message_notice .= 'Received message: <i>Too Many Requests in a short amount of time.</i><br />';
838 $error_message_notice .= 'Plugin will retry uploading automatically after 2 minutes.<br />';
839
840 $this->showNotice('upload_issue', $error_message_notice, 60 * 2);
841 } else {
842
843 Logger::error('[BMI PRO] Error during file upload (BackupBliss) code:' . $code);
844 if (isset($response['response_data']) && is_string($response['response_data'])) {
845 Logger::error('[BMI PRO] Message received (body):' . print_r($response['response_data'], true));
846 }
847
848 $error_message_notice = 'Upload to BackupBliss could not finish, due to an error:<br />';
849 if (is_string($response['response_data'])) {
850 $error = json_decode($response['response_data']);
851 if (isset($error->error->message) || isset($error->message)) {
852 $errorMessage = isset($error->error->message) ? $error->error->message : $error->message;
853 $error_message_notice .= "Code: $code - Received message:<i>" . $errorMessage . '</i><br />';
854 } else if ($error == null) {
855 $cleanResponse = strip_tags(trim($response["response_data"]));
856 $cleanResponse = preg_replace('/\s+/', ' ', $cleanResponse);
857 if (empty($cleanResponse)) {
858 $cleanResponse = 'Unknown server error occurred';
859 }
860 $error_message_notice .= "HTTP Error Response: <i>" . htmlspecialchars($cleanResponse) . '</i><br />';
861 }
862 }
863
864 if ($code == 0) {
865 $error_message_notice .= "Received Message: <i>Connection timed out. (This is most likely due to a connection issue in your server.)</i><br />";
866 }
867
868
869
870 $error_message_notice .= "Plugin will retry uploading automatically within a minute since this error.";
871 $this->showNotice("upload_issue", $error_message_notice, 60);
872 }
873
874
875 if (isset($error_message_notice)) {
876 $task = $toBeUploaded['current_upload']['task'];
877 // Requeueing is handled globally
878 // $toBeUploaded['queue'][$task] = [
879 // 'name' => $toBeUploaded['current_upload']['name'],
880 // 'md5' => $toBeUploaded['current_upload']['md5'],
881 // 'json' => $toBeUploaded['current_upload']['json']
882 // ];
883
884 $toBeUploaded['current_upload'] = [];
885 if (!isset($toBeUploaded['failed'])) $toBeUploaded['failed'] = [];
886 if (isset($toBeUploaded['failed'][$task])) $toBeUploaded['failed'][$task]++;
887 else $toBeUploaded['failed'][$task] = 1;
888
889 update_option('bmip_to_be_uploaded', $toBeUploaded);
890 }
891
892 delete_transient('bmip_upload_ongoing');
893 return ['status' => 'success', 'data' => $response];
894 }
895
896 }
897