%PDF- %PDF-
Direktori : /home/bitrix/www/bitrix/modules/clouds/classes/general/ |
Current File : /home/bitrix/www/bitrix/modules/clouds/classes/general/storage_service_s3.php |
<? IncludeModuleLangFile(__FILE__); class CCloudStorageService_AmazonS3 extends CCloudStorageService { protected $status = 0; protected $verb = ''; protected $host = ''; protected $url = ''; protected $headers =/*.(array[string]string).*/array(); protected $set_headers =/*.(array[string]string).*/array(); protected $errno = 0; protected $errstr = ''; protected $result = ''; protected $new_end_point = ''; protected $_public = true; protected $location = ''; /** * @return int */ function GetLastRequestStatus() { return $this->status; } /** * @return CCloudStorageService */ function GetObject() { return new CCloudStorageService_AmazonS3(); } /** * @return string */ function GetID() { return "amazon_s3"; } /** * @return string */ function GetName() { return "Amazon Simple Storage Service"; } /** * @return array[string]string */ function GetLocationList() { // http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region return array( "" => "US Standard", "us-east-2" => "US East (Ohio)", "us-west-1" => "US West (Northern California)", "us-west-2" => "US West (Oregon)", "ca-central-1" => "Canada (Central)", "ap-south-1" => "Asia Pacific (Mumbai)", "ap-northeast-2" => "Asia Pacific (Seoul)", "ap-southeast-1" => "Asia Pacific (Singapore)", "ap-southeast-2" => "Asia Pacific (Sydney)", "ap-northeast-1" => "Asia Pacific (Tokyo)", "eu-central-1" => "EU (Frankfurt)", "eu-west-1" => "EU (Ireland)", "eu-west-2" => "EU (London)", "sa-east-1" => "South America (Sao Paulo)", ); } /** * @return array[string]string */ function GetAPList() { // http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region return array( "" => "s3.amazonaws.com", "us-east-2" => "s3.us-east-2.amazonaws.com", "us-west-1" => "s3-us-west-1.amazonaws.com", "us-west-2" => "s3-us-west-2.amazonaws.com", "ca-central-1" => "s3.ca-central-1.amazonaws.com", "ap-south-1" => "s3.ap-south-1.amazonaws.com", "ap-northeast-2" => "s3.ap-northeast-2.amazonaws.com", "ap-southeast-1" => "s3-ap-southeast-1.amazonaws.com", "ap-southeast-2" => "s3-ap-southeast-2.amazonaws.com", "ap-northeast-1" => "s3-ap-northeast-1.amazonaws.com", "eu-central-1" => "s3.eu-central-1.amazonaws.com", "eu-west-1" => "s3-eu-west-1.amazonaws.com", "eu-west-2" => "s3.eu-west-2.amazonaws.com", "sa-east-1" => "South America (Sao Paulo)", ); } /** * @param array[string]string $arBucket * @param bool $bServiceSet * @param string $cur_SERVICE_ID * @param bool $bVarsFromForm * @return string */ function GetSettingsHTML($arBucket, $bServiceSet, $cur_SERVICE_ID, $bVarsFromForm) { if($bVarsFromForm) $arSettings = $_POST["SETTINGS"][$this->GetID()]; else $arSettings = unserialize($arBucket["SETTINGS"]); if(!is_array($arSettings)) $arSettings = array("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_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_1_'.$htmlID.'" style="display:'.($cur_SERVICE_ID === $this->GetID() || !$bServiceSet? '': 'none').'" 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_2_'.$htmlID.'" style="display:'.($cur_SERVICE_ID === $this->GetID() || !$bServiceSet? '': 'none').'" class="settings-tr"> <td> </td> <td>'.BeginNote().GetMessage("CLO_STORAGE_S3_EDIT_HELP").EndNote().'</td> </tr> '; return $result; } /** * @param array[string]string $arBucket * @param array[string]string $arSettings * @return bool */ function CheckSettings($arBucket, &$arSettings) { global $APPLICATION; $aMsg =/*.(array[int][string]string).*/array(); $result = array( "ACCESS_KEY" => is_array($arSettings)? trim($arSettings["ACCESS_KEY"]): '', "SECRET_KEY" => is_array($arSettings)? trim($arSettings["SECRET_KEY"]): '', ); if(is_array($arSettings) && array_key_exists("SESSION_TOKEN", $arSettings)) { $result["SESSION_TOKEN"] = trim($arSettings["SESSION_TOKEN"]); } if($arBucket["READ_ONLY"] !== "Y" && $result["ACCESS_KEY"] === '') { $aMsg[] = array( "id" => $this->GetID()."INP_ACCESS_KEY", "text" => GetMessage("CLO_STORAGE_S3_EMPTY_ACCESS_KEY"), ); } if($arBucket["READ_ONLY"] !== "Y" && $result["SECRET_KEY"] === '') { $aMsg[] = array( "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 */ function hmacsha1($data, $key) { if(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 */ function SignRequest($arSettings, $RequestMethod, $bucket, $RequestURI, $ContentType, $additional_headers, $params = "", $content = "") { static $search = array("+", "="); static $replace = array("%20", "%3D"); $CanonicalizedResource = strlen($RequestURI)? str_replace($search, $replace, $RequestURI): "/"; $CanonicalQuery = explode("&", ltrim($params, "?")); sort($CanonicalQuery); $CanonicalQueryString = implode("&", $CanonicalQuery); $CanonicalHeaders = array(); foreach($additional_headers as $key => $value) { $key = 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)); 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); } $CanonicalRequest = ""; $CanonicalRequest .= $RequestMethod."\n"; $CanonicalRequest .= $CanonicalizedResource."\n"; $CanonicalRequest .= $CanonicalQueryString."\n"; $CanonicalRequest .= $CanonicalHeadersString."\n\n"; $CanonicalRequest .= $SignedHeaders."\n"; $CanonicalRequest .= $HashedPayload; $Algorithm = "AWS4-HMAC-SHA256"; $Time = time(); $RequestDate = gmdate('Ymd', $Time); $RequestTime = gmdate('Ymd', $Time).'T'.gmdate('His', $Time).'Z'; $Region = $this->location? $this->location: 'us-east-1'; $Service = "s3"; $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 array( "Date" => $RequestTime, "Authorization" => $Authorization, "x-amz-content-sha256" => $HashedPayload, ); } /** * @param string $location * @return void **/ function SetLocation($location) { if ($location) $this->location = $location; else $this->location = ""; } protected function GetRequestHost($bucket) { if( $this->new_end_point != "" && preg_match('#^(http|https)://'.preg_quote($bucket, '#').'(.+?)/#', $this->new_end_point, $match) > 0 ) { return $bucket.$match[2]; } elseif ($this->location) { return $bucket.".s3-".$this->location.".amazonaws.com"; } else { return $bucket.".s3.amazonaws.com"; } } /** * @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 */ function SendRequest($arSettings, $verb, $bucket, $file_name='/', $params='', $content='', $additional_headers=/*.(array[string]string).*/array()) { global $APPLICATION; $this->status = 0; $obRequest = new CHTTP; if (isset($additional_headers["option-file-result"])) { $obRequest->fp = $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"]; } $additional_headers["host"] = $this->GetRequestHost($bucket); foreach($this->SignRequest($arSettings, $verb, $bucket, $file_name, $ContentType, $additional_headers, $params, $content) as $key => $value) { $obRequest->additional_headers[$key] = $value; } foreach($additional_headers as $key => $value) if(preg_match("/^(option-|host\$)/", $key) == 0) $obRequest->additional_headers[$key] = $value; $host = $additional_headers["host"]; $was_end_point = $this->new_end_point; $this->new_end_point = ''; $obRequest->Query($verb, $host, 80, $file_name.$params, $content, '', $ContentType); $this->status = $obRequest->status; $this->host = $host; $this->verb = $verb; $this->url = $file_name.$params; $this->headers = $obRequest->headers; $this->errno = $obRequest->errno; $this->errstr = $obRequest->errstr; $this->result = $obRequest->result; if($obRequest->status == 200) { if( isset($additional_headers["option-raw-result"]) || isset($additional_headers["option--result"]) ) { return $obRequest->result; } elseif($obRequest->result != "") { $obXML = new CDataXML; $text = preg_replace("/<"."\\?XML.*?\\?".">/i", "", $obRequest->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', array('#errno#'=>'1'))); $APPLICATION->ThrowException($e); return false; } else { //Empty success result return array(); } } elseif( $obRequest->status == 307 //Temporary redirect && isset($obRequest->headers["Location"]) && $was_end_point === "" //No recurse yet ) { $this->new_end_point = $obRequest->headers["Location"]; return $this->SendRequest( $arSettings, $verb, $bucket, $file_name, $params, $content, $additional_headers ); } elseif($obRequest->status > 0) { if($obRequest->result != "") { $obXML = new CDataXML; if($obXML->LoadString($obRequest->result)) { $node = $obXML->SelectNodes("/Error/Message"); if (is_object($node)) { $errorMessage = trim($node->textContent(), '.'); $e = new CApplicationException(GetMessage('CLO_STORAGE_S3_XML_ERROR', array( '#errmsg#' => $errorMessage, ))); $APPLICATION->ThrowException($e); return false; } } } $e = new CApplicationException(GetMessage('CLO_STORAGE_S3_XML_PARSE_ERROR', array('#errno#'=>'2'))); $APPLICATION->ThrowException($e); return false; } else { $e = new CApplicationException(GetMessage('CLO_STORAGE_S3_XML_PARSE_ERROR', array('#errno#'=>'3'))); $APPLICATION->ThrowException($e); return false; } } /** * @param array[string]string $arBucket * @return bool */ function CreateBucket($arBucket) { global $APPLICATION; $arFiles = $this->ListFiles($arBucket, '/'); if(is_array($arFiles)) return true; 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; } else { return is_array($response); } } /** * @param array[string]string $arBucket * @return bool */ 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; } else { return is_array($response); } } /** * @param array[string]string $arBucket * @return bool */ 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)) { 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 * @return string */ function GetFileSRC($arBucket, $arFile) { $proto = CMain::IsHTTPS()? "https": "http"; static $aps = null; if (!$aps) $aps = self::GetAPList(); if($arBucket["CNAME"] != "") { $host = $arBucket["CNAME"]; $pref = ""; } elseif ($proto === "https" && strpos($arBucket["BUCKET"], ".") !== false) { if (isset($aps[$arBucket["LOCATION"]])) $host = $aps[$arBucket["LOCATION"]]; else $host = $aps[""]; $pref = $arBucket["BUCKET"]; } else { if (isset($aps[$arBucket["LOCATION"]])) $host = $arBucket["BUCKET"].".".$aps[$arBucket["LOCATION"]]; else $host = $aps[""]; $pref = ""; } if(is_array($arFile)) $URI = ltrim($arFile["SUBDIR"]."/".$arFile["FILE_NAME"], "/"); else $URI = ltrim($arFile, "/"); if ($arBucket["PREFIX"] != "") { if(substr($URI, 0, strlen($arBucket["PREFIX"])+1) !== $arBucket["PREFIX"]."/") $URI = $arBucket["PREFIX"]."/".$URI; } if ($pref !== "") { $URI = $pref."/".$URI; } return $proto."://$host/".CCloudUtil::URLEncode($URI, "UTF-8"); } /** * @param array[string]string $arBucket * @param string $filePath * @return bool */ function FileExists($arBucket, $filePath) { global $APPLICATION; if($arBucket["PREFIX"] != "") { if(substr($filePath, 0, strlen($arBucket["PREFIX"])+2) != "/".$arBucket["PREFIX"]."/") $filePath = "/".$arBucket["PREFIX"]."/".ltrim($filePath, "/"); } $filePath = CCloudUtil::URLEncode($filePath, "UTF-8"); $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; } else//if($this->status == 404) { $APPLICATION->ResetException(); return false; } } function FileCopy($arBucket, $arFile, $filePath) { global $APPLICATION; if($arBucket["PREFIX"]) { if(substr($filePath, 0, strlen($arBucket["PREFIX"])+2) != "/".$arBucket["PREFIX"]."/") $filePath = "/".$arBucket["PREFIX"]."/".ltrim($filePath, "/"); } $additional_headers = array(); if($this->_public) $additional_headers["x-amz-acl"] = "public-read"; $additional_headers["x-amz-copy-source"] = CCloudUtil::URLEncode("/".$arBucket["BUCKET"]."/".($arBucket["PREFIX"]? $arBucket["PREFIX"]."/": "").($arFile["SUBDIR"]? $arFile["SUBDIR"]."/": "").$arFile["FILE_NAME"], "UTF-8"); $additional_headers["Content-Type"] = $arFile["CONTENT_TYPE"]; $this->SetLocation($arBucket["LOCATION"]); $this->SendRequest( $arBucket["SETTINGS"], 'PUT', $arBucket["BUCKET"], CCloudUtil::URLEncode($filePath, "UTF-8"), '', '', $additional_headers ); if($this->status == 200) { return $this->GetFileSRC($arBucket, $filePath); } else//if($this->status == 404) { $APPLICATION->ResetException(); return false; } } function DownloadToFile($arBucket, $arFile, $filePath) { $io = CBXVirtualIo::GetInstance(); $obRequest = new CHTTP; $obRequest->follow_redirect = true; return $obRequest->Download($this->GetFileSRC($arBucket, $arFile), $io->GetPhysicalName($filePath)); } function DeleteFile($arBucket, $filePath) { global $APPLICATION; if($arBucket["PREFIX"]) { if(substr($filePath, 0, strlen($arBucket["PREFIX"])+2) != "/".$arBucket["PREFIX"]."/") $filePath = "/".$arBucket["PREFIX"]."/".ltrim($filePath, "/"); } $filePath = CCloudUtil::URLEncode($filePath, "UTF-8"); $this->SetLocation($arBucket["LOCATION"]); $this->SendRequest( $arBucket["SETTINGS"], 'DELETE', $arBucket["BUCKET"], $filePath ); if($this->status == 204) { $APPLICATION->ResetException(); return true; } else//if($this->status == 404) { $APPLICATION->ResetException(); return false; } } function SaveFile($arBucket, $filePath, $arFile) { global $APPLICATION; if($arBucket["PREFIX"]) { if(substr($filePath, 0, strlen($arBucket["PREFIX"])+2) != "/".$arBucket["PREFIX"]."/") $filePath = "/".$arBucket["PREFIX"]."/".ltrim($filePath, "/"); } $filePath = CCloudUtil::URLEncode($filePath, "UTF-8"); $additional_headers = array(); 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)? CUtil::BinStrlen($arFile["content"]): filesize($arFile["tmp_name"])); $this->SetLocation($arBucket["LOCATION"]); $this->SendRequest( $arBucket["SETTINGS"], 'PUT', $arBucket["BUCKET"], $filePath, '', (array_key_exists("content", $arFile)? $arFile["content"]: fopen($arFile["tmp_name"], "rb")), $additional_headers ); if($this->status == 200) { return true; } elseif ($this->status == 400 && strpos($this->result, 'ExpiredToken') !== false) { $this->tokenHasExpired = true; return false; } elseif($this->status == 403) { AddMessage2Log($this); return false; } else { AddMessage2Log($this); $APPLICATION->ResetException(); return false; } } function ListFiles($arBucket, $filePath, $bRecursive = false) { global $APPLICATION; $result = array( "dir" => array(), "file" => array(), "file_size" => array(), ); $filePath = trim($filePath, '/'); if(strlen($filePath)) $filePath .= '/'; if($arBucket["PREFIX"]) { if(substr($filePath, 0, strlen($arBucket["PREFIX"])+2) != "/".$arBucket["PREFIX"]."/") $filePath = $arBucket["PREFIX"]."/".ltrim($filePath, "/"); } $filePath = $APPLICATION->ConvertCharset($filePath, LANG_CHARSET, "UTF-8"); $this->SetLocation($arBucket["LOCATION"]); $marker = ''; while(true) { $response = $this->SendRequest( $arBucket["SETTINGS"], 'GET', $arBucket["BUCKET"], '/', '?'.($bRecursive? '': 'delimiter=%2F&').'prefix='.urlencode($filePath) .'&marker='.str_replace("+", "%20", urlencode($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 = substr(rtrim($a["#"]["Prefix"][0]["#"], "/"), strlen($filePath)); $result["dir"][] = $APPLICATION->ConvertCharset(urldecode($dir_name), "UTF-8", LANG_CHARSET); } } $lastKey = null; if( isset($response["ListBucketResult"]["#"]["Contents"]) && is_array($response["ListBucketResult"]["#"]["Contents"]) ) { foreach($response["ListBucketResult"]["#"]["Contents"] as $a) { $file_name = substr($a["#"]["Key"][0]["#"], strlen($filePath)); $result["file"][] = $APPLICATION->ConvertCharset($file_name, "UTF-8", LANG_CHARSET); $result["file_size"][] = $a["#"]["Size"][0]["#"]; $lastKey = $a["#"]["Key"][0]["#"]; } } if( isset($response["ListBucketResult"]["#"]["IsTruncated"]) && is_array($response["ListBucketResult"]["#"]["IsTruncated"]) && $response["ListBucketResult"]["#"]["IsTruncated"][0]["#"] === "true" ) { if (strlen($response["ListBucketResult"]["#"]["NextMarker"][0]["#"]) > 0) { $marker = $response["ListBucketResult"]["#"]["NextMarker"][0]["#"]; continue; } elseif ($lastKey !== null); { $marker = $lastKey; continue; } } break; } elseif ($this->status == 400 && strpos($this->result, 'ExpiredToken') !== false) { $this->tokenHasExpired = true; return false; } else { return false; } } return $result; } function InitiateMultipartUpload($arBucket, &$NS, $filePath, $fileSize, $ContentType) { $filePath = '/'.trim($filePath, '/'); if($arBucket["PREFIX"]) { if(substr($filePath, 0, strlen($arBucket["PREFIX"])+2) != "/".$arBucket["PREFIX"]."/") $filePath = "/".$arBucket["PREFIX"].$filePath; } $filePathU = CCloudUtil::URLEncode($filePath, "UTF-8"); $additional_headers = array(); 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 = array( "filePath" => $filePath, "UploadId" => $response["InitiateMultipartUploadResult"]["#"]["UploadId"][0]["#"], "Parts" => array(), ); return true; } else { return false; } } function GetMinUploadPartSize() { return BX_S3_MIN_UPLOAD_PART_SIZE; } function UploadPartNo($arBucket, &$NS, $data, $part_no) { $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"); $this->SetLocation($arBucket["LOCATION"]); $this->SendRequest( $arBucket["SETTINGS"], 'PUT', $arBucket["BUCKET"], $filePath, '?partNumber='.($part_no + 1).'&uploadId='.urlencode($NS["UploadId"]), $data ); if($this->status == 200 && is_array($this->headers)) { foreach ($this->headers as $key => $value) { if (strtolower($key) === "etag") { $NS["Parts"][$part_no] = $value; return true; } } } return false; } function UploadPart($arBucket, &$NS, $data) { return $this->UploadPartNo($arBucket, $NS, $data, count($NS["Parts"])); } function CompleteMultipartUpload($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"); ksort($NS["Parts"]); $data = ""; foreach($NS["Parts"] as $PartNumber => $ETag) { $data .= "<Part><PartNumber>".($PartNumber+1)."</PartNumber><ETag>".$ETag."</ETag></Part>\n"; } $this->SetLocation($arBucket["LOCATION"]); $this->SendRequest( $arBucket["SETTINGS"], 'POST', $arBucket["BUCKET"], $filePath, '?uploadId='.urlencode($NS["UploadId"]), "<CompleteMultipartUpload>".$data."</CompleteMultipartUpload>" ); return $this->status == 200; } function setPublic($state = true) { $this->_public = $state !== false; } function setHeader($key, $value) { $this->set_headers[$key] = $value; } function unsetHeader($key) { unset($this->set_headers[$key]); } function getHeaders() { return $this->headers; } }