Completed
Pull Request — master (#8235)
by Robin
48:31 queued 31:15
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 2 patches
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.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -55,9 +55,9 @@  discard block
 block discarded – undo
55 55
 			throw new \Exception('missing IObjectStore instance');
56 56
 		}
57 57
 		if (isset($params['storageid'])) {
58
-			$this->id = 'object::store:' . $params['storageid'];
58
+			$this->id = 'object::store:'.$params['storageid'];
59 59
 		} else {
60
-			$this->id = 'object::store:' . $this->objectStore->getStorageId();
60
+			$this->id = 'object::store:'.$this->objectStore->getStorageId();
61 61
 		}
62 62
 		if (isset($params['objectPrefix'])) {
63 63
 			$this->objectPrefix = $params['objectPrefix'];
@@ -191,7 +191,7 @@  discard block
 block discarded – undo
191 191
 				if ($ex->getCode() !== 404) {
192 192
 					$this->logger->logException($ex, [
193 193
 						'app' => 'objectstore',
194
-						'message' => 'Could not delete object ' . $this->getURN($stat['fileid']) . ' for ' . $path,
194
+						'message' => 'Could not delete object '.$this->getURN($stat['fileid']).' for '.$path,
195 195
 					]);
196 196
 					return false;
197 197
 				}
@@ -223,7 +223,7 @@  discard block
 block discarded – undo
223 223
 	 */
224 224
 	protected function getURN($fileId) {
225 225
 		if (is_numeric($fileId)) {
226
-			return $this->objectPrefix . $fileId;
226
+			return $this->objectPrefix.$fileId;
227 227
 		}
228 228
 		return null;
229 229
 	}
@@ -271,7 +271,7 @@  discard block
 block discarded – undo
271 271
 					} catch (\Exception $ex) {
272 272
 						$this->logger->logException($ex, [
273 273
 							'app' => 'objectstore',
274
-							'message' => 'Count not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
274
+							'message' => 'Count not get object '.$this->getURN($stat['fileid']).' for file '.$path,
275 275
 						]);
276 276
 						return false;
277 277
 					}
@@ -301,7 +301,7 @@  discard block
 block discarded – undo
301 301
 					file_put_contents($tmpFile, $source);
302 302
 				}
303 303
 				$handle = fopen($tmpFile, $mode);
304
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
304
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
305 305
 					$this->writeBack($tmpFile, $path);
306 306
 				});
307 307
 		}
@@ -310,7 +310,7 @@  discard block
 block discarded – undo
310 310
 
311 311
 	public function file_exists($path) {
312 312
 		$path = $this->normalizePath($path);
313
-		return (bool)$this->stat($path);
313
+		return (bool) $this->stat($path);
314 314
 	}
315 315
 
316 316
 	public function rename($source, $target) {
@@ -367,7 +367,7 @@  discard block
 block discarded – undo
367 367
 			} catch (\Exception $ex) {
368 368
 				$this->logger->logException($ex, [
369 369
 					'app' => 'objectstore',
370
-					'message' => 'Could not create object for ' . $path,
370
+					'message' => 'Could not create object for '.$path,
371 371
 				]);
372 372
 				throw $ex;
373 373
 			}
@@ -407,7 +407,7 @@  discard block
 block discarded – undo
407 407
 			$this->getCache()->remove($path);
408 408
 			$this->logger->logException($ex, [
409 409
 				'app' => 'objectstore',
410
-				'message' => 'Could not create object ' . $this->getURN($fileId) . ' for ' . $path,
410
+				'message' => 'Could not create object '.$this->getURN($fileId).' for '.$path,
411 411
 			]);
412 412
 			throw $ex; // make this bubble up
413 413
 		}
Please login to merge, or discard this patch.
lib/private/Files/ObjectStore/Swift.php 2 patches
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -76,14 +76,14 @@  discard block
 block discarded – undo
76 76
 
77 77
 		if (isset($params['apiKey'])) {
78 78
 			$this->client = new Rackspace($params['url'], $params);
79
-			$cacheKey = $params['username'] . '@' . $params['url'] . '/' . $params['bucket'];
79
+			$cacheKey = $params['username'].'@'.$params['url'].'/'.$params['bucket'];
80 80
 		} else {
81 81
 			$this->client = new OpenStack($params['url'], $params);
82
-			$cacheKey = $params['username'] . '@' . $params['url'] . '/' . $params['bucket'];
82
+			$cacheKey = $params['username'].'@'.$params['url'].'/'.$params['bucket'];
83 83
 		}
84 84
 
