Completed
Pull Request — master (#8235)
by Robin
20:03 queued 03:57
created
lib/private/Files/Mount/ObjectHomeMountProvider.php 1 patch
Indentation   +100 added lines, -100 removed lines patch added patch discarded remove patch
@@ -32,107 +32,107 @@
 block discarded – undo
32 32
  * Mount provider for object store home storages
33 33
  */
34 34
 class ObjectHomeMountProvider implements IHomeMountProvider {
35
-	/**
36
-	 * @var IConfig
37
-	 */
38
-	private $config;
39
-
40
-	/**
41
-	 * ObjectStoreHomeMountProvider constructor.
42
-	 *
43
-	 * @param IConfig $config
44
-	 */
45
-	public function __construct(IConfig $config) {
46
-		$this->config = $config;
47
-	}
48
-
49
-	/**
50
-	 * Get the cache mount for a user
51
-	 *
52
-	 * @param IUser $user
53
-	 * @param IStorageFactory $loader
54
-	 * @return \OCP\Files\Mount\IMountPoint
55
-	 */
56
-	public function getHomeMountForUser(IUser $user, IStorageFactory $loader) {
57
-
58
-		$config = $this->getMultiBucketObjectStoreConfig($user);
59
-		if ($config === null) {
60
-			$config = $this->getSingleBucketObjectStoreConfig($user);
61
-		}
62
-
63
-		if ($config === null) {
64
-			return null;
65
-		}
66
-
67
-		return new MountPoint('\OC\Files\ObjectStore\HomeObjectStoreStorage', '/' . $user->getUID(), $config['arguments'], $loader);
68
-	}
69
-
70
-	/**
71
-	 * @param IUser $user
72
-	 * @return array|null
73
-	 */
74
-	private function getSingleBucketObjectStoreConfig(IUser $user) {
75
-		$config = $this->config->getSystemValue('objectstore');
76
-		if (!is_array($config)) {
77
-			return null;
78
-		}
79
-
80
-		// sanity checks
81
-		if (empty($config['class'])) {
82
-			\OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
83
-		}
84
-		if (!isset($config['arguments'])) {
85
-			$config['arguments'] = [];
86
-		}
87
-		// instantiate object store implementation
88
-		$config['arguments']['objectstore'] = new $config['class']($config['arguments']);
89
-
90
-		$config['arguments']['user'] = $user;
91
-
92
-		return $config;
93
-	}
94
-
95
-	/**
96
-	 * @param IUser $user
97
-	 * @return array|null
98
-	 */
99
-	private function getMultiBucketObjectStoreConfig(IUser $user) {
100
-		$config = $this->config->getSystemValue('objectstore_multibucket');
101
-		if (!is_array($config)) {
102
-			return null;
103
-		}
104
-
105
-		// sanity checks
106
-		if (empty($config['class'])) {
107
-			\OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
108
-		}
109
-		if (!isset($config['arguments'])) {
110
-			$config['arguments'] = [];
111
-		}
112
-		$config['arguments']['user'] = $user;
113
-
114
-		$bucket = $this->config->getUserValue($user->getUID(), 'homeobjectstore', 'bucket', null);
115
-
116
-		if ($bucket === null) {
117
-			/*
35
+    /**
36
+     * @var IConfig
37
+     */
38
+    private $config;
39
+
40
+    /**
41
+     * ObjectStoreHomeMountProvider constructor.
42
+     *
43
+     * @param IConfig $config
44
+     */
45
+    public function __construct(IConfig $config) {
46
+        $this->config = $config;
47
+    }
48
+
49
+    /**
50
+     * Get the cache mount for a user
51
+     *
52
+     * @param IUser $user
53
+     * @param IStorageFactory $loader
54
+     * @return \OCP\Files\Mount\IMountPoint
55
+     */
56
+    public function getHomeMountForUser(IUser $user, IStorageFactory $loader) {
57
+
58
+        $config = $this->getMultiBucketObjectStoreConfig($user);
59
+        if ($config === null) {
60
+            $config = $this->getSingleBucketObjectStoreConfig($user);
61
+        }
62
+
63
+        if ($config === null) {
64
+            return null;
65
+        }
66
+
67
+        return new MountPoint('\OC\Files\ObjectStore\HomeObjectStoreStorage', '/' . $user->getUID(), $config['arguments'], $loader);
68
+    }
69
+
70
+    /**
71
+     * @param IUser $user
72
+     * @return array|null
73
+     */
74
+    private function getSingleBucketObjectStoreConfig(IUser $user) {
75
+        $config = $this->config->getSystemValue('objectstore');
76
+        if (!is_array($config)) {
77
+            return null;
78
+        }
79
+
80
+        // sanity checks
81
+        if (empty($config['class'])) {
82
+            \OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
83
+        }
84
+        if (!isset($config['arguments'])) {
85
+            $config['arguments'] = [];
86
+        }
87
+        // instantiate object store implementation
88
+        $config['arguments']['objectstore'] = new $config['class']($config['arguments']);
89
+
90
+        $config['arguments']['user'] = $user;
91
+
92
+        return $config;
93
+    }
94
+
95
+    /**
96
+     * @param IUser $user
97
+     * @return array|null
98
+     */
99
+    private function getMultiBucketObjectStoreConfig(IUser $user) {
100
+        $config = $this->config->getSystemValue('objectstore_multibucket');
101
+        if (!is_array($config)) {
102
+            return null;
103
+        }
104
+
105
+        // sanity checks
106
+        if (empty($config['class'])) {
107
+            \OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
108
+        }
109
+        if (!isset($config['arguments'])) {
110
+            $config['arguments'] = [];
111
+        }
112
+        $config['arguments']['user'] = $user;
113
+
114
+        $bucket = $this->config->getUserValue($user->getUID(), 'homeobjectstore', 'bucket', null);
115
+
116
+        if ($bucket === null) {
117
+            /*
118 118
 			 * Use any provided bucket argument as prefix
119 119
 			 * and add the mapping from username => bucket
120 120
 			 */
121
-			if (!isset($config['arguments']['bucket'])) {
122
-				$config['arguments']['bucket'] = '';
123
-			}
124
-			$mapper = new \OC\Files\ObjectStore\Mapper($user);
125
-			$numBuckets = isset($config['arguments']['num_buckets']) ? $config['arguments']['num_buckets'] : 64;
126
-			$config['arguments']['bucket'] .= $mapper->getBucket($numBuckets);
127
-
128
-			$this->config->setUserValue($user->getUID(), 'homeobjectstore', 'bucket', $config['arguments']['bucket']);
129
-		} else {
130
-			$config['arguments']['bucket'] = $bucket;
131
-		}
132
-
133
-		// instantiate object store implementation
134
-		$config['arguments']['objectstore'] = new $config['class']($config['arguments']);
135
-
136
-		return $config;
137
-	}
121
+            if (!isset($config['arguments']['bucket'])) {
122
+                $config['arguments']['bucket'] = '';
123
+            }
124
+            $mapper = new \OC\Files\ObjectStore\Mapper($user);
125
+            $numBuckets = isset($config['arguments']['num_buckets']) ? $config['arguments']['num_buckets'] : 64;
126
+            $config['arguments']['bucket'] .= $mapper->getBucket($numBuckets);
127
+
128
+            $this->config->setUserValue($user->getUID(), 'homeobjectstore', 'bucket', $config['arguments']['bucket']);
129
+        } else {
130
+            $config['arguments']['bucket'] = $bucket;
131
+        }
132
+
133
+        // instantiate object store implementation
134
+        $config['arguments']['objectstore'] = new $config['class']($config['arguments']);
135
+
136
+        return $config;
137
+    }
138 138
 }
Please login to merge, or discard this patch.
lib/private/Files/ObjectStore/ObjectStoreStorage.php 1 patch
Indentation   +392 added lines, -392 removed lines patch added patch discarded remove patch
@@ -31,396 +31,396 @@
 block discarded – undo
31 31
 use OCP\Files\ObjectStore\IObjectStore;
32 32
 
33 33
 class ObjectStoreStorage extends \OC\Files\Storage\Common {
34
-	/**
35
-	 * @var \OCP\Files\ObjectStore\IObjectStore $objectStore
36
-	 */
37
-	protected $objectStore;
38
-	/**
39
-	 * @var string $id
40
-	 */
41
-	protected $id;
42
-	/**
43
-	 * @var \OC\User\User $user
44
-	 */
45
-	protected $user;
46
-
47
-	private $objectPrefix = 'urn:oid:';
48
-
49
-	private $logger;
50
-
51
-	public function __construct($params) {
52
-		if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) {
53
-			$this->objectStore = $params['objectstore'];
54
-		} else {
55
-			throw new \Exception('missing IObjectStore instance');
56
-		}
57
-		if (isset($params['storageid'])) {
58
-			$this->id = 'object::store:' . $params['storageid'];
59
-		} else {
60
-			$this->id = 'object::store:' . $this->objectStore->getStorageId();
61
-		}
62
-		if (isset($params['objectPrefix'])) {
63
-			$this->objectPrefix = $params['objectPrefix'];
64
-		}
65
-		//initialize cache with root directory in cache
66
-		if (!$this->is_dir('/')) {
67
-			$this->mkdir('/');
68
-		}
69
-
70
-		$this->logger = \OC::$server->getLogger();
71
-	}
72
-
73
-	public function mkdir($path) {
74
-		$path = $this->normalizePath($path);
75
-
76
-		if ($this->file_exists($path)) {
77
-			return false;
78
-		}
79
-
80
-		$mTime = time();
81
-		$data = [
82
-			'mimetype' => 'httpd/unix-directory',
83
-			'size' => 0,
84
-			'mtime' => $mTime,
85
-			'storage_mtime' => $mTime,
86
-			'permissions' => \OCP\Constants::PERMISSION_ALL,
87
-		];
88
-		if ($path === '') {
89
-			//create root on the fly
90
-			$data['etag'] = $this->getETag('');
91
-			$this->getCache()->put('', $data);
92
-			return true;
93
-		} else {
94
-			// if parent does not exist, create it
95
-			$parent = $this->normalizePath(dirname($path));
96
-			$parentType = $this->filetype($parent);
97
-			if ($parentType === false) {
98
-				if (!$this->mkdir($parent)) {
99
-					// something went wrong
100
-					return false;
101
-				}
102
-			} else if ($parentType === 'file') {
103
-				// parent is a file
104
-				return false;
105
-			}
106
-			// finally create the new dir
107
-			$mTime = time(); // update mtime
108
-			$data['mtime'] = $mTime;
109
-			$data['storage_mtime'] = $mTime;
110
-			$data['etag'] = $this->getETag($path);
111
-			$this->getCache()->put($path, $data);
112
-			return true;
113
-		}
114
-	}
115
-
116
-	/**
117
-	 * @param string $path
118
-	 * @return string
119
-	 */
120
-	private function normalizePath($path) {
121
-		$path = trim($path, '/');
122
-		//FIXME why do we sometimes get a path like 'files//username'?
123
-		$path = str_replace('//', '/', $path);
124
-
125
-		// dirname('/folder') returns '.' but internally (in the cache) we store the root as ''
126
-		if (!$path || $path === '.') {
127
-			$path = '';
128
-		}
129
-
130
-		return $path;
131
-	}
132
-
133
-	/**
134
-	 * Object Stores use a NoopScanner because metadata is directly stored in
135
-	 * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere.
136
-	 *
137
-	 * @param string $path
138
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
139
-	 * @return \OC\Files\ObjectStore\NoopScanner
140
-	 */
141
-	public function getScanner($path = '', $storage = null) {
142
-		if (!$storage) {
143
-			$storage = $this;
144
-		}
145
-		if (!isset($this->scanner)) {
146
-			$this->scanner = new NoopScanner($storage);
147
-		}
148
-		return $this->scanner;
149
-	}
150
-
151
-	public function getId() {
152
-		return $this->id;
153
-	}
154
-
155
-	public function rmdir($path) {
156
-		$path = $this->normalizePath($path);
157
-
158
-		if (!$this->is_dir($path)) {
159
-			return false;
160
-		}
161
-
162
-		$this->rmObjects($path);
163
-
164
-		$this->getCache()->remove($path);
165
-
166
-		return true;
167
-	}
168
-
169
-	private function rmObjects($path) {
170
-		$children = $this->getCache()->getFolderContents($path);
171
-		foreach ($children as $child) {
172
-			if ($child['mimetype'] === 'httpd/unix-directory') {
173
-				$this->rmObjects($child['path']);
174
-			} else {
175
-				$this->unlink($child['path']);
176
-			}
177
-		}
178
-	}
179
-
180
-	public function unlink($path) {
181
-		$path = $this->normalizePath($path);
182
-		$stat = $this->stat($path);
183
-
184
-		if ($stat && isset($stat['fileid'])) {
185
-			if ($stat['mimetype'] === 'httpd/unix-directory') {
186
-				return $this->rmdir($path);
187
-			}
188
-			try {
189
-				$this->objectStore->deleteObject($this->getURN($stat['fileid']));
190
-			} catch (\Exception $ex) {
191
-				if ($ex->getCode() !== 404) {
192
-					$this->logger->logException($ex, [
193
-						'app' => 'objectstore',
194
-						'message' => 'Could not delete object ' . $this->getURN($stat['fileid']) . ' for ' . $path,
195
-					]);
196
-					return false;
197
-				}
198
-				//removing from cache is ok as it does not exist in the objectstore anyway
199
-			}
200
-			$this->getCache()->remove($path);
201
-			return true;
202
-		}
203
-		return false;
204
-	}
205
-
206
-	public function stat($path) {
207
-		$path = $this->normalizePath($path);
208
-		$cacheEntry = $this->getCache()->get($path);
209
-		if ($cacheEntry instanceof CacheEntry) {
210
-			return $cacheEntry->getData();
211
-		} else {
212
-			return false;
213
-		}
214
-	}
215
-
216
-	/**
217
-	 * Override this method if you need a different unique resource identifier for your object storage implementation.
218
-	 * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users.
219
-	 * You may need a mapping table to store your URN if it cannot be generated from the fileid.
220
-	 *
221
-	 * @param int $fileId the fileid
222
-	 * @return null|string the unified resource name used to identify the object
223
-	 */
224
-	protected function getURN($fileId) {
225
-		if (is_numeric($fileId)) {
226
-			return $this->objectPrefix . $fileId;
227
-		}
228
-		return null;
229
-	}
230
-
231
-	public function opendir($path) {
232
-		$path = $this->normalizePath($path);
233
-
234
-		try {
235
-			$files = array();
236
-			$folderContents = $this->getCache()->getFolderContents($path);
237
-			foreach ($folderContents as $file) {
238
-				$files[] = $file['name'];
239
-			}
240
-
241
-			return IteratorDirectory::wrap($files);
242
-		} catch (\Exception $e) {
243
-			$this->logger->logException($e);
244
-			return false;
245
-		}
246
-	}
247
-
248
-	public function filetype($path) {
249
-		$path = $this->normalizePath($path);
250
-		$stat = $this->stat($path);
251
-		if ($stat) {
252
-			if ($stat['mimetype'] === 'httpd/unix-directory') {
253
-				return 'dir';
254
-			}
255
-			return 'file';
256
-		} else {
257
-			return false;
258
-		}
259
-	}
260
-
261
-	public function fopen($path, $mode) {
262
-		$path = $this->normalizePath($path);
263
-
264
-		switch ($mode) {
265
-			case 'r':
266
-			case 'rb':
267
-				$stat = $this->stat($path);
268
-				if (is_array($stat)) {
269
-					try {
270
-						return $this->objectStore->readObject($this->getURN($stat['fileid']));
271
-					} catch (\Exception $ex) {
272
-						$this->logger->logException($ex, [
273
-							'app' => 'objectstore',
274
-							'message' => 'Count not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
275
-						]);
276
-						return false;
277
-					}
278
-				} else {
279
-					return false;
280
-				}
281
-			case 'w':
282
-			case 'wb':
283
-			case 'a':
284
-			case 'ab':
285
-			case 'r+':
286
-			case 'w+':
287
-			case 'wb+':
288
-			case 'a+':
289
-			case 'x':
290
-			case 'x+':
291
-			case 'c':
292
-			case 'c+':
293
-				if (strrpos($path, '.') !== false) {
294
-					$ext = substr($path, strrpos($path, '.'));
295
-				} else {
296
-					$ext = '';
297
-				}
298
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
299
-				if ($this->file_exists($path)) {
300
-					$source = $this->fopen($path, 'r');
301
-					file_put_contents($tmpFile, $source);
302
-				}
303
-				$handle = fopen($tmpFile, $mode);
304
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
305
-					$this->writeBack($tmpFile, $path);
306
-				});
307
-		}
308
-		return false;
309
-	}
310
-
311
-	public function file_exists($path) {
312
-		$path = $this->normalizePath($path);
313
-		return (bool)$this->stat($path);
314
-	}
315
-
316
-	public function rename($source, $target) {
317
-		$source = $this->normalizePath($source);
318
-		$target = $this->normalizePath($target);
319
-		$this->remove($target);
320
-		$this->getCache()->move($source, $target);
321
-		$this->touch(dirname($target));
322
-		return true;
323
-	}
324
-
325
-	public function getMimeType($path) {
326
-		$path = $this->normalizePath($path);
327
-		$stat = $this->stat($path);
328
-		if (is_array($stat)) {
329
-			return $stat['mimetype'];
330
-		} else {
331
-			return false;
332
-		}
333
-	}
334
-
335
-	public function touch($path, $mtime = null) {
336
-		if (is_null($mtime)) {
337
-			$mtime = time();
338
-		}
339
-
340
-		$path = $this->normalizePath($path);
341
-		$dirName = dirname($path);
342
-		$parentExists = $this->is_dir($dirName);
343
-		if (!$parentExists) {
344
-			return false;
345
-		}
346
-
347
-		$stat = $this->stat($path);
348
-		if (is_array($stat)) {
349
-			// update existing mtime in db
350
-			$stat['mtime'] = $mtime;
351
-			$this->getCache()->update($stat['fileid'], $stat);
352
-		} else {
353
-			try {
354
-				//create a empty file, need to have at least on char to make it
355
-				// work with all object storage implementations
356
-				$this->file_put_contents($path, ' ');
357
-				$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
358
-				$stat = array(
359
-					'etag' => $this->getETag($path),
360
-					'mimetype' => $mimeType,
361
-					'size' => 0,
362
-					'mtime' => $mtime,
363
-					'storage_mtime' => $mtime,
364
-					'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
365
-				);
366
-				$this->getCache()->put($path, $stat);
367
-			} catch (\Exception $ex) {
368
-				$this->logger->logException($ex, [
369
-					'app' => 'objectstore',
370
-					'message' => 'Could not create object for ' . $path,
371
-				]);
372
-				throw $ex;
373
-			}
374
-		}
375
-		return true;
376
-	}
377
-
378
-	public function writeBack($tmpFile, $path) {
379
-		$stat = $this->stat($path);
380
-		if (empty($stat)) {
381
-			// create new file
382
-			$stat = array(
383
-				'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
384
-			);
385
-		}
386
-		// update stat with new data
387
-		$mTime = time();
388
-		$stat['size'] = filesize($tmpFile);
389
-		$stat['mtime'] = $mTime;
390
-		$stat['storage_mtime'] = $mTime;
391
-
392
-		// run path based detection first, to use file extension because $tmpFile is only a random string
393
-		$mimetypeDetector = \OC::$server->getMimeTypeDetector();
394
-		$mimetype = $mimetypeDetector->detectPath($path);
395
-		if ($mimetype === 'application/octet-stream') {
396
-			$mimetype = $mimetypeDetector->detect($tmpFile);
397
-		}
398
-
399
-		$stat['mimetype'] = $mimetype;
400
-		$stat['etag'] = $this->getETag($path);
401
-
402
-		$fileId = $this->getCache()->put($path, $stat);
403
-		try {
404
-			//upload to object storage
405
-			$this->objectStore->writeObject($this->getURN($fileId), fopen($tmpFile, 'r'));
406
-		} catch (\Exception $ex) {
407
-			$this->getCache()->remove($path);
408
-			$this->logger->logException($ex, [
409
-				'app' => 'objectstore',
410
-				'message' => 'Could not create object ' . $this->getURN($fileId) . ' for ' . $path,
411
-			]);
412
-			throw $ex; // make this bubble up
413
-		}
414
-	}
415
-
416
-	/**
417
-	 * external changes are not supported, exclusive access to the object storage is assumed
418
-	 *
419
-	 * @param string $path
420
-	 * @param int $time
421
-	 * @return false
422
-	 */
423
-	public function hasUpdated($path, $time) {
424
-		return false;
425
-	}
34
+    /**
35
+     * @var \OCP\Files\ObjectStore\IObjectStore $objectStore
36
+     */
37
+    protected $objectStore;
38
+    /**
39
+     * @var string $id
40
+     */
41
+    protected $id;
42
+    /**
43
+     * @var \OC\User\User $user
44
+     */
45
+    protected $user;
46
+
47
+    private $objectPrefix = 'urn:oid:';
48
+
49
+    private $logger;
50
+
51
+    public function __construct($params) {
52
+        if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) {
53
+            $this->objectStore = $params['objectstore'];
54
+        } else {
55
+            throw new \Exception('missing IObjectStore instance');
56
+        }
57
+        if (isset($params['storageid'])) {
58
+            $this->id = 'object::store:' . $params['storageid'];
59
+        } else {
60
+            $this->id = 'object::store:' . $this->objectStore->getStorageId();
61
+        }
62
+        if (isset($params['objectPrefix'])) {
63
+            $this->objectPrefix = $params['objectPrefix'];
64
+        }
65
+        //initialize cache with root directory in cache
66
+        if (!$this->is_dir('/')) {
67
+            $this->mkdir('/');
68
+        }
69
+
70
+        $this->logger = \OC::$server->getLogger();
71
+    }
72
+
73
+    public function mkdir($path) {
74
+        $path = $this->normalizePath($path);
75
+
76
+        if ($this->file_exists($path)) {
77
+            return false;
78
+        }
79
+
80
+        $mTime = time();
81
+        $data = [
82
+            'mimetype' => 'httpd/unix-directory',
83
+            'size' => 0,
84
+            'mtime' => $mTime,
85
+            'storage_mtime' => $mTime,
86
+            'permissions' => \OCP\Constants::PERMISSION_ALL,
87
+        ];
88
+        if ($path === '') {
89
+            //create root on the fly
90
+            $data['etag'] = $this->getETag('');
91
+            $this->getCache()->put('', $data);
92
+            return true;
93
+        } else {
94
+            // if parent does not exist, create it
95
+            $parent = $this->normalizePath(dirname($path));
96
+            $parentType = $this->filetype($parent);
97
+            if ($parentType === false) {
98
+                if (!$this->mkdir($parent)) {
99
+                    // something went wrong
100
+                    return false;
101
+                }
102
+            } else if ($parentType === 'file') {
103
+                // parent is a file
104
+                return false;
105
+            }
106
+            // finally create the new dir
107
+            $mTime = time(); // update mtime
108
+            $data['mtime'] = $mTime;
109
+            $data['storage_mtime'] = $mTime;
110
+            $data['etag'] = $this->getETag($path);
111
+            $this->getCache()->put($path, $data);
112
+            return true;
113
+        }
114
+    }
115
+
116
+    /**
117
+     * @param string $path
118
+     * @return string
119
+     */
120
+    private function normalizePath($path) {
121
+        $path = trim($path, '/');
122
+        //FIXME why do we sometimes get a path like 'files//username'?
123
+        $path = str_replace('//', '/', $path);
124
+
125
+        // dirname('/folder') returns '.' but internally (in the cache) we store the root as ''
126
+        if (!$path || $path === '.') {
127
+            $path = '';
128
+        }
129
+
130
+        return $path;
131
+    }
132
+
133
+    /**
134
+     * Object Stores use a NoopScanner because metadata is directly stored in
135
+     * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere.
136
+     *
137
+     * @param string $path
138
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
139
+     * @return \OC\Files\ObjectStore\NoopScanner
140
+     */
141
+    public function getScanner($path = '', $storage = null) {
142
+        if (!$storage) {
143
+            $storage = $this;
144
+        }
145
+        if (!isset($this->scanner)) {
146
+            $this->scanner = new NoopScanner($storage);
147
+        }
148
+        return $this->scanner;
149
+    }
150
+
151
+    public function getId() {
152
+        return $this->id;
153
+    }
154
+
155
+    public function rmdir($path) {
156
+        $path = $this->normalizePath($path);
157
+
158
+        if (!$this->is_dir($path)) {
159
+            return false;
160
+        }
161
+
162
+        $this->rmObjects($path);
163
+
164
+        $this->getCache()->remove($path);
165
+
166
+        return true;
167
+    }
168
+
169
+    private function rmObjects($path) {
170
+        $children = $this->getCache()->getFolderContents($path);
171
+        foreach ($children as $child) {
172
+            if ($child['mimetype'] === 'httpd/unix-directory') {
173
+                $this->rmObjects($child['path']);
174
+            } else {
175
+                $this->unlink($child['path']);
176
+            }
177
+        }
178
+    }
179
+
180
+    public function unlink($path) {
181
+        $path = $this->normalizePath($path);
182
+        $stat = $this->stat($path);
183
+
184
+        if ($stat && isset($stat['fileid'])) {
185
+            if ($stat['mimetype'] === 'httpd/unix-directory') {
186
+                return $this->rmdir($path);
187
+            }
188
+            try {
189
+                $this->objectStore->deleteObject($this->getURN($stat['fileid']));
190
+            } catch (\Exception $ex) {
191
+                if ($ex->getCode() !== 404) {
192
+                    $this->logger->logException($ex, [
193
+                        'app' => 'objectstore',
194
+                        'message' => 'Could not delete object ' . $this->getURN($stat['fileid']) . ' for ' . $path,
195
+                    ]);
196
+                    return false;
197
+                }
198
+                //removing from cache is ok as it does not exist in the objectstore anyway
199
+            }
200
+            $this->getCache()->remove($path);
201
+            return true;
202
+        }
203
+        return false;
204
+    }
205
+
206
+    public function stat($path) {
207
+        $path = $this->normalizePath($path);
208
+        $cacheEntry = $this->getCache()->get($path);
209
+        if ($cacheEntry instanceof CacheEntry) {
210
+            return $cacheEntry->getData();
211
+        } else {
212
+            return false;
213
+        }
214
+    }
215
+
216
+    /**
217
+     * Override this method if you need a different unique resource identifier for your object storage implementation.
218
+     * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users.
219
+     * You may need a mapping table to store your URN if it cannot be generated from the fileid.
220
+     *
221
+     * @param int $fileId the fileid
222
+     * @return null|string the unified resource name used to identify the object
223
+     */
224
+    protected function getURN($fileId) {
225
+        if (is_numeric($fileId)) {
226
+            return $this->objectPrefix . $fileId;
227
+        }
228
+        return null;
229
+    }
230
+
231
+    public function opendir($path) {
232
+        $path = $this->normalizePath($path);
233
+
234
+        try {
235
+            $files = array();
236
+            $folderContents = $this->getCache()->getFolderContents($path);
237
+            foreach ($folderContents as $file) {
238
+                $files[] = $file['name'];
239
+            }
240
+
241
+            return IteratorDirectory::wrap($files);
242
+        } catch (\Exception $e) {
243
+            $this->logger->logException($e);
244
+            return false;
245
+        }
246
+    }
247
+
248
+    public function filetype($path) {
249
+        $path = $this->normalizePath($path);
250
+        $stat = $this->stat($path);
251
+        if ($stat) {
252
+            if ($stat['mimetype'] === 'httpd/unix-directory') {
253
+                return 'dir';
254
+            }
255
+            return 'file';
256
+        } else {
257
+            return false;
258
+        }
259
+    }
260
+
261
+    public function fopen($path, $mode) {
262
+        $path = $this->normalizePath($path);
263
+
264
+        switch ($mode) {
265
+            case 'r':
266
+            case 'rb':
267
+                $stat = $this->stat($path);
268
+                if (is_array($stat)) {
269
+                    try {
270
+                        return $this->objectStore->readObject($this->getURN($stat['fileid']));
271
+                    } catch (\Exception $ex) {
272
+                        $this->logger->logException($ex, [
273
+                            'app' => 'objectstore',
274
+                            'message' => 'Count not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
275
+                        ]);
276
+                        return false;
277
+                    }
278
+                } else {
279
+                    return false;
280
+                }
281
+            case 'w':
282
+            case 'wb':
283
+            case 'a':
284
+            case 'ab':
285
+            case 'r+':
286
+            case 'w+':
287
+            case 'wb+':
288
+            case 'a+':
289
+            case 'x':
290
+            case 'x+':
291
+            case 'c':
292
+            case 'c+':
293
+                if (strrpos($path, '.') !== false) {
294
+                    $ext = substr($path, strrpos($path, '.'));
295
+                } else {
296
+                    $ext = '';
297
+                }
298
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
299
+                if ($this->file_exists($path)) {
300
+                    $source = $this->fopen($path, 'r');
301
+                    file_put_contents($tmpFile, $source);
302
+                }
303
+                $handle = fopen($tmpFile, $mode);
304
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
305
+                    $this->writeBack($tmpFile, $path);
306
+                });
307
+        }
308
+        return false;
309
+    }
310
+
311
+    public function file_exists($path) {
312
+        $path = $this->normalizePath($path);
313
+        return (bool)$this->stat($path);
314
+    }
315
+
316
+    public function rename($source, $target) {
317
+        $source = $this->normalizePath($source);
318
+        $target = $this->normalizePath($target);
319
+        $this->remove($target);
320
+        $this->getCache()->move($source, $target);
321
+        $this->touch(dirname($target));
322
+        return true;
323
+    }
324
+
325
+    public function getMimeType($path) {
326
+        $path = $this->normalizePath($path);
327
+        $stat = $this->stat($path);
328
+        if (is_array($stat)) {
329
+            return $stat['mimetype'];
330
+        } else {
331
+            return false;
332
+        }
333
+    }
334
+
335
+    public function touch($path, $mtime = null) {
336
+        if (is_null($mtime)) {
337
+            $mtime = time();
338
+        }
339
+
340
+        $path = $this->normalizePath($path);
341
+        $dirName = dirname($path);
342
+        $parentExists = $this->is_dir($dirName);
343
+        if (!$parentExists) {
344
+            return false;
345
+        }
346
+
347
+        $stat = $this->stat($path);
348
+        if (is_array($stat)) {
349
+            // update existing mtime in db
350
+            $stat['mtime'] = $mtime;
351
+            $this->getCache()->update($stat['fileid'], $stat);
352
+        } else {
353
+            try {
354
+                //create a empty file, need to have at least on char to make it
355
+                // work with all object storage implementations
356
+                $this->file_put_contents($path, ' ');
357
+                $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
358
+                $stat = array(
359
+                    'etag' => $this->getETag($path),
360
+                    'mimetype' => $mimeType,
361
+                    'size' => 0,
362
+                    'mtime' => $mtime,
363
+                    'storage_mtime' => $mtime,
364
+                    'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
365
+                );
366
+                $this->getCache()->put($path, $stat);
367
+            } catch (\Exception $ex) {
368
+                $this->logger->logException($ex, [
369
+                    'app' => 'objectstore',
370
+                    'message' => 'Could not create object for ' . $path,
371
+                ]);
372
+                throw $ex;
373
+            }
374
+        }
375
+        return true;
376
+    }
377
+
378
+    public function writeBack($tmpFile, $path) {
379
+        $stat = $this->stat($path);
380
+        if (empty($stat)) {
381
+            // create new file
382
+            $stat = array(
383
+                'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
384
+            );
385
+        }
386
+        // update stat with new data
387
+        $mTime = time();
388
+        $stat['size'] = filesize($tmpFile);
389
+        $stat['mtime'] = $mTime;
390
+        $stat['storage_mtime'] = $mTime;
391
+
392
+        // run path based detection first, to use file extension because $tmpFile is only a random string
393
+        $mimetypeDetector = \OC::$server->getMimeTypeDetector();
394
+        $mimetype = $mimetypeDetector->detectPath($path);
395
+        if ($mimetype === 'application/octet-stream') {
396
+            $mimetype = $mimetypeDetector->detect($tmpFile);
397
+        }
398
+
399
+        $stat['mimetype'] = $mimetype;
400
+        $stat['etag'] = $this->getETag($path);
401
+
402
+        $fileId = $this->getCache()->put($path, $stat);
403
+        try {
404
+            //upload to object storage
405
+            $this->objectStore->writeObject($this->getURN($fileId), fopen($tmpFile, 'r'));
406
+        } catch (\Exception $ex) {
407
+            $this->getCache()->remove($path);
408
+            $this->logger->logException($ex, [
409
+                'app' => 'objectstore',
410
+                'message' => 'Could not create object ' . $this->getURN($fileId) . ' for ' . $path,
411
+            ]);
412
+            throw $ex; // make this bubble up
413
+        }
414
+    }
415
+
416
+    /**
417
+     * external changes are not supported, exclusive access to the object storage is assumed
418
+     *
419
+     * @param string $path
420
+     * @param int $time
421
+     * @return false
422
+     */
423
+    public function hasUpdated($path, $time) {
424
+        return false;
425
+    }
426 426
 }
