403Webshell
Server IP : 80.87.202.40  /  Your IP : 216.73.216.169
Web Server : Apache
System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64
User : bitrix ( 600)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF |  cURL : ON |  WGET : ON |  Perl : ON |  Python : OFF |  Sudo : ON |  Pkexec : ON
Directory :  /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/clouds/classes/general/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/clouds/classes/general/storage_service_s3.php
<?php
IncludeModuleLangFile(__FILE__);

class CCloudStorageService_S3 extends CCloudStorageService
{
	protected $set_headers = /*.(array[string]string).*/[];
	protected $new_end_point = '';
	protected $_public = true;
	protected $location = '';

	/**
	 * @return CCloudStorageService
	*/
	public function GetObject()
	{
		return new CCloudStorageService_S3();
	}

	/**
	 * @return string
	*/
	public function GetID()
	{
		return 'generic_s3';
	}

	/**
	 * @return string
	*/
	public function GetName()
	{
		return 'S3 compatible storage';
	}

	/**
	 * @return array[string]string|false
	*/
	public function GetLocationList()
	{
		return false;
	}

	/**
	 * @param array[string]string $arBucket
	 * @param bool $bServiceSet
	 * @param string $cur_SERVICE_ID
	 * @param bool $bVarsFromForm
	 * @return string
	*/
	public function GetSettingsHTML($arBucket, $bServiceSet, $cur_SERVICE_ID, $bVarsFromForm)
	{
		if ($bVarsFromForm)
		{
			$arSettings = $_POST['SETTINGS'][$this->GetID()];
		}
		else
		{
			$arSettings = unserialize($arBucket['SETTINGS'], ['allowed_classes' => false]);
		}

		if (!is_array($arSettings))
		{
			$arSettings = [
				'HOST' => '',
				'ACCESS_KEY' => '',
				'SECRET_KEY' => '',
			];
		}

		$htmlID = htmlspecialcharsbx($this->GetID());
		$show = (($cur_SERVICE_ID === $this->GetID()) || !$bServiceSet) ? '' : 'none';
		$useHttps = $arSettings['USE_HTTPS'] ?? 'N';

		$result = '
		<tr id="SETTINGS_0_' . $htmlID . '" style="display:' . $show . '" class="settings-tr adm-detail-required-field">
			<td>' . GetMessage('CLO_STORAGE_S3_EDIT_HOST') . ':</td>
			<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>
		</tr>
		<tr id="SETTINGS_1_' . $htmlID . '" style="display:' . $show . '" class="settings-tr adm-detail-required-field">
			<td>' . GetMessage('CLO_STORAGE_S3_EDIT_ACCESS_KEY') . ':</td>
			<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>
		</tr>
		<tr id="SETTINGS_2_' . $htmlID . '" style="display:' . $show . '" class="settings-tr adm-detail-required-field">
			<td>' . GetMessage('CLO_STORAGE_S3_EDIT_SECRET_KEY') . ':</td>
			<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>
		</tr>
		<tr id="SETTINGS_3_' . $htmlID . '" style="display:' . $show . '" class="settings-tr">
			<td>' . GetMessage('CLO_STORAGE_S3_EDIT_USE_HTTPS') . ':</td>
			<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>
		</tr>
		';
		return $result;
	}

	/**
	 * @param array[string]string $arBucket
	 * @param array[string]string & $arSettings
	 * @return bool
	*/
	public function CheckSettings($arBucket, &$arSettings)
	{
		global $APPLICATION;
		$aMsg = /*.(array[int][string]string).*/[];

		$result = [
			'HOST' => is_array($arSettings) ? trim($arSettings['HOST']) : '',
			'ACCESS_KEY' => is_array($arSettings) ? trim($arSettings['ACCESS_KEY']) : '',
			'SECRET_KEY' => is_array($arSettings) ? trim($arSettings['SECRET_KEY']) : '',
			'USE_HTTPS' => is_array($arSettings) && $arSettings['USE_HTTPS'] == 'Y' ? 'Y' : 'N',
		];

		if ($arBucket['READ_ONLY'] !== 'Y' && $result['HOST'] === '')
		{
			$aMsg[] = [
				'id' => $this->GetID() . 'INP_HOST',
				'text' => GetMessage('CLO_STORAGE_S3_EMPTY_HOST'),
			];
		}

		if ($arBucket['READ_ONLY'] !== 'Y' && $result['ACCESS_KEY'] === '')
		{
			$aMsg[] = [
				'id' => $this->GetID() . 'INP_ACCESS_KEY',
				'text' => GetMessage('CLO_STORAGE_S3_EMPTY_ACCESS_KEY'),
			];
		}

		if ($arBucket['READ_ONLY'] !== 'Y' && $result['SECRET_KEY'] === '')
		{
			$aMsg[] = [
				'id' => $this->GetID() . 'INP_SECRET_KEY',
				'text' => GetMessage('CLO_STORAGE_S3_EMPTY_SECRET_KEY'),
			];
		}

		if (!empty($aMsg))
		{
			$e = new CAdminException($aMsg);
			$APPLICATION->ThrowException($e);
			return false;
		}
		else
		{
			$arSettings = $result;
		}

		return true;
	}

	/**
	 * @param string $data
	 * @param string $key
	 * @return string
	*/
	public function hmacsha1($data, $key)
	{
		if (mb_strlen($key) > 64)
		{
			$key = pack('H*', sha1($key));
		}
		$key = str_pad($key, 64, chr(0x00));
		$ipad = str_repeat(chr(0x36), 64);
		$opad = str_repeat(chr(0x5c), 64);
		$hmac = pack('H*', sha1(($key ^ $opad) . pack('H*', sha1(($key ^ $ipad) . $data))));
		return $hmac;
	}

