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/ilovecveti.ru/bitrix/modules/clouds/classes/general/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

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

class CCloudStorageService_GoogleStorage extends CCloudStorageService
{
	protected $new_end_point;

	public function GetObject()
	{
		return new CCloudStorageService_GoogleStorage();
	}

	public function GetID()
	{
		return 'google_storage';
	}

	public function GetName()
	{
		return 'Google Storage';
	}

	public function GetLocationList()
	{
		return [
			'EU' => 'Europe',
			'US' => 'United States',
		];
	}

	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 = ['PROJECT_ID' => '', 'ACCESS_KEY' => '', 'SECRET_KEY' => ''];
		}

		$htmlID = htmlspecialcharsbx($this->GetID());

		$result = '
		<tr id="SETTINGS_0_' . $htmlID . '" style="display:' . ($cur_SERVICE_ID == $this->GetID() || !$bServiceSet ? '' : 'none') . '" class="settings-tr adm-detail-required-field">
			<td>' . GetMessage('CLO_STORAGE_GOOGLE_EDIT_PROJECT_ID') . ':</td>
			<td><input type="hidden" name="SETTINGS[' . $htmlID . '][PROJECT_ID]" id="' . $htmlID . 'PROJECT_ID" value="' . htmlspecialcharsbx($arSettings['PROJECT_ID']) . '"><input type="text" size="55" name="' . $htmlID . 'INP_" id="' . $htmlID . 'INP_PROJECT_ID" value="' . htmlspecialcharsbx($arSettings['PROJECT_ID']) . '" ' . ($arBucket['READ_ONLY'] == 'Y' ? '"disabled"' : '') . ' onchange="BX(\'' . $htmlID . 'PROJECT_ID\').value = this.value"></td>
		</tr>
		<tr id="SETTINGS_1_' . $htmlID . '" style="display:' . ($cur_SERVICE_ID == $this->GetID() || !$bServiceSet ? '' : 'none') . '" class="settings-tr adm-detail-required-field">
			<td>' . GetMessage('CLO_STORAGE_GOOGLE_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:' . ($cur_SERVICE_ID == $this->GetID() || !$bServiceSet ? '' : 'none') . '" class="settings-tr adm-detail-required-field">
			<td>' . GetMessage('CLO_STORAGE_GOOGLE_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:' . ($cur_SERVICE_ID == $this->GetID() || !$bServiceSet ? '' : 'none') . '" class="settings-tr">
			<td>&nbsp;</td>
			<td>' . BeginNote() . GetMessage('CLO_STORAGE_GOOGLE_EDIT_HELP') . EndNote() . '</td>
		</tr>
		';
		return $result;
	}

	public function CheckSettings($arBucket, &$arSettings)
	{
		global $APPLICATION;
		$aMsg = [];

		$result = [
			'PROJECT_ID' => is_array($arSettings) ? trim($arSettings['PROJECT_ID']) : '',
			'ACCESS_KEY' => is_array($arSettings) ? trim($arSettings['ACCESS_KEY']) : '',
			'SECRET_KEY' => is_array($arSettings) ? trim($arSettings['SECRET_KEY']) : '',
		];

		if ($arBucket['READ_ONLY'] !== 'Y' && !mb_strlen($result['PROJECT_ID']))
		{
			$aMsg[] = ['id' => $this->GetID() . 'INP_PROJECT_ID', 'text' => GetMessage('CLO_STORAGE_GOOGLE_EMPTY_PROJECT_ID')];
		}

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

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

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

		return true;
	}

	public function CreateBucket($arBucket)
	{
		global $APPLICATION;

		if ($arBucket['LOCATION'])
		{
			$content =
				'<CreateBucketConfiguration>'
				. '<LocationConstraint>' . $arBucket['LOCATION'] . '</LocationConstraint>'
				. '</CreateBucketConfiguration>';
		}
		else
		{
			$content = '';
		}

		$response = $this->SendRequest(
			$arBucket['SETTINGS']['ACCESS_KEY'],
			$arBucket['SETTINGS']['SECRET_KEY'],
			'PUT',
			$arBucket['BUCKET'],
			'/',
			'',
			$content,
			[
				'x-goog-project-id' => $arBucket['SETTINGS']['PROJECT_ID'],
			]
		);

		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;
		}
	}

	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;
			}

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

		$response = $this->SendRequest(
			$arBucket['SETTINGS']['ACCESS_KEY'],
			$arBucket['SETTINGS']['SECRET_KEY'],
			'DELETE',
			$arBucket['BUCKET']
		);

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

	public function IsEmptyBucket($arBucket)
	{
		global $APPLICATION;

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

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

	/**
	 * @param array[string]string $arBucket
	 * @param mixed $arFile
	 * @param bool $encoded
	 * @return string
	*/
	public function GetFileSRC($arBucket, $arFile, $encoded = true)
	{
		if ($arBucket['CNAME'])
		{
			$host = $arBucket['CNAME'];
		}
		else
		{
			$host = $arBucket['BUCKET'] . '.commondatastorage.googleapis.com';
		}

		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;
			}
		}

		/* @var \Bitrix\Main\HttpRequest $request */
		$request = \Bitrix\Main\Context::getCurrent()->getRequest();
		$proto = $request->isHttps() ? 'https' : 'http';

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

	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->SendRequest(
			$arBucket['SETTINGS']['ACCESS_KEY'],
			$arBucket['SETTINGS']['SECRET_KEY'],
			'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;
		}
		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, '/');
			}
		}

		$this->SendRequest(
			$arBucket['SETTINGS']['ACCESS_KEY'],
			$arBucket['SETTINGS']['SECRET_KEY'],
			'PUT',
			$arBucket['BUCKET'],
			CCloudUtil::URLEncode($filePath, 'UTF-8', true),
			'',
			'',
			[
				'x-goog-acl' => 'public-read',
				'x-goog-copy-source' => CCloudUtil::URLEncode('/' . $arBucket['BUCKET'] . '/' . ($arBucket['PREFIX'] ? $arBucket['PREFIX'] . '/' : '') . ($arFile['SUBDIR'] ? $arFile['SUBDIR'] . '/' : '') . $arFile['FILE_NAME'], 'UTF-8', true),
				'Content-Type' => $arFile['CONTENT_TYPE']
			]
		);

		if ($this->status == 200)
		{
			return $this->GetFileSRC($arBucket, $filePath);
		}
		else//if($this->status == 404)
		{
			if (defined('BX_CLOUDS_ERROR_DEBUG'))
			{
				AddMessage2Log($this);
			}
			$APPLICATION->ResetException();
			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->SendRequest(
			$arBucket['SETTINGS']['ACCESS_KEY'],
			$arBucket['SETTINGS']['SECRET_KEY'],
			'DELETE',
			$arBucket['BUCKET'],
			$filePath
		);

		if ($this->status == 204 || $this->status == 404)
		{
			$APPLICATION->ResetException();
			return true;
		}
		else
		{
			if (defined('BX_CLOUDS_ERROR_DEBUG'))
			{
				AddMessage2Log($this);
			}
			$APPLICATION->ResetException();
			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);

		$this->SendRequest(
			$arBucket['SETTINGS']['ACCESS_KEY'],
			$arBucket['SETTINGS']['SECRET_KEY'],
			'PUT',
			$arBucket['BUCKET'],
			$filePath,
			'',
			$arFile['content'] ?? fopen($arFile['tmp_name'], 'rb'),
			[
				'x-goog-acl' => 'public-read',
				'Content-Type' => $arFile['type'],
				'Content-Length' => (array_key_exists('content', $arFile) ? strlen($arFile['content']) : filesize($arFile['tmp_name'])),
			]
		);

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

	public function ListFiles($arBucket, $filePath, $bRecursive = false)
	{
		global $APPLICATION;

		$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, '/');
			}
		}
		$filePath = str_replace(' ', '+', $filePath);

		$marker = '';
		while (true)
		{
			$response = $this->SendRequest(
				$arBucket['SETTINGS']['ACCESS_KEY'],
				$arBucket['SETTINGS']['SECRET_KEY'],
				'GET',
				$arBucket['BUCKET'],
				'/',
				'?' . ($bRecursive ? '' : 'delimiter=/&') . 'prefix=' . 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;
					}
				}

				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));
						$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;
					}
				}

				if (
					isset($response['ListBucketResult']['#']['IsTruncated'])
					&& is_array($response['ListBucketResult']['#']['IsTruncated'])
					&& $response['ListBucketResult']['#']['IsTruncated'][0]['#'] === 'true'
					&& $response['ListBucketResult']['#']['NextMarker'][0]['#'] <> ''
				)
				{
					$marker = $response['ListBucketResult']['#']['NextMarker'][0]['#'];
					continue;
				}
				else
				{
					break;
				}
			}
			else
			{
				if (defined('BX_CLOUDS_ERROR_DEBUG'))
				{
					AddMessage2Log($this);
				}
				$APPLICATION->ResetException();
				return false;
			}
		}

		return $result;
	}

	protected function StartUpload($arBucket, $filePath, $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);

		$this->SendRequest(
			$arBucket['SETTINGS']['ACCESS_KEY'],
			$arBucket['SETTINGS']['SECRET_KEY'],
			'POST',
			$arBucket['BUCKET'],
			$filePathU,
			'',
			'',
			[
				'x-goog-acl' => 'public-read',
				'x-goog-resumable' => 'start',
				'Content-Type' => $ContentType,
			]
		);

		if (
			$this->status == 201
			&& is_array($this->headers)
			&& isset($this->headers['Location'])
			&& preg_match('/upload_id=(.*)$/', $this->headers['Location'], $match)
		)
		{
			return [
				'filePath' => $filePath,
				'filePos' => 0,
				'upload_id' => $match[1],
			];
		}

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

		return false;
	}

	public function InitiateMultipartUpload($arBucket, &$NS, $filePath, $fileSize, $ContentType)
	{
		$upload_info = $this->StartUpload($arBucket, $filePath, $ContentType);
		if ($upload_info)
		{
			$upload_info['fileSize'] = $fileSize;
			$upload_info['ContentType'] = $ContentType;
			$NS = $upload_info;
			return true;
		}
		else
		{
			return false;
		}
	}

	public function GetMinUploadPartSize()
	{
		return 5 * 1024 * 1024; //5MB
	}

	private function UploadRange($filePathU, $arBucket, &$NS, $data, $pos)
	{
		$this->SendRequest(
			$arBucket['SETTINGS']['ACCESS_KEY'],
			$arBucket['SETTINGS']['SECRET_KEY'],
			'PUT',
			$arBucket['BUCKET'],
			$filePathU . '?upload_id=' . rawurlencode($NS['upload_id']),
			'',
			'',
			[
				'Content-Range' => 'bytes */' . $NS['fileSize'],
			]
		);

		$data_len = strlen($data);

		$this->SendRequest(
			$arBucket['SETTINGS']['ACCESS_KEY'],
			$arBucket['SETTINGS']['SECRET_KEY'],
			'PUT',
			$arBucket['BUCKET'],
			$filePathU . '?upload_id=' . rawurlencode($NS['upload_id']),
			'',
			$data,
			[
				'Content-Range' => 'bytes ' . $pos . '-' . ($pos + $data_len - 1) . '/' . $NS['fileSize'],
			]
		);
	}

	public function UploadPartNo($arBucket, &$NS, $data, $part_no)
	{
		global $APPLICATION;
		$part_no = intval($part_no);

		$found = false;
		if (isset($NS['Parts']))
		{
			foreach ($NS['Parts'] as $first_part_no => $part)
			{
				if ($part['part_no'] === ($part_no - 1))
				{
					$found = $first_part_no;
					break;
				}
			}
		}
		else
		{
			$NS['Parts'] = [];
		}

		if ($found === false)
		{
			$partFileName = '/' . trim($NS['filePath'], '/') . '.tmp' . $part_no;
			if ($arBucket['PREFIX'])
			{
				if (mb_substr($partFileName, 0, mb_strlen($arBucket['PREFIX']) + 2) !== '/' . $arBucket['PREFIX'] . '/')
				{
					$partFileName = '/' . $arBucket['PREFIX'] . $partFileName;
				}
			}
			$upload_info = $this->StartUpload($arBucket, $partFileName, $NS['ContentType']);
			if ($upload_info)
			{
				$upload_info['fileSize'] = '*';
				$upload_info['part_no'] = $part_no;
				$found = $part_no;
				$NS['Parts'][$part_no] = $upload_info;
				ksort($NS['Parts']);
			}
			else
			{
				return false;
			}
		}

		$NS['Parts'][$found]['part_no'] = $part_no;
		if (
			(isset($NS['Parts'][$part_no + 1]))
			|| (($NS['Parts'][$found]['part_no'] * $this->GetMinUploadPartSize() + $this->GetMinUploadPartSize()) >= $NS['fileSize'])
		)
		{
			$data_len = strlen($data);
			$NS['Parts'][$found]['fileSize'] = $NS['Parts'][$found]['filePos'] + $data_len;
		}

		$filePath = $NS['Parts'][$found]['filePath'];
		$filePathU = CCloudUtil::URLEncode($filePath, 'UTF-8', true);

		$this->UploadRange($filePathU, $arBucket, $NS['Parts'][$found], $data, $NS['Parts'][$found]['filePos']);

		if (
			$this->status == 308
			&& is_array($this->headers)
			&& preg_match('/^bytes=(\\d+)-(\\d+)$/', $this->headers['Range'], $match)
		)
		{
			$APPLICATION->ResetException();
			$NS['Parts'][$found]['filePos'] = $match[2] + 1;
			return true;
		}
		elseif ($this->status == 200)
		{
			return true;
		}
		else
		{
			if (defined('BX_CLOUDS_ERROR_DEBUG'))
			{
				AddMessage2Log($this);
			}
			return false;
		}
	}

	public function UploadPart($arBucket, &$NS, $data)
	{
		global $APPLICATION;

		$filePath = '/' . trim($NS['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);

		$this->UploadRange($filePathU, $arBucket, $NS, $data, $NS['filePos']);

		if (
			$this->status == 308
			&& is_array($this->headers)
			&& preg_match('/^bytes=(\\d+)-(\\d+)$/', $this->headers['Range'], $match)
		)
		{
			$APPLICATION->ResetException();
			$NS['filePos'] = $match[2] + 1;
			return true;
		}
		elseif ($this->status == 200)
		{
			return true;
		}
		else
		{
			if (defined('BX_CLOUDS_ERROR_DEBUG'))
			{
				AddMessage2Log($this);
			}
			return false;
		}
	}

	public function CompleteMultipartUpload($arBucket, &$NS)
	{
		if (isset($NS['Parts']))
		{
			// https://cloud.google.com/storage/docs/xml-api/put-object-compose
			$filePath = '/' . trim($NS['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);

			$xml = '<ComposeRequest>';
			foreach ($NS['Parts'] as $part)
			{
				$xml .= '<Component><Name>' . ltrim($part['filePath'], '/') . '</Name></Component>';
			}
			$xml .= '</ComposeRequest>';

			$this->SendRequest(
				$arBucket['SETTINGS']['ACCESS_KEY'],
				$arBucket['SETTINGS']['SECRET_KEY'],
				'PUT',
				$arBucket['BUCKET'],
				$filePathU . '?compose',
				'',
				$xml,
				[
					'x-goog-acl' => 'public-read',
					'Content-Type' => $NS['ContentType'],
				]
			);

			if ($this->status == 200)
			{
				foreach ($NS['Parts'] as $part)
				{
					$this->DeleteFile($arBucket, $part['filePath']);
				}
				return true;
			}
			else
			{
				if (defined('BX_CLOUDS_ERROR_DEBUG'))
				{
					AddMessage2Log($this);
				}
				return false;
			}
		}
		return true;
	}

	public function SendRequest($access_key, $secret_key, $verb, $bucket, $file_name='/', $params='', $content='', $additional_headers=[])
	{
		global $APPLICATION;
		$this->status = 0;

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

		if (!array_key_exists('x-goog-api-version', $additional_headers))
		{
			$additional_headers['x-goog-api-version'] = '1';
		}

		$RequestMethod = $verb;
		$RequestURI = $file_name;
		$RequestDATE = gmdate('D, d M Y H:i:s', time()) . ' GMT';

		//Prepare Signature
		$CanonicalizedAmzHeaders = '';
		ksort($additional_headers);
		foreach ($additional_headers as $key => $value)
		{
			if (preg_match('/^x-goog-/', $key))
			{
				$CanonicalizedAmzHeaders .= $key . ':' . $value . "\n";
			}
		}

		$CanonicalizedResource = '/' . $bucket . $RequestURI;

		$StringToSign = $RequestMethod . "\n\n"
			. $ContentType . "\n"
			. $RequestDATE . "\n"
			. $CanonicalizedAmzHeaders . $CanonicalizedResource
		;

		$Signature = base64_encode($this->hmacsha1($StringToSign, $secret_key));
		$Authorization = 'GOOG1 ' . $access_key . ':' . $Signature;

		$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']);
		}

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

		if (
			$this->new_end_point
			&& preg_match('#^(http|https)://' . preg_quote($bucket, '#') . '(.+)/#', $this->new_end_point, $match))
		{
			$host = $match[2];
		}
		else
		{
			$host = $bucket . '.commondatastorage.googleapis.com';
		}

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

		$this->status = 0;
		$this->host = $host;
		$this->verb = $RequestMethod;
		$this->url = 'http://' . $host . $RequestURI . $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,
			];
			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']))
			{
				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
				$APPLICATION->ThrowException(GetMessage('CLO_STORAGE_GOOGLE_XML_PARSE_ERROR', ['#errno#' => 1]));
				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(
				$access_key,
				$secret_key,
				$verb,
				$bucket,
				$file_name,
				$params,
				$content,
				$additional_headers
			);
		}
		elseif ($this->status > 0)
		{
			if ($this->result)
			{
				$obXML = new CDataXML;
				if ($obXML->LoadString($this->result))
				{
					$arXML = $obXML->GetArray();
					if (is_array($arXML) && is_string($arXML['Error']['#']['Message'][0]['#']))
					{
						$APPLICATION->ThrowException(GetMessage('CLO_STORAGE_GOOGLE_XML_ERROR', ['#errmsg#' => trim($arXML['Error']['#']['Message'][0]['#'], '.')]));
						return false;
					}
				}
			}
			$APPLICATION->ThrowException(GetMessage('CLO_STORAGE_GOOGLE_XML_PARSE_ERROR', ['#errno#' => 2]));
			return false;
		}
		else
		{
			$APPLICATION->ThrowException(GetMessage('CLO_STORAGE_GOOGLE_XML_PARSE_ERROR', ['#errno#' => 3]));
			return false;
		}
	}

	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;
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit