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 / google-drive.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
google-drive.php
807 lines
1 <?php
2
3 // Namespace
4 namespace BMI\Plugin\External;
5
6 // Use
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\Dashboard as Dashboard;
13
14 // Exit on direct access
15 if (!defined('ABSPATH')) {
16 exit;
17 }
18
19 /**
20 * BMI_External_GDrive
21 */
22 class BMI_External_GDrive {
23
24 private $gdrive_access_token = false;
25
26 public function __construct() {
27
28 add_action('bmi_premium_remove_backup_file', [&$this, 'deleteGoogleDriveBackup']);
29 add_action('bmi_premium_remove_backup_json_file', [&$this, 'deleteGoogleDriveJson']);
30
31 }
32
33 /**
34 * checkForBackupsToUpload - Will check for backups that requires to be in sync with cloud
35 *
36 * @return string[]
37 */
38 public function checkForBackupsToUpload() {
39
40 $isEnabled = Dashboard\bmi_get_config('STORAGE::EXTERNAL::GDRIVE');
41 if (!($isEnabled === true || $isEnabled === 'true')) {
42 return;
43 }
44
45 // Upload Object
46 $requiresUpload = get_option('bmip_to_be_uploaded', [
47 'current_upload' => [],
48 'queue' => [],
49 'failed' => []
50 ]);
51
52 // Local Backups
53 require_once BMI_INCLUDES . DIRECTORY_SEPARATOR . 'scanner' . DIRECTORY_SEPARATOR . 'backups.php';
54 $backups = new Backups();
55 $backupsAvailable = $backups->getAvailableBackups("local");
56 $localBackups = $backupsAvailable['local'];
57 $localBackups = array_reverse($localBackups);
58
59 // Google Drive
60 $gdriveFailed = false;
61 $googleDriveBackups = $this->getGoogleDriveBackups();
62 if ($googleDriveBackups && is_object($googleDriveBackups['data']) && isset($googleDriveBackups['data']->files)) {
63 $googleDriveParsed = $this->parseGoogleDriveFiles($googleDriveBackups['data']->files);
64 } else $gdriveFailed = true;
65
66 $uploadedBackupStatus = get_option('bmi_uploaded_backups_status', []);
67 foreach ($localBackups as $name => $details) {
68
69 $manifestName = $details[0];
70 $md5 = $details[7];
71 if (isset($uploadedBackupStatus[$md5]) && isset($uploadedBackupStatus[$md5]['gdrive'])) {
72 continue;
73 }
74
75 // Google Drive
76 if (!$gdriveFailed && !(isset($googleDriveParsed['md5_' . $md5]) && isset($googleDriveParsed['file_' . $md5 . '.json']))) {
77
78 // File is not uploaded action required
79 if (!isset($requiresUpload['queue']['gdrive_' . $md5])) {
80 $isAnyTaskATM = isset($requiresUpload['current_upload']['task']);
81 if (($isAnyTaskATM && $requiresUpload['current_upload']['task'] != 'gdrive_' . $md5) || !$isAnyTaskATM) {
82 $requiresUpload['queue']['gdrive_' . $md5] = [
83 'name' => $name,
84 'md5' => $md5,
85 'json' => $md5 . '.json'
86 ];
87 }
88 }
89 }
90 }
91
92 update_option('bmip_to_be_uploaded', $requiresUpload);
93 return [ 'status' => 'success' ];
94
95 }
96
97 /**
98 * parseGoogleDriveFiles - Parses Google Drive output files
99 *
100 * @param object $files Google Drive Files of Return
101 * @return array of parsed files
102 */
103 public function parseGoogleDriveFiles(&$files) {
104
105 $parsedFiles = [];
106 foreach ($files as $index => $file) {
107 $parsedFiles['file_' . $file->name] = [
108 'md5' => $file->md5Checksum,
109 'originalName' => $file->originalFilename,
110 'id' => $file->id,
111 'size' => $file->size
112 ];
113 $parsedFiles['md5_' . $file->md5Checksum] = [
114 'name' => $file->name,
115 'originalName' => $file->originalFilename,
116 'id' => $file->id,
117 'size' => $file->size
118 ];
119 }
120
121 return $parsedFiles;
122
123 }
124
125 /**
126 * getGoogleDriveAccessToken - Generates Access Token for API communication
127 *
128 * @return json status
129 */
130 private function getGoogleDriveAccessToken( $forceGetNewAccessToken = false ) {
131
132 $uri = home_url();
133 if (substr($uri, 0, 4) != 'http') {
134 if (is_ssl()) $uri = 'https://' . home_url();
135 else $uri = 'http://' . home_url();
136 }
137
138 if ($this->gdrive_access_token != false) {
139 return $this->gdrive_access_token;
140 }
141
142 $client_token = get_option('bmi_pro_gd_client_id', '');
143 $site_token = get_option('bmi_pro_gd_token', '');
144
145 if (strlen($site_token) < 60 || strlen($client_token) < 60) {
146 return false;
147 }
148
149 $savedAccessToken = get_transient('bmi_pro_access_token');
150 if ($savedAccessToken) return $savedAccessToken;
151
152 $url = 'https://authentication.backupbliss.com/v1/gdrive/token';
153 $response = wp_remote_post($url, array(
154 'method' => 'POST',
155 'timeout' => 15,
156 'redirection' => 2,
157 'httpversion' => '1.0',
158 'blocking' => true,
159 'body' => array(
160 'client_id' => get_option('bmi_pro_gd_client_id', ''),
161 'site_token' => get_option('bmi_pro_gd_token', ''),
162 'force_refresh' => $forceGetNewAccessToken,
163 'redirect_uri' => $uri
164 )
165 ));
166
167 if (is_wp_error($response)) {
168 $error_message = $response->get_error_message();
169 Logger::error('[BMI PRO] Something went wrong during getting token:' . $error_message);
170 return false;
171 } else {
172 $result = json_decode($response['body']);
173 if (isset($result->expiration) && isset($result->access_token)) {
174 $expiresInSeconds = intval($result->expiration) - intval(microtime(true));
175 $accessToken = $result->access_token;
176 set_transient('bmi_pro_access_token', $accessToken, $expiresInSeconds);
177 if (in_array(get_transient('bmip_gd_issue'), ['auth_error', 'auth_error_disconnected'])) {
178 delete_transient('bmip_gd_issue');
179 }
180
181 $this->gdrive_access_token = $accessToken;
182 return $this->gdrive_access_token;
183 }
184 return false;
185 }
186
187 }
188
189 public function verifyConnection($forceGetNewAccessToken = false) {
190 $baseurl = home_url();
191 if (substr($baseurl, 0, 4) != 'http') {
192 if (is_ssl()) $baseurl = 'https://' . home_url();
193 else $baseurl = 'http://' . home_url();
194 }
195
196 $client_token = get_option('bmi_pro_gd_client_id', '');
197 $site_token = get_option('bmi_pro_gd_token', '');
198
199 if (strlen($site_token) < 60 || strlen($client_token) < 60) {
200 return ['status' => 'success', 'result' => 'disconnected'];
201 }
202
203 $url = 'https://authentication.backupbliss.com/v1/gdrive/verify';
204 $response = wp_remote_post($url, array(
205 'method' => 'POST',
206 'timeout' => 15,
207 'redirection' => 2,
208 'httpversion' => '1.0',
209 'blocking' => true,
210 'body' => array(
211 'client_id' => get_option('bmi_pro_gd_client_id', ''),
212 'site_token' => get_option('bmi_pro_gd_token', ''),
213 'force_refresh' => $forceGetNewAccessToken || ( get_transient('bmi_pro_access_token') && get_transient('bmip_gd_issue') === 'auth_error'),
214 'redirect_uri' => $baseurl
215 )
216 ));
217
218 $res = 'disconnected';
219 if (is_wp_error($response)) {
220 $error_message = $response->get_error_message();
221 Logger::error('[BMI PRO] Something went wrong during GDrive connection verification:' . $error_message);
222 return ['status' => 'error', 'result' => 'disconnected'];
223 } else {
224 $result = json_decode($response['body']);
225 if (isset($result->status)) {
226 if (isset($result->expiration) && isset($result->access_token)) {
227 $expiresInSeconds = intval($result->expiration) - intval(microtime(true));
228 $accessToken = $result->access_token;
229 set_transient('bmi_pro_access_token', $accessToken, $expiresInSeconds);
230 }
231
232 if ($result->status == 'disconnected') {
233 $res = 'disconnected';
234 if (get_transient('bmip_gd_issue') === 'auth_error' && get_transient('bmi_pro_access_token')) {
235 set_transient('bmip_gd_issue', 'auth_error_disconnected');
236 delete_transient('bmi_pro_access_token');
237 }
238 }
239 if ($result->status == 'connected'){
240 $res = 'connected';
241 if (in_array(get_transient('bmip_gd_issue'), ['auth_error', 'auth_error_disconnected'])) delete_transient('bmip_gd_issue');
242 }
243 if ($result->status == 'error') $res = 'disconnected';
244 }
245 return ['status' => 'success', 'result' => $res];
246 }
247
248 }
249
250 /**
251 * makeGoogleDriveAPICall - Makes Call to the Google Drive API GET
252 *
253 * @return json status
254 */
255 private function makeGoogleDriveAPICall($uri, $range = false) {
256
257 $access_token = $this->getGoogleDriveAccessToken();
258
259 if ($access_token === false || $access_token === 'false') {
260 return 'error';
261 }
262
263 $headers = array(
264 'Authorization: Bearer ' . rawurlencode($access_token)
265 );
266
267 if ($range != false) $headers[] = 'Range: bytes=' . $range;
268 else $headers[] = 'Content-Type: application/json';
269
270 $url = 'https://www.googleapis.com/drive/v3/' . $uri;
271 $ch = curl_init();
272 curl_setopt($ch, CURLOPT_URL, $url);
273 curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
274 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
275 curl_setopt($ch, CURLOPT_TIMEOUT, 30);
276 curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
277
278 $response = curl_exec($ch);
279
280 if (curl_errno($ch)) {
281 $error_message = curl_error($ch);
282 Logger::error('[BMI PRO] Something went wrong during getting file list/content/download:' . $error_message);
283 return 'error';
284 }
285
286 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
287 if ($http_code == 401) {
288 if (get_transient('bmi_pro_access_token') && get_transient('bmip_gd_issue') != 'auth_error_disconnected') set_transient('bmip_gd_issue', 'auth_error', HOUR_IN_SECONDS);
289 }
290
291 // Close the cURL session
292 curl_close($ch);
293
294 if ($range != false) return $response;
295 else return json_decode($response);
296
297 }
298
299 /**
300 * makeGoogleDriveAPICallDelete - Makes Call to the Google Drive API DELETE
301 *
302 * @return json status
303 */
304 private function makeGoogleDriveAPICallDelete($fileId) {
305
306 $access_token = $this->getGoogleDriveAccessToken();
307
308 if ($access_token === false || $access_token === 'false') {
309 return 'error';
310 }
311
312 $url = 'https://www.googleapis.com/drive/v3/files/' . $fileId;
313 $response = wp_remote_get($url, array(
314 'method' => 'DELETE',
315 'timeout' => 15,
316 'redirection' => 5,
317 'httpversion' => '1.0',
318 'blocking' => true,
319 'headers' => array(
320 'Authorization' => 'Bearer ' . rawurlencode($access_token)
321 )
322 ));
323
324 $http_code = wp_remote_retrieve_response_code($response);
325 if ($http_code == 401) {
326 if (get_transient('bmi_pro_access_token') && get_transient('bmip_gd_issue') != 'auth_error_disconnected') set_transient('bmip_gd_issue', 'auth_error', HOUR_IN_SECONDS);
327 }
328
329 if (is_wp_error($response)) {
330 $error_message = $response->get_error_message();
331 Logger::error('[BMI PRO] Something went wrong during getting file list:' . $error_message);
332 return 'error';
333 } else return json_decode($response['body']);
334
335 }
336
337 /**
338 * makeGoogleDriveAPICallPost - Makes Call to the Google Drive API POST
339 *
340 * @return json status
341 */
342 private function makeGoogleDriveAPICallPost($uri, $postdata, $type = false) {
343
344 $access_token = $this->getGoogleDriveAccessToken();
345 if ($access_token === false || $access_token === 'false') {
346 return 'error';
347 }
348
349 if ($type == 'upload') $url = 'https://www.googleapis.com/upload/drive/v3/' . $uri;
350 else $url = 'https://www.googleapis.com/drive/v3/' . $uri;
351
352 $response = wp_remote_post($url, array(
353 'method' => 'post',
354 'timeout' => 15,
355 'redirection' => 5,
356 'httpversion' => '1.0',
357 'blocking' => true,
358 'headers' => array(
359 'Content-Type' => 'application/json',
360 'Authorization' => 'Bearer ' . rawurlencode($access_token)
361 ),
362 'body' => wp_json_encode($postdata)
363 ));
364
365 $http_code = wp_remote_retrieve_response_code($response);
366 if ($http_code == 401) {
367 if (get_transient('bmi_pro_access_token') && get_transient('bmip_gd_issue') != 'auth_error_disconnected') set_transient('bmip_gd_issue', 'auth_error', HOUR_IN_SECONDS);
368 }
369
370 if (is_wp_error($response)) {
371 $error_message = $response->get_error_message();
372 Logger::error('[BMI PRO] Something went wrong during getting file list:' . $error_message);
373 return 'error';
374 } else {
375 if ($type == 'upload') return $response['headers']['location'];
376 else return json_decode($response['body']);
377 }
378
379 }
380
381 /**
382 * makeGoogleDriveAPICallPutSingle - Makes Call to the Google Drive API PUT
383 * supports only single request upload
384 *
385 * @return json status
386 */
387 private function makeGoogleDriveAPICallPutSingle($url, &$data) {
388
389 $access_token = $this->getGoogleDriveAccessToken();
390 if ($access_token === false || $access_token === 'false') {
391 return 'error';
392 }
393
394 $response = wp_remote_post($url, array(
395 'method' => 'PUT',
396 'timeout' => 15,
397 'redirection' => 5,
398 'httpversion' => '1.0',
399 'blocking' => true,
400 'headers' => array(
401 'Content-Type' => 'text/plain',
402 'Content-Length' => strlen($data),
403 'Authorization' => 'Bearer ' . rawurlencode($access_token)
404 ),
405 'body' => $data
406 ));
407
408 $http_code = wp_remote_retrieve_response_code($response);
409 if ($http_code == 401) {
410 if (get_transient('bmi_pro_access_token') && get_transient('bmip_gd_issue') != 'auth_error_disconnected') set_transient('bmip_gd_issue', 'auth_error', HOUR_IN_SECONDS);
411 }
412
413 if (is_wp_error($response)) {
414 $error_message = $response->get_error_message();
415 Logger::error('[BMI PRO] Something went wrong during PUT upload (single):' . $error_message);
416 return 'error';
417 } else {
418 return $response;
419 }
420
421 }
422
423 /**
424 * makeGoogleDriveAPICallPut - Makes Call to the Google Drive API PUT
425 *
426 * @return json status
427 */
428 private function makeGoogleDriveAPICallPut($url, &$binaryData, $chunkSize, $chunkRange) {
429
430 $access_token = $this->getGoogleDriveAccessToken();
431 if ($access_token === false || $access_token === 'false') {
432 return 'error';
433 }
434
435 $max = ini_get('max_execution_time') - 2;
436 if ($max > 120) $max = 120;
437
438 $response = wp_remote_post($url, array(
439 'method' => 'PUT',
440 'timeout' => $max,
441 'redirection' => 5,
442 'httpversion' => '1.0',
443 'blocking' => true,
444 'headers' => array(
445 'Content-Type' => 'application/octet-stream',
446 'Authorization' => 'Bearer ' . rawurlencode($access_token),
447 'Content-Length' => $chunkSize,
448 'Content-Range' => $chunkRange
449 ),
450 'body' => $binaryData
451 ));
452
453 $http_code = wp_remote_retrieve_response_code($response);
454 if ($http_code == 401) {
455 if (get_transient('bmi_pro_access_token') && get_transient('bmip_gd_issue') != 'auth_error_disconnected') set_transient('bmip_gd_issue', 'auth_error', HOUR_IN_SECONDS);
456 }
457
458 if (is_wp_error($response)) {
459 $error_message = $response->get_error_message();
460 Logger::error('[BMI PRO] Something went wrong during file upload (resumable):' . $error_message);
461 return 'error';
462 } else {
463 return $response;
464 }
465
466 }
467
468 /**
469 * getBMIDirectoryID - Return list of Google Drive backups and their MD5s
470 *
471 * @return json status
472 */
473 private function getBMIDirectoryID() {
474
475 $dirname = esc_attr(sanitize_text_field(Dashboard\bmi_get_config('STORAGE::EXTERNAL::GDRIVE::DIRNAME')));
476
477 $search = rawurlencode("name = '" . $dirname . "' and trashed = false and mimeType = 'application/vnd.google-apps.folder'");
478 $uri = 'files?corpora=user&orderBy=folder&q=' . $search;
479 $api = $this->makeGoogleDriveAPICall($uri);
480
481 if ($api == 'error') return [ 'status' => 'error1' ];
482 if (!isset($api->files)) return [ 'status' => 'error2' ];
483
484 if (sizeof($api->files) <= 0 && !isset($api->files[0])) {
485
486 $directoryCreationData = [
487 'mimeType' => 'application/vnd.google-apps.folder',
488 'name' => $dirname,
489 'parents' => ['root'],
490 ];
491
492 $api = $this->makeGoogleDriveAPICallPost('files', $directoryCreationData);
493 $bmiAppDirectoryID = $api->id;
494
495 } else {
496
497 $bmiAppDirectoryID = $api->files[0]->id;
498
499 }
500
501 return $bmiAppDirectoryID;
502
503 }
504
505 /**
506 * getGoogleDriveBackups - Return list of Google Drive backups and their MD5s
507 *
508 * @return json status
509 */
510 public function getGoogleDriveBackups() {
511
512 $bmiAppDirectoryID = $this->getBMIDirectoryID();
513
514 if (is_array($bmiAppDirectoryID)) return false;
515
516 $search = rawurlencode("'" . $bmiAppDirectoryID . "'" . ' in parents and trashed = false');
517 $uri = 'files?corpora=user&orderBy=folder&fields=files(md5Checksum,+originalFilename,+size,+mimeType,+name,+id)&q=' . $search;
518 $api = $this->makeGoogleDriveAPICall($uri);
519
520 return [ 'status' => 'success', 'data' => $api ];
521
522 }
523
524 /**
525 * createUploadGoogleDriveURL - It will create resumable upload URL
526 *
527 * @return json status
528 */
529 public function createUploadGoogleDriveURL($backupPath, $manifestPath, $forManifest = false) {
530
531 $bmiAppDirectoryID = $this->getBMIDirectoryID();
532
533 $uri = 'files?uploadType=resumable';
534
535 // Make file LINK for RESUMABLE upload
536 $uploadBody = array();
537 if ($forManifest === true) $uploadBody['name'] = basename($manifestPath);
538 else $uploadBody['name'] = basename($backupPath);
539 $uploadBody['parents'] = [$bmiAppDirectoryID];
540 $uploadURL = $this->makeGoogleDriveAPICallPost($uri, $uploadBody, 'upload');
541
542 return [ 'status' => 'success', 'uploadURL' => $uploadURL ];
543
544 }
545
546 /**
547 * uploadManifestFile - It will upload manifest file into BMI directory on Google Drive
548 *
549 * @return array|false status
550 */
551 private function uploadManifestFile($manifestUploadURL, $manifestPath) {
552
553 if (!file_exists($manifestPath)) {
554 return false;
555 }
556
557 $contents = file_get_contents($manifestPath);
558 $api = $this->makeGoogleDriveAPICallPutSingle($manifestUploadURL, $contents);
559
560 return [ 'status' => 'success', 'data' => $api ];
561
562 }
563
564 /**
565 * getGoogleDriveFileContents - Gets file body by Google Drive file ID
566 *
567 * @return json status
568 */
569 public function getGoogleDriveFileContents($fileId, $range = false) {
570
571 $uri = 'files/' . sanitize_text_field($fileId) . '?alt=media';
572 $api = $this->makeGoogleDriveAPICall($uri, $range);
573
574 return [ 'status' => 'success', 'data' => $api ];
575
576 }
577
578 /**
579 * getGoogleDriveFileMeta - Gets file meta data by Google Drive file ID
580 *
581 * @return json status
582 */
583 public function getGoogleDriveFileMeta($fileId) {
584
585 $uri = 'files/' . sanitize_text_field($fileId) . '?fields=md5Checksum,originalFilename,size,mimeType,name,id';
586 $api = $this->makeGoogleDriveAPICall($uri);
587
588 return [ 'status' => 'success', 'data' => $api ];
589
590 }
591
592 /**
593 * deleteGoogleDriveBackup - Deletes Backup from Google Drive
594 *
595 * @return json status
596 */
597 public function deleteGoogleDriveBackup($md5) {
598
599 $files = $this->getGoogleDriveBackups();
600 if (isset($files['status']) && $files['status'] == 'success') {
601 $files = $files['data']->files;
602 foreach ($files as $index => $file) {
603 if ($file->md5Checksum == $md5) {
604 $fileId = $file->id;
605 $this->makeGoogleDriveAPICallDelete($fileId);
606 }
607 }
608 }
609
610 }
611
612 /**
613 * deleteGoogleDriveJson - Deletes JSON manifest from Google Drive
614 *
615 * @return json status
616 */
617 public function deleteGoogleDriveJson($fileName) {
618
619 $files = $this->getGoogleDriveBackups();
620 if (isset($files['status']) && $files['status'] == 'success') {
621 $files = $files['data']->files;
622 foreach ($files as $index => $file) {
623 if ($file->originalFilename == $fileName) {
624 $fileId = $file->id;
625 $this->makeGoogleDriveAPICallDelete($fileId);
626 }
627 }
628 }
629
630 }
631
632 /**
633 * uploadGoogleDriveFile - It will upload particular file into BMI directory on Google Drive
634 *
635 * @return json status
636 */
637 public function uploadGoogleDriveFile($uploadURL, $filePath, $manifestPath, $md5, $batch, $bytesPerRequest) {
638
639 if (!file_exists($filePath)) {
640
641 update_option('bmip_to_be_uploaded', [
642 'current_upload' => [],
643 'queue' => [],
644 'failed' => []
645 ]);
646
647 return ['status' => 'error'];
648
649 }
650
651 set_transient('bmip_upload_ongoing', '1', 31);
652
653 $batchNumber = intval($batch);
654 $maxLength = filesize($filePath);
655
656 $chunkSize = 256 * 1024 * 4 * intval($bytesPerRequest / 1024 / 1024);
657
658 $chunkOffset = (($batchNumber - 1) * $chunkSize);
659 $rangeEnd = (($chunkSize * $batchNumber) - 1);
660
661 if ($rangeEnd >= $maxLength) $rangeEnd = $maxLength - 1;
662 if (($chunkSize + $chunkOffset) >= $maxLength) $chunkSize = $rangeEnd - $chunkOffset + 1;
663 $range = 'bytes ' . $chunkOffset . '-' . $rangeEnd . '/' . $maxLength;
664 $nextShouldStartAt = $rangeEnd + 1;
665
666
667 if ($stream = fopen($filePath, 'r')) {
668 $binaryData = stream_get_contents($stream, $chunkSize, $chunkOffset);
669 fclose($stream);
670 }
671
672 $api = $this->makeGoogleDriveAPICallPut($uploadURL, $binaryData, $chunkSize, $range);
673 $toBeUploaded = get_option('bmip_to_be_uploaded', false);
674 if (isset($api['response']) && isset($api['response']['code'])) {
675 $code = intval($api['response']['code']);
676 if ($code == 308) {
677
678 $task = $toBeUploaded['current_upload']['task'];
679 if (!isset($toBeUploaded['failed'])) $toBeUploaded['failed'] = [];
680 if (isset($toBeUploaded['failed'][$task])) unset($toBeUploaded['failed'][$task]);
681
682 // All is good
683 if ($toBeUploaded) {
684 $toBeUploaded['current_upload']['batch'] = intval($batch) + 1;
685 $toBeUploaded['current_upload']['progress'] = number_format(($rangeEnd / $maxLength) * 100, 2) . '%';
686 update_option('bmip_to_be_uploaded', $toBeUploaded);
687 }
688
689 } else if ($code == 200) {
690
691 // Upload finished, upload manifest now
692 $manifestUploadURL = $this->createUploadGoogleDriveURL($filePath, $manifestPath, true);
693 $manifestRes = $this->uploadManifestFile($manifestUploadURL['uploadURL'], $manifestPath);
694 if ($manifestRes['status'] === 'success') {
695 $uploadedBackupStatus = get_option('bmi_uploaded_backups_status', []);
696 if (!isset($uploadedBackupStatus[$md5])) {
697 $uploadedBackupStatus[$md5] = [];
698 }
699 $uploadedBackupStatus[$md5]['gdrive'] = true;
700 update_option('bmi_uploaded_backups_status', $uploadedBackupStatus);
701 }
702
703 $task = $toBeUploaded['current_upload']['task'];
704 $toBeUploaded['current_upload'] = [];
705 if (!isset($toBeUploaded['failed'])) $toBeUploaded['failed'] = [];
706 if (isset($toBeUploaded['failed'][$task])) unset($toBeUploaded['failed'][$task]);
707
708 update_option('bmip_to_be_uploaded', $toBeUploaded);
709
710 } else if ($code == 403) {
711
712 $message = 'Backup file upload to Google Drive could not be completed due to insufficient free space on Google Drive.<br />';
713 $message .= 'The plugin will automatically retry uploading the backup file within an hour of this error message.<br />';
714 $message .= 'During this time, please try to resolve any issues, such as freeing up space on your Google Drive.<br />';
715
716 // Add required space option to check later
717 add_option('bmip_gd_required_space', filesize($filePath));
718 // Display message
719 set_transient('bmip_display_quota_issues', $message, HOUR_IN_SECONDS);
720 // Force to show the message again
721 delete_option('bmip_dismissed_quota_notice');
722
723 // Mark the backup as failed to upload
724 $task = $toBeUploaded['current_upload']['task'];
725 // Requeueing is handled globally
726 // $toBeUploaded['queue'][$task] = [
727 // 'name' => $toBeUploaded['current_upload']['name'],
728 // 'md5' => $toBeUploaded['current_upload']['md5'],
729 // 'json' => $toBeUploaded['current_upload']['json']
730 // ];
731
732 $toBeUploaded['current_upload'] = [];
733 if (!isset($toBeUploaded['failed'])) $toBeUploaded['failed'] = [];
734 if (isset($toBeUploaded['failed'][$task])) $toBeUploaded['failed'][$task]++;
735 else $toBeUploaded['failed'][$task] = 1;
736
737 update_option('bmip_to_be_uploaded', $toBeUploaded);
738
739 } else if ($code == 429) {
740
741 $message = 'Backup file upload to Google Drive could not be completed due to a limit error.<br />';
742 $message .= 'Received message: <i>Too Many Requests in a short amount of time.</i><br />';
743 $message .= 'The plugin will automatically retry uploading the backup file within 2 minutes of this error message.<br />';
744
745
746 set_transient('bmip_display_quota_issues', $message, 2 * MINUTE_IN_SECONDS);
747
748 } else {
749
750 Logger::error('[BMI PRO] Error during file upload (Google Drive) code:' . $code);
751 if (isset($api['body']) && is_string($api['body'])) {
752 Logger::error('[BMI PRO] Message received (body):' . print_r($api['body'], true));
753 }
754
755 $task = $toBeUploaded['current_upload']['task'];
756 // Requeueing is handled globally
757 // $toBeUploaded['queue'][$task] = [
758 // 'name' => $toBeUploaded['current_upload']['name'],
759 // 'md5' => $toBeUploaded['current_upload']['md5'],
760 // 'json' => $toBeUploaded['current_upload']['json']
761 // ];
762
763 $toBeUploaded['current_upload'] = [];
764 if (!isset($toBeUploaded['failed'])) $toBeUploaded['failed'] = [];
765 if (isset($toBeUploaded['failed'][$task])) $toBeUploaded['failed'][$task]++;
766 else $toBeUploaded['failed'][$task] = 1;
767
768 update_option('bmip_to_be_uploaded', $toBeUploaded);
769
770 }
771 } else {
772
773 $task = $toBeUploaded['current_upload']['task'];
774 // Requeueing is handled globally
775 // $toBeUploaded['queue'][$task] = [
776 // 'name' => $toBeUploaded['current_upload']['name'],
777 // 'md5' => $toBeUploaded['current_upload']['md5'],
778 // 'json' => $toBeUploaded['current_upload']['json']
779 // ];
780
781 $toBeUploaded['current_upload'] = [];
782 if (!isset($toBeUploaded['failed'])) $toBeUploaded['failed'] = [];
783 if (isset($toBeUploaded['failed'][$task])) $toBeUploaded['failed'][$task]++;
784 else $toBeUploaded['failed'][$task] = 1;
785
786 update_option('bmip_to_be_uploaded', $toBeUploaded);
787
788 }
789
790 delete_transient('bmip_upload_ongoing');
791 return [ 'status' => 'success', 'data' => $api ];
792
793 }
794
795 public function getGoogleDriveAvailableStorage() {
796 $uri = 'about?fields=storageQuota';
797 $api = $this->makeGoogleDriveAPICall($uri);
798 $quota = $api->storageQuota;
799 $totalStorage = $quota->limit;
800 $totalUsage = $quota->usage;
801 $availableStorage = $totalStorage - $totalUsage;
802
803 return $availableStorage;
804 }
805
806 }
807