	/**
	 * @param array[string]string $arSettings
	 * @param string $RequestMethod
	 * @param string $bucket
	 * @param string $RequestURI
	 * @param string $ContentType
	 * @param array[string]string $additional_headers
	 * @param string $params
	 * @param string|resource $content
	 * @return array[string]string
	*/
	public function SignRequest($arSettings, $RequestMethod, $bucket, $RequestURI, $ContentType, $additional_headers, $params = '', $content = '', $Service = 's3')
	{
		if (is_resource($content))
		{
			$streamPosition = ftell($content);
			$hashResource = hash_init('sha256');
			hash_update_stream($hashResource, $content);
			$HashedPayload = hash_final($hashResource);
			fseek($content, $streamPosition);
		}
		else
		{
			$HashedPayload = hash('sha256', $content, false);
		}
		$additional_headers['x-amz-content-sha256'] = $HashedPayload;

		$Time = time();
		$RequestDate = gmdate('Ymd', $Time);
		$RequestTime = gmdate('Ymd', $Time) . 'T' . gmdate('His', $Time) . 'Z';
		$additional_headers['x-amz-date'] = $RequestTime;

		do
		{
			$CanonicalizedResource = $RequestURI !== '' ? str_replace('%2F', '/', $RequestURI) : '/';
		}
		while (strpos($CanonicalizedResource, '%2F') !== false);

		$CanonicalQuery = explode('&', ltrim($params, '?'));
		sort($CanonicalQuery);
		$CanonicalQueryString = str_replace('%7E', '~', implode('&', $CanonicalQuery));

		$CanonicalHeaders = /*.(array[string]string).*/ [];
		foreach ($additional_headers as $key => $value)
		{
			$key = mb_strtolower($key);
			if (isset($CanonicalHeaders[$key]))
			{
				$CanonicalHeaders[$key] .= ',';
			}
			else
			{
				$CanonicalHeaders[$key] = $key . ':';
			}
			$CanonicalHeaders[$key] .= trim($value, " \t\n\r");
		}
		ksort($CanonicalHeaders);
		$CanonicalHeadersString = implode("\n", $CanonicalHeaders);
		$SignedHeaders = implode(';', array_keys($CanonicalHeaders));

		$CanonicalRequest = '';
		$CanonicalRequest .= $RequestMethod . "\n";
		$CanonicalRequest .= $CanonicalizedResource . "\n";
		$CanonicalRequest .= $CanonicalQueryString . "\n";
		$CanonicalRequest .= $CanonicalHeadersString . "\n\n";
		$CanonicalRequest .= $SignedHeaders . "\n";
		$CanonicalRequest .= $HashedPayload;

		$Algorithm = 'AWS4-HMAC-SHA256';
		$Region = $this->location ?: 'us-east-1';
		$Scope = $RequestDate . '/' . $Region . '/' . $Service . '/aws4_request';

		$StringToSign = '';
		$StringToSign .= $Algorithm . "\n";
		$StringToSign .= $RequestTime . "\n";
		$StringToSign .= $Scope . "\n";
		$StringToSign .= hash('sha256', $CanonicalRequest, false);

		$kSecret  = $arSettings['SECRET_KEY'];
		$kDate    = hash_hmac('sha256', $RequestDate, 'AWS4' . $kSecret, true);
		$kRegion  = hash_hmac('sha256', $Region, $kDate, true);
		$kService = hash_hmac('sha256', $Service, $kRegion, true);
		$kSigning = hash_hmac('sha256', 'aws4_request', $kService, true);

		$Signature = hash_hmac('sha256', $StringToSign, $kSigning, false);

		$Authorization = $Algorithm . ' Credential=' . $arSettings['ACCESS_KEY'] . '/' . $Scope . ',SignedHeaders=' . $SignedHeaders . ',Signature=' . $Signature;

		return [
			'Date' => $RequestTime,
			'Authorization' => $Authorization,
			'x-amz-date' => $RequestTime,
			'x-amz-content-sha256' => $HashedPayload,
		];
	}

	/**
	 * @param string $location
	 * @return void
	 **/
	public function SetLocation($location)
	{
		if ($location)
		{
			$this->location = $location;
		}
		else
		{
			$this->location = '';
		}
	}

	/**
	 * @param string $bucket
	 * @return string
	 **/
	protected function GetRequestHost($bucket, $arSettings)
	{
		if (
			$this->new_end_point != ''
			&& preg_match('#^(http|https)://' . preg_quote($bucket, '#') . '(.+?)/#', $this->new_end_point, $match) > 0
		)
		{
			return $bucket . $match[2];
		}
		else
		{
			if ($bucket !== '')
			{
				return $bucket . '.' . $arSettings['HOST'];
			}
			else
			{
				return $arSettings['HOST'];
			}
		}
	}

