Completed
Push — master ( 422c3e...f698aa )
by Lukas
14:15
created
apps/files_external/lib/Lib/Storage/Swift.php 1 patch
Indentation   +596 added lines, -596 removed lines patch added patch discarded remove patch
@@ -48,601 +48,601 @@
 block discarded – undo
48 48
 
49 49
 class Swift extends \OC\Files\Storage\Common {
50 50
 
51
-	/**
52
-	 * @var \OpenCloud\ObjectStore\Service
53
-	 */
54
-	private $connection;
55
-	/**
56
-	 * @var \OpenCloud\ObjectStore\Resource\Container
57
-	 */
58
-	private $container;
59
-	/**
60
-	 * @var \OpenCloud\OpenStack
61
-	 */
62
-	private $anchor;
63
-	/**
64
-	 * @var string
65
-	 */
66
-	private $bucket;
67
-	/**
68
-	 * Connection parameters
69
-	 *
70
-	 * @var array
71
-	 */
72
-	private $params;
73
-
74
-	/** @var string  */
75
-	private $id;
76
-
77
-	/**
78
-	 * Key value cache mapping path to data object. Maps path to
79
-	 * \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject for existing
80
-	 * paths and path to false for not existing paths.
81
-	 * @var \OCP\ICache
82
-	 */
83
-	private $objectCache;
84
-
85
-	/**
86
-	 * @param string $path
87
-	 */
88
-	private function normalizePath($path) {
89
-		$path = trim($path, '/');
90
-
91
-		if (!$path) {
92
-			$path = '.';
93
-		}
94
-
95
-		$path = str_replace('#', '%23', $path);
96
-
97
-		return $path;
98
-	}
99
-
100
-	const SUBCONTAINER_FILE = '.subcontainers';
101
-
102
-	/**
103
-	 * translate directory path to container name
104
-	 *
105
-	 * @param string $path
106
-	 * @return string
107
-	 */
108
-
109
-	/**
110
-	 * Fetches an object from the API.
111
-	 * If the object is cached already or a
112
-	 * failed "doesn't exist" response was cached,
113
-	 * that one will be returned.
114
-	 *
115
-	 * @param string $path
116
-	 * @return \OpenCloud\ObjectStore\Resource\DataObject|bool object
117
-	 * or false if the object did not exist
118
-	 */
119
-	private function fetchObject($path) {
120
-		if ($this->objectCache->hasKey($path)) {
121
-			// might be "false" if object did not exist from last check
122
-			return $this->objectCache->get($path);
123
-		}
124
-		try {
125
-			$object = $this->getContainer()->getPartialObject($path);
126
-			$this->objectCache->set($path, $object);
127
-			return $object;
128
-		} catch (ClientErrorResponseException $e) {
129
-			// this exception happens when the object does not exist, which
130
-			// is expected in most cases
131
-			$this->objectCache->set($path, false);
132
-			return false;
133
-		} catch (ClientErrorResponseException $e) {
134
-			// Expected response is "404 Not Found", so only log if it isn't
135
-			if ($e->getResponse()->getStatusCode() !== 404) {
136
-				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
137
-			}
138
-			return false;
139
-		}
140
-	}
141
-
142
-	/**
143
-	 * Returns whether the given path exists.
144
-	 *
145
-	 * @param string $path
146
-	 *
147
-	 * @return bool true if the object exist, false otherwise
148
-	 */
149
-	private function doesObjectExist($path) {
150
-		return $this->fetchObject($path) !== false;
151
-	}
152
-
153
-	public function __construct($params) {
154
-		if ((empty($params['key']) and empty($params['password']))
155
-			or empty($params['user']) or empty($params['bucket'])
156
-			or empty($params['region'])
157
-		) {
158
-			throw new \Exception("API Key or password, Username, Bucket and Region have to be configured.");
159
-		}
160
-
161
-		$this->id = 'swift::' . $params['user'] . md5($params['bucket']);
162
-
163
-		$bucketUrl = Url::factory($params['bucket']);
164
-		if ($bucketUrl->isAbsolute()) {
165
-			$this->bucket = end(($bucketUrl->getPathSegments()));
166
-			$params['endpoint_url'] = $bucketUrl->addPath('..')->normalizePath();
167
-		} else {
168
-			$this->bucket = $params['bucket'];
169
-		}
170
-
171
-		if (empty($params['url'])) {
172
-			$params['url'] = 'https://identity.api.rackspacecloud.com/v2.0/';
173
-		}
174
-
175
-		if (empty($params['service_name'])) {
176
-			$params['service_name'] = 'cloudFiles';
177
-		}
178
-
179
-		$this->params = $params;
180
-		// FIXME: private class...
181
-		$this->objectCache = new \OC\Cache\CappedMemoryCache();
182
-	}
183
-
184
-	public function mkdir($path) {
185
-		$path = $this->normalizePath($path);
186
-
187
-		if ($this->is_dir($path)) {
188
-			return false;
189
-		}
190
-
191
-		if ($path !== '.') {
192
-			$path .= '/';
193
-		}
194
-
195
-		try {
196
-			$customHeaders = array('content-type' => 'httpd/unix-directory');
197
-			$metadataHeaders = DataObject::stockHeaders(array());
198
-			$allHeaders = $customHeaders + $metadataHeaders;
199
-			$this->getContainer()->uploadObject($path, '', $allHeaders);
200
-			// invalidate so that the next access gets the real object
201
-			// with all properties
202
-			$this->objectCache->remove($path);
203
-		} catch (Exceptions\CreateUpdateError $e) {
204
-			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
205
-			return false;
206
-		}
207
-
208
-		return true;
209
-	}
210
-
211
-	public function file_exists($path) {
212
-		$path = $this->normalizePath($path);
213
-
214
-		if ($path !== '.' && $this->is_dir($path)) {
215
-			$path .= '/';
216
-		}
217
-
218
-		return $this->doesObjectExist($path);
219
-	}
220
-
221
-	public function rmdir($path) {
222
-		$path = $this->normalizePath($path);
223
-
224
-		if (!$this->is_dir($path) || !$this->isDeletable($path)) {
225
-			return false;
226
-		}
227
-
228
-		$dh = $this->opendir($path);
229
-		while ($file = readdir($dh)) {
230
-			if (\OC\Files\Filesystem::isIgnoredDir($file)) {
231
-				continue;
232
-			}
233
-
234
-			if ($this->is_dir($path . '/' . $file)) {
235
-				$this->rmdir($path . '/' . $file);
236
-			} else {
237
-				$this->unlink($path . '/' . $file);
238
-			}
239
-		}
240
-
241
-		try {
242
-			$this->getContainer()->dataObject()->setName($path . '/')->delete();
243
-			$this->objectCache->remove($path . '/');
244
-		} catch (Exceptions\DeleteError $e) {
245
-			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
246
-			return false;
247
-		}
248
-
249
-		return true;
250
-	}
251
-
252
-	public function opendir($path) {
253
-		$path = $this->normalizePath($path);
254
-
255
-		if ($path === '.') {
256
-			$path = '';
257
-		} else {
258
-			$path .= '/';
259
-		}
260
-
261
-		$path = str_replace('%23', '#', $path); // the prefix is sent as a query param, so revert the encoding of #
262
-
263
-		try {
264
-			$files = array();
265
-			/** @var OpenCloud\Common\Collection $objects */
266
-			$objects = $this->getContainer()->objectList(array(
267
-				'prefix' => $path,
268
-				'delimiter' => '/'
269
-			));
270
-
271
-			/** @var OpenCloud\ObjectStore\Resource\DataObject $object */
272
-			foreach ($objects as $object) {
273
-				$file = basename($object->getName());
274
-				if ($file !== basename($path) && $file !== '.') {
275
-					$files[] = $file;
276
-				}
277
-			}
278
-
279
-			return IteratorDirectory::wrap($files);
280
-		} catch (\Exception $e) {
281
-			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
282
-			return false;
283
-		}
284
-
285
-	}
286
-
287
-	public function stat($path) {
288
-		$path = $this->normalizePath($path);
289
-
290
-		if ($path === '.') {
291
-			$path = '';
292
-		} else if ($this->is_dir($path)) {
293
-			$path .= '/';
294
-		}
295
-
296
-		try {
297
-			/** @var DataObject $object */
298
-			$object = $this->fetchObject($path);
299
-			if (!$object) {
300
-				return false;
301
-			}
302
-		} catch (ClientErrorResponseException $e) {
303
-			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
304
-			return false;
305
-		}
306
-
307
-		$dateTime = \DateTime::createFromFormat(\DateTime::RFC1123, $object->getLastModified());
308
-		if ($dateTime !== false) {
309
-			$mtime = $dateTime->getTimestamp();
310
-		} else {
311
-			$mtime = null;
312
-		}
313
-		$objectMetadata = $object->getMetadata();
314
-		$metaTimestamp = $objectMetadata->getProperty('timestamp');
315
-		if (isset($metaTimestamp)) {
316
-			$mtime = $metaTimestamp;
317
-		}
318
-
319
-		if (!empty($mtime)) {
320
-			$mtime = floor($mtime);
321
-		}
322
-
323
-		$stat = array();
324
-		$stat['size'] = (int)$object->getContentLength();
325
-		$stat['mtime'] = $mtime;
326
-		$stat['atime'] = time();
327
-		return $stat;
328
-	}
329
-
330
-	public function filetype($path) {
331
-		$path = $this->normalizePath($path);
332
-
333
-		if ($path !== '.' && $this->doesObjectExist($path)) {
334
-			return 'file';
335
-		}
336
-
337
-		if ($path !== '.') {
338
-			$path .= '/';
339
-		}
340
-
341
-		if ($this->doesObjectExist($path)) {
342
-			return 'dir';
343
-		}
344
-	}
345
-
346
-	public function unlink($path) {
347
-		$path = $this->normalizePath($path);
348
-
349
-		if ($this->is_dir($path)) {
350
-			return $this->rmdir($path);
351
-		}
352
-
353
-		try {
354
-			$this->getContainer()->dataObject()->setName($path)->delete();
355
-			$this->objectCache->remove($path);
356
-			$this->objectCache->remove($path . '/');
357
-		} catch (ClientErrorResponseException $e) {
358
-			if ($e->getResponse()->getStatusCode() !== 404) {
359
-				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
360
-			}
361
-			return false;
362
-		}
363
-
364
-		return true;
365
-	}
366
-
367
-	public function fopen($path, $mode) {
368
-		$path = $this->normalizePath($path);
369
-
370
-		switch ($mode) {
371
-			case 'a':
372
-			case 'ab':
373
-			case 'a+':
374
-				return false;
375
-			case 'r':
376
-			case 'rb':
377
-				try {
378
-					$c = $this->getContainer();
379
-					$streamFactory = new \Guzzle\Stream\PhpStreamRequestFactory();
380
-					/** @var \OpenCloud\Common\Http\Client $client */
381
-					$client = $c->getClient();
382
-					$streamInterface = $streamFactory->fromRequest($client->get($c->getUrl($path)));
383
-					$streamInterface->rewind();
384
-					$stream = $streamInterface->getStream();
385
-					stream_context_set_option($stream, 'swift','content', $streamInterface);
386
-					if(!strrpos($streamInterface
387
-						->getMetaData('wrapper_data')[0], '404 Not Found')) {
388
-						return $stream;
389
-					}
390
-					return false;
391
-				} catch (\Guzzle\Http\Exception\BadResponseException $e) {
392
-					\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
393
-					return false;
394
-				}
395
-			case 'w':
396
-			case 'wb':
397
-			case 'r+':
398
-			case 'w+':
399
-			case 'wb+':
400
-			case 'x':
401
-			case 'x+':
402
-			case 'c':
403
-			case 'c+':
404
-				if (strrpos($path, '.') !== false) {
405
-					$ext = substr($path, strrpos($path, '.'));
406
-				} else {
407
-					$ext = '';
408
-				}
409
-				$tmpFile = \OCP\Files::tmpFile($ext);
410
-				// Fetch existing file if required
411
-				if ($mode[0] !== 'w' && $this->file_exists($path)) {
412
-					if ($mode[0] === 'x') {
413
-						// File cannot already exist
414
-						return false;
415
-					}
416
-					$source = $this->fopen($path, 'r');
417
-					file_put_contents($tmpFile, $source);
418
-				}
419
-				$handle = fopen($tmpFile, $mode);
420
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
421
-					$this->writeBack($tmpFile, $path);
422
-				});
423
-		}
424
-	}
425
-
426
-	public function touch($path, $mtime = null) {
427
-		$path = $this->normalizePath($path);
428
-		if (is_null($mtime)) {
429
-			$mtime = time();
430
-		}
431
-		$metadata = array('timestamp' => $mtime);
432
-		if ($this->file_exists($path)) {
433
-			if ($this->is_dir($path) && $path !== '.') {
434
-				$path .= '/';
435
-			}
436
-
437
-			$object = $this->fetchObject($path);
438
-			if ($object->saveMetadata($metadata)) {
439
-				// invalidate target object to force repopulation on fetch
440
-				$this->objectCache->remove($path);
441
-			}
442
-			return true;
443
-		} else {
444
-			$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
445
-			$customHeaders = array('content-type' => $mimeType);
446
-			$metadataHeaders = DataObject::stockHeaders($metadata);
447
-			$allHeaders = $customHeaders + $metadataHeaders;
448
-			$this->getContainer()->uploadObject($path, '', $allHeaders);
449
-			// invalidate target object to force repopulation on fetch
450
-			$this->objectCache->remove($path);
451
-			return true;
452
-		}
453
-	}
454
-
455
-	public function copy($path1, $path2) {
456
-		$path1 = $this->normalizePath($path1);
457
-		$path2 = $this->normalizePath($path2);
458
-
459
-		$fileType = $this->filetype($path1);
460
-		if ($fileType === 'file') {
461
-
462
-			// make way
463
-			$this->unlink($path2);
464
-
465
-			try {
466
-				$source = $this->fetchObject($path1);
467
-				$source->copy($this->bucket . '/' . $path2);
468
-				// invalidate target object to force repopulation on fetch
469
-				$this->objectCache->remove($path2);
470
-				$this->objectCache->remove($path2 . '/');
471
-			} catch (ClientErrorResponseException $e) {
472
-				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
473
-				return false;
474
-			}
475
-
476
-		} else if ($fileType === 'dir') {
477
-
478
-			// make way
479
-			$this->unlink($path2);
480
-
481
-			try {
482
-				$source = $this->fetchObject($path1 . '/');
483
-				$source->copy($this->bucket . '/' . $path2 . '/');
484
-				// invalidate target object to force repopulation on fetch
485
-				$this->objectCache->remove($path2);
486
-				$this->objectCache->remove($path2 . '/');
487
-			} catch (ClientErrorResponseException $e) {
488
-				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
489
-				return false;
490
-			}
491
-
492
-			$dh = $this->opendir($path1);
493
-			while ($file = readdir($dh)) {
494
-				if (\OC\Files\Filesystem::isIgnoredDir($file)) {
495
-					continue;
496
-				}
497
-
498
-				$source = $path1 . '/' . $file;
499
-				$target = $path2 . '/' . $file;
500
-				$this->copy($source, $target);
501
-			}
502
-
503
-		} else {
504
-			//file does not exist
505
-			return false;
506
-		}
507
-
508
-		return true;
509
-	}
510
-
511
-	public function rename($path1, $path2) {
512
-		$path1 = $this->normalizePath($path1);
513
-		$path2 = $this->normalizePath($path2);
514
-
515
-		$fileType = $this->filetype($path1);
516
-
517
-		if ($fileType === 'dir' || $fileType === 'file') {
518
-			// copy
519
-			if ($this->copy($path1, $path2) === false) {
520
-				return false;
521
-			}
522
-
523
-			// cleanup
524
-			if ($this->unlink($path1) === false) {
525
-				$this->unlink($path2);
526
-				return false;
527
-			}
528
-
529
-			return true;
530
-		}
531
-
532
-		return false;
533
-	}
534
-
535
-	public function getId() {
536
-		return $this->id;
537
-	}
538
-
539
-	/**
540
-	 * Returns the connection
541
-	 *
542
-	 * @return OpenCloud\ObjectStore\Service connected client
543
-	 * @throws \Exception if connection could not be made
544
-	 */
545
-	public function getConnection() {
546
-		if (!is_null($this->connection)) {
547
-			return $this->connection;
548
-		}
549
-
550
-		$settings = array(
551
-			'username' => $this->params['user'],
552
-		);
553
-
554
-		if (!empty($this->params['password'])) {
555
-			$settings['password'] = $this->params['password'];
556
-		} else if (!empty($this->params['key'])) {
557
-			$settings['apiKey'] = $this->params['key'];
558
-		}
559
-
560
-		if (!empty($this->params['tenant'])) {
561
-			$settings['tenantName'] = $this->params['tenant'];
562
-		}
563
-
564
-		if (!empty($this->params['timeout'])) {
565
-			$settings['timeout'] = $this->params['timeout'];
566
-		}
567
-
568
-		if (isset($settings['apiKey'])) {
569
-			$this->anchor = new Rackspace($this->params['url'], $settings);
570
-		} else {
571
-			$this->anchor = new OpenStack($this->params['url'], $settings);
572
-		}
573
-
574
-		$connection = $this->anchor->objectStoreService($this->params['service_name'], $this->params['region']);
575
-
576
-		if (!empty($this->params['endpoint_url'])) {
577
-			$endpoint = $connection->getEndpoint();
578
-			$endpoint->setPublicUrl($this->params['endpoint_url']);
579
-			$endpoint->setPrivateUrl($this->params['endpoint_url']);
580
-			$connection->setEndpoint($endpoint);
581
-		}
582
-
583
-		$this->connection = $connection;
584
-
585
-		return $this->connection;
586
-	}
587
-
588
-	/**
589
-	 * Returns the initialized object store container.
590
-	 *
591
-	 * @return OpenCloud\ObjectStore\Resource\Container
592
-	 */
593
-	public function getContainer() {
594
-		if (!is_null($this->container)) {
595
-			return $this->container;
596
-		}
597
-
598
-		try {
599
-			$this->container = $this->getConnection()->getContainer($this->bucket);
600
-		} catch (ClientErrorResponseException $e) {
601
-			$this->container = $this->getConnection()->createContainer($this->bucket);
602
-		}
603
-
604
-		if (!$this->file_exists('.')) {
605
-			$this->mkdir('.');
606
-		}
607
-
608
-		return $this->container;
609
-	}
610
-
611
-	public function writeBack($tmpFile, $path) {
612
-		$fileData = fopen($tmpFile, 'r');
613
-		$this->getContainer()->uploadObject($path, $fileData);
614
-		// invalidate target object to force repopulation on fetch
615
-		$this->objectCache->remove($path);
616
-		unlink($tmpFile);
617
-	}
618
-
619
-	public function hasUpdated($path, $time) {
620
-		if ($this->is_file($path)) {
621
-			return parent::hasUpdated($path, $time);
622
-		}
623
-		$path = $this->normalizePath($path);
624
-		$dh = $this->opendir($path);
625
-		$content = array();
626
-		while (($file = readdir($dh)) !== false) {
627
-			$content[] = $file;
628
-		}
629
-		if ($path === '.') {
630
-			$path = '';
631
-		}
632
-		$cachedContent = $this->getCache()->getFolderContents($path);
633
-		$cachedNames = array_map(function ($content) {
634
-			return $content['name'];
635
-		}, $cachedContent);
636
-		sort($cachedNames);
637
-		sort($content);
638
-		return $cachedNames !== $content;
639
-	}
640
-
641
-	/**
642
-	 * check if curl is installed
643
-	 */
644
-	public static function checkDependencies() {
645
-		return true;
646
-	}
51
+    /**
52
+     * @var \OpenCloud\ObjectStore\Service
53
+     */
54
+    private $connection;
55
+    /**
56
+     * @var \OpenCloud\ObjectStore\Resource\Container
57
+     */
58
+    private $container;
59
+    /**
60
+     * @var \OpenCloud\OpenStack
61
+     */
62
+    private $anchor;
63
+    /**
64
+     * @var string
65
+     */
66
+    private $bucket;
67
+    /**
68
+     * Connection parameters
69
+     *
70
+     * @var array
71
+     */
72
+    private $params;
73
+
74
+    /** @var string  */
75
+    private $id;
76
+
77
+    /**
78
+     * Key value cache mapping path to data object. Maps path to
79
+     * \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject for existing
80
+     * paths and path to false for not existing paths.
81
+     * @var \OCP\ICache
82
+     */
83
+    private $objectCache;
84
+
85
+    /**
86
+     * @param string $path
87
+     */
88
+    private function normalizePath($path) {
89
+        $path = trim($path, '/');
90
+
91
+        if (!$path) {
92
+            $path = '.';
93
+        }
94
+
95
+        $path = str_replace('#', '%23', $path);
96
+
97
+        return $path;
98
+    }
99
+
100
+    const SUBCONTAINER_FILE = '.subcontainers';
101
+
102
+    /**
103
+     * translate directory path to container name
104
+     *
105
+     * @param string $path
106
+     * @return string
107
+     */
108
+
109
+    /**
110
+     * Fetches an object from the API.
111
+     * If the object is cached already or a
112
+     * failed "doesn't exist" response was cached,
113
+     * that one will be returned.
114
+     *
115
+     * @param string $path
116
+     * @return \OpenCloud\ObjectStore\Resource\DataObject|bool object
117
+     * or false if the object did not exist
118
+     */
119
+    private function fetchObject($path) {
120
+        if ($this->objectCache->hasKey($path)) {
121
+            // might be "false" if object did not exist from last check
122
+            return $this->objectCache->get($path);
123
+        }
124
+        try {
125
+            $object = $this->getContainer()->getPartialObject($path);
126
+            $this->objectCache->set($path, $object);
127
+            return $object;
128
+        } catch (ClientErrorResponseException $e) {
129
+            // this exception happens when the object does not exist, which
130
+            // is expected in most cases
131
+            $this->objectCache->set($path, false);
132
+            return false;
133
+        } catch (ClientErrorResponseException $e) {
134
+            // Expected response is "404 Not Found", so only log if it isn't
135
+            if ($e->getResponse()->getStatusCode() !== 404) {
136
+                \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
137
+            }
138
+            return false;
139
+        }
140
+    }
141
+
142
+    /**
143
+     * Returns whether the given path exists.
144
+     *
145
+     * @param string $path
146
+     *
147
+     * @return bool true if the object exist, false otherwise
148
+     */
149
+    private function doesObjectExist($path) {
150
+        return $this->fetchObject($path) !== false;
151
+    }
152
+
153
+    public function __construct($params) {
154
+        if ((empty($params['key']) and empty($params['password']))
155
+            or empty($params['user']) or empty($params['bucket'])
156
+            or empty($params['region'])
157
+        ) {
158
+            throw new \Exception("API Key or password, Username, Bucket and Region have to be configured.");
159
+        }
160
+
161
+        $this->id = 'swift::' . $params['user'] . md5($params['bucket']);
162
+
163
+        $bucketUrl = Url::factory($params['bucket']);
164
+        if ($bucketUrl->isAbsolute()) {
165
+            $this->bucket = end(($bucketUrl->getPathSegments()));
166
+            $params['endpoint_url'] = $bucketUrl->addPath('..')->normalizePath();
167
+        } else {
168
+            $this->bucket = $params['bucket'];
169
+        }
170
+
171
+        if (empty($params['url'])) {
172
+            $params['url'] = 'https://identity.api.rackspacecloud.com/v2.0/';
173
+        }
174
+
175
+        if (empty($params['service_name'])) {
176
+            $params['service_name'] = 'cloudFiles';
177
+        }
178
+
179
+        $this->params = $params;
180
+        // FIXME: private class...
181
+        $this->objectCache = new \OC\Cache\CappedMemoryCache();
182
+    }
183
+
184
+    public function mkdir($path) {
185
+        $path = $this->normalizePath($path);
186
+
187
+        if ($this->is_dir($path)) {
188
+            return false;
189
+        }
190
+
191
+        if ($path !== '.') {
192
+            $path .= '/';
193
+        }
194
+
195
+        try {
196
+            $customHeaders = array('content-type' => 'httpd/unix-directory');
197
+            $metadataHeaders = DataObject::stockHeaders(array());
198
+            $allHeaders = $customHeaders + $metadataHeaders;
199
+            $this->getContainer()->uploadObject($path, '', $allHeaders);
200
+            // invalidate so that the next access gets the real object
201
+            // with all properties
202
+            $this->objectCache->remove($path);
203
+        } catch (Exceptions\CreateUpdateError $e) {
204
+            \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
205
+            return false;
206
+        }
207
+
208
+        return true;
209
+    }
210
+
211
+    public function file_exists($path) {
212
+        $path = $this->normalizePath($path);
213
+
214
+        if ($path !== '.' && $this->is_dir($path)) {
215
+            $path .= '/';
216
+        }
217
+
218
+        return $this->doesObjectExist($path);
219
+    }
220
+
221
+    public function rmdir($path) {
222
+        $path = $this->normalizePath($path);
223
+
224
+        if (!$this->is_dir($path) || !$this->isDeletable($path)) {
225
+            return false;
226
+        }
227
+
228
+        $dh = $this->opendir($path);
229
+        while ($file = readdir($dh)) {
230
+            if (\OC\Files\Filesystem::isIgnoredDir($file)) {
231
+                continue;
232
+            }
233
+
234
+            if ($this->is_dir($path . '/' . $file)) {
235
+                $this->rmdir($path . '/' . $file);
236
+            } else {
237
+                $this->unlink($path . '/' . $file);
238
+            }
239
+        }
240
+
241
+        try {
242
+            $this->getContainer()->dataObject()->setName($path . '/')->delete();
243
+            $this->objectCache->remove($path . '/');
244
+        } catch (Exceptions\DeleteError $e) {
245
+            \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
246
+            return false;
247
+        }
248
+
249
+        return true;
250
+    }
251
+
252
+    public function opendir($path) {
253
+        $path = $this->normalizePath($path);
254
+
255
+        if ($path === '.') {
256
+            $path = '';
257
+        } else {
258
+            $path .= '/';
259
+        }
260
+
261
+        $path = str_replace('%23', '#', $path); // the prefix is sent as a query param, so revert the encoding of #
262
+
263
+        try {
264
+            $files = array();
265
+            /** @var OpenCloud\Common\Collection $objects */
266
+            $objects = $this->getContainer()->objectList(array(
267
+                'prefix' => $path,
268
+                'delimiter' => '/'
269
+            ));
270
+
271
+            /** @var OpenCloud\ObjectStore\Resource\DataObject $object */
272
+            foreach ($objects as $object) {
273
+                $file = basename($object->getName());
274
+                if ($file !== basename($path) && $file !== '.') {
275
+                    $files[] = $file;
276
+                }
277
+            }
278
+
279
+            return IteratorDirectory::wrap($files);
280
+        } catch (\Exception $e) {
281
+            \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
282
+            return false;
283
+        }
284
+
285
+    }
286
+
287
+    public function stat($path) {
288
+        $path = $this->normalizePath($path);
289
+
290
+        if ($path === '.') {
291
+            $path = '';
292
+        } else if ($this->is_dir($path)) {
293
+            $path .= '/';
294
+        }
295
+
296
+        try {
297
+            /** @var DataObject $object */
298
+            $object = $this->fetchObject($path);
299
+            if (!$object) {
300
+                return false;
301
+            }
302
+        } catch (ClientErrorResponseException $e) {
303
+            \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
304
+            return false;
305
+        }
306
+
307
+        $dateTime = \DateTime::createFromFormat(\DateTime::RFC1123, $object->getLastModified());
308
+        if ($dateTime !== false) {
309
+            $mtime = $dateTime->getTimestamp();
310
+        } else {
311
+            $mtime = null;
312
+        }
313
+        $objectMetadata = $object->getMetadata();
314
+        $metaTimestamp = $objectMetadata->getProperty('timestamp');
315
+        if (isset($metaTimestamp)) {
316
+            $mtime = $metaTimestamp;
317
+        }
318
+
319
+        if (!empty($mtime)) {
320
+            $mtime = floor($mtime);
321
+        }
322
+
323
+        $stat = array();
324
+        $stat['size'] = (int)$object->getContentLength();
325
+        $stat['mtime'] = $mtime;
326
+        $stat['atime'] = time();
327
+        return $stat;
328
+    }
329
+
330
+    public function filetype($path) {
331
+        $path = $this->normalizePath($path);
332
+
333
+        if ($path !== '.' && $this->doesObjectExist($path)) {
334
+            return 'file';
335
+        }
336
+
337
+        if ($path !== '.') {
338
+            $path .= '/';
339
+        }
340
+
341
+        if ($this->doesObjectExist($path)) {
342
+            return 'dir';
343
+        }
344
+    }
345
+
346
+    public function unlink($path) {
347
+        $path = $this->normalizePath($path);
348
+
349
+        if ($this->is_dir($path)) {
350
+            return $this->rmdir($path);
351
+        }
352
+
353
+        try {
354
+            $this->getContainer()->dataObject()->setName($path)->delete();
355
+            $this->objectCache->remove($path);
356
+            $this->objectCache->remove($path . '/');
357
+        } catch (ClientErrorResponseException $e) {
358
+            if ($e->getResponse()->getStatusCode() !== 404) {
359
+                \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
360
+            }
361
+            return false;
362
+        }
363
+
364
+        return true;
365
+    }
366
+
367
+    public function fopen($path, $mode) {
368
+        $path = $this->normalizePath($path);
369
+
370
+        switch ($mode) {
371
+            case 'a':
372
+            case 'ab':
373
+            case 'a+':
374
+                return false;
375
+            case 'r':
376
+            case 'rb':
377
+                try {
378
+                    $c = $this->getContainer();
379
+                    $streamFactory = new \Guzzle\Stream\PhpStreamRequestFactory();
380
+                    /** @var \OpenCloud\Common\Http\Client $client */
381
+                    $client = $c->getClient();
382
+                    $streamInterface = $streamFactory->fromRequest($client->get($c->getUrl($path)));
383
+                    $streamInterface->rewind();
384
+                    $stream = $streamInterface->getStream();
385
+                    stream_context_set_option($stream, 'swift','content', $streamInterface);
386
+                    if(!strrpos($streamInterface
387
+                        ->getMetaData('wrapper_data')[0], '404 Not Found')) {
388
+                        return $stream;
389
+                    }
390
+                    return false;
391
+                } catch (\Guzzle\Http\Exception\BadResponseException $e) {
392
+                    \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
393
+                    return false;
394
+                }
395
+            case 'w':
396
+            case 'wb':
397
+            case 'r+':
398
+            case 'w+':
399
+            case 'wb+':
400
+            case 'x':
401
+            case 'x+':
402
+            case 'c':
403
+            case 'c+':
404
+                if (strrpos($path, '.') !== false) {
405
+                    $ext = substr($path, strrpos($path, '.'));
406
+                } else {
407
+                    $ext = '';
408
+                }
409
+                $tmpFile = \OCP\Files::tmpFile($ext);
410
+                // Fetch existing file if required
411
+                if ($mode[0] !== 'w' && $this->file_exists($path)) {
412
+                    if ($mode[0] === 'x') {
413
+                        // File cannot already exist
414
+                        return false;
415
+                    }
416
+                    $source = $this->fopen($path, 'r');
417
+                    file_put_contents($tmpFile, $source);
418
+                }
419
+                $handle = fopen($tmpFile, $mode);
420
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
421
+                    $this->writeBack($tmpFile, $path);
422
+                });
423
+        }
424
+    }
425
+
426
+    public function touch($path, $mtime = null) {
427
+        $path = $this->normalizePath($path);
428
+        if (is_null($mtime)) {
429
+            $mtime = time();
430
+        }
431
+        $metadata = array('timestamp' => $mtime);
432
+        if ($this->file_exists($path)) {
433
+            if ($this->is_dir($path) && $path !== '.') {
434
+                $path .= '/';
435
+            }
436
+
437
+            $object = $this->fetchObject($path);
438
+            if ($object->saveMetadata($metadata)) {
439
+                // invalidate target object to force repopulation on fetch
440
+                $this->objectCache->remove($path);
441
+            }
442
+            return true;
443
+        } else {
444
+            $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
445
+            $customHeaders = array('content-type' => $mimeType);
446
+            $metadataHeaders = DataObject::stockHeaders($metadata);
447
+            $allHeaders = $customHeaders + $metadataHeaders;
448
+            $this->getContainer()->uploadObject($path, '', $allHeaders);
449
+            // invalidate target object to force repopulation on fetch
450
+            $this->objectCache->remove($path);
451
+            return true;
452
+        }
453
+    }
454
+
455
+    public function copy($path1, $path2) {
456
+        $path1 = $this->normalizePath($path1);
457
+        $path2 = $this->normalizePath($path2);
458
+
459
+        $fileType = $this->filetype($path1);
460
+        if ($fileType === 'file') {
461
+
462
+            // make way
463
+            $this->unlink($path2);
464
+
465
+            try {
466
+                $source = $this->fetchObject($path1);
467
+                $source->copy($this->bucket . '/' . $path2);
468
+                // invalidate target object to force repopulation on fetch
469
+                $this->objectCache->remove($path2);
470
+                $this->objectCache->remove($path2 . '/');
471
+            } catch (ClientErrorResponseException $e) {
472
+                \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
473
+                return false;
474
+            }
475
+
476
+        } else if ($fileType === 'dir') {
477
+
478
+            // make way
479
+            $this->unlink($path2);
480
+
481
+            try {
482
+                $source = $this->fetchObject($path1 . '/');
483
+                $source->copy($this->bucket . '/' . $path2 . '/');
484
+                // invalidate target object to force repopulation on fetch
485
+                $this->objectCache->remove($path2);
486
+                $this->objectCache->remove($path2 . '/');
487
+            } catch (ClientErrorResponseException $e) {
488
+                \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
489
+                return false;
490
+            }
491
+
492
+            $dh = $this->opendir($path1);
493
+            while ($file = readdir($dh)) {
494
+                if (\OC\Files\Filesystem::isIgnoredDir($file)) {
495
+                    continue;
496
+                }
497
+
498
+                $source = $path1 . '/' . $file;
499
+                $target = $path2 . '/' . $file;
500
+                $this->copy($source, $target);
501
+            }
502
+
503
+        } else {
504
+            //file does not exist
505
+            return false;
506
+        }
507
+
508
+        return true;
509
+    }
510
+
511
+    public function rename($path1, $path2) {
512
+        $path1 = $this->normalizePath($path1);
513
+        $path2 = $this->normalizePath($path2);
514
+
515
+        $fileType = $this->filetype($path1);
516
+
517
+        if ($fileType === 'dir' || $fileType === 'file') {
518
+            // copy
519
+            if ($this->copy($path1, $path2) === false) {
520
+                return false;
521
+            }
522
+
523
+            // cleanup
524
+            if ($this->unlink($path1) === false) {
525
+                $this->unlink($path2);
526
+                return false;
527
+            }
528
+
529
+            return true;
530
+        }
531
+
532
+        return false;
533
+    }
534
+
535
+    public function getId() {
536
+        return $this->id;
537
+    }
538
+
539
+    /**
540
+     * Returns the connection
541
+     *
542
+     * @return OpenCloud\ObjectStore\Service connected client
543
+     * @throws \Exception if connection could not be made
544
+     */
545
+    public function getConnection() {
546
+        if (!is_null($this->connection)) {
547
+            return $this->connection;
548
+        }
549
+
550
+        $settings = array(
551
+            'username' => $this->params['user'],
552
+        );
553
+
554
+        if (!empty($this->params['password'])) {
555
+            $settings['password'] = $this->params['password'];
556
+        } else if (!empty($this->params['key'])) {
557
+            $settings['apiKey'] = $this->params['key'];
558
+        }
559
+
560
+        if (!empty($this->params['tenant'])) {
561
+            $settings['tenantName'] = $this->params['tenant'];
562
+        }
563
+
564
+        if (!empty($this->params['timeout'])) {
565
+            $settings['timeout'] = $this->params['timeout'];
566
+        }
567
+
568
+        if (isset($settings['apiKey'])) {
569
+            $this->anchor = new Rackspace($this->params['url'], $settings);
570
+        } else {
571
+            $this->anchor = new OpenStack($this->params['url'], $settings);
572
+        }
573
+
574
+        $connection = $this->anchor->objectStoreService($this->params['service_name'], $this->params['region']);
575
+
576
+        if (!empty($this->params['endpoint_url'])) {
577
+            $endpoint = $connection->getEndpoint();
578
+            $endpoint->setPublicUrl($this->params['endpoint_url']);
579
+            $endpoint->setPrivateUrl($this->params['endpoint_url']);
580
+            $connection->setEndpoint($endpoint);
581
+        }
582
+
583
+        $this->connection = $connection;
584
+
585
+        return $this->connection;
586
+    }
587
+
588
+    /**
589
+     * Returns the initialized object store container.
590
+     *
591
+     * @return OpenCloud\ObjectStore\Resource\Container
592
+     */
593
+    public function getContainer() {
594
+        if (!is_null($this->container)) {
595
+            return $this->container;
596
+        }
597
+
598
+        try {
599
+            $this->container = $this->getConnection()->getContainer($this->bucket);
600
+        } catch (ClientErrorResponseException $e) {
601
+            $this->container = $this->getConnection()->createContainer($this->bucket);
602
+        }
603
+
604
+        if (!$this->file_exists('.')) {
605
+            $this->mkdir('.');
606
+        }
607
+
608
+        return $this->container;
609
+    }
610
+
611
+    public function writeBack($tmpFile, $path) {
612
+        $fileData = fopen($tmpFile, 'r');
613
+        $this->getContainer()->uploadObject($path, $fileData);
614
+        // invalidate target object to force repopulation on fetch
615
+        $this->objectCache->remove($path);
616
+        unlink($tmpFile);
617
+    }
618
+
619
+    public function hasUpdated($path, $time) {
620
+        if ($this->is_file($path)) {
621
+            return parent::hasUpdated($path, $time);
622
+        }
623
+        $path = $this->normalizePath($path);
624
+        $dh = $this->opendir($path);
625
+        $content = array();
626
+        while (($file = readdir($dh)) !== false) {
627
+            $content[] = $file;
628
+        }
629
+        if ($path === '.') {
630
+            $path = '';
631
+        }
632
+        $cachedContent = $this->getCache()->getFolderContents($path);
633
+        $cachedNames = array_map(function ($content) {
634
+            return $content['name'];
635
+        }, $cachedContent);
636
+        sort($cachedNames);
637
+        sort($content);
638
+        return $cachedNames !== $content;
639
+    }
640
+
641
+    /**
642
+     * check if curl is installed
643
+     */
644
+    public static function checkDependencies() {
645
+        return true;
646
+    }
647 647
 
648 648
 }
Please login to merge, or discard this patch.