1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
storage_service_s3.php
См. документацию.
1<?php
3
5{
6 protected $set_headers = /*.(array[string]string).*/[];
7 protected $new_end_point = '';
8 protected $_public = true;
9 protected $location = '';
10
14 public function GetObject()
15 {
16 return new CCloudStorageService_S3();
17 }
18
22 public function GetID()
23 {
24 return 'generic_s3';
25 }
26
30 public function GetName()
31 {
32 return 'S3 compatible storage';
33 }
34
38 public function GetLocationList()
39 {
40 return false;
41 }
42
50 public function GetSettingsHTML($arBucket, $bServiceSet, $cur_SERVICE_ID, $bVarsFromForm)
51 {
53 {
54 $arSettings = $_POST['SETTINGS'][$this->GetID()];
55 }
56 else
57 {
58 $arSettings = unserialize($arBucket['SETTINGS'], ['allowed_classes' => false]);
59 }
60
61 if (!is_array($arSettings))
62 {
63 $arSettings = [
64 'HOST' => '',
65 'ACCESS_KEY' => '',
66 'SECRET_KEY' => '',
67 ];
68 }
69
70 $htmlID = htmlspecialcharsbx($this->GetID());
71 $show = (($cur_SERVICE_ID === $this->GetID()) || !$bServiceSet) ? '' : 'none';
72 $useHttps = $arSettings['USE_HTTPS'] ?? 'N';
73
74 $result = '
75 <tr id="SETTINGS_0_' . $htmlID . '" style="display:' . $show . '" class="settings-tr adm-detail-required-field">
76 <td>' . GetMessage('CLO_STORAGE_S3_EDIT_HOST') . ':</td>
77 <td><input type="hidden" name="SETTINGS[' . $htmlID . '][HOST]" id="' . $htmlID . 'HOST" value="' . htmlspecialcharsbx($arSettings['HOST']) . '"><input type="text" size="55" name="' . $htmlID . 'INP_HOST" id="' . $htmlID . 'INP_HOST" value="' . htmlspecialcharsbx($arSettings['HOST']) . '" ' . ($arBucket['READ_ONLY'] == 'Y' ? '"disabled"' : '') . ' onchange="BX(\'' . $htmlID . 'HOST\').value = this.value"></td>
78 </tr>
79 <tr id="SETTINGS_1_' . $htmlID . '" style="display:' . $show . '" class="settings-tr adm-detail-required-field">
80 <td>' . GetMessage('CLO_STORAGE_S3_EDIT_ACCESS_KEY') . ':</td>
81 <td><input type="hidden" name="SETTINGS[' . $htmlID . '][ACCESS_KEY]" id="' . $htmlID . 'ACCESS_KEY" value="' . htmlspecialcharsbx($arSettings['ACCESS_KEY']) . '"><input type="text" size="55" name="' . $htmlID . 'INP_ACCESS_KEY" id="' . $htmlID . 'INP_ACCESS_KEY" value="' . htmlspecialcharsbx($arSettings['ACCESS_KEY']) . '" ' . ($arBucket['READ_ONLY'] === 'Y' ? '"disabled"' : '') . ' onchange="BX(\'' . $htmlID . 'ACCESS_KEY\').value = this.value"></td>
82 </tr>
83 <tr id="SETTINGS_2_' . $htmlID . '" style="display:' . $show . '" class="settings-tr adm-detail-required-field">
84 <td>' . GetMessage('CLO_STORAGE_S3_EDIT_SECRET_KEY') . ':</td>
85 <td><input type="hidden" name="SETTINGS[' . $htmlID . '][SECRET_KEY]" id="' . $htmlID . 'SECRET_KEY" value="' . htmlspecialcharsbx($arSettings['SECRET_KEY']) . '"><input type="text" size="55" name="' . $htmlID . 'INP_SECRET_KEY" id="' . $htmlID . 'INP_SECRET_KEY" value="' . htmlspecialcharsbx($arSettings['SECRET_KEY']) . '" autocomplete="off" ' . ($arBucket['READ_ONLY'] === 'Y' ? '"disabled"' : '') . ' onchange="BX(\'' . $htmlID . 'SECRET_KEY\').value = this.value"></td>
86 </tr>
87 <tr id="SETTINGS_3_' . $htmlID . '" style="display:' . $show . '" class="settings-tr">
88 <td>' . GetMessage('CLO_STORAGE_S3_EDIT_USE_HTTPS') . ':</td>
89 <td><input type="hidden" name="SETTINGS[' . $htmlID . '][USE_HTTPS]" id="' . $htmlID . 'KEY" value="N"><input type="checkbox" name="SETTINGS[' . $htmlID . '][USE_HTTPS]" id="' . $htmlID . 'USE_HTTPS" value="Y" ' . ($useHttps == 'Y' ? 'checked="checked"' : '') . '></td>
90 </tr>
91 ';
92 return $result;
93 }
94
100 public function CheckSettings($arBucket, &$arSettings)
101 {
102 global $APPLICATION;
103 $aMsg = /*.(array[int][string]string).*/[];
104
105 $result = [
106 'HOST' => is_array($arSettings) ? trim($arSettings['HOST']) : '',
107 'ACCESS_KEY' => is_array($arSettings) ? trim($arSettings['ACCESS_KEY']) : '',
108 'SECRET_KEY' => is_array($arSettings) ? trim($arSettings['SECRET_KEY']) : '',
109 'USE_HTTPS' => is_array($arSettings) && $arSettings['USE_HTTPS'] == 'Y' ? 'Y' : 'N',
110 ];
111
112 if ($arBucket['READ_ONLY'] !== 'Y' && $result['HOST'] === '')
113 {
114 $aMsg[] = [
115 'id' => $this->GetID() . 'INP_HOST',
116 'text' => GetMessage('CLO_STORAGE_S3_EMPTY_HOST'),
117 ];
118 }
119
120 if ($arBucket['READ_ONLY'] !== 'Y' && $result['ACCESS_KEY'] === '')
121 {
122 $aMsg[] = [
123 'id' => $this->GetID() . 'INP_ACCESS_KEY',
124 'text' => GetMessage('CLO_STORAGE_S3_EMPTY_ACCESS_KEY'),
125 ];
126 }
127
128 if ($arBucket['READ_ONLY'] !== 'Y' && $result['SECRET_KEY'] === '')
129 {
130 $aMsg[] = [
131 'id' => $this->GetID() . 'INP_SECRET_KEY',
132 'text' => GetMessage('CLO_STORAGE_S3_EMPTY_SECRET_KEY'),
133 ];
134 }
135
136 if (!empty($aMsg))
137 {
138 $e = new CAdminException($aMsg);
139 $APPLICATION->ThrowException($e);
140 return false;
141 }
142 else
143 {
144 $arSettings = $result;
145 }
146
147 return true;
148 }
149
155 public function hmacsha1($data, $key)
156 {
157 if (mb_strlen($key) > 64)
158 {
159 $key = pack('H*', sha1($key));
160 }
161 $key = str_pad($key, 64, chr(0x00));
162 $ipad = str_repeat(chr(0x36), 64);
163 $opad = str_repeat(chr(0x5c), 64);
164 $hmac = pack('H*', sha1(($key ^ $opad) . pack('H*', sha1(($key ^ $ipad) . $data))));
165 return $hmac;
166 }
167
179 public function SignRequest($arSettings, $RequestMethod, $bucket, $RequestURI, $ContentType, $additional_headers, $params = '', $content = '', $Service = 's3')
180 {
181 if (is_resource($content))
182 {
183 $streamPosition = ftell($content);
184 $hashResource = hash_init('sha256');
185 hash_update_stream($hashResource, $content);
186 $HashedPayload = hash_final($hashResource);
187 fseek($content, $streamPosition);
188 }
189 else
190 {
191 $HashedPayload = hash('sha256', $content, false);
192 }
193 $additional_headers['x-amz-content-sha256'] = $HashedPayload;
194
195 $Time = time();
196 $RequestDate = gmdate('Ymd', $Time);
197 $RequestTime = gmdate('Ymd', $Time) . 'T' . gmdate('His', $Time) . 'Z';
198 $additional_headers['x-amz-date'] = $RequestTime;
199
200 do
201 {
202 $CanonicalizedResource = $RequestURI !== '' ? str_replace('%2F', '/', $RequestURI) : '/';
203 }
204 while (strpos($CanonicalizedResource, '%2F') !== false);
205
206 $CanonicalQuery = explode('&', ltrim($params, '?'));
207 sort($CanonicalQuery);
208 $CanonicalQueryString = str_replace('%7E', '~', implode('&', $CanonicalQuery));
209
210 $CanonicalHeaders = /*.(array[string]string).*/ [];
211 foreach ($additional_headers as $key => $value)
212 {
213 $key = mb_strtolower($key);
214 if (isset($CanonicalHeaders[$key]))
215 {
216 $CanonicalHeaders[$key] .= ',';
217 }
218 else
219 {
220 $CanonicalHeaders[$key] = $key . ':';
221 }
222 $CanonicalHeaders[$key] .= trim($value, " \t\n\r");
223 }
224 ksort($CanonicalHeaders);
225 $CanonicalHeadersString = implode("\n", $CanonicalHeaders);
226 $SignedHeaders = implode(';', array_keys($CanonicalHeaders));
227
228 $CanonicalRequest = '';
229 $CanonicalRequest .= $RequestMethod . "\n";
230 $CanonicalRequest .= $CanonicalizedResource . "\n";
231 $CanonicalRequest .= $CanonicalQueryString . "\n";
232 $CanonicalRequest .= $CanonicalHeadersString . "\n\n";
233 $CanonicalRequest .= $SignedHeaders . "\n";
234 $CanonicalRequest .= $HashedPayload;
235
236 $Algorithm = 'AWS4-HMAC-SHA256';
237 $Region = $this->location ?: 'us-east-1';
238 $Scope = $RequestDate . '/' . $Region . '/' . $Service . '/aws4_request';
239
240 $StringToSign = '';
241 $StringToSign .= $Algorithm . "\n";
242 $StringToSign .= $RequestTime . "\n";
243 $StringToSign .= $Scope . "\n";
244 $StringToSign .= hash('sha256', $CanonicalRequest, false);
245
246 $kSecret = $arSettings['SECRET_KEY'];
247 $kDate = hash_hmac('sha256', $RequestDate, 'AWS4' . $kSecret, true);
248 $kRegion = hash_hmac('sha256', $Region, $kDate, true);
249 $kService = hash_hmac('sha256', $Service, $kRegion, true);
250 $kSigning = hash_hmac('sha256', 'aws4_request', $kService, true);
251
252 $Signature = hash_hmac('sha256', $StringToSign, $kSigning, false);
253
254 $Authorization = $Algorithm . ' Credential=' . $arSettings['ACCESS_KEY'] . '/' . $Scope . ',SignedHeaders=' . $SignedHeaders . ',Signature=' . $Signature;
255
256 return [
257 'Date' => $RequestTime,
258 'Authorization' => $Authorization,
259 'x-amz-date' => $RequestTime,
260 'x-amz-content-sha256' => $HashedPayload,
261 ];
262 }
263
268 public function SetLocation($location)
269 {
270 if ($location)
271 {
272 $this->location = $location;
273 }
274 else
275 {
276 $this->location = '';
277 }
278 }
279
284 protected function GetRequestHost($bucket, $arSettings)
285 {
286 if (
287 $this->new_end_point != ''
288 && preg_match('#^(http|https)://' . preg_quote($bucket, '#') . '(.+?)/#', $this->new_end_point, $match) > 0
289 )
290 {
291 return $bucket . $match[2];
292 }
293 else
294 {
295 if ($bucket !== '')
296 {
297 return $bucket . '.' . $arSettings['HOST'];
298 }
299 else
300 {
301 return $arSettings['HOST'];
302 }
303 }
304 }
305
316 public function SendRequest($arSettings, $verb, $bucket, $file_name='/', $params='', $content='', $additional_headers=/*.(array[string]string).*/[])
317 {
318 global $APPLICATION;
319 $this->status = 0;
320
322 'redirect' => false,
323 'streamTimeout' => $this->streamTimeout,
324 ]);
325 if (isset($additional_headers['option-file-result']))
326 {
327 $request->setOutputStream($additional_headers['option-file-result']);
328 }
329
330 if (isset($additional_headers['Content-Type']))
331 {
332 $ContentType = $additional_headers['Content-Type'];
333 }
334 else
335 {
336 $ContentType = $content !== '' ? 'text/plain' : '';
337 }
338 unset($additional_headers['Content-Type']);
339
340 foreach ($this->set_headers as $key => $value)
341 {
342 $additional_headers[$key] = $value;
343 }
344
345 if (array_key_exists('SESSION_TOKEN', $arSettings))
346 {
347 $additional_headers['x-amz-security-token'] = $arSettings['SESSION_TOKEN'];
348 }
349
350 $host = $additional_headers['Host'] = $this->GetRequestHost($bucket, $arSettings);
351
352 foreach ($this->SignRequest($arSettings, $verb, $bucket, $file_name, $ContentType, $additional_headers, $params, $content) as $key => $value)
353 {
354 $request->setHeader($key, $value);
355 }
356
357 foreach ($additional_headers as $key => $value)
358 {
359 if (!preg_match('/^option-/', $key))
360 {
361 $request->setHeader($key, $value);
362 }
363 }
364
365 $was_end_point = $this->new_end_point;
366 $this->new_end_point = '';
367
368 $useHttps = $arSettings['USE_HTTPS'] ?? 'N';
369 $this->status = 0;
370 $this->host = $host;
371 $this->verb = $verb;
372 $this->url = ($useHttps === 'Y' ? 'https' : 'http') . '://' . $host . $file_name . $params;
373 $this->headers = [];
374 $this->errno = 0;
375 $this->errstr = '';
376 $this->result = '';
377
378 $stime = 0;
379 $logRequest = false;
380 if (defined('BX_CLOUDS_TRACE') && $verb !== 'GET' && $verb !== 'HEAD')
381 {
382 $stime = microtime(1);
383 $logRequest = [
384 'request_id' => md5((string)mt_rand()),
385 'portal' => $_SERVER['HTTP_HOST'],
386 'verb' => $this->verb,
387 'url' => $this->url,
388 ];
389 if (function_exists('getmypid'))
390 {
391 $logRequest['pid'] = getmypid();
392 }
393 AddMessage2Log(json_encode($logRequest), 'clouds', 20);
394 }
395
396 $request->setHeader('Content-type', $ContentType);
397 $request->query($this->verb, $this->url, $content);
398
399 $this->status = $request->getStatus();
400 foreach ($request->getHeaders() as $key => $value)
401 {
402 $this->headers[$key] = is_array($value) ? $value[0] : $value;
403 }
404 $this->errstr = implode("\n", $request->getError());
405 $this->errno = $this->errstr ? 255 : 0;
406 $this->result = $request->getResult();
407
408 if ($logRequest)
409 {
410 $logRequest['status'] = $this->status;
411 $logRequest['time'] = round(microtime(true) - $stime, 6);
412 $logRequest['headers'] = $this->headers;
413 AddMessage2Log(json_encode($logRequest), 'clouds', 0);
414 }
415
416 if ($this->status == 200)
417 {
418 if (
419 isset($additional_headers['option-raw-result'])
420 || isset($additional_headers['option--result'])
421 )
422 {
423 return $this->result;
424 }
425 elseif ($this->result !== '')
426 {
427 $obXML = new CDataXML;
428 $text = preg_replace('/<' . '\\?XML.*?\\?' . '>/i', '', $this->result);
429 if ($obXML->LoadString($text))
430 {
431 $arXML = $obXML->GetArray();
432 if (is_array($arXML))
433 {
434 return $arXML;
435 }
436 }
437 //XML parse error
438 $e = new CApplicationException(GetMessage('CLO_STORAGE_S3_XML_PARSE_ERROR', ['#errno#' => '1']));
439 $APPLICATION->ThrowException($e);
440 return false;
441 }
442 else
443 {
444 //Empty success result
445 return [];
446 }
447 }
448 elseif (
449 $this->status == 307 //Temporary redirect
450 && isset($this->headers['Location'])
451 && $was_end_point === '' //No recurse yet
452 )
453 {
454 $this->new_end_point = $this->headers['Location'];
455 return $this->SendRequest(
456 $arSettings,
457 $verb,
458 $bucket,
459 $file_name,
460 $params,
461 $content,
462 $additional_headers
463 );
464 }
465 elseif ($this->status > 0)
466 {
467 if ($this->result !== '')
468 {
469 $obXML = new CDataXML;
470 if ($obXML->LoadString($this->result))
471 {
472 $node = $obXML->SelectNodes('/Error/Message');
473 if (is_object($node))
474 {
475 $errorMessage = trim($node->textContent(), '.');
476 $e = new CApplicationException(GetMessage('CLO_STORAGE_S3_XML_ERROR', [
477 '#errmsg#' => $errorMessage,
478 ]));
479 $APPLICATION->ThrowException($e);
480 return false;
481 }
482 $node = $obXML->SelectNodes('/Error/Code');
483 if (is_object($node))
484 {
485 $errorMessage = trim($node->textContent(), '.');
486 $e = new CApplicationException(GetMessage('CLO_STORAGE_S3_XML_ERROR', [
487 '#errmsg#' => $errorMessage,
488 ]));
489 $APPLICATION->ThrowException($e);
490 return false;
491 }
492 }
493 }
494 $e = new CApplicationException(GetMessage('CLO_STORAGE_S3_XML_PARSE_ERROR', ['#errno#' => '2']));
495 $APPLICATION->ThrowException($e);
496 return false;
497 }
498 else
499 {
500 $e = new CApplicationException(GetMessage('CLO_STORAGE_S3_XML_PARSE_ERROR', ['#errno#' => '3']));
501 $APPLICATION->ThrowException($e);
502 return false;
503 }
504 }
505
506 public function ListBuckets($arBucket)
507 {
508 $result = [
509 'bucket' => [],
510 'ctime' => [],
511 ];
512
513 $this->SetLocation($arBucket['LOCATION']);
514 while (true)
515 {
516 $response = $this->SendRequest(
517 $arBucket['SETTINGS'],
518 'GET',
519 '',
520 '/',
521 ''
522 );
523
524 if (
525 $this->status == 200
526 && is_array($response)
527 && isset($response['ListAllMyBucketsResult'])
528 && is_array($response['ListAllMyBucketsResult'])
529 && isset($response['ListAllMyBucketsResult']['#'])
530 && is_array($response['ListAllMyBucketsResult']['#'])
531 )
532 {
533 $ListAllMyBucketsResult = $response['ListAllMyBucketsResult']['#'];
534 if (
535 isset($ListAllMyBucketsResult['Buckets'])
536 && is_array($ListAllMyBucketsResult['Buckets'])
537 && isset($ListAllMyBucketsResult['Buckets'][0])
538 && is_array($ListAllMyBucketsResult['Buckets'][0])
539 && isset($ListAllMyBucketsResult['Buckets'][0]['#'])
540 && is_array($ListAllMyBucketsResult['Buckets'][0]['#'])
541 )
542 {
543 foreach ($ListAllMyBucketsResult['Buckets'][0]['#']['Bucket'] as $Bucket)
544 {
545 $Name = $Bucket['#']['Name'][0]['#'];
546 $CreationDate = $Bucket['#']['CreationDate'][0]['#'];
547 $result['bucket'][] = urldecode($Name);
548 $result['ctime'][] = strtotime($CreationDate);
549 }
550 }
551 }
552 elseif ($this->checkForTokenExpiration($this->status, $this->result))
553 {
554 $this->tokenHasExpired = true;
555 return false;
556 }
557 else
558 {
559 if (defined('BX_CLOUDS_ERROR_DEBUG'))
560 {
561 AddMessage2Log($this);
562 }
563 return false;
564 }
565 break;
566 }
567
568 return $result;
569 }
570
575 public function CreateBucket($arBucket)
576 {
577 global $APPLICATION;
578
579 $arFiles = $this->ListFiles($arBucket, '/', false, 1);
580 if (is_array($arFiles))
581 {
582 return true;
583 }
584
585 // The bucket already exists and user has specified wrong location
586 if (
587 $this->status == 301
588 && $arBucket['LOCATION'] != ''
589 && $this->GetLastRequestHeader('x-amz-bucket-region') !== ''
590 && $this->GetLastRequestHeader('x-amz-bucket-region') !== $arBucket['LOCATION']
591 )
592 {
593 return false;
594 }
595
596 if ($arBucket['LOCATION'] != '')
597 {
598 $content =
599 '<CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">'
600 . '<LocationConstraint>' . $arBucket['LOCATION'] . '</LocationConstraint>'
601 . '</CreateBucketConfiguration>';
602 }
603 else
604 {
605 $content = '';
606 }
607
608 $this->SetLocation($arBucket['LOCATION']);
609 $response = $this->SendRequest(
610 $arBucket['SETTINGS'],
611 'PUT',
612 '',
613 '/' . $arBucket['BUCKET'],
614 '',
616 );
617
618 if ($this->status == 409/*Already exists*/)
619 {
620 $APPLICATION->ResetException();
621 return true;
622 }
623 elseif (is_array($response))
624 {
625 return true;
626 }
627 else
628 {
629 if (defined('BX_CLOUDS_ERROR_DEBUG'))
630 {
631 AddMessage2Log($this);
632 }
633 return false;
634 }
635 }
636
641 public function IsEmptyBucket($arBucket)
642 {
643 global $APPLICATION;
644
645 $this->SetLocation($arBucket['LOCATION']);
646 $response = $this->SendRequest(
647 $arBucket['SETTINGS'],
648 'GET',
649 '',
650 '/' . $arBucket['BUCKET'],
651 '?max-keys=1' . ($arBucket['PREFIX'] != '' ? '&prefix=' . $arBucket['PREFIX'] . '/' : '')
652 );
653
654 if ($this->status == 404 || $this->status == 403)
655 {
656 $APPLICATION->ResetException();
657 return true;
658 }
659 elseif (is_array($response))
660 {
661 if (
662 !isset($response['ListBucketResult'])
663 || !is_array($response['ListBucketResult'])
664 || !isset($response['ListBucketResult']['#'])
665 || !is_array($response['ListBucketResult']['#'])
666 || !isset($response['ListBucketResult']['#']['Contents'])
667 || !is_array($response['ListBucketResult']['#']['Contents'])
668 )
669 {
670 return true;
671 }
672
673 return false;
674 }
675
676 if (defined('BX_CLOUDS_ERROR_DEBUG'))
677 {
678 AddMessage2Log($this);
679 }
680
681 return false;
682 }
683
688 public function DeleteBucket($arBucket)
689 {
690 global $APPLICATION;
691
692 if ($arBucket['PREFIX'] != '')
693 {
694 //Do not delete bucket if there is some files left
695 if (!$this->IsEmptyBucket($arBucket))
696 {
697 return false;
698 }
699
700 //Let's pretend we deleted the bucket
701 return true;
702 }
703
704 $this->SetLocation($arBucket['LOCATION']);
705 $response = $this->SendRequest(
706 $arBucket['SETTINGS'],
707 'DELETE',
708 '',
709 '/' . $arBucket['BUCKET']
710 );
711
712 if (
713 $this->status == 204/*No content*/
714 || $this->status == 404/*Not exists*/
715 || $this->status == 403/*Access denied*/
716 )
717 {
718 $APPLICATION->ResetException();
719 return true;
720 }
721 elseif (is_array($response))
722 {
723 return true;
724 }
725 else
726 {
727 if (defined('BX_CLOUDS_ERROR_DEBUG'))
728 {
729 AddMessage2Log($this);
730 }
731 return false;
732 }
733 }
734
741 public function GetFileSRC($arBucket, $arFile, $encoded = true)
742 {
743 if ($arBucket['SETTINGS']['USE_HTTPS'] === 'Y')
744 {
745 $proto = 'https';
746 }
747 else
748 {
749 /* @var \Bitrix\Main\HttpRequest $request */
750 $request = \Bitrix\Main\Context::getCurrent()->getRequest();
751 $proto = $request->isHttps() ? 'https' : 'http';
752 }
753
754 if ($arBucket['CNAME'] != '')
755 {
756 $host = $arBucket['CNAME'];
757 $pref = '';
758 }
759 else
760 {
761 $host = $arBucket['SETTINGS']['HOST'];
762 $pref = $arBucket['BUCKET'];
763 }
764
765 if (is_array($arFile))
766 {
767 $URI = ltrim($arFile['SUBDIR'] . '/' . $arFile['FILE_NAME'], '/');
768 }
769 else
770 {
771 $URI = ltrim($arFile, '/');
772 }
773
774 if ($arBucket['PREFIX'] != '')
775 {
776 if (mb_substr($URI, 0, mb_strlen($arBucket['PREFIX']) + 1) !== $arBucket['PREFIX'] . '/')
777 {
778 $URI = $arBucket['PREFIX'] . '/' . $URI;
779 }
780 }
781
782 if ($pref !== '')
783 {
784 $URI = $pref . '/' . $URI;
785 }
786
787 if ($encoded)
788 {
789 return $proto . '://' . $host . '/' . CCloudUtil::URLEncode($URI, 'UTF-8', true);
790 }
791 else
792 {
793 return $proto . '://' . $host . '/' . $URI;
794 }
795 }
796
802 public function FileExists($arBucket, $filePath)
803 {
804 global $APPLICATION;
805
806 if ($arBucket['PREFIX'] != '')
807 {
808 if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
809 {
810 $filePath = '/' . $arBucket['PREFIX'] . '/' . ltrim($filePath, '/');
811 }
812 }
813 $filePath = CCloudUtil::URLEncode($filePath, 'UTF-8', true);
814
815 $this->SetLocation($arBucket['LOCATION']);
816 $this->SendRequest(
817 $arBucket['SETTINGS'],
818 'HEAD',
819 '',
820 '/' . $arBucket['BUCKET'] . $filePath
821 );
822
823 if ($this->status == 200)
824 {
825 if (isset($this->headers['Content-Length']) && $this->headers['Content-Length'] > 0)
826 {
827 return $this->headers['Content-Length'];
828 }
829 else
830 {
831 return true;
832 }
833 }
834 elseif ($this->status == 206)
835 {
836 $APPLICATION->ResetException();
837 return true;
838 }
839 elseif ($this->checkForTokenExpiration($this->status, $this->result))
840 {
841 $this->tokenHasExpired = true;
842 return false;
843 }
844 else//if($this->status == 404)
845 {
846 $APPLICATION->ResetException();
847 return false;
848 }
849 }
850
851 public function FileCopy($arBucket, $arFile, $filePath)
852 {
853 global $APPLICATION;
854
855 if ($arBucket['PREFIX'])
856 {
857 if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
858 {
859 $filePath = '/' . $arBucket['PREFIX'] . '/' . ltrim($filePath, '/');
860 }
861 }
862
863 $sourcePath = '/' . $arBucket['BUCKET'] . '/' . ($arBucket['PREFIX'] ? $arBucket['PREFIX'] . '/' : '') . ($arFile['SUBDIR'] ? $arFile['SUBDIR'] . '/' : '') . $arFile['FILE_NAME'];
864 $additional_headers = [];
865 if ($this->_public)
866 {
867 $additional_headers['x-amz-acl'] = 'public-read';
868 }
869 $additional_headers['x-amz-copy-source'] = CCloudUtil::URLEncode($sourcePath, 'UTF-8', true);
870 $additional_headers['Content-Type'] = $arFile['CONTENT_TYPE'];
871
872 if (
873 defined('BX_CLOUDS_COUNTERS_DEBUG')
874 && !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
875 )
876 {
877 \CCloudsDebug::getInstance('copy')->startAction($filePath);
878 }
879
880 $this->SetLocation($arBucket['LOCATION']);
881 $this->SendRequest(
882 $arBucket['SETTINGS'],
883 'PUT',
884 '',
885 '/' . $arBucket['BUCKET'] . CCloudUtil::URLEncode($filePath, 'UTF-8', true),
886 '',
887 '',
888 $additional_headers
889 );
890
891 if (
892 defined('BX_CLOUDS_COUNTERS_DEBUG')
893 && !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
894 )
895 {
896 \CCloudsDebug::getInstance('copy')->endAction();
897 }
898
899 if (
900 defined('BX_CLOUDS_COUNTERS_DEBUG')
901 && $this->status == 200
902 && !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
903 )
904 {
905 \CCloudsDebug::getInstance()->startAction(CCloudUtil::URLEncode($filePath, 'UTF-8', true));
906 }
907
908 if ($this->status == 200)
909 {
910 return $this->GetFileSRC($arBucket, $filePath);
911 }
912 elseif ($this->checkForTokenExpiration($this->status, $this->result))
913 {
914 $this->tokenHasExpired = true;
915 return false;
916 }
917 elseif (
918 $this->status == 400
919 && ($e = $APPLICATION->GetException())
920 && is_object($e)
921 && preg_match('/The specified copy source is larger than the maximum allowable size for a copy source: (\d+)/i', $e->GetString(), $match)
922 )
923 {
924 $sizeLimit = $match[1];
925 $this->SendRequest(
926 $arBucket['SETTINGS'],
927 'HEAD',
928 '',
929 '/' . $arBucket['BUCKET'] . CCloudUtil::URLEncode('/' . ($arBucket['PREFIX'] ? $arBucket['PREFIX'] . '/' : '') . ($arFile['SUBDIR'] ? $arFile['SUBDIR'] . '/' : '') . $arFile['FILE_NAME'], 'UTF-8', true)
930 );
931
932 $fileSize = false;
933 if ($this->status == 200)
934 {
935 if (isset($this->headers['Content-Length']) && $this->headers['Content-Length'] > 0)
936 {
937 $fileSize = $this->headers['Content-Length'];
938 }
939 }
940 if (!$fileSize)
941 {
942 $APPLICATION->ResetException();
943 if (defined('BX_CLOUDS_ERROR_DEBUG'))
944 {
945 AddMessage2Log($this);
946 }
947 return false;
948 }
949
950 //Multipart copy goes here
951 $additional_headers = [];
952 if ($this->_public)
953 {
954 $additional_headers['x-amz-acl'] = 'public-read';
955 }
956 $additional_headers['Content-Type'] = $arFile['CONTENT_TYPE'];
957
958 $response = $this->SendRequest(
959 $arBucket['SETTINGS'],
960 'POST',
961 '',
962 '/' . $arBucket['BUCKET'] . CCloudUtil::URLEncode($filePath, 'UTF-8', true),
963 '?uploads=',
964 '',
965 $additional_headers
966 );
967
968 if (
969 $this->status == 200
970 && is_array($response)
971 && isset($response['InitiateMultipartUploadResult'])
972 && is_array($response['InitiateMultipartUploadResult'])
973 && isset($response['InitiateMultipartUploadResult']['#'])
974 && is_array($response['InitiateMultipartUploadResult']['#'])
975 && isset($response['InitiateMultipartUploadResult']['#']['UploadId'])
976 && is_array($response['InitiateMultipartUploadResult']['#']['UploadId'])
977 && isset($response['InitiateMultipartUploadResult']['#']['UploadId'][0])
978 && is_array($response['InitiateMultipartUploadResult']['#']['UploadId'][0])
979 && isset($response['InitiateMultipartUploadResult']['#']['UploadId'][0]['#'])
980 && is_string($response['InitiateMultipartUploadResult']['#']['UploadId'][0]['#'])
981 )
982 {
983 $uploadId = $response['InitiateMultipartUploadResult']['#']['UploadId'][0]['#'];
984 $parts = [];
985 }
986 else
987 {
988 $APPLICATION->ResetException();
989 if (defined('BX_CLOUDS_ERROR_DEBUG'))
990 {
991 AddMessage2Log($this);
992 }
993 return false;
994 }
995
996 $pos = 0;
997 $part_no = 0;
998 while ($pos < $fileSize)
999 {
1000 $additional_headers = [];
1001 $additional_headers['x-amz-copy-source'] = CCloudUtil::URLEncode('/' . $arBucket['BUCKET'] . '/' . ($arBucket['PREFIX'] ? $arBucket['PREFIX'] . '/' : '') . ($arFile['SUBDIR'] ? $arFile['SUBDIR'] . '/' : '') . $arFile['FILE_NAME'], 'UTF-8', true);
1002 $additional_headers['x-amz-copy-source-range'] = 'bytes=' . $pos . '-' . (min($fileSize, $pos + $sizeLimit) - 1);
1003
1004 $response = $this->SendRequest(
1005 $arBucket['SETTINGS'],
1006 'PUT',
1007 '',
1008 '/' . $arBucket['BUCKET'] . CCloudUtil::URLEncode($filePath, 'UTF-8', true),
1009 '?partNumber=' . ($part_no + 1) . '&uploadId=' . rawurlencode($uploadId),
1010 '',
1011 $additional_headers
1012 );
1013
1014 if (
1015 $this->status == 200
1016 && is_array($response)
1017 && isset($response['CopyPartResult'])
1018 && is_array($response['CopyPartResult'])
1019 && isset($response['CopyPartResult']['#'])
1020 && is_array($response['CopyPartResult']['#'])
1021 && isset($response['CopyPartResult']['#']['ETag'])
1022 && is_array($response['CopyPartResult']['#']['ETag'])
1023 && isset($response['CopyPartResult']['#']['ETag'][0])
1024 && is_array($response['CopyPartResult']['#']['ETag'][0])
1025 && isset($response['CopyPartResult']['#']['ETag'][0]['#'])
1026 && is_string($response['CopyPartResult']['#']['ETag'][0]['#'])
1027 )
1028 {
1029 $parts[$part_no] = trim($response['CopyPartResult']['#']['ETag'][0]['#'], '"');
1030 }
1031 else
1032 {
1033 $APPLICATION->ResetException();
1034 if (defined('BX_CLOUDS_ERROR_DEBUG'))
1035 {
1036 AddMessage2Log($this);
1037 }
1038 return false;
1039 }
1040 $part_no++;
1041 $pos += $sizeLimit;
1042 }
1043
1044 ksort($parts);
1045 $data = '';
1046 foreach ($parts as $PartNumber => $ETag)
1047 {
1048 $data .= '<Part><PartNumber>' . ($PartNumber + 1) . '</PartNumber><ETag>' . $ETag . "</ETag></Part>\n";
1049 }
1050
1051 $this->SendRequest(
1052 $arBucket['SETTINGS'],
1053 'POST',
1054 '',
1055 '/' . $arBucket['BUCKET'] . CCloudUtil::URLEncode($filePath, 'UTF-8', true),
1056 '?uploadId=' . rawurlencode($uploadId),
1057 '<CompleteMultipartUpload>' . $data . '</CompleteMultipartUpload>'
1058 );
1059
1060 if ($this->status == 200)
1061 {
1062 return $this->GetFileSRC($arBucket, $filePath);
1063 }
1064
1065 $APPLICATION->ResetException();
1066 if (defined('BX_CLOUDS_ERROR_DEBUG'))
1067 {
1068 AddMessage2Log($this);
1069 }
1070 return false;
1071 }
1072 else//if($this->status == 404)
1073 {
1074 $APPLICATION->ResetException();
1075 if (defined('BX_CLOUDS_ERROR_DEBUG'))
1076 {
1077 AddMessage2Log($this);
1078 }
1079 return false;
1080 }
1081 }
1082
1083 public function DeleteFile($arBucket, $filePath)
1084 {
1085 global $APPLICATION;
1086
1087 if ($arBucket['PREFIX'])
1088 {
1089 if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
1090 {
1091 $filePath = '/' . $arBucket['PREFIX'] . '/' . ltrim($filePath, '/');
1092 }
1093 }
1094 $filePath = CCloudUtil::URLEncode($filePath, 'UTF-8', true);
1095
1096 $this->SetLocation($arBucket['LOCATION']);
1097 $this->SendRequest(
1098 $arBucket['SETTINGS'],
1099 'DELETE',
1100 '',
1101 '/' . $arBucket['BUCKET'] . $filePath
1102 );
1103
1104 if (
1105 defined('BX_CLOUDS_COUNTERS_DEBUG')
1106 && $this->status == 204
1107 && !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
1108 )
1109 {
1110 \CCloudsDebug::getInstance()->startAction($filePath);
1111 }
1112
1113 if ($this->status == 204)
1114 {
1115 $APPLICATION->ResetException();
1116 return true;
1117 }
1118 elseif ($this->checkForTokenExpiration($this->status, $this->result))
1119 {
1120 $this->tokenHasExpired = true;
1121 return false;
1122 }
1123 else
1124 {
1125 $APPLICATION->ResetException();
1126 if (defined('BX_CLOUDS_ERROR_DEBUG'))
1127 {
1128 AddMessage2Log($this);
1129 }
1130 return false;
1131 }
1132 }
1133
1134 public function SaveFile($arBucket, $filePath, $arFile)
1135 {
1136 global $APPLICATION;
1137
1138 if ($arBucket['PREFIX'])
1139 {
1140 if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
1141 {
1142 $filePath = '/' . $arBucket['PREFIX'] . '/' . ltrim($filePath, '/');
1143 }
1144 }
1145 $filePath = CCloudUtil::URLEncode($filePath, 'UTF-8', true);
1146
1147 $additional_headers = [];
1148 if ($this->_public)
1149 {
1150 $additional_headers['x-amz-acl'] = 'public-read';
1151 }
1152 $additional_headers['Content-Type'] = $arFile['type'];
1153 $additional_headers['Content-Length'] = (array_key_exists('content', $arFile) ? strlen($arFile['content']) : filesize($arFile['tmp_name']));
1154
1155 if (
1156 defined('BX_CLOUDS_COUNTERS_DEBUG')
1157 && !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
1158 )
1159 {
1160 \CCloudsDebug::getInstance('put')->startAction($filePath);
1161 }
1162
1163 $this->SetLocation($arBucket['LOCATION']);
1164 $this->SendRequest(
1165 $arBucket['SETTINGS'],
1166 'PUT',
1167 '',
1168 '/' . $arBucket['BUCKET'] . $filePath,
1169 '',
1170 $arFile['content'] ?? fopen($arFile['tmp_name'], 'rb'),
1171 $additional_headers
1172 );
1173
1174 if (
1175 defined('BX_CLOUDS_COUNTERS_DEBUG')
1176 && !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
1177 )
1178 {
1179 \CCloudsDebug::getInstance('put')->endAction();
1180 }
1181
1182 if (
1183 defined('BX_CLOUDS_COUNTERS_DEBUG')
1184 && $this->status == 200
1185 && !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
1186 )
1187 {
1188 \CCloudsDebug::getInstance()->startAction($filePath);
1189 }
1190
1191 if ($this->status == 200)
1192 {
1193 return true;
1194 }
1195 elseif ($this->checkForTokenExpiration($this->status, $this->result))
1196 {
1197 $this->tokenHasExpired = true;
1198 return false;
1199 }
1200 elseif ($this->status == 403)
1201 {
1202 if (defined('BX_CLOUDS_ERROR_DEBUG'))
1203 {
1204 AddMessage2Log($this);
1205 }
1206 return false;
1207 }
1208 else
1209 {
1210 $APPLICATION->ResetException();
1211 if (defined('BX_CLOUDS_ERROR_DEBUG'))
1212 {
1213 AddMessage2Log($this);
1214 }
1215 return false;
1216 }
1217 }
1218
1219 public function ListFiles($arBucket, $filePath, $bRecursive = false, $pageSize = 0, $pageMarker = '')
1220 {
1221 static $search = ['%7E'];
1222 static $replace = ['~'];
1223 $result = [
1224 'dir' => [],
1225 'file' => [],
1226 'file_size' => [],
1227 'file_mtime' => [],
1228 'file_hash' => [],
1229 'last_key' => '',
1230 ];
1231
1232 $filePath = trim($filePath, '/');
1233 if ($filePath !== '')
1234 {
1235 $filePath .= '/';
1236 }
1237
1238 if ($arBucket['PREFIX'])
1239 {
1240 if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
1241 {
1242 $filePath = $arBucket['PREFIX'] . '/' . ltrim($filePath, '/');
1243 }
1244 }
1245
1246 $this->SetLocation($arBucket['LOCATION']);
1247 $marker = $pageSize > 0 ? $filePath . $pageMarker : '';
1248 while (true)
1249 {
1250 $response = $this->SendRequest(
1251 $arBucket['SETTINGS'],
1252 'GET',
1253 '',
1254 '/' . $arBucket['BUCKET'],
1255 '?' . ($bRecursive ? '' : 'delimiter=%2F&') . 'prefix=' . str_replace($search, $replace, rawurlencode($filePath))
1256 . '&marker=' . rawurlencode($marker)
1257 );
1258
1259 if (
1260 $this->status == 200
1261 && is_array($response)
1262 && isset($response['ListBucketResult'])
1263 && is_array($response['ListBucketResult'])
1264 && isset($response['ListBucketResult']['#'])
1265 && is_array($response['ListBucketResult']['#'])
1266 )
1267 {
1268 if (
1269 isset($response['ListBucketResult']['#']['CommonPrefixes'])
1270 && is_array($response['ListBucketResult']['#']['CommonPrefixes'])
1271 )
1272 {
1273 foreach ($response['ListBucketResult']['#']['CommonPrefixes'] as $a)
1274 {
1275 $dir_name = mb_substr(rtrim($a['#']['Prefix'][0]['#'], '/'), mb_strlen($filePath));
1276 $result['dir'][] = $dir_name;
1277 }
1278 }
1279
1280 $lastKey = null;
1281 if (
1282 isset($response['ListBucketResult']['#']['Contents'])
1283 && is_array($response['ListBucketResult']['#']['Contents'])
1284 )
1285 {
1286 foreach ($response['ListBucketResult']['#']['Contents'] as $a)
1287 {
1288 $file_name = mb_substr($a['#']['Key'][0]['#'], mb_strlen($filePath));
1289 if ($file_name !== '' && mb_substr($file_name, -1) !== '/')
1290 {
1291 $result['file'][] = $file_name;
1292 $result['file_size'][] = $a['#']['Size'][0]['#'];
1293 $result['file_mtime'][] = mb_substr($a['#']['LastModified'][0]['#'], 0, 19);
1294 $result['file_hash'][] = trim($a['#']['ETag'][0]['#'], '"');
1295 $result['last_key'] = $file_name;
1296 $lastKey = $a['#']['Key'][0]['#'];
1297 if ($pageSize > 0 && count($result['file']) >= $pageSize)
1298 {
1299 return $result;
1300 }
1301 }
1302 }
1303 }
1304
1305 if (
1306 isset($response['ListBucketResult']['#']['IsTruncated'])
1307 && is_array($response['ListBucketResult']['#']['IsTruncated'])
1308 && $response['ListBucketResult']['#']['IsTruncated'][0]['#'] === 'true'
1309 )
1310 {
1311 if ($response['ListBucketResult']['#']['NextMarker'][0]['#'] <> '')
1312 {
1313 $marker = $response['ListBucketResult']['#']['NextMarker'][0]['#'];
1314 continue;
1315 }
1316 elseif ($lastKey !== null)
1317 {
1318 $marker = $lastKey;
1319 continue;
1320 }
1321 }
1322
1323 break;
1324 }
1325 elseif ($this->checkForTokenExpiration($this->status, $this->result))
1326 {
1327 $this->tokenHasExpired = true;
1328 return false;
1329 }
1330 else
1331 {
1332 if (defined('BX_CLOUDS_ERROR_DEBUG'))
1333 {
1334 AddMessage2Log($this);
1335 }
1336 return false;
1337 }
1338 }
1339
1340 return $result;
1341 }
1342
1343 public function GetFileInfo($arBucket, $filePath)
1344 {
1345 global $APPLICATION;
1346
1347 if ($arBucket['PREFIX'] != '')
1348 {
1349 if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
1350 {
1351 $filePath = '/' . $arBucket['PREFIX'] . '/' . ltrim($filePath, '/');
1352 }
1353 }
1354 $filePath = CCloudUtil::URLEncode($filePath, 'UTF-8', true);
1355
1356 $this->SetLocation($arBucket['LOCATION']);
1357 $this->SendRequest(
1358 $arBucket['SETTINGS'],
1359 'HEAD',
1360 '',
1361 '/' . $arBucket['BUCKET'] . $filePath
1362 );
1363
1364 if ($this->status == 200)
1365 {
1366 $result = [];
1367 foreach ($this->headers as $name => $value)
1368 {
1369 $name = strtolower($name);
1370 if ($name === 'content-length')
1371 {
1372 $result['size'] = $value;
1373 }
1374 elseif ($name === 'etag')
1375 {
1376 $result['hash'] = trim($value, '"');
1377 }
1378 elseif ($name === 'last-modified')
1379 {
1380 $ts = strtotime($value);
1381 $result['mtime'] = mb_substr(gmdate('c', $ts), 0, 19);
1382 }
1383 }
1384
1385 return count($result) == 3 ? $result : null;
1386 }
1387 elseif ($this->checkForTokenExpiration($this->status, $this->result))
1388 {
1389 $this->tokenHasExpired = true;
1390 return false;
1391 }
1392 else
1393 {
1394 $APPLICATION->ResetException();
1395 return false;
1396 }
1397 }
1398
1399 public function InitiateMultipartUpload($arBucket, &$NS, $filePath, $fileSize, $ContentType)
1400 {
1401 $filePath = '/' . trim($filePath, '/');
1402 if ($arBucket['PREFIX'])
1403 {
1404 if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
1405 {
1406 $filePath = '/' . $arBucket['PREFIX'] . $filePath;
1407 }
1408 }
1409 $filePathU = CCloudUtil::URLEncode($filePath, 'UTF-8', true);
1410
1411 $additional_headers = [];
1412 if ($this->_public)
1413 {
1414 $additional_headers['x-amz-acl'] = 'public-read';
1415 }
1416 $additional_headers['Content-Type'] = $ContentType;
1417
1418 $this->SetLocation($arBucket['LOCATION']);
1419 $response = $this->SendRequest(
1420 $arBucket['SETTINGS'],
1421 'POST',
1422 '',
1423 '/' . $arBucket['BUCKET'] . $filePathU,
1424 '?uploads=',
1425 '',
1426 $additional_headers
1427 );
1428
1429 if (
1430 $this->status == 200
1431 && is_array($response)
1432 && isset($response['InitiateMultipartUploadResult'])
1433 && is_array($response['InitiateMultipartUploadResult'])
1434 && isset($response['InitiateMultipartUploadResult']['#'])
1435 && is_array($response['InitiateMultipartUploadResult']['#'])
1436 && isset($response['InitiateMultipartUploadResult']['#']['UploadId'])
1437 && is_array($response['InitiateMultipartUploadResult']['#']['UploadId'])
1438 && isset($response['InitiateMultipartUploadResult']['#']['UploadId'][0])
1439 && is_array($response['InitiateMultipartUploadResult']['#']['UploadId'][0])
1440 && isset($response['InitiateMultipartUploadResult']['#']['UploadId'][0]['#'])
1441 && is_string($response['InitiateMultipartUploadResult']['#']['UploadId'][0]['#'])
1442 )
1443 {
1444 $NS = [
1445 'filePath' => $filePath,
1446 'UploadId' => $response['InitiateMultipartUploadResult']['#']['UploadId'][0]['#'],
1447 'Parts' => [],
1448 ];
1449 return true;
1450 }
1451 elseif ($this->checkForTokenExpiration($this->status, $this->result))
1452 {
1453 $this->tokenHasExpired = true;
1454 return false;
1455 }
1456 else
1457 {
1458 if (defined('BX_CLOUDS_ERROR_DEBUG'))
1459 {
1460 AddMessage2Log($this);
1461 }
1462 return false;
1463 }
1464 }
1465
1466 public function GetMinUploadPartSize()
1467 {
1468 return BX_S3_MIN_UPLOAD_PART_SIZE;
1469 }
1470
1471 public function UploadPartNo($arBucket, &$NS, $data, $part_no)
1472 {
1473 $filePath = '/' . trim($NS['filePath'], '/');
1474 if ($arBucket['PREFIX'])
1475 {
1476 if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
1477 {
1478 $filePath = '/' . $arBucket['PREFIX'] . $filePath;
1479 }
1480 }
1481 $filePath = CCloudUtil::URLEncode($filePath, 'UTF-8', true);
1482
1483 $this->SetLocation($arBucket['LOCATION']);
1484 $this->SendRequest(
1485 $arBucket['SETTINGS'],
1486 'PUT',
1487 '',
1488 '/' . $arBucket['BUCKET'] . $filePath,
1489 '?partNumber=' . ($part_no + 1) . '&uploadId=' . rawurlencode($NS['UploadId']),
1490 $data
1491 );
1492
1493 if ($this->status == 200 && is_array($this->headers))
1494 {
1495 foreach ($this->headers as $key => $value)
1496 {
1497 if (mb_strtolower($key) === 'etag')
1498 {
1499 $NS['Parts'][$part_no] = $value;
1500 return true;
1501 }
1502 }
1503 }
1504
1505 if (defined('BX_CLOUDS_ERROR_DEBUG'))
1506 {
1507 AddMessage2Log($this);
1508 }
1509 return false;
1510 }
1511
1518 public function UploadPart($arBucket, &$NS, $data)
1519 {
1520 return $this->UploadPartNo($arBucket, $NS, $data, count($NS['Parts']));
1521 }
1522
1528 public function CompleteMultipartUpload($arBucket, &$NS)
1529 {
1530 $filePath = '/' . trim($NS['filePath'], '/');
1531 if ($arBucket['PREFIX'])
1532 {
1533 if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
1534 {
1535 $filePath = '/' . $arBucket['PREFIX'] . $filePath;
1536 }
1537 }
1538 $filePath = CCloudUtil::URLEncode($filePath, 'UTF-8', true);
1539
1540 ksort($NS['Parts']);
1541 $data = '';
1542 foreach ($NS['Parts'] as $PartNumber => $ETag)
1543 {
1544 $data .= '<Part><PartNumber>' . ($PartNumber + 1) . '</PartNumber><ETag>' . $ETag . "</ETag></Part>\n";
1545 }
1546
1547 if (
1548 defined('BX_CLOUDS_COUNTERS_DEBUG')
1549 && !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
1550 )
1551 {
1552 \CCloudsDebug::getInstance('post')->startAction($filePath);
1553 }
1554
1555 $this->SetLocation($arBucket['LOCATION']);
1556 $this->SendRequest(
1557 $arBucket['SETTINGS'],
1558 'POST',
1559 '',
1560 '/' . $arBucket['BUCKET'] . $filePath,
1561 '?uploadId=' . rawurlencode($NS['UploadId']),
1562 '<CompleteMultipartUpload>' . $data . '</CompleteMultipartUpload>'
1563 );
1564
1565 if (
1566 defined('BX_CLOUDS_COUNTERS_DEBUG')
1567 && !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
1568 )
1569 {
1570 \CCloudsDebug::getInstance('post')->endAction();
1571 }
1572
1573 if (
1574 defined('BX_CLOUDS_COUNTERS_DEBUG')
1575 && $this->status == 200
1576 && !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
1577 )
1578 {
1579 \CCloudsDebug::getInstance()->startAction($filePath);
1580 }
1581
1582 if ($this->status == 200)
1583 {
1584 return true;
1585 }
1586 else
1587 {
1588 if (defined('BX_CLOUDS_ERROR_DEBUG'))
1589 {
1590 AddMessage2Log($this);
1591 }
1592 return false;
1593 }
1594 }
1595
1601 public function CancelMultipartUpload($arBucket, &$NS)
1602 {
1603 $filePath = '/' . trim($NS['filePath'], '/');
1604 if ($arBucket['PREFIX'])
1605 {
1606 if (substr($filePath, 0, strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
1607 {
1608 $filePath = '/' . $arBucket['PREFIX'] . $filePath;
1609 }
1610 }
1611 $filePath = CCloudUtil::URLEncode($filePath, 'UTF-8', true);
1612
1613 if ($NS['UploadId'])
1614 {
1615 $this->SetLocation($arBucket['LOCATION']);
1616 $this->SendRequest(
1617 $arBucket['SETTINGS'],
1618 'DELETE',
1619 '',
1620 '/' . $arBucket['BUCKET'] . $filePath,
1621 '?uploadId=' . rawurlencode($NS['UploadId']),
1622 ''
1623 );
1624 }
1625 }
1626
1631 public function SetPublic($state = true)
1632 {
1633 $this->_public = $state !== false;
1634 }
1635
1641 public function SetHeader($name, $value)
1642 {
1643 $this->set_headers[$name] = $value;
1644 }
1645
1650 public function UnsetHeader($name)
1651 {
1652 unset($this->set_headers[$name]);
1653 }
1654
1661 {
1662 if ($status == 400 && mb_strpos($result, 'ExpiredToken') !== false)
1663 {
1664 return true;
1665 }
1666 if ($status == 400 && mb_strpos($result, 'token is malformed') !== false)
1667 {
1668 return true;
1669 }
1670 if ($status == 400 && $result === '')
1671 {
1672 return true;
1673 }
1674 if ($status == 403 && mb_strpos($result, 'The AWS Access Key Id you provided does not exist in our records.') !== false)
1675 {
1676 return true;
1677 }
1678 if ($status == 403 && $result === '')
1679 {
1680 return true;
1681 }
1682 return false;
1683 }
1684}
global $APPLICATION
Определения include.php:80
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
Определения catalog_reindex.php:36
SetLocation($location)
Определения storage_service_s3.php:268
hmacsha1($data, $key)
Определения storage_service_s3.php:155
DeleteFile($arBucket, $filePath)
Определения storage_service_s3.php:1083
GetRequestHost($bucket, $arSettings)
Определения storage_service_s3.php:284
checkForTokenExpiration($status, $result)
Определения storage_service_s3.php:1660
GetFileInfo($arBucket, $filePath)
Определения storage_service_s3.php:1343
DeleteBucket($arBucket)
Определения storage_service_s3.php:688
InitiateMultipartUpload($arBucket, &$NS, $filePath, $fileSize, $ContentType)
Определения storage_service_s3.php:1399
FileExists($arBucket, $filePath)
Определения storage_service_s3.php:802
UploadPart($arBucket, &$NS, $data)
Определения storage_service_s3.php:1518
SetHeader($name, $value)
Определения storage_service_s3.php:1641
ListFiles($arBucket, $filePath, $bRecursive=false, $pageSize=0, $pageMarker='')
Определения storage_service_s3.php:1219
GetFileSRC($arBucket, $arFile, $encoded=true)
Определения storage_service_s3.php:741
ListBuckets($arBucket)
Определения storage_service_s3.php:506
FileCopy($arBucket, $arFile, $filePath)
Определения storage_service_s3.php:851
UploadPartNo($arBucket, &$NS, $data, $part_no)
Определения storage_service_s3.php:1471
CompleteMultipartUpload($arBucket, &$NS)
Определения storage_service_s3.php:1528
SendRequest($arSettings, $verb, $bucket, $file_name='/', $params='', $content='', $additional_headers=[])
Определения storage_service_s3.php:316
SaveFile($arBucket, $filePath, $arFile)
Определения storage_service_s3.php:1134
IsEmptyBucket($arBucket)
Определения storage_service_s3.php:641
CancelMultipartUpload($arBucket, &$NS)
Определения storage_service_s3.php:1601
SetPublic($state=true)
Определения storage_service_s3.php:1631
SignRequest($arSettings, $RequestMethod, $bucket, $RequestURI, $ContentType, $additional_headers, $params='', $content='', $Service='s3')
Определения storage_service_s3.php:179
CreateBucket($arBucket)
Определения storage_service_s3.php:575
CheckSettings($arBucket, &$arSettings)
Определения storage_service_s3.php:100
GetSettingsHTML($arBucket, $bServiceSet, $cur_SERVICE_ID, $bVarsFromForm)
Определения storage_service_s3.php:50
GetLastRequestHeader($headerName)
Определения storage_service.php:300
static URLEncode($str, $charset, $file_name=false)
Определения util.php:10
static getInstance($action='counters')
Определения include.php:53
Определения xml.php:396
$content
Определения commerceml.php:144
$data['IS_AVAILABLE']
Определения .description.php:13
$bVarsFromForm
Определения file_edit.php:44
while($arParentIBlockProperty=$dbParentIBlockProperty->Fetch()) $errorMessage
$_SERVER["DOCUMENT_ROOT"]
Определения cron_frame.php:9
$pageSize
Определения csv_new_run.php:34
if(!defined('NOT_CHECK_PERMISSIONS')) $NS
Определения backup.php:24
AddMessage2Log($text, $module='', $traceDepth=6, $showArgs=false)
Определения tools.php:3941
htmlspecialcharsbx($string, $flags=ENT_COMPAT, $doubleEncode=true)
Определения tools.php:2701
IncludeModuleLangFile($filepath, $lang=false, $bReturnArray=false)
Определения tools.php:3778
GetMessage($name, $aReplace=null)
Определения tools.php:3397
$name
Определения menu_edit.php:35
$arFiles
Определения options.php:60
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
if(empty($signedUserToken)) $key
Определения quickway.php:257
$text
Определения template_pdf.php:79
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
else $a
Определения template.php:137
$response
Определения result.php:21