	/**
	 * @param array[string]string $arSettings
	 * @param string $verb
	 * @param string $bucket
	 * @param string $file_name
	 * @param string $params
	 * @param string $content
	 * @param array[string]string $additional_headers
	 * @return mixed
	*/
	public function SendRequest($arSettings, $verb, $bucket, $file_name='/', $params='', $content='', $additional_headers=/*.(array[string]string).*/[])
	{
		global $APPLICATION;
		$this->status = 0;

		$request = new Bitrix\Main\Web\HttpClient([
			'redirect' => false,
			'streamTimeout' => $this->streamTimeout,
		]);
		if (isset($additional_headers['option-file-result']))
		{
			$request->setOutputStream($additional_headers['option-file-result']);
		}

		if (isset($additional_headers['Content-Type']))
		{
			$ContentType = $additional_headers['Content-Type'];
		}
		else
		{
			$ContentType = $content !== '' ? 'text/plain' : '';
		}
		unset($additional_headers['Content-Type']);

		foreach ($this->set_headers as $key => $value)
		{
			$additional_headers[$key] = $value;
		}

		if (array_key_exists('SESSION_TOKEN', $arSettings))
		{
			$additional_headers['x-amz-security-token'] = $arSettings['SESSION_TOKEN'];
		}

		$host = $additional_headers['Host'] = $this->GetRequestHost($bucket, $arSettings);

		foreach ($this->SignRequest($arSettings, $verb, $bucket, $file_name, $ContentType, $additional_headers, $params, $content) as $key => $value)
		{
			$request->setHeader($key, $value);
		}

		foreach ($additional_headers as $key => $value)
		{
			if (!preg_match('/^option-/', $key))
			{
				$request->setHeader($key, $value);
			}
		}

		$was_end_point = $this->new_end_point;
		$this->new_end_point = '';

		$useHttps = $arSettings['USE_HTTPS'] ?? 'N';
		$this->status = 0;
		$this->host = $host;
		$this->verb = $verb;
		$this->url = ($useHttps === 'Y' ? 'https' : 'http') . '://' . $host . $file_name . $params;
		$this->headers = [];
		$this->errno = 0;
		$this->errstr = '';
		$this->result = '';

		$stime = 0;
		$logRequest = false;
		if (defined('BX_CLOUDS_TRACE') && $verb !== 'GET' && $verb !== 'HEAD')
		{
			$stime = microtime(1);
			$logRequest = [
				'request_id' => md5((string)mt_rand()),
				'portal' => $_SERVER['HTTP_HOST'],
				'verb' => $this->verb,
				'url' => $this->url,
			];
			if (function_exists('getmypid'))
			{
				$logRequest['pid'] = getmypid();
			}
			AddMessage2Log(json_encode($logRequest), 'clouds', 20);
		}

		$request->setHeader('Content-type', $ContentType);
		$request->query($this->verb, $this->url, $content);

		$this->status = $request->getStatus();
		foreach ($request->getHeaders() as $key => $value)
		{
			$this->headers[$key] = is_array($value) ? $value[0] : $value;
		}
		$this->errstr = implode("\n", $request->getError());
		$this->errno = $this->errstr ? 255 : 0;
		$this->result = $request->getResult();

		if ($logRequest)
		{
			$logRequest['status'] = $this->status;
			$logRequest['time'] = round(microtime(true) - $stime, 6);
			$logRequest['headers'] = $this->headers;
			AddMessage2Log(json_encode($logRequest), 'clouds', 0);
		}

		if ($this->status == 200)
		{
			if (
				isset($additional_headers['option-raw-result'])
				|| isset($additional_headers['option--result'])
			)
			{
				return $this->result;
			}
			elseif ($this->result !== '')
			{
				$obXML = new CDataXML;
				$text = preg_replace('/<' . '\\?XML.*?\\?' . '>/i', '', $this->result);
				if ($obXML->LoadString($text))
				{
					$arXML = $obXML->GetArray();
					if (is_array($arXML))
					{
						return $arXML;
					}
				}
				//XML parse error
				$e = new CApplicationException(GetMessage('CLO_STORAGE_S3_XML_PARSE_ERROR', ['#errno#' => '1']));
				$APPLICATION->ThrowException($e);
				return false;
			}
			else
			{
				//Empty success result
				return [];
			}
		}
		elseif (
			$this->status == 307  //Temporary redirect
			&& isset($this->headers['Location'])
			&& $was_end_point === '' //No recurse yet
		)
		{
			$this->new_end_point = $this->headers['Location'];
			return $this->SendRequest(
				$arSettings,
				$verb,
				$bucket,
				$file_name,
				$params,
				$content,
				$additional_headers
			);
		}
		elseif ($this->status > 0)
		{
			if ($this->result !== '')
			{
				$obXML = new CDataXML;
				if ($obXML->LoadString($this->result))
				{
					$node = $obXML->SelectNodes('/Error/Message');
					if (is_object($node))
					{
						$errorMessage = trim($node->textContent(), '.');
						$e = new CApplicationException(GetMessage('CLO_STORAGE_S3_XML_ERROR', [
							'#errmsg#' => $errorMessage,
						]));
						$APPLICATION->ThrowException($e);
						return false;
					}
					$node = $obXML->SelectNodes('/Error/Code');
					if (is_object($node))
					{
						$errorMessage = trim($node->textContent(), '.');
						$e = new CApplicationException(GetMessage('CLO_STORAGE_S3_XML_ERROR', [
							'#errmsg#' => $errorMessage,
						]));
						$APPLICATION->ThrowException($e);
						return false;
					}
				}
			}
			$e = new CApplicationException(GetMessage('CLO_STORAGE_S3_XML_PARSE_ERROR', ['#errno#' => '2']));
			$APPLICATION->ThrowException($e);
			return false;
		}
		else
		{
			$e = new CApplicationException(GetMessage('CLO_STORAGE_S3_XML_PARSE_ERROR', ['#errno#' => '3']));
			$APPLICATION->ThrowException($e);
			return false;
		}
	}

	public function ListBuckets($arBucket)
	{
		$result = [
			'bucket' => [],
			'ctime' => [],
		];

		$this->SetLocation($arBucket['LOCATION']);
		while (true)
		{
			$response = $this->SendRequest(
				$arBucket['SETTINGS'],
				'GET',
				'',
				'/',
				''
			);

			if (
				$this->status == 200
				&& is_array($response)
				&& isset($response['ListAllMyBucketsResult'])
				&& is_array($response['ListAllMyBucketsResult'])
				&& isset($response['ListAllMyBucketsResult']['#'])
				&& is_array($response['ListAllMyBucketsResult']['#'])
			)
			{
				$ListAllMyBucketsResult = $response['ListAllMyBucketsResult']['#'];
				if (
					isset($ListAllMyBucketsResult['Buckets'])
					&& is_array($ListAllMyBucketsResult['Buckets'])
					&& isset($ListAllMyBucketsResult['Buckets'][0])
					&& is_array($ListAllMyBucketsResult['Buckets'][0])
					&& isset($ListAllMyBucketsResult['Buckets'][0]['#'])
					&& is_array($ListAllMyBucketsResult['Buckets'][0]['#'])
				)
				{
					foreach ($ListAllMyBucketsResult['Buckets'][0]['#']['Bucket'] as $Bucket)
					{
						$Name = $Bucket['#']['Name'][0]['#'];
						$CreationDate = $Bucket['#']['CreationDate'][0]['#'];
						$result['bucket'][] = urldecode($Name);
						$result['ctime'][] = strtotime($CreationDate);
					}
				}
			}
			elseif ($this->checkForTokenExpiration($this->status, $this->result))
			{
				$this->tokenHasExpired = true;
				return false;
			}
			else
			{
				if (defined('BX_CLOUDS_ERROR_DEBUG'))
				{
					AddMessage2Log($this);
				}
				return false;
			}
			break;
		}

		return $result;
	}