Please login to merge, or discard this patch.
lib/private/Files/ObjectStore/Swift.php 1 patch
Indentation   +255 added lines, -255 removed lines patch added patch discarded remove patch
@@ -40,260 +40,260 @@
 block discarded – undo
40 40
 
41 41
 class Swift implements IObjectStore {
42 42
 
43
-	/**
44
-	 * @var \OpenCloud\OpenStack
45
-	 */
46
-	private $client;
47
-
48
-	/**
49
-	 * @var array
50
-	 */
51
-	private $params;
52
-
53
-	/**
54
-	 * @var \OpenCloud\ObjectStore\Service
55
-	 */
56
-	private $objectStoreService;
57
-
58
-	/**
59
-	 * @var \OpenCloud\ObjectStore\Resource\Container
60
-	 */
61
-	private $container;
62
-
63
-	private $memcache;
64
-
65
-	public function __construct($params) {
66
-		if (isset($params['bucket'])) {
67
-			$params['container'] = $params['bucket'];
68
-		}
69
-		if (!isset($params['container'])) {
70
-			$params['container'] = 'owncloud';
71
-		}
72
-		if (!isset($params['autocreate'])) {
73
-			// should only be true for tests
74
-			$params['autocreate'] = false;
75
-		}
76
-
77
-		if (isset($params['apiKey'])) {
78
-			$this->client = new Rackspace($params['url'], $params);
79
-			$cacheKey = $params['username'] . '@' . $params['url'] . '/' . $params['bucket'];
80
-		} else {
81
-			$this->client = new OpenStack($params['url'], $params);
82
-			$cacheKey = $params['username'] . '@' . $params['url'] . '/' . $params['bucket'];
83
-		}
84
-
85
-		$cacheFactory = \OC::$server->getMemCacheFactory();
86
-		$this->memcache = $cacheFactory->createDistributed('swift::' . $cacheKey);
87
-
88
-		$this->params = $params;
89
-	}
90
-
91
-	protected function init() {
92
-		if ($this->container) {
93
-			return;
94
-		}
95
-
96
-		$this->importToken();
97
-
98
-		/** @var Token $token */
99
-		$token = $this->client->getTokenObject();
100
-
101
-		if (!$token || $token->hasExpired()) {
102
-			try {
103
-				$this->client->authenticate();
104
-				$this->exportToken();
105
-			} catch (ClientErrorResponseException $e) {
106
-				$statusCode = $e->getResponse()->getStatusCode();
107
-				if ($statusCode == 412) {
108
-					throw new StorageAuthException('Precondition failed, verify the keystone url', $e);
109
-				} else if ($statusCode === 401) {
110
-					throw new StorageAuthException('Authentication failed, verify the username, password and possibly tenant', $e);
111
-				} else {
112
-					throw new StorageAuthException('Unknown error', $e);
113
-				}
114
-			}
115
-		}
116
-
117
-
118
-		/** @var Catalog $catalog */
119
-		$catalog = $this->client->getCatalog();
120
-
121
-		if (count($catalog->getItems()) === 0) {
122
-			throw new StorageAuthException('Keystone did not provide a valid catalog, verify the credentials');
123
-		}
124
-
125
-		if (isset($this->params['serviceName'])) {
126
-			$serviceName = $this->params['serviceName'];
127
-		} else {
128
-			$serviceName = Service::DEFAULT_NAME;
129
-		}
130
-
131
-		if (isset($this->params['urlType'])) {
132
-			$urlType = $this->params['urlType'];
133
-			if ($urlType !== 'internalURL' && $urlType !== 'publicURL') {
134
-				throw new StorageNotAvailableException('Invalid url type');
135
-			}
136
-		} else {
137
-			$urlType = Service::DEFAULT_URL_TYPE;
138
-		}
139
-
140
-		$catalogItem = $this->getCatalogForService($catalog, $serviceName);
141
-		if (!$catalogItem) {
142
-			$available = implode(', ', $this->getAvailableServiceNames($catalog));
143
-			throw new StorageNotAvailableException(
144
-				"Service $serviceName not found in service catalog, available services: $available"
145
-			);
146
-		} else if (isset($this->params['region'])) {
147
-			$this->validateRegion($catalogItem, $this->params['region']);
148
-		}
149
-
150
-		$this->objectStoreService = $this->client->objectStoreService($serviceName, $this->params['region'], $urlType);
151
-
152
-		try {
153
-			$this->container = $this->objectStoreService->getContainer($this->params['container']);
154
-		} catch (ClientErrorResponseException $ex) {
155
-			// if the container does not exist and autocreate is true try to create the container on the fly
156
-			if (isset($this->params['autocreate']) && $this->params['autocreate'] === true) {
157
-				$this->container = $this->objectStoreService->createContainer($this->params['container']);
158
-			} else {
159
-				throw $ex;
160
-			}
161
-		} catch (CurlException $e) {
162
-			if ($e->getErrorNo() === 7) {
163
-				$host = $e->getCurlHandle()->getUrl()->getHost() . ':' . $e->getCurlHandle()->getUrl()->getPort();
164
-				\OC::$server->getLogger()->error("Can't connect to object storage server at $host");
165
-				throw new StorageNotAvailableException("Can't connect to object storage server at $host", StorageNotAvailableException::STATUS_ERROR, $e);
166
-			}
167
-			throw $e;
168
-		}
169
-	}
170
-
171
-	private function exportToken() {
172
-		$export = $this->client->exportCredentials();
173
-		$export['catalog'] = array_map(function (CatalogItem $item) {
174
-			return [
175
-				'name' => $item->getName(),
176
-				'endpoints' => $item->getEndpoints(),
177
-				'type' => $item->getType()
178
-			];
179
-		}, $export['catalog']->getItems());
180
-		$this->memcache->set('token', json_encode($export));
181
-	}
182
-
183
-	private function importToken() {
184
-		$cachedTokenString = $this->memcache->get('token');
185
-		if ($cachedTokenString) {
186
-			$cachedToken = json_decode($cachedTokenString, true);
187
-			$cachedToken['catalog'] = array_map(function (array $item) {
188
-				$itemClass = new \stdClass();
189
-				$itemClass->name = $item['name'];
190
-				$itemClass->endpoints = array_map(function (array $endpoint) {
191
-					return (object)$endpoint;
192
-				}, $item['endpoints']);
193
-				$itemClass->type = $item['type'];
194
-
195
-				return $itemClass;
196
-			}, $cachedToken['catalog']);
197
-			try {
198
-				$this->client->importCredentials($cachedToken);
199
-			} catch (\Exception $e) {
200
-				$this->client->setTokenObject(new Token());
201
-			}
202
-		}
203
-	}
204
-
205
-	/**
206
-	 * @param Catalog $catalog
207
-	 * @param $name
208
-	 * @return null|CatalogItem
209
-	 */
210
-	private function getCatalogForService(Catalog $catalog, $name) {
211
-		foreach ($catalog->getItems() as $item) {
212
-			/** @var CatalogItem $item */
213
-			if ($item->hasType(Service::DEFAULT_TYPE) && $item->hasName($name)) {
214
-				return $item;
215
-			}
216
-		}
217
-
218
-		return null;
219
-	}
220
-
221
-	private function validateRegion(CatalogItem $item, $region) {
222
-		$endPoints = $item->getEndpoints();
223
-		foreach ($endPoints as $endPoint) {
224
-			if ($endPoint->region === $region) {
225
-				return;
226
-			}
227
-		}
228
-
229
-		$availableRegions = implode(', ', array_map(function ($endpoint) {
230
-			return $endpoint->region;
231
-		}, $endPoints));
232
-
233
-		throw new StorageNotAvailableException("Invalid region '$region', available regions: $availableRegions");
234
-	}
235
-
236
-	private function getAvailableServiceNames(Catalog $catalog) {
237
-		return array_map(function (CatalogItem $item) {
238
-			return $item->getName();
239
-		}, array_filter($catalog->getItems(), function (CatalogItem $item) {
240
-			return $item->hasType(Service::DEFAULT_TYPE);
241
-		}));
242
-	}
243
-
244
-	/**
245
-	 * @return string the container name where objects are stored
246
-	 */
247
-	public function getStorageId() {
248
-		return $this->params['container'];
249
-	}
250
-
251
-	/**
252
-	 * @param string $urn the unified resource name used to identify the object
253
-	 * @param resource $stream stream with the data to write
254
-	 * @throws Exception from openstack lib when something goes wrong
255
-	 */
256
-	public function writeObject($urn, $stream) {
257
-		$this->init();
258
-		$this->container->uploadObject($urn, $stream);
259
-	}
260
-
261
-	/**
262
-	 * @param string $urn the unified resource name used to identify the object
263
-	 * @return resource stream with the read data
264
-	 * @throws Exception from openstack lib when something goes wrong
265
-	 */
266
-	public function readObject($urn) {
267
-		$this->init();
268
-		$object = $this->container->getObject($urn);
269
-
270
-		// we need to keep a reference to objectContent or
271
-		// the stream will be closed before we can do anything with it
272
-		/** @var $objectContent \Guzzle\Http\EntityBody * */
273
-		$objectContent = $object->getContent();
274
-		$objectContent->rewind();
275
-
276
-		$stream = $objectContent->getStream();
277
-		// save the object content in the context of the stream to prevent it being gc'd until the stream is closed
278
-		stream_context_set_option($stream, 'swift', 'content', $objectContent);
279
-
280
-		return RetryWrapper::wrap($stream);
281
-	}
282
-
283
-	/**
284
-	 * @param string $urn Unified Resource Name
285
-	 * @return void
286
-	 * @throws Exception from openstack lib when something goes wrong
287
-	 */
288
-	public function deleteObject($urn) {
289
-		$this->init();
290
-		// see https://github.com/rackspace/php-opencloud/issues/243#issuecomment-30032242
291
-		$this->container->dataObject()->setName($urn)->delete();
292
-	}
293
-
294
-	public function deleteContainer($recursive = false) {
295
-		$this->init();
296
-		$this->container->delete($recursive);
297
-	}
43
+    /**
44
+     * @var \OpenCloud\OpenStack
45
+     */
46
+    private $client;
47
+
48
+    /**
49
+     * @var array
50
+     */
51
+    private $params;
52
+
53
+    /**
54
+     * @var \OpenCloud\ObjectStore\Service
55
+     */
56
+    private $objectStoreService;
57
+
58
+    /**
59
+     * @var \OpenCloud\ObjectStore\Resource\Container
60
+     */
61
+    private $container;
62
+
63
+    private $memcache;
64
+
65
+    public function __construct($params) {
66
+        if (isset($params['bucket'])) {
67
+            $params['container'] = $params['bucket'];
68
+        }
69
+        if (!isset($params['container'])) {
70
+            $params['container'] = 'owncloud';
71
+        }
72
+        if (!isset($params['autocreate'])) {
73
+            // should only be true for tests
74
+            $params['autocreate'] = false;
75
+        }
76
+
77
+        if (isset($params['apiKey'])) {
78
+            $this->client = new Rackspace($params['url'], $params);
79
+            $cacheKey = $params['username'] . '@' . $params['url'] . '/' . $params['bucket'];
80
+        } else {
81
+            $this->client = new OpenStack($params['url'], $params);
82
+            $cacheKey = $params['username'] . '@' . $params['url'] . '/' . $params['bucket'];
83
+        }
84
+
85
+        $cacheFactory = \OC::$server->getMemCacheFactory();
86
+        $this->memcache = $cacheFactory->createDistributed('swift::' . $cacheKey);
87
+
88
+        $this->params = $params;
89
+    }
90
+
91
+    protected function init() {
92
+        if ($this->container) {
93
+            return;
94
+        }
95
+
96
+        $this->importToken();
97
+
98
+        /** @var Token $token */
99
+        $token = $this->client->getTokenObject();
100
+
101
+        if (!$token || $token->hasExpired()) {
102
+            try {
103
+                $this->client->authenticate();
104
+                $this->exportToken();
105
+            } catch (ClientErrorResponseException $e) {
106
+                $statusCode = $e->getResponse()->getStatusCode();
107
+                if ($statusCode == 412) {
108
+                    throw new StorageAuthException('Precondition failed, verify the keystone url', $e);
109
+                } else if ($statusCode === 401) {
110
+                    throw new StorageAuthException('Authentication failed, verify the username, password and possibly tenant', $e);
111
+                } else {
112
+                    throw new StorageAuthException('Unknown error', $e);
113
+                }
114
+            }
115
+        }
116
+
117
+
118
+        /** @var Catalog $catalog */
119
+        $catalog = $this->client->getCatalog();
120
+
121
+        if (count($catalog->getItems()) === 0) {
122
+            throw new StorageAuthException('Keystone did not provide a valid catalog, verify the credentials');
123
+        }
124
+
125
+        if (isset($this->params['serviceName'])) {
126
+            $serviceName = $this->params['serviceName'];
127
+        } else {
128
+            $serviceName = Service::DEFAULT_NAME;
129
+        }
130
+
131
+        if (isset($this->params['urlType'])) {
132
+            $urlType = $this->params['urlType'];
133
+            if ($urlType !== 'internalURL' && $urlType !== 'publicURL') {
134
+                throw new StorageNotAvailableException('Invalid url type');
135
+            }
136
+        } else {
137
+            $urlType = Service::DEFAULT_URL_TYPE;
138
+        }
139
+
140
+        $catalogItem = $this->getCatalogForService($catalog, $serviceName);
141
+        if (!$catalogItem) {
142
+            $available = implode(', ', $this->getAvailableServiceNames($catalog));
143
+            throw new StorageNotAvailableException(
144
+                "Service $serviceName not found in service catalog, available services: $available"
145
+            );
146
+        } else if (isset($this->params['region'])) {
147
+            $this->validateRegion($catalogItem, $this->params['region']);
148
+        }
149
+
150
+        $this->objectStoreService = $this->client->objectStoreService($serviceName, $this->params['region'], $urlType);
151
+
152
+        try {
153
+            $this->container = $this->objectStoreService->getContainer($this->params['container']);
154
+        } catch (ClientErrorResponseException $ex) {
155
+            // if the container does not exist and autocreate is true try to create the container on the fly
156
+            if (isset($this->params['autocreate']) && $this->params['autocreate'] === true) {
157
+                $this->container = $this->objectStoreService->createContainer($this->params['container']);
158
+            } else {
159
+                throw $ex;
160
+            }
161
+        } catch (CurlException $e) {
162
+            if ($e->getErrorNo() === 7) {
163
+                $host = $e->getCurlHandle()->getUrl()->getHost() . ':' . $e->getCurlHandle()->getUrl()->getPort();
164
+                \OC::$server->getLogger()->error("Can't connect to object storage server at $host");
165
+                throw new StorageNotAvailableException("Can't connect to object storage server at $host", StorageNotAvailableException::STATUS_ERROR, $e);
166
+            }
167
+            throw $e;
168
+        }
169
+    }
170
+
171
+    private function exportToken() {
172
+        $export = $this->client->exportCredentials();
173
+        $export['catalog'] = array_map(function (CatalogItem $item) {
174
+            return [
175
+                'name' => $item->getName(),
176
+                'endpoints' => $item->getEndpoints(),
177
+                'type' => $item->getType()
178
+            ];
179
+        }, $export['catalog']->getItems());
180
+        $this->memcache->set('token', json_encode($export));
181
+    }
182
+
183
+    private function importToken() {
184
+        $cachedTokenString = $this->memcache->get('token');
185
+        if ($cachedTokenString) {
186
+            $cachedToken = json_decode($cachedTokenString, true);
187
+            $cachedToken['catalog'] = array_map(function (array $item) {
188
+                $itemClass = new \stdClass();
189
+                $itemClass->name = $item['name'];
190
+                $itemClass->endpoints = array_map(function (array $endpoint) {
191
+                    return (object)$endpoint;
192
+                }, $item['endpoints']);
193
+                $itemClass->type = $item['type'];
194
+
195
+                return $itemClass;
196
+            }, $cachedToken['catalog']);
197
+            try {
198
+                $this->client->importCredentials($cachedToken);
199
+            } catch (\Exception $e) {
200
+                $this->client->setTokenObject(new Token());
201
+            }
202
+        }
203
+    }
204
+
205
+    /**
206
+     * @param Catalog $catalog
207
+     * @param $name
208
+     * @return null|CatalogItem
209
+     */
210
+    private function getCatalogForService(Catalog $catalog, $name) {
211
+        foreach ($catalog->getItems() as $item) {
212
+            /** @var CatalogItem $item */
213
+            if ($item->hasType(Service::DEFAULT_TYPE) && $item->hasName($name)) {
214
+                return $item;
215
+            }
216
+        }
217
+
218
+        return null;
219
+    }
220
+
221
+    private function validateRegion(CatalogItem $item, $region) {
222
+        $endPoints = $item->getEndpoints();
223
+        foreach ($endPoints as $endPoint) {
224
+            if ($endPoint->region === $region) {
225
+                return;
226
+            }
227
+        }
228
+
229
+        $availableRegions = implode(', ', array_map(function ($endpoint) {
230
+            return $endpoint->region;
231
+        }, $endPoints));
232
+
233
+        throw new StorageNotAvailableException("Invalid region '$region', available regions: $availableRegions");
234
+    }
235
+
236
+    private function getAvailableServiceNames(Catalog $catalog) {
237
+        return array_map(function (CatalogItem $item) {
238
+            return $item->getName();
239
+        }, array_filter($catalog->getItems(), function (CatalogItem $item) {
240
+            return $item->hasType(Service::DEFAULT_TYPE);
241
+        }));
242
+    }
243
+
244
+    /**
245
+     * @return string the container name where objects are stored
246
+     */
247
+    public function getStorageId() {
248
+        return $this->params['container'];
249
+    }
250
+
251
+    /**
252
+     * @param string $urn the unified resource name used to identify the object
253
+     * @param resource $stream stream with the data to write
254
+     * @throws Exception from openstack lib when something goes wrong
255
+     */
256
+    public function writeObject($urn, $stream) {
257
+        $this->init();
258
+        $this->container->uploadObject($urn, $stream);
259
+    }
260
+
261
+    /**
262
+     * @param string $urn the unified resource name used to identify the object
263
+     * @return resource stream with the read data
264
+     * @throws Exception from openstack lib when something goes wrong
265
+     */
266
+    public function readObject($urn) {
267
+        $this->init();
268
+        $object = $this->container->getObject($urn);
269
+
270
+        // we need to keep a reference to objectContent or
271
+        // the stream will be closed before we can do anything with it
272
+        /** @var $objectContent \Guzzle\Http\EntityBody * */
273
+        $objectContent = $object->getContent();
274
+        $objectContent->rewind();
275
+
276
+        $stream = $objectContent->getStream();
277
+        // save the object content in the context of the stream to prevent it being gc'd until the stream is closed
278
+        stream_context_set_option($stream, 'swift', 'content', $objectContent);
279
+
280
+        return RetryWrapper::wrap($stream);
281
+    }
282
+
283
+    /**
284
+     * @param string $urn Unified Resource Name
285
+     * @return void
286
+     * @throws Exception from openstack lib when something goes wrong
287
+     */
288
+    public function deleteObject($urn) {
289
+        $this->init();
290
+        // see https://github.com/rackspace/php-opencloud/issues/243#issuecomment-30032242
291
+        $this->container->dataObject()->setName($urn)->delete();
292
+    }
293
+
294
+    public function deleteContainer($recursive = false) {
295
+        $this->init();
296
+        $this->container->delete($recursive);
297
+    }
298 298
 
299 299
 }
Please login to merge, or discard this patch.