PluginProbe
Media Cloud Sync / 1.3.10
Media Cloud Sync v1.3.10
1.4.1 1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 All 35 releases
media-cloud-sync / includes / base / services / s3.php

s3.php in Media Cloud Sync 1.3.10, at includes/base/services/s3.php

1,227 lines 50.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Dudlewebs\WPMCS;
3
4 defined('ABSPATH') || exit;
5
6 // Libraries
7 use Dudlewebs\WPMCS\s3\Aws\S3\S3Client;
8 use Dudlewebs\WPMCS\s3\Aws\Exception\AwsException;
9 use Dudlewebs\WPMCS\s3\Aws\S3\Exception\S3Exception;
10 use Dudlewebs\WPMCS\s3\Aws\S3\MultipartUploader;
11 use Dudlewebs\WPMCS\s3\Aws\Exception\MultipartUploadException;
12 use Exception;
13
14 class S3 {
15 private $assets_url;
16 private $version;
17 private $token;
18
19 protected $config;
20 protected $bucketConfig;
21 protected $settings;
22 protected $credentials;
23 protected $bucket_name;
24 protected $cdnConfig;
25
26 public $service = 's3';
27 public $s3Client = false;
28
29 /**
30 * Admin constructor.
31 * @since 1.0.0
32 */
33 public function __construct() {
34 $this->assets_url = WPMCS_ASSETS_URL;
35 $this->version = WPMCS_VERSION;
36 $this->token = WPMCS_TOKEN;
37
38 // Initialize setup
39 $this->init();
40 }
41
42 /**
43 * Initialise Client
44 */
45 public function init() {
46 $this->settings = Utils::get_settings();
47 $this->credentials = Utils::get_credentials();
48 $this->config = isset($this->credentials['config']) && !empty($this->credentials['config'])
49 ? $this->credentials['config']
50 : [];
51 $this->bucketConfig = isset($this->credentials['bucketConfig']) && !empty($this->credentials['bucketConfig'])
52 ? $this->credentials['bucketConfig']
53 : [];
54 $this->bucket_name = isset($this->bucketConfig['bucket_name']) && !empty($this->bucketConfig['bucket_name'])
55 ? $this->bucketConfig['bucket_name']
56 : '';
57 $this->cdnConfig = isset($this->credentials['cdn']) && !empty($this->credentials['cdn'])
58 ? $this->credentials['cdn']
59 : [];
60
61 if (
62 isset($this->config['region']) && !empty($this->config['region']) &&
63 isset($this->config['access_key']) && !empty($this->config['access_key']) &&
64 isset($this->config['secret_key']) && !empty($this->config['secret_key'])
65 ) {
66 $this->s3Client = new S3Client([
67 'version' => '2006-03-01',
68 'region' => $this->config['region'],
69 'use_accelerate_endpoint' => isset($this->bucketConfig['transfer_acceleration'])
70 ? $this->bucketConfig['transfer_acceleration'] : false,
71 'use_aws_shared_config_files' => false,
72 'credentials' => [
73 'key' => $this->config['access_key'],
74 'secret' => $this->config['secret_key'],
75 ],
76 ]);
77 }
78
79 }
80
81
82 /**
83 * Verify Credentials
84 * @since 1.0.0
85 * @return boolean
86 */
87 public function verifyCredentials($config = []) {
88 $region = isset($config['region']) ? $config['region'] : '';
89 $access_key = isset($config['access_key']) ? $config['access_key'] : '';
90 $secret_key = isset($config['secret_key']) ? $config['secret_key'] : '';
91
92 if (!empty($region) && !empty($access_key) && !empty($secret_key)) {
93 try {
94 $s3Client = new S3Client([
95 'version' => '2006-03-01',
96 'region' => $region,
97 'use_aws_shared_config_files' => false,
98 'credentials' => [
99 'key' => $access_key,
100 'secret' => $secret_key,
101 ],
102 ]);
103
104 $result = [
105 'success' => false,
106 'code' => 200,
107 'message' => esc_html__('Please check the authorization details', 'media-cloud-sync'),
108 ];
109
110 try {
111 $s3Client->listObjectsV2([
112 'Bucket' => $this->token . '_dummy-bucket-for-auth-check'
113 ]);
114
115 // If we reach here, the credentials are valid
116 $result = [
117 'success' => true,
118 'code' => 200,
119 'message' => esc_html__('Credentials are valid', 'media-cloud-sync'),
120 ];
121 } catch (AwsException $e) {
122 $code = $e->getAwsErrorCode();
123
124 $validErrors = [
125 'AccessDenied',
126 'NoSuchBucket',
127 'AllAccessDisabled',
128 'AuthorizationHeaderMalformed',
129 'PermanentRedirect',
130 'InvalidBucketName',
131 ];
132
133 if (in_array($code, $validErrors)) {
134 // If we reach here, the credentials are valid
135 $result = [
136 'success' => true,
137 'code' => 200,
138 'message' => esc_html__('Credentials are valid', 'media-cloud-sync'),
139 ];
140 }
141 }
142
143 if($result['success'] == false) {
144 return $result;
145 }
146
147 try {
148 $buckets = $s3Client->listBuckets();
149 $newBucketFormat = [];
150 if(isset($buckets['Buckets']) && !empty($buckets['Buckets'])){
151 foreach($buckets['Buckets'] as $bucket) {
152 if(isset($bucket['Name'])) {
153 $newBucketFormat[] = ['Name' => $bucket['Name'], 'CreationDate' => $bucket['CreationDate']];
154 }
155 }
156 }
157 $result['buckets_data']['buckets'] = $newBucketFormat;
158 $result['buckets_data']['message'] = esc_html__('Buckets listed successfully', 'media-cloud-sync');
159 $result['buckets_data']['status'] = true;
160 } catch (S3Exception $e) {
161 $result ['buckets_data']['buckets'] = [];
162 $result ['buckets_data']['message'] = esc_html__('Unable to list buckets, Please check the bucket listing permission', 'media-cloud-sync');
163 $result ['buckets_data']['status'] = false;
164 } catch (Exception $e) {
165 $result ['buckets_data']['buckets'] = [];
166 $result ['buckets_data']['message'] = esc_html__('Unable to list buckets, Please check the bucket listing permission', 'media-cloud-sync');
167 $result ['buckets_data']['status'] = false;
168 }
169 return $result;
170 }
171 catch (S3Exception $ex) {
172 return array('message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false);
173 } catch (Exception $ex) {
174 return array('message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false);
175 }
176 }
177 return array('message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false);
178 }
179
180 /**
181 * Verify Bucket Exist
182 * @since 1.0.0
183 * @return boolean
184 */
185 public function verifyBucketExist( $config = [], $bucketConfig = [] ) {
186 $region = isset($config['region']) ? $config['region'] : '';
187 $access_key = isset($config['access_key']) ? $config['access_key'] : '';
188 $secret_key = isset($config['secret_key']) ? $config['secret_key'] : '';
189 $bucket_name = isset($bucketConfig['bucket_name']) ? $bucketConfig['bucket_name'] : '';
190 $transfer_acceleration = isset($bucketConfig['transfer_acceleration']) ? $bucketConfig['transfer_acceleration'] : false;
191
192 if (!empty($region) && !empty($access_key) && !empty($secret_key) && !empty($bucket_name)) {
193 try {
194 $s3Client = new S3Client([
195 'version' => '2006-03-01',
196 'region' => $region,
197 'use_accelerate_endpoint' => $transfer_acceleration,
198 'use_aws_shared_config_files' => false,
199 'credentials' => [
200 'key' => $access_key,
201 'secret' => $secret_key,
202 ],
203 ]);
204
205 //get S3 object
206 $bucket_found = false;
207 try {
208 $s3Client->getObject([
209 'Bucket' => $bucket_name,
210 'Key' => $this->token . '_dummy-object-for-bucket-exist-check'
211 ]);
212 $bucket_found = true;
213 } catch (AwsException $e) {
214 $code = $e->getAwsErrorCode();
215 if ($code === 'NoSuchKey') {
216 $bucket_found = true;
217 }
218 }
219
220 if($bucket_found) {
221 return array('message' => esc_html__('Bucket exist', 'media-cloud-sync'), 'code' => 200, 'success' => true);
222 } else {
223 return array('message' => esc_html__("Bucket choosen does not exist / does not have read permission", 'media-cloud-sync'), 'code' => 200, 'success' => false);
224 }
225 }
226 catch (S3Exception $ex) {
227 return array('message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false);
228 } catch (Exception $ex) {
229 return array('message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false);
230 }
231 }
232 return array('message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false);
233 }
234
235 /**
236 * Create Bucket
237 * @since 1.0.0
238 * @return boolean
239 */
240 public function createBucket( $config = [], $bucketConfig = [] ) {
241 $region = isset($config['region']) ? $config['region'] : '';
242 $access_key = isset($config['access_key']) ? $config['access_key'] : '';
243 $secret_key = isset($config['secret_key']) ? $config['secret_key'] : '';
244 $bucket_name = isset($bucketConfig['bucket_name']) ? $bucketConfig['bucket_name'] : '';
245 $transfer_acceleration = isset($bucketConfig['transfer_acceleration']) ? $bucketConfig['transfer_acceleration'] : false;
246
247 if (!empty($region) && !empty($access_key) && !empty($secret_key) && !empty($bucket_name)) {
248 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
249 }
250
251 try {
252 $s3ClientConfig = [
253 'version' => '2006-03-01',
254 'region' => $region,
255 'use_aws_shared_config_files' => false,
256 'credentials' => [
257 'key' => $access_key,
258 'secret' => $secret_key,
259 ],
260 ];
261
262 $s3Client = new S3Client($s3ClientConfig);
263
264 // Create Bucket
265 if ($region === 'us-east-1') {
266 $s3Client->createBucket([
267 'Bucket' => $bucket_name,
268 ]);
269 } else {
270 $s3Client->createBucket([
271 'Bucket' => $bucket_name,
272 'CreateBucketConfiguration' => [
273 'LocationConstraint' => $region,
274 ],
275 ]);
276 }
277
278 try {
279 $this->blockPublicAccess($bucket_name, false, $s3Client);
280 $this->putBucketPolicy($bucket_name, $s3Client);
281 $this->changeBucketOwnership($bucket_name, $s3Client);
282 try {
283
284 $this->changeTransferAccilaration($bucket_name, $s3Client, $transfer_acceleration);
285
286 return [
287 'message' => esc_html__('Bucket created successfully. Choose bucket from list to select the bucket.', 'media-cloud-sync'),
288 'data' => [
289 'Name' => $bucket_name,
290 'CreationDate' => date('Y-m-d\TH:i:s\Z'),
291 ],
292 'code' => 200,
293 'success' => true,
294 ];
295 } catch (AwsException $ex) {
296 return ['message' => esc_html__('Bucket created and made public. But the following error happened while setting the transfer accilaration,', 'media-cloud-sync') . ' ' . $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
297 }
298 } catch (AwsException $ex) {
299 return ['message' => esc_html__('Bucket created. But the following error happened while setting the public access,', 'media-cloud-sync') . ' ' . $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
300 }
301 } catch (AwsException $ex) {
302 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
303 } catch (S3Exception $ex) {
304 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
305 } catch (Exception $ex) {
306 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
307 }
308 }
309
310 /**
311 * Check Bucket Write Permission
312 * @since 1.0.0
313 */
314 public function verifyObjectWritePermission( $config = [], $bucketConfig = [] ) {
315 $region = isset($config['region']) ? $config['region'] : '';
316 $access_key = isset($config['access_key']) ? $config['access_key'] : '';
317 $secret_key = isset($config['secret_key']) ? $config['secret_key'] : '';
318 $bucket_name = isset($bucketConfig['bucket_name']) ? $bucketConfig['bucket_name'] : '';
319 $transfer_acceleration = isset($bucketConfig['transfer_acceleration']) ? $bucketConfig['transfer_acceleration'] : false;
320
321 if (empty($region) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
322 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
323 }
324
325 try {
326 $s3ClientConfig = [
327 'version' => '2006-03-01',
328 'region' => $region,
329 'use_accelerate_endpoint' => $transfer_acceleration,
330 'use_aws_shared_config_files' => false,
331 'credentials' => [
332 'key' => $access_key,
333 'secret' => $secret_key,
334 ],
335 ];
336
337 $s3Client = new S3Client($s3ClientConfig);
338
339 $object_key = Utils::generate_object_key($this->token . '_dummy-object-for-bucket-permission-check', '');
340
341
342 // Create a dummy object to check write permission
343 $s3Client->putObject([
344 'Bucket' => $bucket_name,
345 'Key' => $object_key,
346 'Body' => 'This is a test object to check write permission.',
347 ]);
348 // Check if the object was created successfully
349 if ($this->exists($object_key, $bucket_name, $s3Client)) {
350 return ['message' => esc_html__('Bucket write permission verified successfully', 'media-cloud-sync'), 'code' => 200, 'success' => true];
351 } else {
352 return ['message' => esc_html__('Bucket write permission not verified', 'media-cloud-sync'), 'code' => 200, 'success' => false];
353 }
354
355 } catch (AwsException $ex) {
356 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
357 } catch (S3Exception $ex) {
358 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
359 } catch (Exception $ex) {
360 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
361 }
362 }
363
364
365
366 /**
367 * Check Bucket Delete Permission
368 * @since 1.0.0
369 */
370 public function verifyObjectDeletePermission( $config = [], $bucketConfig = [] ) {
371 $region = isset($config['region']) ? $config['region'] : '';
372 $access_key = isset($config['access_key']) ? $config['access_key'] : '';
373 $secret_key = isset($config['secret_key']) ? $config['secret_key'] : '';
374 $bucket_name = isset($bucketConfig['bucket_name']) ? $bucketConfig['bucket_name'] : '';
375 $transfer_acceleration = isset($bucketConfig['transfer_acceleration']) ? $bucketConfig['transfer_acceleration'] : false;
376
377 if (empty($region) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
378 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
379 }
380
381 try {
382 $s3ClientConfig = [
383 'version' => '2006-03-01',
384 'region' => $region,
385 'use_accelerate_endpoint' => $transfer_acceleration,
386 'use_aws_shared_config_files' => false,
387 'credentials' => [
388 'key' => $access_key,
389 'secret' => $secret_key,
390 ],
391 ];
392
393 $s3Client = new S3Client($s3ClientConfig);
394
395 $object_key = Utils::generate_object_key($this->token . '_dummy-object-for-bucket-permission-check', '');
396
397 // Create a dummy object to check dlete permission
398 $s3Client->deleteObject([
399 'Bucket' => $bucket_name,
400 'Key' => $object_key,
401 ]);
402
403 // Check if the object was created successfully
404 if (!$this->exists($object_key, $bucket_name, $s3Client)) {
405 return ['message' => esc_html__('Bucket delete permission verified successfully', 'media-cloud-sync'), 'code' => 200, 'success' => true];
406 } else {
407 return ['message' => esc_html__('Bucket delete permission not verified', 'media-cloud-sync'), 'code' => 200, 'success' => false];
408 }
409
410 } catch (AwsException $ex) {
411 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
412 } catch (S3Exception $ex) {
413 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
414 } catch (Exception $ex) {
415 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
416 }
417 }
418
419
420 /**
421 * Check Bucket Read Permission
422 * @since 1.2.4
423 */
424 public function verifyObjectReadPermission() {
425 $result = [
426 'status' => false,
427 'message' => '',
428 'lastChecked' => time(),
429 ];
430
431 if (empty($this->s3Client) || empty($this->bucket_name)) {
432 $result['message'] = esc_html__('Invalid Request', 'media-cloud-sync');
433 Utils::set_status('cdnRead', $result);
434 return ['message' => esc_html__('Invalid Request', 'media-cloud-sync'), 'code' => 200, 'success' => false, 'lastChecked' => time()];
435 }
436
437 try {
438 $object_key = Utils::generate_object_key($this->token . '_dummy-object-for-bucket-permission-check', '');
439
440 // Check if the object was created successfully
441 if (!$this->exists($object_key)) {
442 // Create a dummy object to check write permission
443 $this->s3Client->putObject([
444 'Bucket' => $this->bucket_name,
445 'Key' => $object_key,
446 'Body' => 'This is a test object to check permission.',
447 ]);
448 }
449
450
451 $url = $this->generate_file_url($object_key);
452 $cdn_url = Cdn::may_generate_cdn_url($url, $object_key);
453 $headers = @get_headers($cdn_url);
454 if (strpos($headers[0], '200') !== false) {
455 $result['status'] = true;
456 $result['message'] = esc_html__('Objects are accessible to Read', 'media-cloud-sync');
457 } else if (strpos($headers[0], '403') !== false) {
458 $result['status'] = false;
459 if($this->cdnConfig['service'] == $this->service) {
460 $result['message'] = esc_html__('Access Denied. Please check your bucket policy. Public Read Access is required.', 'media-cloud-sync');
461 } else {
462 $result['message'] = esc_html__('Access Denied. Please check your bucket policy', 'media-cloud-sync');
463 }
464 $result['message'] = esc_html__('Access Denied. Please check your bucket policy', 'media-cloud-sync');
465 } else if (strpos($headers[0], '404') !== false) {
466 $result['status'] = false;
467 $result['message'] = esc_html__('Object not found. Please check your bucket policy', 'media-cloud-sync');
468 } else if (strpos($headers[0], '500') !== false) {
469 $result['status'] = false;
470 $result['message'] = esc_html__('Internal Server error. Please check your bucket policy', 'media-cloud-sync');
471 } else {
472 $result['status'] = false;
473 $result['message'] = esc_html__('Objects are not accessible to read', 'media-cloud-sync');
474 }
475 Utils::set_status('cdnRead', $result);
476
477 $this->deleteSingle($object_key);
478 return [
479 'message' => $result['message'],
480 'code' => 200,
481 'success' => $result['status'],
482 'lastChecked' => $result['lastChecked'],
483 ];
484 } catch (AwsException $ex) {
485 $result['message'] = $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync');
486 Utils::set_status('cdnRead', $result);
487 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false, 'lastChecked' => time()];
488 } catch (S3Exception $ex) {
489 $result['message'] = $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync');
490 Utils::set_status('cdnRead', $result);
491 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false, 'lastChecked' => time()];
492 } catch (Exception $ex) {
493 $result['message'] = $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync');
494 Utils::set_status('cdnRead', $result);
495 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false, 'lastChecked' => time()];
496 }
497 }
498
499
500 /**
501 * get Bucket Security Settings
502 */
503 public function getBucketSecuritySettings( $config = [], $bucketConfig = [] ) {
504 $region = isset($config['region']) ? $config['region'] : '';
505 $access_key = isset($config['access_key']) ? $config['access_key'] : '';
506 $secret_key = isset($config['secret_key']) ? $config['secret_key'] : '';
507 $bucket_name = isset($bucketConfig['bucket_name']) ? $bucketConfig['bucket_name'] : '';
508 $transfer_acceleration = isset($bucketConfig['transfer_acceleration']) ? $bucketConfig['transfer_acceleration'] : false;
509
510 if (empty($region) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
511 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
512 }
513
514 $errors = [];
515 try {
516 $s3ClientConfig = [
517 'version' => '2006-03-01',
518 'region' => $region,
519 'use_accelerate_endpoint' => $transfer_acceleration,
520 'use_aws_shared_config_files' => false,
521 'credentials' => [
522 'key' => $access_key,
523 'secret' => $secret_key,
524 ],
525 ];
526
527 $s3Client = new S3Client($s3ClientConfig);
528
529 // Check public access block configuration
530 $security['block_public_access'] = false;
531 try {
532 $publicAccessBlock = $s3Client->getPublicAccessBlock([
533 'Bucket' => $bucket_name,
534 ]);
535
536 $publicAccessBlockConfig = $publicAccessBlock['PublicAccessBlockConfiguration'];
537 $security = [];
538 if (
539 $publicAccessBlockConfig['BlockPublicAcls'] &&
540 $publicAccessBlockConfig['IgnorePublicAcls'] &&
541 $publicAccessBlockConfig['BlockPublicPolicy'] &&
542 $publicAccessBlockConfig['RestrictPublicBuckets']
543 ) {
544 $security['block_public_access'] = true;
545 }
546 } catch (S3Exception $ex) {
547 // If the bucket does not have public access block configuration, we assume it is not blocked
548 $errors['block_public_access'] = $ex->getMessage();
549 } catch (Exception $ex) {
550 $errors['block_public_access'] = $ex->getMessage();
551 }
552
553
554 $security['object_ownership_enforced'] = false;
555 try {
556 $ownershipControls = $s3Client->getBucketOwnershipControls([
557 'Bucket' => $bucket_name,
558 ]);
559
560 $ownershipRule = $ownershipControls['OwnershipControls']['Rules'][0]['ObjectOwnership'];
561
562 if ($ownershipRule === 'BucketOwnerEnforced') {
563 $security['object_ownership_enforced'] = true;
564 }
565 } catch (S3Exception $ex) {
566 $errors['object_ownership_enforced'] = $ex->getMessage();
567 } catch (Exception $ex) {
568 $errors['object_ownership_enforced'] = $ex->getMessage();
569 }
570
571 return ['message' => '', 'code' => 200, 'success' => empty($errors), 'security' => $security, 'errors' => $errors];
572 } catch (AwsException $ex) {
573 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
574 } catch (S3Exception $ex) {
575 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
576 } catch (Exception $ex) {
577 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
578 }
579 }
580
581
582
583 /**
584 * Change Bucket Public Access
585 */
586
587 public function changePublicAccess( $config = [], $bucketConfig = [], $value = false ) {
588 $region = isset($config['region']) ? $config['region'] : '';
589 $access_key = isset($config['access_key']) ? $config['access_key'] : '';
590 $secret_key = isset($config['secret_key']) ? $config['secret_key'] : '';
591 $bucket_name = isset($bucketConfig['bucket_name']) ? $bucketConfig['bucket_name'] : '';
592 $transfer_acceleration = isset($bucketConfig['transfer_acceleration']) ? $bucketConfig['transfer_acceleration'] : false;
593
594 if (empty($region) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
595 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
596 }
597
598 try {
599 $s3ClientConfig = [
600 'version' => '2006-03-01',
601 'region' => $region,
602 'use_accelerate_endpoint' => $transfer_acceleration,
603 'use_aws_shared_config_files' => false,
604 'credentials' => [
605 'key' => $access_key,
606 'secret' => $secret_key,
607 ],
608 ];
609
610 $s3Client = new S3Client($s3ClientConfig);
611
612 try {
613 $result = $this->blockPublicAccess($bucket_name, $value, $s3Client);
614 return ['message' => '', 'code' => 200, 'success' => true, 'result' => $result];
615 } catch (AwsException $ex) {
616 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
617 } catch (S3Exception $ex) {
618 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
619 } catch (Exception $ex) {
620 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
621 }
622 } catch (AwsException $ex) {
623 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
624 } catch (S3Exception $ex) {
625 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
626 } catch (Exception $ex) {
627 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
628 }
629 }
630
631
632
633 /**
634 * Change Bucket Ownership
635 */
636
637 public function changeObjectOwnership( $config = [], $bucketConfig = [], $value = false ) {
638 $region = isset($config['region']) ? $config['region'] : '';
639 $access_key = isset($config['access_key']) ? $config['access_key'] : '';
640 $secret_key = isset($config['secret_key']) ? $config['secret_key'] : '';
641 $bucket_name = isset($bucketConfig['bucket_name']) ? $bucketConfig['bucket_name'] : '';
642 $transfer_acceleration = isset($bucketConfig['transfer_acceleration']) ? $bucketConfig['transfer_acceleration'] : false;
643
644 if (empty($region) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
645 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
646 }
647
648 try {
649 $s3ClientConfig = [
650 'version' => '2006-03-01',
651 'region' => $region,
652 'use_accelerate_endpoint' => $transfer_acceleration,
653 'use_aws_shared_config_files' => false,
654 'credentials' => [
655 'key' => $access_key,
656 'secret' => $secret_key,
657 ],
658 ];
659
660 $s3Client = new S3Client($s3ClientConfig);
661
662 $ownership = $value ? 'BucketOwnerEnforced' : 'BucketOwnerPreferred';
663
664 try {
665 $result = $this->changeBucketOwnership( $bucket_name, $s3Client, $ownership );
666 return ['message' => '', 'code' => 200, 'success' => true, 'result' => $result];
667 } catch (AwsException $ex) {
668 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
669 } catch (S3Exception $ex) {
670 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
671 } catch (Exception $ex) {
672 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
673 }
674
675 }
676 catch (AwsException $ex) {
677 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
678 } catch (S3Exception $ex) {
679 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
680 } catch (Exception $ex) {
681 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
682 }
683 }
684
685
686
687
688 /**
689 * Block Public Access
690 * @since 1.0.0
691 */
692 private function blockPublicAccess($bucket, $block = true, $s3Client = false) {
693 if($s3Client == false) {
694 $s3Client = $this->s3Client;
695 }
696
697 if(empty($bucket)) return false;
698
699 try {
700 $s3Client->putPublicAccessBlock([
701 'Bucket' => $bucket,
702 'PublicAccessBlockConfiguration' => [
703 'BlockPublicPolicy' => $block,
704 'BlockPublicAcls' => $block,
705 'IgnorePublicAcls' => $block,
706 'RestrictPublicBuckets' => $block,
707 ]
708 ]);
709
710 return true;
711 } catch (AwsException $ex) {
712 return false;
713 }
714 }
715
716 /**
717 * Add Bucket Policy
718 */
719 private function putBucketPolicy($bucket, $s3Client = false) {
720 if($s3Client == false) {
721 $s3Client = $this->s3Client;
722 }
723
724 if(empty($bucket)) return false;
725
726 $policy = json_encode([
727 "Version" => "2012-10-17",
728 "Statement" => [
729 [
730 "Effect" => "Allow",
731 "Principal" => "*",
732 "Action" => [
733 "s3:DeleteObjectTagging",
734 "s3:ListBucketMultipartUploads",
735 "s3:DeleteObjectVersion",
736 "s3:ListBucket",
737 "s3:DeleteObjectVersionTagging",
738 "s3:GetBucketAcl",
739 "s3:ListMultipartUploadParts",
740 "s3:PutObject",
741 "s3:GetObjectAcl",
742 "s3:GetObject",
743 "s3:AbortMultipartUpload",
744 "s3:DeleteObject",
745 "s3:GetBucketLocation",
746 "s3:PutObjectAcl",
747 "s3:putBucketOwnershipControls",
748 "s3:putBucketPolicy"
749 ],
750 "Resource" => [
751 "arn:aws:s3:::$bucket/*",
752 "arn:aws:s3:::$bucket"
753 ]
754 ]
755 ]
756 ]);
757
758 try {
759 // Add bucket policy
760 $s3Client->putBucketPolicy(['Bucket' => $bucket, 'Policy' => $policy]);
761
762 return true;
763 } catch (AwsException $ex) {
764 return false;
765 } catch (S3Exception $ex) {
766 return false;
767 } catch (Exception $ex) {
768 return false;
769 }
770 }
771
772 /**
773 * Add Bucket Ownership
774 */
775 private function changeBucketOwnership($bucket, $s3Client = false, $ownership = 'BucketOwnerPreferred') {
776 if($s3Client == false) {
777 $s3Client = $this->s3Client;
778 }
779
780 if(empty($bucket)) return false;
781
782 try {
783 // Change object ownership ACL enabled
784 $s3Client->putBucketOwnershipControls([
785 'Bucket' => $bucket,
786 'OwnershipControls' => [
787 'Rules' => [['ObjectOwnership' => $ownership]],
788 ],
789 ]);
790
791 return true;
792 } catch (AwsException $ex) {
793 return false;
794 } catch (S3Exception $ex) {
795 return false;
796 } catch (Exception $ex) {
797 return false;
798 }
799 }
800
801 /**
802 * Change transfer accilaration
803 */
804 private function changeTransferAccilaration($bucket, $s3Client = false, $enable=false, $force = false) {
805 if($s3Client == false) {
806 $s3Client = $this->s3Client;
807 }
808
809 if(empty($bucket)) return false;
810 if(!$force && !$enable) return true;
811
812 // Check if the bucket already has transfer acceleration enabled
813 try {
814 $s3Client->putBucketAccelerateConfiguration([
815 'Bucket' => $bucket,
816 'AccelerateConfiguration' => [
817 'Status' => $enable ? 'Enabled' : 'Suspended'
818 ]
819 ]);
820 return true;
821 } catch (AwsException $ex) {
822 return false;
823 } catch (S3Exception $ex) {
824 return false;
825 } catch (Exception $ex) {
826 return false;
827 }
828 }
829
830 /**
831 * isConfigured Function To Identify the congfigurations are correct
832 * @since 1.0.0
833 */
834 public function isConfigured(){
835 if ($this->s3Client) {
836 try {
837 $this->s3Client->listObjectsV2([
838 'Bucket' => $this->token . '_dummy-bucket-for-auth-check'
839 ]);
840
841 // If we reach here, the credentials are valid
842 return true;
843 } catch (AwsException $ex) {
844 $code = $e->getAwsErrorCode();
845
846 $validErrors = [
847 'AccessDenied',
848 'NoSuchBucket',
849 'AllAccessDisabled',
850 'AuthorizationHeaderMalformed',
851 'PermanentRedirect',
852 'InvalidBucketName'
853 ];
854
855 if (in_array($code, $validErrors)) {
856 // If we reach here, the credentials are valid
857 return true;
858 } else {
859 return false;
860 }
861 } catch (S3Exception $ex) {
862 return false;
863 } catch (Exception $ex) {
864 return false;
865 }
866 }
867 return false;
868 }
869
870 /**
871 * Make Object Private
872 * @since 1.0.0
873 *
874 */
875 public function toPrivate($key) {
876 if(!$key) return false;
877 try {
878 $this->s3Client->putObjectAcl([
879 'Bucket' => $this->bucket_name,
880 'Key' => $key,
881 'ACL' => 'private'
882 ]);
883 return true;
884 } catch (AwsException $ex) {
885 return false;
886 }
887 return false;
888 }
889
890
891
892 /**
893 * Make Object Public
894 * @since 1.0.0
895 *
896 */
897 public function toPublic($key) {
898 if(!$key) return false;
899 try {
900 $this->s3Client->putObjectAcl([
901 'Bucket' => $this->bucket_name,
902 'Key' => $key,
903 'ACL' => 'public-read'
904 ]);
905 return true;
906 } catch (AwsException $ex) {
907 return false;
908 }
909 return false;
910 }
911
912
913
914 /**
915 * Check the object exist
916 * @since 1.1.8
917 */
918 public function exists($key, $bucket_name = '', $client = null) {
919 if(!$key) return false;
920 try {
921 $bucket_name = $bucket_name ? $bucket_name : $this->bucket_name;
922 $client = $client ?? $this->s3Client;
923 if($client->doesObjectExistV2( $bucket_name, $key)) {
924 return true;
925 }
926 } catch (AwsException $ex) {
927 return false;
928 } catch (S3Exception $ex) {
929 return false;
930 } catch (Exception $ex) {
931 return false;
932 }
933 }
934
935 /**
936 * Upload Single
937 * @since 1.0.0
938 * @return boolean
939 */
940 public function uploadSingle($absolute_source_path, $relative_source_path, $prefix='') {
941 $result = array();
942 if (
943 isset($absolute_source_path) && !empty($absolute_source_path) &&
944 isset($relative_source_path) && !empty($relative_source_path)
945 ) {
946 $file_name = wp_basename( $relative_source_path );
947 if ($file_name) {
948 $upload_path = Utils::generate_object_key($relative_source_path, $prefix);
949
950 // Decide Multipart upload or normal put object
951 if (filesize($absolute_source_path) <= Schema::getConstant('S3_MULTIPART_MIN_FILE_SIZE')) {
952 // Upload a publicly accessible file. The file size and type are determined by the SDK.
953 try {
954 $handle = fopen($absolute_source_path, 'rb');
955
956 $upload = $this->s3Client->putObject([
957 'Bucket' => $this->bucket_name,
958 'Key' => $upload_path,
959 'Body' => $handle,
960 ]);
961
962 if (is_resource($handle)) {
963 fclose($handle);
964 }
965
966 $result = array(
967 'success' => true,
968 'code' => 200,
969 'file_url' => $this->generate_file_url($upload_path),
970 'key' => $upload_path,
971 'message' => esc_html__('File Uploaded Successfully', 'media-cloud-sync')
972 );
973 } catch (AwsException $e) {
974 $result = array(
975 'success' => false,
976 'code' => 200,
977 'message' => $e->getMessage()
978 );
979 }
980 } else {
981 $multiUploader = new MultipartUploader($this->s3Client, $absolute_source_path, [
982 'bucket' => $this->bucket_name,
983 'key' => $upload_path,
984 ]);
985
986 try {
987 do {
988 try {
989 $uploaded = $multiUploader->upload();
990 } catch (MultipartUploadException $e) {
991 $multiUploader = new MultipartUploader($this->s3Client, $absolute_source_path, [
992 'state' => $e->getState(),
993 ]);
994 }
995 } while (!isset($uploaded));
996
997 if (isset($uploaded['ObjectURL']) && !empty($uploaded['ObjectURL'])) {
998 $result = array(
999 'success' => true,
1000 'code' => 200,
1001 'file_url' => $this->generate_file_url($upload_path),
1002 'key' => $upload_path,
1003 'message' => esc_html__('File Uploaded Successfully', 'media-cloud-sync')
1004 );
1005 } else {
1006 $result = array(
1007 'success' => false,
1008 'code' => 200,
1009 'message' => esc_html__('Something happened while uploading to server', 'media-cloud-sync')
1010 );
1011 }
1012 } catch (MultipartUploadException $e) {
1013 $result = array(
1014 'success' => false,
1015 'code' => 200,
1016 'message' => $e->getMessage()
1017 );
1018 }
1019 }
1020 } else {
1021 $result = array(
1022 'success' => false,
1023 'code' => 200,
1024 'message' => esc_html__('Check the file you are trying to upload. Please try again', 'media-cloud-sync')
1025 );
1026 }
1027 } else {
1028 $result = array(
1029 'success' => false,
1030 'code' => 200,
1031 'message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync')
1032 );
1033 }
1034 return $result;
1035 }
1036
1037 /**
1038 * Save object to server
1039 * @since 1.0.0
1040 */
1041 public function object_to_server($key, $save_path) {
1042 try {
1043 $getObject = $this->s3Client->GetObject([
1044 'Bucket' => $this->bucket_name,
1045 'Key' => $key,
1046 'SaveAs' => $save_path
1047 ]);
1048 if (file_exists($save_path)) {
1049 return true;
1050 }
1051 } catch (AwsException $e) {
1052 return false;
1053 }
1054 return false;
1055 }
1056
1057
1058 /**
1059 * Copy object to new path
1060 * @since 1.3.4
1061 */
1062 public function copy_to_new_path($key, $new_path) {
1063 try {
1064 // Step 1: Verify object exists at old location
1065 if (!$this->exists($key)) {
1066 return [
1067 'message' => esc_html__('Original file not found' , 'media-cloud-sync'),
1068 'code' => 200,
1069 'success' => false
1070 ];
1071 }
1072 // Step 2: Copy object
1073 if (!$this->exists($new_path)) {
1074 $this->s3Client->copyObject([
1075 'Bucket' => $this->bucket_name,
1076 'CopySource' => "{$this->bucket_name}/{$key}",
1077 'Key' => $new_path,
1078 'MetadataDirective' => 'COPY',
1079 ]);
1080 }
1081
1082 // Step 3: Verify object exists at new location
1083 if ($this->exists($new_path)) {
1084 return [
1085 'success' => true,
1086 'code' => 200,
1087 'message' => esc_html__('File copied successfully', 'media-cloud-sync')
1088 ];
1089 }
1090 } catch (AwsException $e) {
1091 return [
1092 'success' => false,
1093 'code' => 200,
1094 'message' => $e->getMessage()
1095 ];
1096 }
1097 return [
1098 'success' => false,
1099 'code' => 200,
1100 'message' => esc_html__('Something went wrong', 'media-cloud-sync')
1101 ];
1102 }
1103
1104
1105 /**
1106 * Delete Single
1107 * @since 1.0.0
1108 * @return boolean
1109 */
1110 public function deleteSingle($key) {
1111 $result = array();
1112 if (isset($key) && !empty($key)) {
1113 try {
1114 $this->s3Client->deleteObject([
1115 'Bucket' => $this->bucket_name,
1116 'Key' => $key
1117 ]);
1118
1119 if (!$this->exists($key)) {
1120 $result = array(
1121 'success' => true,
1122 'code' => 200,
1123 'message' => esc_html__('Deleted Successfully', 'media-cloud-sync')
1124 );
1125 } else {
1126 $result = array(
1127 'success' => false,
1128 'code' => 200,
1129 'message' => esc_html__('File not deleted', 'media-cloud-sync')
1130 );
1131 }
1132 } catch (AwsException $e) {
1133 $result = array(
1134 'success' => false,
1135 'code' => 200,
1136 'message' => $e->getMessage()
1137 );
1138 }
1139 } else {
1140 $result = array(
1141 'success' => false,
1142 'code' => 200,
1143 'message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync')
1144 );
1145 }
1146 return $result;
1147 }
1148
1149 /**
1150 * get private URL
1151 * @since 1.0.0
1152 * @return boolean
1153 */
1154 public function get_private_url($key) {
1155 $result = array();
1156 if (isset($key) && !empty($key)) {
1157 try {
1158 $cmd = $this->s3Client->getCommand('GetObject', [
1159 'Bucket' => $this->bucket_name,
1160 'Key' => $key
1161 ]);
1162
1163 $expires = isset($this->settings['private_url_expire']) ? $this->settings['private_url_expire'] : 20;
1164
1165 $request = $this->s3Client->createPresignedRequest($cmd, sprintf('+%s minutes', $expires));
1166
1167 if ($privateUrl = (string)$request->getUri()) {
1168 $result = array(
1169 'success' => true,
1170 'code' => 200,
1171 'file_url' => $privateUrl,
1172 'message' => esc_html__('Got Private URL Successfully', 'media-cloud-sync')
1173 );
1174 } else {
1175 $result = array(
1176 'success' => false,
1177 'code' => 200,
1178 'message' => esc_html__('Error getting private URL', 'media-cloud-sync')
1179 );
1180 }
1181 } catch (AwsException $e) {
1182 $result = array(
1183 'success' => false,
1184 'code' => 200,
1185 'message' => $e->getMessage()
1186 );
1187 }
1188 } else {
1189 $result = array(
1190 'success' => false,
1191 'code' => 200,
1192 'message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync')
1193 );
1194 }
1195 return $result;
1196 }
1197
1198 /**
1199 * Generate file URL
1200 */
1201 public function generate_file_url($key){
1202 $domain = $this->get_domain();
1203
1204 return apply_filters('wpmcs_generate_s3_file_url',
1205 $domain . '/' . $key,
1206 $domain, $key
1207 );
1208 }
1209
1210 /**
1211 * Is Provider URL
1212 * @since 1.3.6
1213 */
1214 public function is_provider_url($url) {
1215 $domain = $this->get_domain();
1216 return (strpos($url, $domain . '/') !== false);
1217 }
1218
1219 /**
1220 * Get domain URL
1221 */
1222 public function get_domain() {
1223 $region = isset($this->config['region']) ? $this->config['region'] : '';
1224 return "https://{$this->bucket_name}.s3.{$region}.amazonaws.com";
1225 }
1226
1227 }