	/**
	 * @param array[string]string $arBucket
	 * @return bool
	*/
	public function CreateBucket($arBucket)
	{
		global $APPLICATION;

		$arFiles = $this->ListFiles($arBucket, '/', false, 1);
		if (is_array($arFiles))
		{
			return true;
		}

		// The bucket already exists and user has specified wrong location
		if (
			$this->status == 301
			&& $arBucket['LOCATION'] != ''
			&& $this->GetLastRequestHeader('x-amz-bucket-region') !== ''
			&& $this->GetLastRequestHeader('x-amz-bucket-region') !== $arBucket['LOCATION']
		)
		{
			return false;
		}

		if ($arBucket['LOCATION'] != '')
		{
			$content =
				'<CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">'
				. '<LocationConstraint>' . $arBucket['LOCATION'] . '</LocationConstraint>'
				. '</CreateBucketConfiguration>';
		}
		else
		{
			$content = '';
		}

		$this->SetLocation($arBucket['LOCATION']);
		$response = $this->SendRequest(
			$arBucket['SETTINGS'],
			'PUT',
			'',
			'/' . $arBucket['BUCKET'],
			'',
			$content
		);

		if ($this->status == 409/*Already exists*/)
		{
			$APPLICATION->ResetException();
			return true;
		}
		elseif (is_array($response))
		{
			return true;
		}
		else
		{
			if (defined('BX_CLOUDS_ERROR_DEBUG'))
			{
				AddMessage2Log($this);
			}
			return false;
		}
	}

	/**
	 * @param array[string]string $arBucket
	 * @return bool
	*/
	public function IsEmptyBucket($arBucket)
	{
		global $APPLICATION;

		$this->SetLocation($arBucket['LOCATION']);
		$response = $this->SendRequest(
			$arBucket['SETTINGS'],
			'GET',
			'',
			'/' . $arBucket['BUCKET'],
			'?max-keys=1' . ($arBucket['PREFIX'] != '' ? '&prefix=' . $arBucket['PREFIX'] . '/' : '')
		);

		if ($this->status == 404 || $this->status == 403)
		{
			$APPLICATION->ResetException();
			return true;
		}
		elseif (is_array($response))
		{
			if (
				!isset($response['ListBucketResult'])
				|| !is_array($response['ListBucketResult'])
				|| !isset($response['ListBucketResult']['#'])
				|| !is_array($response['ListBucketResult']['#'])
				|| !isset($response['ListBucketResult']['#']['Contents'])
				|| !is_array($response['ListBucketResult']['#']['Contents'])
			)
			{
				return true;
			}

			return false;
		}

		if (defined('BX_CLOUDS_ERROR_DEBUG'))
		{
			AddMessage2Log($this);
		}

		return false;
	}

	/**
	 * @param array[string]string $arBucket
	 * @return bool
	*/
	public function DeleteBucket($arBucket)
	{
		global $APPLICATION;

		if ($arBucket['PREFIX'] != '')
		{
			//Do not delete bucket if there is some files left
			if (!$this->IsEmptyBucket($arBucket))
			{
				return false;
			}

			//Let's pretend we deleted the bucket
			return true;
		}

		$this->SetLocation($arBucket['LOCATION']);
		$response = $this->SendRequest(
			$arBucket['SETTINGS'],
			'DELETE',
			'',
			'/' . $arBucket['BUCKET']
		);

		if (
			$this->status == 204/*No content*/
			|| $this->status == 404/*Not exists*/
			|| $this->status == 403/*Access denied*/
		)
		{
			$APPLICATION->ResetException();
			return true;
		}
		elseif (is_array($response))
		{
			return true;
		}
		else
		{
			if (defined('BX_CLOUDS_ERROR_DEBUG'))
			{
				AddMessage2Log($this);
			}
			return false;
		}
	}

	/**
	 * @param array[string]string $arBucket
	 * @param mixed $arFile
	 * @param bool $encoded
	 * @return string
	*/
	public function GetFileSRC($arBucket, $arFile, $encoded = true)
	{
		if ($arBucket['SETTINGS']['USE_HTTPS'] === 'Y')
		{
			$proto = 'https';
		}
		else
		{
			/* @var \Bitrix\Main\HttpRequest $request */
			$request = \Bitrix\Main\Context::getCurrent()->getRequest();
			$proto = $request->isHttps() ? 'https' : 'http';
		}

		if ($arBucket['CNAME'] != '')
		{
			$host = $arBucket['CNAME'];
			$pref = '';
		}
		else
		{
			$host = $arBucket['SETTINGS']['HOST'];
			$pref = $arBucket['BUCKET'];
		}

		if (is_array($arFile))
		{
			$URI = ltrim($arFile['SUBDIR'] . '/' . $arFile['FILE_NAME'], '/');
		}
		else
		{
			$URI = ltrim($arFile, '/');
		}

		if ($arBucket['PREFIX'] != '')
		{
			if (mb_substr($URI, 0, mb_strlen($arBucket['PREFIX']) + 1) !== $arBucket['PREFIX'] . '/')
			{
				$URI = $arBucket['PREFIX'] . '/' . $URI;
			}
		}

		if ($pref !== '')
		{
			$URI = $pref . '/' . $URI;
		}

		if ($encoded)
		{
			return $proto . '://' . $host . '/' . CCloudUtil::URLEncode($URI, 'UTF-8', true);
		}
		else
		{
			return $proto . '://' . $host . '/' . $URI;
		}
	}

	/**
	 * @param array[string]string $arBucket
	 * @param string $filePath
	 * @return bool
	*/
	public function FileExists($arBucket, $filePath)
	{
		global $APPLICATION;

		if ($arBucket['PREFIX'] != '')
		{
			if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
			{
				$filePath = '/' . $arBucket['PREFIX'] . '/' . ltrim($filePath, '/');
			}
		}
		$filePath = CCloudUtil::URLEncode($filePath, 'UTF-8', true);

		$this->SetLocation($arBucket['LOCATION']);
		$this->SendRequest(
			$arBucket['SETTINGS'],
			'HEAD',
			'',
			'/' . $arBucket['BUCKET'] . $filePath
		);

		if ($this->status == 200)
		{
			if (isset($this->headers['Content-Length']) && $this->headers['Content-Length'] > 0)
			{
				return $this->headers['Content-Length'];
			}
			else
			{
				return true;
			}
		}
		elseif ($this->status == 206)
		{
			$APPLICATION->ResetException();
			return true;
		}
		elseif ($this->checkForTokenExpiration($this->status, $this->result))
		{
			$this->tokenHasExpired = true;
			return false;
		}
		else//if($this->status == 404)
		{
			$APPLICATION->ResetException();
			return false;
		}
	}