85 85
 		$cacheFactory = \OC::$server->getMemCacheFactory();
86
-		$this->memcache = $cacheFactory->createDistributed('swift::' . $cacheKey);
86
+		$this->memcache = $cacheFactory->createDistributed('swift::'.$cacheKey);
87 87
 
88 88
 		$this->params = $params;
89 89
 	}
@@ -160,7 +160,7 @@  discard block
 block discarded – undo
160 160
 			}
161 161
 		} catch (CurlException $e) {
162 162
 			if ($e->getErrorNo() === 7) {
163
-				$host = $e->getCurlHandle()->getUrl()->getHost() . ':' . $e->getCurlHandle()->getUrl()->getPort();
163
+				$host = $e->getCurlHandle()->getUrl()->getHost().':'.$e->getCurlHandle()->getUrl()->getPort();
164 164
 				\OC::$server->getLogger()->error("Can't connect to object storage server at $host");
165 165
 				throw new StorageNotAvailableException("Can't connect to object storage server at $host", StorageNotAvailableException::STATUS_ERROR, $e);
166 166
 			}
@@ -170,7 +170,7 @@  discard block
 block discarded – undo
170 170
 
171 171
 	private function exportToken() {
172 172
 		$export = $this->client->exportCredentials();
173
-		$export['catalog'] = array_map(function (CatalogItem $item) {
173
+		$export['catalog'] = array_map(function(CatalogItem $item) {
174 174
 			return [
175 175
 				'name' => $item->getName(),
176 176
 				'endpoints' => $item->getEndpoints(),
@@ -184,11 +184,11 @@  discard block
 block discarded – undo
184 184
 		$cachedTokenString = $this->memcache->get('token');
185 185
 		if ($cachedTokenString) {
186 186
 			$cachedToken = json_decode($cachedTokenString, true);
187
-			$cachedToken['catalog'] = array_map(function (array $item) {
187
+			$cachedToken['catalog'] = array_map(function(array $item) {
188 188
 				$itemClass = new \stdClass();
189 189
 				$itemClass->name = $item['name'];
190
-				$itemClass->endpoints = array_map(function (array $endpoint) {
191
-					return (object)$endpoint;
190
+				$itemClass->endpoints = array_map(function(array $endpoint) {
191
+					return (object) $endpoint;
192 192
 				}, $item['endpoints']);
193 193
 				$itemClass->type = $item['type'];
194 194
 
@@ -226,7 +226,7 @@  discard block
 block discarded – undo
226 226
 			}
227 227
 		}
228 228
 
229
-		$availableRegions = implode(', ', array_map(function ($endpoint) {
229
+		$availableRegions = implode(', ', array_map(function($endpoint) {
230 230
 			return $endpoint->region;
231 231
 		}, $endPoints));
232 232
 
@@ -234,9 +234,9 @@  discard block
 block discarded – undo
234 234
 	}
235 235
 
236 236
 	private function getAvailableServiceNames(Catalog $catalog) {
237
-		return array_map(function (CatalogItem $item) {
237
+		return array_map(function(CatalogItem $item) {
238 238
 			return $item->getName();
239
-		}, array_filter($catalog->getItems(), function (CatalogItem $item) {
239
+		}, array_filter($catalog->getItems(), function(CatalogItem $item) {
240 240
 			return $item->hasType(Service::DEFAULT_TYPE);
241 241
 		}));
242 242
 	}
Please login to merge, or discard this patch.
Indentation   +256 added lines, -256 removed lines patch added patch discarded remove patch
@@ -40,261 +40,261 @@
 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
-		/** @suppress PhanNonClassMethodCall */
122
-		if (count($catalog->getItems()) === 0) {
123
-			throw new StorageAuthException('Keystone did not provide a valid catalog, verify the credentials');
124
-		}
125
-
126
-		if (isset($this->params['serviceName'])) {
127
-			$serviceName = $this->params['serviceName'];
128
-		} else {
129
-			$serviceName = Service::DEFAULT_NAME;
130
-		}
131
-
132
-		if (isset($this->params['urlType'])) {
133
-			$urlType = $this->params['urlType'];
134
-			if ($urlType !== 'internalURL' && $urlType !== 'publicURL') {
135
-				throw new StorageNotAvailableException('Invalid url type');
136
-			}
137
-		} else {
138
-			$urlType = Service::DEFAULT_URL_TYPE;
139
-		}
140
-
141
-		$catalogItem = $this->getCatalogForService($catalog, $serviceName);
142
-		if (!$catalogItem) {
143
-			$available = implode(', ', $this->getAvailableServiceNames($catalog));
144
-			throw new StorageNotAvailableException(
145
-				"Service $serviceName not found in service catalog, available services: $available"
146
-			);
147
-		} else if (isset($this->params['region'])) {
148
-			$this->validateRegion($catalogItem, $this->params['region']);
149
-		}
150
-
151
-		$this->objectStoreService = $this->client->objectStoreService($serviceName, $this->params['region'], $urlType);
152
-
153
-		try {
154
-			$this->container = $this->objectStoreService->getContainer($this->params['container']);
155
-		} catch (ClientErrorResponseException $ex) {
156
-			// if the container does not exist and autocreate is true try to create the container on the fly
157
-			if (isset($this->params['autocreate']) && $this->params['autocreate'] === true) {
158
-				$this->container = $this->objectStoreService->createContainer($this->params['container']);
159
-			} else {
160
-				throw $ex;
161
-			}
162
-		} catch (CurlException $e) {
163
-			if ($e->getErrorNo() === 7) {
164
-				$host = $e->getCurlHandle()->getUrl()->getHost() . ':' . $e->getCurlHandle()->getUrl()->getPort();
165
-				\OC::$server->getLogger()->error("Can't connect to object storage server at $host");
166
-				throw new StorageNotAvailableException("Can't connect to object storage server at $host", StorageNotAvailableException::STATUS_ERROR, $e);
167
-			}
168
-			throw $e;
169
-		}
170
-	}
171
-
172
-	private function exportToken() {
173
-		$export = $this->client->exportCredentials();
174
-		$export['catalog'] = array_map(function (CatalogItem $item) {
175
-			return [
176
-				'name' => $item->getName(),
177
-				'endpoints' => $item->getEndpoints(),
178
-				'type' => $item->getType()
179
-			];
180
-		}, $export['catalog']->getItems());
181
-		$this->memcache->set('token', json_encode($export));
182
-	}
183
-
184
-	private function importToken() {
185
-		$cachedTokenString = $this->memcache->get('token');
186
-		if ($cachedTokenString) {
187
-			$cachedToken = json_decode($cachedTokenString, true);
188
-			$cachedToken['catalog'] = array_map(function (array $item) {
189
-				$itemClass = new \stdClass();
190
-				$itemClass->name = $item['name'];
191
-				$itemClass->endpoints = array_map(function (array $endpoint) {
192
-					return (object)$endpoint;
193
-				}, $item['endpoints']);
194
-				$itemClass->type = $item['type'];
195
-
196
-				return $itemClass;
197
-			}, $cachedToken['catalog']);
198
-			try {
199
-				$this->client->importCredentials($cachedToken);
200
-			} catch (\Exception $e) {
201
-				$this->client->setTokenObject(new Token());
202
-			}
203
-		}
204
-	}
205
-
206
-	/**
207
-	 * @param Catalog $catalog
208
-	 * @param $name
209
-	 * @return null|CatalogItem
210
-	 */
211
-	private function getCatalogForService(Catalog $catalog, $name) {
212
-		foreach ($catalog->getItems() as $item) {
213
-			/** @var CatalogItem $item */
214
-			if ($item->hasType(Service::DEFAULT_TYPE) && $item->hasName($name)) {
215
-				return $item;
216
-			}
217
-		}
218
-
219
-		return null;
220
-	}
221
-
222
-	private function validateRegion(CatalogItem $item, $region) {
223
-		$endPoints = $item->getEndpoints();
224
-		foreach ($endPoints as $endPoint) {
225
-			if ($endPoint->region === $region) {
226
-				return;
227
-			}
228
-		}
229
-
230
-		$availableRegions = implode(', ', array_map(function ($endpoint) {
231
-			return $endpoint->region;
232
-		}, $endPoints));
233
-
234
-		throw new StorageNotAvailableException("Invalid region '$region', available regions: $availableRegions");
235
-	}
236
-
237
-	private function getAvailableServiceNames(Catalog $catalog) {
238
-		return array_map(function (CatalogItem $item) {
239
-			return $item->getName();
240
-		}, array_filter($catalog->getItems(), function (CatalogItem $item) {
241
-			return $item->hasType(Service::DEFAULT_TYPE);
242
-		}));
243
-	}
244
-
245
-	/**
246
-	 * @return string the container name where objects are stored
247
-	 */
248
-	public function getStorageId() {
249
-		return $this->params['container'];
250
-	}
251
-
252
-	/**
253
-	 * @param string $urn the unified resource name used to identify the object
254
-	 * @param resource $stream stream with the data to write
255
-	 * @throws Exception from openstack lib when something goes wrong
256
-	 */
257
-	public function writeObject($urn, $stream) {
258
-		$this->init();
259
-		$this->container->uploadObject($urn, $stream);
260
-	}
261
-
262
-	/**
263
-	 * @param string $urn the unified resource name used to identify the object
264
-	 * @return resource stream with the read data
265
-	 * @throws Exception from openstack lib when something goes wrong
266
-	 */
267
-	public function readObject($urn) {
268
-		$this->init();
269
-		$object = $this->container->getObject($urn);
270
-
271
-		// we need to keep a reference to objectContent or
272
-		// the stream will be closed before we can do anything with it
273
-		/** @var $objectContent \Guzzle\Http\EntityBody * */
274
-		$objectContent = $object->getContent();
275
-		$objectContent->rewind();
276
-
277
-		$stream = $objectContent->getStream();
278
-		// save the object content in the context of the stream to prevent it being gc'd until the stream is closed
279
-		stream_context_set_option($stream, 'swift', 'content', $objectContent);
280
-
281
-		return RetryWrapper::wrap($stream);
282
-	}
283
-
284
-	/**
285
-	 * @param string $urn Unified Resource Name
286
-	 * @return void
287
-	 * @throws Exception from openstack lib when something goes wrong
288
-	 */
289
-	public function deleteObject($urn) {
290
-		$this->init();
291
-		// see https://github.com/rackspace/php-opencloud/issues/243#issuecomment-30032242
292
-		$this->container->dataObject()->setName($urn)->delete();
293
-	}
294
-
295
-	public function deleteContainer($recursive = false) {
296
-		$this->init();
297
-		$this->container->delete($recursive);
298
-	}
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
+        /** @suppress PhanNonClassMethodCall */
122
+        if (count($catalog->getItems()) === 0) {
123
+            throw new StorageAuthException('Keystone did not provide a valid catalog, verify the credentials');
124
+        }
125
+
126
+        if (isset($this->params['serviceName'])) {
127
+            $serviceName = $this->params['serviceName'];
128
+        } else {
129
+            $serviceName = Service::DEFAULT_NAME;
130
+        }
131
+
132
+        if (isset($this->params['urlType'])) {
133
+            $urlType = $this->params['urlType'];
134
+            if ($urlType !== 'internalURL' && $urlType !== 'publicURL') {
135
+                throw new StorageNotAvailableException('Invalid url type');
136
+            }
137
+        } else {
138
+            $urlType = Service::DEFAULT_URL_TYPE;
139
+        }
140
+
141
+        $catalogItem = $this->getCatalogForService($catalog, $serviceName);
142
+        if (!$catalogItem) {
143
+            $available = implode(', ', $this->getAvailableServiceNames($catalog));
144
+            throw new StorageNotAvailableException(
145
+                "Service $serviceName not found in service catalog, available services: $available"
146
+            );
147
+        } else if (isset($this->params['region'])) {
148
+            $this->validateRegion($catalogItem, $this->params['region']);
149
+        }
150
+
151
+        $this->objectStoreService = $this->client->objectStoreService($serviceName, $this->params['region'], $urlType);
152
+
153
+        try {
154
+            $this->container = $this->objectStoreService->getContainer($this->params['container']);
155
+        } catch (ClientErrorResponseException $ex) {
156
+            // if the container does not exist and autocreate is true try to create the container on the fly
157
+            if (isset($this->params['autocreate']) && $this->params['autocreate'] === true) {
158
+                $this->container = $this->objectStoreService->createContainer($this->params['container']);
159
+            } else {
160
+                throw $ex;
161
+            }
162
+        } catch (CurlException $e) {
163
+            if ($e->getErrorNo() === 7) {
164
+                $host = $e->getCurlHandle()->getUrl()->getHost() . ':' . $e->getCurlHandle()->getUrl()->getPort();
165
+                \OC::$server->getLogger()->error("Can't connect to object storage server at $host");
166
+                throw new StorageNotAvailableException("Can't connect to object storage server at $host", StorageNotAvailableException::STATUS_ERROR, $e);
167
+            }
168
+            throw $e;
169
+        }
170
+    }
171
+
172
+    private function exportToken() {
173
+        $export = $this->client->exportCredentials();
174
+        $export['catalog'] = array_map(function (CatalogItem $item) {
175
+            return [
176
+                'name' => $item->getName(),
177
+                'endpoints' => $item->getEndpoints(),
178
+                'type' => $item->getType()
179
+            ];
180
+        }, $export['catalog']->getItems());
181
+        $this->memcache->set('token', json_encode($export));
182
+    }
183
+
184
+    private function importToken() {
185
+        $cachedTokenString = $this->memcache->get('token');
186
+        if ($cachedTokenString) {
187
+            $cachedToken = json_decode($cachedTokenString, true);
188
+            $cachedToken['catalog'] = array_map(function (array $item) {
189
+                $itemClass = new \stdClass();
190
+                $itemClass->name = $item['name'];
191
+                $itemClass->endpoints = array_map(function (array $endpoint) {
192
+                    return (object)$endpoint;
193
+                }, $item['endpoints']);
194
+                $itemClass->type = $item['type'];
195
+
196
+                return $itemClass;
197
+            }, $cachedToken['catalog']);
198
+            try {
199
+                $this->client->importCredentials($cachedToken);
200
+            } catch (\Exception $e) {
201
+                $this->client->setTokenObject(new Token());
202
+            }
203
+        }
204
+    }
205
+
206
+    /**
207
+     * @param Catalog $catalog
208
+     * @param $name
209
+     * @return null|CatalogItem
210
+     */
211
+    private function getCatalogForService(Catalog $catalog, $name) {
212
+        foreach ($catalog->getItems() as $item) {
213
+            /** @var CatalogItem $item */
214
+            if ($item->hasType(Service::DEFAULT_TYPE) && $item->hasName($name)) {
215
+                return $item;
216
+            }
217
+        }
218
+
219
+        return null;
220
+    }
221
+
222
+    private function validateRegion(CatalogItem $item, $region) {
223
+        $endPoints = $item->getEndpoints();
224
+        foreach ($endPoints as $endPoint) {
225
+            if ($endPoint->region === $region) {
226
+                return;
227
+            }
228
+        }
229
+
230
+        $availableRegions = implode(', ', array_map(function ($endpoint) {
231
+            return $endpoint->region;
232
+        }, $endPoints));
233
+
234
+        throw new StorageNotAvailableException("Invalid region '$region', available regions: $availableRegions");
235
+    }
236
+
237
+    private function getAvailableServiceNames(Catalog $catalog) {
238
+        return array_map(function (CatalogItem $item) {
239
+            return $item->getName();
240
+        }, array_filter($catalog->getItems(), function (CatalogItem $item) {
241
+            return $item->hasType(Service::DEFAULT_TYPE);
242
+        }));
243
+    }
244
+
245
+    /**
246
+     * @return string the container name where objects are stored
247
+     */
248
+    public function getStorageId() {
249
+        return $this->params['container'];
250
+    }
251
+
252
+    /**
253
+     * @param string $urn the unified resource name used to identify the object
254
+     * @param resource $stream stream with the data to write
255
+     * @throws Exception from openstack lib when something goes wrong
256
+     */
257
+    public function writeObject($urn, $stream) {
258
+        $this->init();
259
+        $this->container->uploadObject($urn, $stream);
260
+    }
261
+
262
+    /**
263
+     * @param string $urn the unified resource name used to identify the object
264
+     * @return resource stream with the read data
265
+     * @throws Exception from openstack lib when something goes wrong
266
+     */
267
+    public function readObject($urn) {
268
+        $this->init();
269
+        $object = $this->container->getObject($urn);
270
+
271
+        // we need to keep a reference to objectContent or
272
+        // the stream will be closed before we can do anything with it
273
+        /** @var $objectContent \Guzzle\Http\EntityBody * */
274
+        $objectContent = $object->getContent();
275
+        $objectContent->rewind();
276
+
277
+        $stream = $objectContent->getStream();
278
+        // save the object content in the context of the stream to prevent it being gc'd until the stream is closed
279
+        stream_context_set_option($stream, 'swift', 'content', $objectContent);
280
+
281
+        return RetryWrapper::wrap($stream);
282
+    }
283
+
284
+    /**
285
+     * @param string $urn Unified Resource Name
286
+     * @return void
287
+     * @throws Exception from openstack lib when something goes wrong
288
+     */
289
+    public function deleteObject($urn) {
290
+        $this->init();
291
+        // see https://github.com/rackspace/php-opencloud/issues/243#issuecomment-30032242
292
+        $this->container->dataObject()->setName($urn)->delete();
293
+    }
294
+
295
+    public function deleteContainer($recursive = false) {
296
+        $this->init();
297
+        $this->container->delete($recursive);
298
+    }
299 299
 
300 300
 }
Please login to merge, or discard this patch.