ÿØÿà JFIF    ÿÛ „  ( %!1"%)+...383-7(-.+  -%%--------+------------/----------------------------ÿÀ  ¶" ÿÄ     ÿÄ @    !1AQaq‘¡"±ÁÑð2’BRráS‚ñ3b¢CT²Ò #cÿÄ    ÿÄ '   1!AQ2a"‘±#ÿÚ   ? ò(/p‘—–¢·,M|¯G»R£üešT. ôŸEoCFñ‡ 4ÔkåóxR|µÁ¨ÓDà!Ç´µ˜‡p ÍQ‹§!þž^õWsŒy  åÁ§$á«%ºq˜™JyyË0y«^Ϗ -3ÃÏËÈ£Æ\P’ä„s&•‹Üs üô'àWt¹pñCp$bÚF`·7.^)¾!)ÒÙ˜–ÅÏš^h¾ø³·ìe±ŒaeÖ—Í,e=B}Jéߦ$$½—šÙÜCC¯¶è8β–Åkèwx LÉngT±æ¹£Ócœ¿Í/©Éÿ ‰ÚCÒ­mq;Tt¶–}0"zÖPcd1š­¤tˆ„ÐÓâqú[™öUCŠ2޿د­É(¸ßúçšmW,Ò ºf‹Ån™ÄLêÁ8üÁkÁ€L¥ÍuJ1œôrÂSŒÓöt!7EUíïb)AËi¬â-ÙHk8òÁiXYwÅ)O^"ÇØriÛô{»wRUKÙÏÚlÓ¬€‚’שT ] ªb—ÊîKC^×M“xùäñz¬zJ× {4[¹M'IÆeH1LCWÕ]œûº WDæà°OÅ‚¹c²ÞuA–rL`HšpCez‡V’— Ž‚Ë„–ñ­'µR0ÕÖ=À:±G»C\nÆÉ5*¢ †3šhQ4ýÒi$Õ1"Ü]¢p-q3zcQ$ªö¨¥æf›‘»´ÝÚHá„e²JÇ–iÊ:·à¬Øc5Åk»Laª*wi [0Ówk ¦a¦0Õ²ÅÅ‚T¸’³q:Æ<¹ú Ò˜Æ}2VE•Ѐp¡Ô3Úº …h³^LJºçì¥Ánó|˜ÖˆÎˆ+« ¦dø‘Mº–d\ªDðÛ„Ì×LÝ——Ð9Ç|•FèçVm™¨€™3õS–9Äå¢<̉Ìm¢±`·º˜&Z²ž Ë®D«¶»6™‰©­HÈ Àá«h­i³04æ’hxœi3ï¨(ÓL·†‹¶ËpŒ ƒÜÒ? :õdsÀ –3æÓ·Üjâ¬9—]8n¼&]I F£¼…ì–—Œ0v°LǐÅfÛ2TÖ’aµ˜N@ÖBSÏ^à´4-±°b×nÈÈœÆD=KdŒø…ÄÈK,›ÖR§fjü$^ÔÎ!„6^ s:Üun\ôkCÞò÷<—Lúl Í›G¹ÀPá¼Nc¡šÒ…ÙWͳœ±vÊáЄ×9‹P‰cEØ‘™›µŸP<×g1 º©Ø¬wiÁ^l5× I/';Ë+´Z‡ÓÄFÕp[Èð’ HÄ ó 9¬Dl4“é±Ï”V=^Hð;žLädÉa‚°Ë,#/Ž~²BÔÛ QãñQtIeórVB,ÏÂf‹4#Μc‹É3ZIÖ Œ¢¥·üxa‡Ãçè„jŠ[5 Ä!U?,3žÎ×…ðdÓ÷háŠA‰ìÄ4÷ÃÜZÃE{‰®+W\ZÍE[‰»µk»MÝ­`¢©†˜ÃV‹-f¢¡†¢a«eŠ%«Y¨©q%jâd,Ôq`) ¤¦…šˆ ¦E>0Ùb²t–Ÿ  ÷jsςҒKÈcø(é›;k(’#ôŒQ ™C> õ³M9óºÆµ¤çâ;uÀ)5>RÔ¸fÓ~Ȧ—’-$fe«"FâUˆÊwµPÏu w+"åÉó¨6s™Û9(À¨ù©hE°½†n`m~“@©›p!Vkhµ Á1šbEqqÊ@ºèl›2 –Yã%‘‚hÛ{a¶‡ÅÞM²¬çL34®Çl]®‹ŽØŒm'RÜe35æöˆ­Á­ cP9šeÁköo´L³Ì=p8]”‡B¶,šº|ɏdzXˆØk*ÇÚ{#ñ‰pã(€¶‡9ý'šÜ³½¯kƒ†Â’ëSOƒ•Å®H6+a¢µˆ‹Y¨b˜b+XˆÖ!°u!¢"µˆˆn2ˆ Å0Ä`Å Ô®c(ÓÜGº•Ä»Œ âWî%q.áÐÄ÷î%umÍ£+ÜQ,Vn¦-GphU¸¢X­&,Gt ·K¢ÅÅ·¥RÄÊÉbKn N­R2g$VµÕó Qr#–ÓZUÏð0ÎtÙÀœz ëöo“Îk¯n„T°"SÞ+³/…€½"ižãO%Í%&Δâ‘ËØ´IˆÓ°Ë‰ÞM3.™yçÁzit›`4C`k¢¼á!álÄï sÂ[×!¤l¾æo"ƒIÔ'@ªu)3›ô®“@Z à¾] üœ'‡4¸PÞkÁ-qÄH‰ÓÍBd´ÿ —¹°ÄA&ÊUC„öz‚‚RæŠtf¬>˜ÑidšEçÑL÷áý¡V±h§­c¿¥¨ƒ@g¼…Ýö^ÞËc.ž„æ“I[ ª+·zÐÒÚ,½Ð»¶€C›7jk$๖€Ž¾ÅÙpp¶ÍþéΤ ½ØÎŒq ñpæžÅ¥#Øâ49ÃÂK^MYV“,¶Éz_òæ:…;Õ«¦I3âJ£¤û5$7Èxün\â]Êe¯(Ÿ&Ž‹¶2<6Ćf× æ¢ƱqWı[(ÙÂŽç‚T7¨@ÈÉÍß‚ïÙ"&3TYlGށ†)†#5ŠA©^T2ÆÁ†)©†©†¤yGXÁ†©"†)©¼ÅV WS†#©]R–r‹q5ÕbâWþBh¯u+ªÅÄ×ü„nÑ^êkªÁj‰jeœWˆÕÕ`µDµ2Ì+Ä µDµµ5Ô{¤Þ0RF’H÷AÛ<ÊÁ§ ƈæ0Ñ£ê4 öÚµ[¬/1²€â<-¬šö²r×0o×w ´xÐÒË„ g01¬„åEM蛇Ã\6µE›‘fÙO k÷CºnÓ1`iîÒ² áÂ!ÑLÄÿ ³¬\õ*:M=Ç»c‹2[‰ØsÕLò\ŒK L^™üÄN\¶#» Ç\……ßïIœÉ$¸ÎñÍ®ÔHÇ Ûí/´>Li”„†g^$ 7NÍÑÑ2M2ªNg=k½Ð}Ÿl6‡ —JWˆ”ÞéT·+µ¦üÂFè¢Vs6ýÆ4»2+2IÝ9ô9®ÇGÙÇ1+Ä8;*Õ§ž½D*îÑWÚçá3Ma¤·ˆ¥t}š²Ü…Q+Æüµ8HíS”üxGéÁˆ¦Ël„éÈ 5 µÄƒ6äÜ(kIš¯S†fÖ&¸®×v|Äyˆ*禌Éù@7-®Ä[]ÎÖ¿ê`º…=:…žJVnݳvðÖKE¼‚ »mà ÷f®:Ë ™–4rð¡4` ܹçž~‹C=œ…¾Ïj|VDtK.ƒ"9 Ï ­–éXá³6WÒïIL-Ї Ç PK& \sR R TreeHash.phpnu[algorithm = $algorithm; $this->reset(); } /** * {@inheritdoc} * @throws \LogicException if the root tree hash is already calculated */ public function update($data) { // Error if hash is already calculated. if ($this->hash) { throw new \LogicException('You may not add more data to a ' . 'complete tree hash.'); } // Buffer incoming data. $this->buffer .= $data; // When there is more than a MB of data, create a checksum. while (strlen($this->buffer) >= self::MB) { $data = substr($this->buffer, 0, self::MB); $this->buffer = substr($this->buffer, self::MB) ?: ''; $this->checksums[] = hash($this->algorithm, $data, true); } return $this; } /** * Add a checksum to the tree hash directly * * @param string $checksum The checksum to add * @param bool $inBinaryForm TRUE if checksum is in binary form * * @return self * @throws \LogicException if the root tree hash is already calculated */ public function addChecksum($checksum, $inBinaryForm = false) { // Error if hash is already calculated if ($this->hash) { throw new \LogicException('You may not add more checksums to a ' . 'complete tree hash.'); } // Convert the checksum to binary form if necessary $this->checksums[] = $inBinaryForm ? $checksum : hex2bin($checksum); return $this; } public function complete() { if (!$this->hash) { // Clear out the remaining buffer. if (strlen($this->buffer) > 0) { $this->checksums[] = hash($this->algorithm, $this->buffer, true); $this->buffer = ''; } // If no hashes, add the EMPTY_HASH. if (!$this->checksums) { $this->checksums[] = hex2bin(self::EMPTY_HASH); } // Perform hashes up the tree to arrive at the root checksum. $hashes = $this->checksums; while (count($hashes) > 1) { $sets = array_chunk($hashes, 2); $hashes = array(); foreach ($sets as $set) { $hashes[] = (count($set) === 1) ? $set[0] : hash($this->algorithm, $set[0] . $set[1], true); } } $this->hash = $hashes[0]; } return $this->hash; } public function reset() { $this->hash = null; $this->checksums = []; $this->buffer = ''; } } PK& \pY-Y-GlacierClient.phpnu[getHandlerList(); $stack->appendBuild($this->getApiVersionMiddleware(), 'glacier.api_version'); $stack->appendBuild($this->getChecksumsMiddleware(), 'glacier.checksum'); $stack->appendBuild( Middleware::contentType(['UploadArchive', 'UploadPart']), 'glacier.content_type' ); $stack->appendInit( Middleware::sourceFile($this->getApi(), 'body', 'sourceFile'), 'glacier.source_file' ); } /** * {@inheritdoc} * * Sets the default accountId to "-" for all operations. */ public function getCommand($name, array $args = []) { return parent::getCommand($name, $args + ['accountId' => '-']); } /** * Creates a middleware that updates a command with the content and tree * hash headers for upload operations. * * @return callable * @throws CouldNotCreateChecksumException if the body is not seekable. */ private function getChecksumsMiddleware() { return function (callable $handler) { return function ( CommandInterface $command, ?RequestInterface $request = null ) use ($handler) { // Accept "ContentSHA256" with a lowercase "c" to match other Glacier params. if (!$command['ContentSHA256'] && $command['contentSHA256']) { $command['ContentSHA256'] = $command['contentSHA256']; unset($command['contentSHA256']); } // If uploading, then make sure checksums are added. $name = $command->getName(); if (($name === 'UploadArchive' || $name === 'UploadMultipartPart') && (!$command['checksum'] || !$command['ContentSHA256']) ) { $body = $request->getBody(); if (!$body->isSeekable()) { throw new CouldNotCreateChecksumException('sha256'); } // Add a tree hash if not provided. if (!$command['checksum']) { $body = new HashingStream( $body, new TreeHash(), function ($result) use (&$request) { $request = $request->withHeader( 'x-amz-sha256-tree-hash', bin2hex($result) ); } ); } // Add a linear content hash if not provided. if (!$command['ContentSHA256']) { $body = new HashingStream( $body, new PhpHash('sha256'), function ($result) use ($command) { $command['ContentSHA256'] = bin2hex($result); } ); } // Read the stream in order to calculate the hashes. while (!$body->eof()) { $body->read(1048576); } $body->seek(0); } // Set the content hash header if a value is in the command. if ($command['ContentSHA256']) { $request = $request->withHeader( 'x-amz-content-sha256', $command['ContentSHA256'] ); } return $handler($command, $request); }; }; } /** * Creates a middleware that adds the API version header for all requests. * * @return callable */ private function getApiVersionMiddleware() { return function (callable $handler) { return function ( CommandInterface $command, ?RequestInterface $request = null ) use ($handler) { return $handler($command, $request->withHeader( 'x-amz-glacier-version', $this->getApi()->getMetadata('apiVersion') )); }; }; } /** * @internal * @codeCoverageIgnore */ public static function applyDocFilters(array $api, array $docs) { // Add the SourceFile parameter. $docs['shapes']['SourceFile']['base'] = 'The path to a file on disk to use instead of the body parameter.'; $api['shapes']['SourceFile'] = ['type' => 'string']; $api['shapes']['UploadArchiveInput']['members']['sourceFile'] = ['shape' => 'SourceFile']; $api['shapes']['UploadMultipartPartInput']['members']['sourceFile'] = ['shape' => 'SourceFile']; // Add the ContentSHA256 parameter. $docs['shapes']['ContentSHA256']['base'] = 'A SHA256 hash of the content of the request body'; $api['shapes']['ContentSHA256'] = ['type' => 'string']; $api['shapes']['UploadArchiveInput']['members']['contentSHA256'] = ['shape' => 'ContentSHA256']; $api['shapes']['UploadMultipartPartInput']['members']['contentSHA256'] = ['shape' => 'ContentSHA256']; // Add information about "checksum" and "ContentSHA256" being optional. $optional = '
The SDK will compute this value ' . 'for you on your behalf if it is not supplied.
'; $docs['shapes']['checksum']['append'] = $optional; $docs['shapes']['ContentSHA256']['append'] = $optional; // Make "accountId" optional for all operations. foreach ($api['operations'] as $operation) { $inputShape =& $api['shapes'][$operation['input']['shape']]; $accountIdIndex = array_search('accountId', $inputShape['required']); unset($inputShape['required'][$accountIdIndex]); } // Add information about the default value for "accountId". $optional = '
The SDK will set this value to "-" by default.
'; foreach ($docs['shapes']['string']['refs'] as $name => &$ref) { if (strpos($name, 'accountId')) { $ref .= $optional; } } return [ new Service($api, ApiProvider::defaultProvider()), new DocModel($docs) ]; } } PK& \39Exception/GlacierException.phpnu[ $accountId, 'vaultName' => $vaultName, 'uploadId' => $uploadId, ]); foreach ($client->getPaginator('ListParts', $state->getId()) as $result) { // Get the part size from the first part in the first result. if (!$state->getPartSize()) { $state->setPartSize($result['PartSizeInBytes']); } // Mark all the parts returned by ListParts as uploaded. foreach ($result['Parts'] as $part) { list($rangeIndex, $rangeSize) = self::parseRange( $part['RangeInBytes'], $state->getPartSize() ); $state->markPartAsUploaded($rangeIndex, [ 'size' => $rangeSize, 'checksum' => $part['SHA256TreeHash'], ]); } } $state->setStatus(UploadState::INITIATED); return $state; } /** * Creates a multipart upload for a Glacier archive. * * The valid configuration options are as follows: * * - account_id: (string, default=string('-')) Account ID for the archive * being uploaded, if different from the account making the request. * - archive_description: (string) Description of the archive. * - before_complete: (callable) Callback to invoke before the * `CompleteMultipartUpload` operation. The callback should have a * function signature like `function (Aws\Command $command) {...}`. * - before_initiate: (callable) Callback to invoke before the * `InitiateMultipartUpload` operation. The callback should have a * function signature like `function (Aws\Command $command) {...}`. * - before_upload: (callable) Callback to invoke before any * `UploadMultipartPart` operations. The callback should have a function * signature like `function (Aws\Command $command) {...}`. * - concurrency: (int, default=int(3)) Maximum number of concurrent * `UploadMultipartPart` operations allowed during the multipart upload. * - part_size: (int, default=int(1048576)) Part size, in bytes, to use when * doing a multipart upload. This must between 1 MB and 4 GB, and must be * a power of 2 (in megabytes). * - prepare_data_source: (callable) Callback to invoke before starting the * multipart upload workflow. The callback should have a function * signature like `function () {...}`. * - state: (Aws\Multipart\UploadState) An object that represents the state * of the multipart upload and that is used to resume a previous upload. * When this options is provided, the `account_id`, `key`, and `part_size` * options are ignored. * - vault_name: (string, required) Vault name to use for the archive being * uploaded. * * @param GlacierClient $client Client used for the upload. * @param mixed $source Source of the data to upload. * @param array $config Configuration used to perform the upload. */ public function __construct(GlacierClient $client, $source, array $config = []) { parent::__construct($client, $source, $config + [ 'account_id' => '-', 'vault_name' => null, ]); } protected function loadUploadWorkflowInfo() { return [ 'command' => [ 'initiate' => 'InitiateMultipartUpload', 'upload' => 'UploadMultipartPart', 'complete' => 'CompleteMultipartUpload', ], 'id' => [ 'account_id' => 'accountId', 'vault_name' => 'vaultName', 'upload_id' => 'uploadId', ], 'part_num' => 'range', ]; } protected function determinePartSize() { // Make sure the part size is set. $partSize = $this->config['part_size'] ?: self::PART_MIN_SIZE; // Ensure that the part size is valid. if (!in_array($partSize, self::$validPartSizes)) { throw new \InvalidArgumentException('The part_size must be a power ' . 'of 2, in megabytes, such that 1 MB <= PART_SIZE <= 4 GB.'); } return $partSize; } protected function createPart($seekable, $number) { $data = []; $firstByte = $this->source->tell(); // Read from the source to create the body stream. This also // calculates the linear and tree hashes as the data is read. if ($seekable) { // Case 1: Stream is seekable, can make stream from new handle. $body = Psr7\Utils::tryFopen($this->source->getMetadata('uri'), 'r'); $body = $this->limitPartStream(Psr7\Utils::streamFor($body)); // Create another stream decorated with hashing streams and read // through it, so we can get the hash values for the part. $decoratedBody = $this->decorateWithHashes($body, $data); while (!$decoratedBody->eof()) $decoratedBody->read(1048576); // Seek the original source forward to the end of the range. $this->source->seek($this->source->tell() + $body->getSize()); } else { // Case 2: Stream is not seekable, must store part in temp stream. $source = $this->limitPartStream($this->source); $source = $this->decorateWithHashes($source, $data); $body = Psr7\Utils::streamFor(); Psr7\Utils::copyToStream($source, $body); } // Do not create a part if the body size is zero. if ($body->getSize() === 0) { return false; } $body->seek(0); $data['body'] = $body; $lastByte = $this->source->tell() - 1; $data['range'] = "bytes {$firstByte}-{$lastByte}/*"; return $data; } protected function handleResult(CommandInterface $command, ResultInterface $result) { list($rangeIndex, $rangeSize) = $this->parseRange( $command['range'], $this->state->getPartSize() ); $this->state->markPartAsUploaded($rangeIndex, [ 'size' => $rangeSize, 'checksum' => $command['checksum'] ]); } protected function getInitiateParams() { $params = ['partSize' => $this->state->getPartSize()]; if (isset($this->config['archive_description'])) { $params['archiveDescription'] = $this->config['archive_description']; } return $params; } protected function getCompleteParams() { $treeHash = new TreeHash(); $archiveSize = 0; foreach ($this->state->getUploadedParts() as $part) { $archiveSize += $part['size']; $treeHash->addChecksum($part['checksum']); } return [ 'archiveSize' => $archiveSize, 'checksum' => bin2hex($treeHash->complete()), ]; } /** * Decorates a stream with a tree AND linear sha256 hashing stream. * * @param Stream $stream Stream to decorate. * @param array $data Data bag that results are injected into. * * @return Stream */ private function decorateWithHashes(Stream $stream, array &$data) { // Make sure that a tree hash is calculated. $stream = new HashingStream($stream, new TreeHash(), function ($result) use (&$data) { $data['checksum'] = bin2hex($result); } ); // Make sure that a linear SHA256 hash is calculated. $stream = new HashingStream($stream, new PhpHash('sha256'), function ($result) use (&$data) { $data['ContentSHA256'] = bin2hex($result); } ); return $stream; } /** * Parses a Glacier range string into a size and part number. * * @param string $range Glacier range string (e.g., "bytes 5-5000/*") * @param int $partSize The chosen part size * * @return array */ private static function parseRange($range, $partSize) { // Strip away the prefix and suffix. if (strpos($range, 'bytes') !== false) { $range = substr($range, 6, -2); } // Split that range into it's parts. list($firstByte, $lastByte) = explode('-', $range); // Calculate and return range index and range size return [ intval($firstByte / $partSize) + 1, $lastByte - $firstByte + 1, ]; } } PK& \sR R TreeHash.phpnu[PK& \pY-Y- GlacierClient.phpnu[PK& \39(;Exception/GlacierException.phpnu[PK& \Y'':<MultipartUploader.phpnu[PKPEd