	public function FileCopy($arBucket, $arFile, $filePath)
	{
		global $APPLICATION;

		if ($arBucket['PREFIX'])
		{
			if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
			{
				$filePath = '/' . $arBucket['PREFIX'] . '/' . ltrim($filePath, '/');
			}
		}

		$sourcePath = '/' . $arBucket['BUCKET'] . '/' . ($arBucket['PREFIX'] ? $arBucket['PREFIX'] . '/' : '') . ($arFile['SUBDIR'] ? $arFile['SUBDIR'] . '/' : '') . $arFile['FILE_NAME'];
		$additional_headers = [];
		if ($this->_public)
		{
			$additional_headers['x-amz-acl'] = 'public-read';
		}
		$additional_headers['x-amz-copy-source'] = CCloudUtil::URLEncode($sourcePath, 'UTF-8', true);
		$additional_headers['Content-Type'] = $arFile['CONTENT_TYPE'];

		if (
			defined('BX_CLOUDS_COUNTERS_DEBUG')
			&& !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
		)
		{
			\CCloudsDebug::getInstance('copy')->startAction($filePath);
		}

		$this->SetLocation($arBucket['LOCATION']);
		$this->SendRequest(
			$arBucket['SETTINGS'],
			'PUT',
			'',
			'/' . $arBucket['BUCKET'] . CCloudUtil::URLEncode($filePath, 'UTF-8', true),
			'',
			'',
			$additional_headers
		);

		if (
			defined('BX_CLOUDS_COUNTERS_DEBUG')
			&& !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
		)
		{
			\CCloudsDebug::getInstance('copy')->endAction();
		}

		if (
			defined('BX_CLOUDS_COUNTERS_DEBUG')
			&& $this->status == 200
			&& !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
		)
		{
			\CCloudsDebug::getInstance()->startAction(CCloudUtil::URLEncode($filePath, 'UTF-8', true));
		}

		if ($this->status == 200)
		{
			return $this->GetFileSRC($arBucket, $filePath);
		}
		elseif ($this->checkForTokenExpiration($this->status, $this->result))
		{
			$this->tokenHasExpired = true;
			return false;
		}
		elseif (
			$this->status == 400
			&& ($e = $APPLICATION->GetException())
			&& is_object($e)
			&& preg_match('/The specified copy source is larger than the maximum allowable size for a copy source: (\d+)/i', $e->GetString(), $match)
		)
		{
			$sizeLimit = $match[1];
			$this->SendRequest(
				$arBucket['SETTINGS'],
				'HEAD',
				'',
				'/' . $arBucket['BUCKET'] . CCloudUtil::URLEncode('/' . ($arBucket['PREFIX'] ? $arBucket['PREFIX'] . '/' : '') . ($arFile['SUBDIR'] ? $arFile['SUBDIR'] . '/' : '') . $arFile['FILE_NAME'], 'UTF-8', true)
			);

			$fileSize = false;
			if ($this->status == 200)
			{
				if (isset($this->headers['Content-Length']) && $this->headers['Content-Length'] > 0)
				{
					$fileSize = $this->headers['Content-Length'];
				}
			}
			if (!$fileSize)
			{
				$APPLICATION->ResetException();
				if (defined('BX_CLOUDS_ERROR_DEBUG'))
				{
					AddMessage2Log($this);
				}
				return false;
			}

			//Multipart copy goes here
			$additional_headers = [];
			if ($this->_public)
			{
				$additional_headers['x-amz-acl'] = 'public-read';
			}
			$additional_headers['Content-Type'] = $arFile['CONTENT_TYPE'];

			$response = $this->SendRequest(
				$arBucket['SETTINGS'],
				'POST',
				'',
				'/' . $arBucket['BUCKET'] . CCloudUtil::URLEncode($filePath, 'UTF-8', true),
				'?uploads=',
				'',
				$additional_headers
			);

			if (
				$this->status == 200
				&& is_array($response)
				&& isset($response['InitiateMultipartUploadResult'])
				&& is_array($response['InitiateMultipartUploadResult'])
				&& isset($response['InitiateMultipartUploadResult']['#'])
				&& is_array($response['InitiateMultipartUploadResult']['#'])
				&& isset($response['InitiateMultipartUploadResult']['#']['UploadId'])
				&& is_array($response['InitiateMultipartUploadResult']['#']['UploadId'])
				&& isset($response['InitiateMultipartUploadResult']['#']['UploadId'][0])
				&& is_array($response['InitiateMultipartUploadResult']['#']['UploadId'][0])
				&& isset($response['InitiateMultipartUploadResult']['#']['UploadId'][0]['#'])
				&& is_string($response['InitiateMultipartUploadResult']['#']['UploadId'][0]['#'])
			)
			{
				$uploadId = $response['InitiateMultipartUploadResult']['#']['UploadId'][0]['#'];
				$parts = [];
			}
			else
			{
				$APPLICATION->ResetException();
				if (defined('BX_CLOUDS_ERROR_DEBUG'))
				{
					AddMessage2Log($this);
				}
				return false;
			}

			$pos = 0;
			$part_no = 0;
			while ($pos < $fileSize)
			{
				$additional_headers = [];
				$additional_headers['x-amz-copy-source'] = CCloudUtil::URLEncode('/' . $arBucket['BUCKET'] . '/' . ($arBucket['PREFIX'] ? $arBucket['PREFIX'] . '/' : '') . ($arFile['SUBDIR'] ? $arFile['SUBDIR'] . '/' : '') . $arFile['FILE_NAME'], 'UTF-8', true);
				$additional_headers['x-amz-copy-source-range'] = 'bytes=' . $pos . '-' . (min($fileSize, $pos + $sizeLimit) - 1);

				$response = $this->SendRequest(
					$arBucket['SETTINGS'],
					'PUT',
					'',
					'/' . $arBucket['BUCKET'] . CCloudUtil::URLEncode($filePath, 'UTF-8', true),
					'?partNumber=' . ($part_no + 1) . '&uploadId=' . rawurlencode($uploadId),
					'',
					$additional_headers
				);

				if (
					$this->status == 200
					&& is_array($response)
					&& isset($response['CopyPartResult'])
					&& is_array($response['CopyPartResult'])
					&& isset($response['CopyPartResult']['#'])
					&& is_array($response['CopyPartResult']['#'])
					&& isset($response['CopyPartResult']['#']['ETag'])
					&& is_array($response['CopyPartResult']['#']['ETag'])
					&& isset($response['CopyPartResult']['#']['ETag'][0])
					&& is_array($response['CopyPartResult']['#']['ETag'][0])
					&& isset($response['CopyPartResult']['#']['ETag'][0]['#'])
					&& is_string($response['CopyPartResult']['#']['ETag'][0]['#'])
				)
				{
					$parts[$part_no] = trim($response['CopyPartResult']['#']['ETag'][0]['#'], '"');
				}
				else
				{
					$APPLICATION->ResetException();
					if (defined('BX_CLOUDS_ERROR_DEBUG'))
					{
						AddMessage2Log($this);
					}
					return false;
				}
				$part_no++;
				$pos += $sizeLimit;
			}

			ksort($parts);
			$data = '';
			foreach ($parts as $PartNumber => $ETag)
			{
				$data .= '<Part><PartNumber>' . ($PartNumber + 1) . '</PartNumber><ETag>' . $ETag . "</ETag></Part>\n";
			}

			$this->SendRequest(
				$arBucket['SETTINGS'],
				'POST',
				'',
				'/' . $arBucket['BUCKET'] . CCloudUtil::URLEncode($filePath, 'UTF-8', true),
				'?uploadId=' . rawurlencode($uploadId),
				'<CompleteMultipartUpload>' . $data . '</CompleteMultipartUpload>'
			);

			if ($this->status == 200)
			{
				return $this->GetFileSRC($arBucket, $filePath);
			}

			$APPLICATION->ResetException();
			if (defined('BX_CLOUDS_ERROR_DEBUG'))
			{
				AddMessage2Log($this);
			}
			return false;
		}
		else//if($this->status == 404)
		{
			$APPLICATION->ResetException();
			if (defined('BX_CLOUDS_ERROR_DEBUG'))
			{
				AddMessage2Log($this);
			}
			return false;
		}
	}

