1<?php 2/** 3 * @copyright Copyright (c) 2016, ownCloud, Inc. 4 * 5 * @author Adrian Brzezinski <adrian.brzezinski@eo.pl> 6 * @author Jörn Friedrich Dreyer <jfd@butonic.de> 7 * @author Morris Jobke <hey@morrisjobke.de> 8 * @author Robin Appelman <robin@icewind.nl> 9 * @author Roeland Jago Douma <roeland@famdouma.nl> 10 * 11 * @license AGPL-3.0 12 * 13 * This code is free software: you can redistribute it and/or modify 14 * it under the terms of the GNU Affero General Public License, version 3, 15 * as published by the Free Software Foundation. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU Affero General Public License for more details. 21 * 22 * You should have received a copy of the GNU Affero General Public License, version 3, 23 * along with this program. If not, see <http://www.gnu.org/licenses/> 24 * 25 */ 26namespace OC\Files\ObjectStore; 27 28use GuzzleHttp\Client; 29use GuzzleHttp\Exception\BadResponseException; 30use function GuzzleHttp\Psr7\stream_for; 31use Icewind\Streams\RetryWrapper; 32use OCP\Files\NotFoundException; 33use OCP\Files\ObjectStore\IObjectStore; 34use OCP\Files\StorageAuthException; 35 36const SWIFT_SEGMENT_SIZE = 1073741824; // 1GB 37 38class Swift implements IObjectStore { 39 /** 40 * @var array 41 */ 42 private $params; 43 44 /** @var SwiftFactory */ 45 private $swiftFactory; 46 47 public function __construct($params, SwiftFactory $connectionFactory = null) { 48 $this->swiftFactory = $connectionFactory ?: new SwiftFactory( 49 \OC::$server->getMemCacheFactory()->createDistributed('swift::'), 50 $params, 51 \OC::$server->getLogger() 52 ); 53 $this->params = $params; 54 } 55 56 /** 57 * @return \OpenStack\ObjectStore\v1\Models\Container 58 * @throws StorageAuthException 59 * @throws \OCP\Files\StorageNotAvailableException 60 */ 61 private function getContainer() { 62 return $this->swiftFactory->getContainer(); 63 } 64 65 /** 66 * @return string the container name where objects are stored 67 */ 68 public function getStorageId() { 69 if (isset($this->params['bucket'])) { 70 return $this->params['bucket']; 71 } 72 73 return $this->params['container']; 74 } 75 76 public function writeObject($urn, $stream, string $mimetype = null) { 77 $tmpFile = \OC::$server->getTempManager()->getTemporaryFile('swiftwrite'); 78 file_put_contents($tmpFile, $stream); 79 $handle = fopen($tmpFile, 'rb'); 80 81 if (filesize($tmpFile) < SWIFT_SEGMENT_SIZE) { 82 $this->getContainer()->createObject([ 83 'name' => $urn, 84 'stream' => stream_for($handle), 85 'contentType' => $mimetype, 86 ]); 87 } else { 88 $this->getContainer()->createLargeObject([ 89 'name' => $urn, 90 'stream' => stream_for($handle), 91 'segmentSize' => SWIFT_SEGMENT_SIZE, 92 'contentType' => $mimetype, 93 ]); 94 } 95 } 96 97 /** 98 * @param string $urn the unified resource name used to identify the object 99 * @return resource stream with the read data 100 * @throws \Exception from openstack or GuzzleHttp libs when something goes wrong 101 * @throws NotFoundException if file does not exist 102 */ 103 public function readObject($urn) { 104 try { 105 $publicUri = $this->getContainer()->getObject($urn)->getPublicUri(); 106 $tokenId = $this->swiftFactory->getCachedTokenId(); 107 108 $response = (new Client())->request('GET', $publicUri, 109 [ 110 'stream' => true, 111 'headers' => [ 112 'X-Auth-Token' => $tokenId, 113 'Cache-Control' => 'no-cache', 114 ], 115 ] 116 ); 117 } catch (BadResponseException $e) { 118 if ($e->getResponse() && $e->getResponse()->getStatusCode() === 404) { 119 throw new NotFoundException("object $urn not found in object store"); 120 } else { 121 throw $e; 122 } 123 } 124 125 return RetryWrapper::wrap($response->getBody()->detach()); 126 } 127 128 /** 129 * @param string $urn Unified Resource Name 130 * @return void 131 * @throws \Exception from openstack lib when something goes wrong 132 */ 133 public function deleteObject($urn) { 134 $this->getContainer()->getObject($urn)->delete(); 135 } 136 137 /** 138 * @return void 139 * @throws \Exception from openstack lib when something goes wrong 140 */ 141 public function deleteContainer() { 142 $this->getContainer()->delete(); 143 } 144 145 public function objectExists($urn) { 146 return $this->getContainer()->objectExists($urn); 147 } 148 149 public function copyObject($from, $to) { 150 $this->getContainer()->getObject($from)->copy([ 151 'destination' => $this->getContainer()->name . '/' . $to 152 ]); 153 } 154} 155