	public function DeleteFile($arBucket, $filePath)
	{
		global $APPLICATION;

		if ($arBucket['PREFIX'])
		{
			if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
			{
				$filePath = '/' . $arBucket['PREFIX'] . '/' . ltrim($filePath, '/');
			}
		}
		$filePath = CCloudUtil::URLEncode($filePath, 'UTF-8', true);

		$this->SetLocation($arBucket['LOCATION']);
		$this->SendRequest(
			$arBucket['SETTINGS'],
			'DELETE',
			'',
			'/' . $arBucket['BUCKET'] . $filePath
		);

		if (
			defined('BX_CLOUDS_COUNTERS_DEBUG')
			&& $this->status == 204
			&& !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
		)
		{
			\CCloudsDebug::getInstance()->startAction($filePath);
		}

		if ($this->status == 204)
		{
			$APPLICATION->ResetException();
			return true;
		}
		elseif ($this->checkForTokenExpiration($this->status, $this->result))
		{
			$this->tokenHasExpired = true;
			return false;
		}
		else
		{
			$APPLICATION->ResetException();
			if (defined('BX_CLOUDS_ERROR_DEBUG'))
			{
				AddMessage2Log($this);
			}
			return false;
		}
	}

	public function SaveFile($arBucket, $filePath, $arFile)
	{
		global $APPLICATION;

		if ($arBucket['PREFIX'])
		{
			if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
			{
				$filePath = '/' . $arBucket['PREFIX'] . '/' . ltrim($filePath, '/');
			}
		}
		$filePath = CCloudUtil::URLEncode($filePath, 'UTF-8', true);

		$additional_headers = [];
		if ($this->_public)
		{
			$additional_headers['x-amz-acl'] = 'public-read';
		}
		$additional_headers['Content-Type'] = $arFile['type'];
		$additional_headers['Content-Length'] = (array_key_exists('content', $arFile) ? strlen($arFile['content']) : filesize($arFile['tmp_name']));

		if (
			defined('BX_CLOUDS_COUNTERS_DEBUG')
			&& !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
		)
		{
			\CCloudsDebug::getInstance('put')->startAction($filePath);
		}

		$this->SetLocation($arBucket['LOCATION']);
		$this->SendRequest(
			$arBucket['SETTINGS'],
			'PUT',
			'',
			'/' . $arBucket['BUCKET'] . $filePath,
			'',
			$arFile['content'] ?? fopen($arFile['tmp_name'], 'rb'),
			$additional_headers
		);

		if (
			defined('BX_CLOUDS_COUNTERS_DEBUG')
			&& !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
		)
		{
			\CCloudsDebug::getInstance('put')->endAction();
		}

		if (
			defined('BX_CLOUDS_COUNTERS_DEBUG')
			&& $this->status == 200
			&& !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
		)
		{
			\CCloudsDebug::getInstance()->startAction($filePath);
		}

		if ($this->status == 200)
		{
			return true;
		}
		elseif ($this->checkForTokenExpiration($this->status, $this->result))
		{
			$this->tokenHasExpired = true;
			return false;
		}
		elseif ($this->status == 403)
		{
			if (defined('BX_CLOUDS_ERROR_DEBUG'))
			{
				AddMessage2Log($this);
			}
			return false;
		}
		else
		{
			$APPLICATION->ResetException();
			if (defined('BX_CLOUDS_ERROR_DEBUG'))
			{
				AddMessage2Log($this);
			}
			return false;
		}
	}

	public function ListFiles($arBucket, $filePath, $bRecursive = false, $pageSize = 0, $pageMarker = '')
	{
		static $search = ['%7E'];
		static $replace = ['~'];
		$result = [
			'dir' => [],
			'file' => [],
			'file_size' => [],
			'file_mtime' => [],
			'file_hash' => [],
			'last_key' => '',
		];

		$filePath = trim($filePath, '/');
		if ($filePath !== '')
		{
			$filePath .= '/';
		}

		if ($arBucket['PREFIX'])
		{
			if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
			{
				$filePath = $arBucket['PREFIX'] . '/' . ltrim($filePath, '/');
			}
		}

		$this->SetLocation($arBucket['LOCATION']);
		$marker = $pageSize > 0 ? $filePath . $pageMarker : '';
		while (true)
		{
			$response = $this->SendRequest(
				$arBucket['SETTINGS'],
				'GET',
				'',
				'/' . $arBucket['BUCKET'],
				'?' . ($bRecursive ? '' : 'delimiter=%2F&') . 'prefix=' . str_replace($search, $replace, rawurlencode($filePath))
					. '&marker=' . rawurlencode($marker)
			);

			if (
				$this->status == 200
				&& is_array($response)
				&& isset($response['ListBucketResult'])
				&& is_array($response['ListBucketResult'])
				&& isset($response['ListBucketResult']['#'])
				&& is_array($response['ListBucketResult']['#'])
			)
			{
				if (
					isset($response['ListBucketResult']['#']['CommonPrefixes'])
					&& is_array($response['ListBucketResult']['#']['CommonPrefixes'])
				)
				{
					foreach ($response['ListBucketResult']['#']['CommonPrefixes'] as $a)
					{
						$dir_name = mb_substr(rtrim($a['#']['Prefix'][0]['#'], '/'), mb_strlen($filePath));
						$result['dir'][] = $dir_name;
					}
				}

				$lastKey = null;
				if (
					isset($response['ListBucketResult']['#']['Contents'])
					&& is_array($response['ListBucketResult']['#']['Contents'])
				)
				{
					foreach ($response['ListBucketResult']['#']['Contents'] as $a)
					{
						$file_name = mb_substr($a['#']['Key'][0]['#'], mb_strlen($filePath));
						if ($file_name !== '' && mb_substr($file_name, -1) !== '/')
						{
							$result['file'][] = $file_name;
							$result['file_size'][] = $a['#']['Size'][0]['#'];
							$result['file_mtime'][] = mb_substr($a['#']['LastModified'][0]['#'], 0, 19);
							$result['file_hash'][] = trim($a['#']['ETag'][0]['#'], '"');
							$result['last_key'] = $file_name;
							$lastKey = $a['#']['Key'][0]['#'];
							if ($pageSize > 0 && count($result['file']) >= $pageSize)
							{
								return $result;
							}
						}
					}
				}

				if (
					isset($response['ListBucketResult']['#']['IsTruncated'])
					&& is_array($response['ListBucketResult']['#']['IsTruncated'])
					&& $response['ListBucketResult']['#']['IsTruncated'][0]['#'] === 'true'
				)
				{
					if ($response['ListBucketResult']['#']['NextMarker'][0]['#'] <> '')
					{
						$marker = $response['ListBucketResult']['#']['NextMarker'][0]['#'];
						continue;
					}
					elseif ($lastKey !== null)
					{
						$marker = $lastKey;
						continue;
					}
				}

				break;
			}
			elseif ($this->checkForTokenExpiration($this->status, $this->result))
			{
				$this->tokenHasExpired = true;
				return false;
			}
			else
			{
				if (defined('BX_CLOUDS_ERROR_DEBUG'))
				{
					AddMessage2Log($this);
				}
				return false;
			}
		}

		return $result;
	}

	public function GetFileInfo($arBucket, $filePath)
	{
		global $APPLICATION;

		if ($arBucket['PREFIX'] != '')
		{
			if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
			{
				$filePath = '/' . $arBucket['PREFIX'] . '/' . ltrim($filePath, '/');
			}
		}
		$filePath = CCloudUtil::URLEncode($filePath, 'UTF-8', true);

		$this->SetLocation($arBucket['LOCATION']);
		$this->SendRequest(
			$arBucket['SETTINGS'],
			'HEAD',
			'',
			'/' . $arBucket['BUCKET'] . $filePath
		);

		if ($this->status == 200)
		{
			$result = [];
			foreach ($this->headers as $name => $value)
			{
				$name = strtolower($name);
				if ($name === 'content-length')
				{
					$result['size'] = $value;
				}
				elseif ($name === 'etag')
				{
					$result['hash'] = trim($value, '"');
				}
				elseif ($name === 'last-modified')
				{
					$ts = strtotime($value);
					$result['mtime'] = mb_substr(gmdate('c', $ts), 0, 19);
				}
			}

			return count($result) == 3 ? $result : null;
		}
		elseif ($this->checkForTokenExpiration($this->status, $this->result))
		{
			$this->tokenHasExpired = true;
			return false;
		}
		else
		{
			$APPLICATION->ResetException();
			return false;
		}
	}

	public function InitiateMultipartUpload($arBucket, &$NS, $filePath, $fileSize, $ContentType)
	{
		$filePath = '/' . trim($filePath, '/');
		if ($arBucket['PREFIX'])
		{
			if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
			{
				$filePath = '/' . $arBucket['PREFIX'] . $filePath;
			}
		}
		$filePathU = CCloudUtil::URLEncode($filePath, 'UTF-8', true);

		$additional_headers = [];
		if ($this->_public)
		{
			$additional_headers['x-amz-acl'] = 'public-read';
		}
		$additional_headers['Content-Type'] = $ContentType;

		$this->SetLocation($arBucket['LOCATION']);
		$response = $this->SendRequest(
			$arBucket['SETTINGS'],
			'POST',
			'',
			'/' . $arBucket['BUCKET'] . $filePathU,
			'?uploads=',
			'',
			$additional_headers
		);

		if (
			$this->status == 200
			&& is_array($response)
			&& isset($response['InitiateMultipartUploadResult'])
			&& is_array($response['InitiateMultipartUploadResult'])
			&& isset($response['InitiateMultipartUploadResult']['#'])
			&& is_array($response['InitiateMultipartUploadResult']['#'])
			&& isset($response['InitiateMultipartUploadResult']['#']['UploadId'])
			&& is_array($response['InitiateMultipartUploadResult']['#']['UploadId'])
			&& isset($response['InitiateMultipartUploadResult']['#']['UploadId'][0])
			&& is_array($response['InitiateMultipartUploadResult']['#']['UploadId'][0])
			&& isset($response['InitiateMultipartUploadResult']['#']['UploadId'][0]['#'])
			&& is_string($response['InitiateMultipartUploadResult']['#']['UploadId'][0]['#'])
		)
		{
			$NS = [
				'filePath' => $filePath,
				'UploadId' => $response['InitiateMultipartUploadResult']['#']['UploadId'][0]['#'],
				'Parts' => [],
			];
			return true;
		}
		elseif ($this->checkForTokenExpiration($this->status, $this->result))
		{
			$this->tokenHasExpired = true;
			return false;
		}
		else
		{
			if (defined('BX_CLOUDS_ERROR_DEBUG'))
			{
				AddMessage2Log($this);
			}
			return false;
		}
	}

	public function GetMinUploadPartSize()
	{
		return BX_S3_MIN_UPLOAD_PART_SIZE;
	}

	public function UploadPartNo($arBucket, &$NS, $data, $part_no)
	{
		$filePath = '/' . trim($NS['filePath'], '/');
		if ($arBucket['PREFIX'])
		{
			if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
			{
				$filePath = '/' . $arBucket['PREFIX'] . $filePath;
			}
		}
		$filePath = CCloudUtil::URLEncode($filePath, 'UTF-8', true);

		$this->SetLocation($arBucket['LOCATION']);
		$this->SendRequest(
			$arBucket['SETTINGS'],
			'PUT',
			'',
			'/' . $arBucket['BUCKET'] . $filePath,
			'?partNumber=' . ($part_no + 1) . '&uploadId=' . rawurlencode($NS['UploadId']),
			$data
		);

		if ($this->status == 200 && is_array($this->headers))
		{
			foreach ($this->headers as $key => $value)
			{
				if (mb_strtolower($key) === 'etag')
				{
					$NS['Parts'][$part_no] = $value;
					return true;
				}
			}
		}

		if (defined('BX_CLOUDS_ERROR_DEBUG'))
		{
			AddMessage2Log($this);
		}
		return false;
	}

	/**
	 * @param array[string]string $arBucket
	 * @param mixed & $NS
	 * @param string $data
	 * @return bool
	*/
	public function UploadPart($arBucket, &$NS, $data)
	{
		return $this->UploadPartNo($arBucket, $NS, $data, count($NS['Parts']));
	}

	/**
	 * @param array[string]string $arBucket
	 * @param mixed & $NS
	 * @return bool
	*/
	public function CompleteMultipartUpload($arBucket, &$NS)
	{
		$filePath = '/' . trim($NS['filePath'], '/');
		if ($arBucket['PREFIX'])
		{
			if (mb_substr($filePath, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
			{
				$filePath = '/' . $arBucket['PREFIX'] . $filePath;
			}
		}
		$filePath = CCloudUtil::URLEncode($filePath, 'UTF-8', true);

		ksort($NS['Parts']);
		$data = '';
		foreach ($NS['Parts'] as $PartNumber => $ETag)
		{
			$data .= '<Part><PartNumber>' . ($PartNumber + 1) . '</PartNumber><ETag>' . $ETag . "</ETag></Part>\n";
		}

		if (
			defined('BX_CLOUDS_COUNTERS_DEBUG')
			&& !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
		)
		{
			\CCloudsDebug::getInstance('post')->startAction($filePath);
		}

		$this->SetLocation($arBucket['LOCATION']);
		$this->SendRequest(
			$arBucket['SETTINGS'],
			'POST',
			'',
			'/' . $arBucket['BUCKET'] . $filePath,
			'?uploadId=' . rawurlencode($NS['UploadId']),
			'<CompleteMultipartUpload>' . $data . '</CompleteMultipartUpload>'
		);

		if (
			defined('BX_CLOUDS_COUNTERS_DEBUG')
			&& !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
		)
		{
			\CCloudsDebug::getInstance('post')->endAction();
		}

		if (
			defined('BX_CLOUDS_COUNTERS_DEBUG')
			&& $this->status == 200
			&& !preg_match(constant('BX_CLOUDS_COUNTERS_DEBUG'), $filePath)
		)
		{
			\CCloudsDebug::getInstance()->startAction($filePath);
		}

		if ($this->status == 200)
		{
			return true;
		}
		else
		{
			if (defined('BX_CLOUDS_ERROR_DEBUG'))
			{
				AddMessage2Log($this);
			}
			return false;
		}
	}

	/**
	 * @param array[string]string $arBucket
	 * @param mixed & $NS
	 * @return bool
	*/
	public function CancelMultipartUpload($arBucket, &$NS)
	{
		$filePath = '/' . trim($NS['filePath'], '/');
		if ($arBucket['PREFIX'])
		{
			if (substr($filePath, 0, strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
			{
				$filePath = '/' . $arBucket['PREFIX'] . $filePath;
			}
		}
		$filePath = CCloudUtil::URLEncode($filePath, 'UTF-8', true);

		if ($NS['UploadId'])
		{
			$this->SetLocation($arBucket['LOCATION']);
			$this->SendRequest(
				$arBucket['SETTINGS'],
				'DELETE',
				'',
				'/' . $arBucket['BUCKET'] . $filePath,
				'?uploadId=' . rawurlencode($NS['UploadId']),
				''
			);
		}
	}

	/**
	 * @param bool $state
	 * @return void
	 */
	public function SetPublic($state = true)
	{
		$this->_public = $state !== false;
	}

	/**
	 * @param string $name
	 * @param string $value
	 * @return void
	*/
	public function SetHeader($name, $value)
	{
		$this->set_headers[$name] = $value;
	}

	/**
	 * @param string $name
	 * @return void
	 */
	public function UnsetHeader($name)
	{
		unset($this->set_headers[$name]);
	}

	/**
	 * @param int $status
	 * @param string $result
	 * @return bool
	*/
	protected function checkForTokenExpiration($status, $result)
	{
		if ($status == 400 && mb_strpos($result, 'ExpiredToken') !== false)
		{
			return true;
		}
		if ($status == 400 && mb_strpos($result, 'token is malformed') !== false)
		{
			return true;
		}
		if ($status == 400 && $result === '')
		{
			return true;
		}
		if ($status == 403 && mb_strpos($result, 'The AWS Access Key Id you provided does not exist in our records.') !== false)
		{
			return true;
		}
		if ($status == 403 && $result === '')
		{
			return true;
		}
		return false;
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit