Completed
Pull Request — master (#8100)
by Robin
17:54
created
apps/files_external/lib/Lib/Storage/AmazonS3.php 2 patches
Indentation   +559 added lines, -559 removed lines patch added patch discarded remove patch
@@ -46,564 +46,564 @@
 block discarded – undo
46 46
 use OCP\Constants;
47 47
 
48 48
 class AmazonS3 extends \OC\Files\Storage\Common {
49
-	use S3ConnectionTrait;
50
-	use S3ObjectTrait;
51
-
52
-	public function needsPartFile() {
53
-		return false;
54
-	}
55
-
56
-	/**
57
-	 * @var int in seconds
58
-	 */
59
-	private $rescanDelay = 10;
60
-
61
-	/** @var CappedMemoryCache|Result[] */
62
-	private $objectCache;
63
-
64
-	public function __construct($parameters) {
65
-		parent::__construct($parameters);
66
-		$this->parseParams($parameters);
67
-		$this->objectCache = new CappedMemoryCache();
68
-	}
69
-
70
-	/**
71
-	 * @param string $path
72
-	 * @return string correctly encoded path
73
-	 */
74
-	private function normalizePath($path) {
75
-		$path = trim($path, '/');
76
-
77
-		if (!$path) {
78
-			$path = '.';
79
-		}
80
-
81
-		return $path;
82
-	}
83
-
84
-	private function isRoot($path) {
85
-		return $path === '.';
86
-	}
87
-
88
-	private function cleanKey($path) {
89
-		if ($this->isRoot($path)) {
90
-			return '/';
91
-		}
92
-		return $path;
93
-	}
94
-
95
-	private function clearCache() {
96
-		$this->objectCache = new CappedMemoryCache();
97
-	}
98
-
99
-	private function invalidateCache($key) {
100
-		unset($this->objectCache[$key]);
101
-		$keys = array_keys($this->objectCache->getData());
102
-		$keyLength = strlen($key);
103
-		foreach ($keys as $existingKey) {
104
-			if (substr($existingKey, 0, $keyLength) === $key) {
105
-				unset($this->objectCache[$existingKey]);
106
-			}
107
-		}
108
-	}
109
-
110
-	/**
111
-	 * @param $key
112
-	 * @return Result|boolean
113
-	 */
114
-	private function headObject($key) {
115
-		if (!isset($this->objectCache[$key])) {
116
-			try {
117
-				$this->objectCache[$key] = $this->getConnection()->headObject(array(
118
-					'Bucket' => $this->bucket,
119
-					'Key' => $key
120
-				));
121
-			} catch (S3Exception $e) {
122
-				if ($e->getStatusCode() >= 500) {
123
-					throw $e;
124
-				}
125
-				$this->objectCache[$key] = false;
126
-			}
127
-		}
128
-
129
-		return $this->objectCache[$key];
130
-	}
131
-
132
-	/**
133
-	 * Updates old storage ids (v0.2.1 and older) that are based on key and secret to new ones based on the bucket name.
134
-	 * TODO Do this in an update.php. requires iterating over all users and loading the mount.json from their home
135
-	 *
136
-	 * @param array $params
137
-	 */
138
-	public function updateLegacyId(array $params) {
139
-		$oldId = 'amazon::' . $params['key'] . md5($params['secret']);
140
-
141
-		// find by old id or bucket
142
-		$stmt = \OC::$server->getDatabaseConnection()->prepare(
143
-			'SELECT `numeric_id`, `id` FROM `*PREFIX*storages` WHERE `id` IN (?, ?)'
144
-		);
145
-		$stmt->execute(array($oldId, $this->id));
146
-		while ($row = $stmt->fetch()) {
147
-			$storages[$row['id']] = $row['numeric_id'];
148
-		}
149
-
150
-		if (isset($storages[$this->id]) && isset($storages[$oldId])) {
151
-			// if both ids exist, delete the old storage and corresponding filecache entries
152
-			\OC\Files\Cache\Storage::remove($oldId);
153
-		} else if (isset($storages[$oldId])) {
154
-			// if only the old id exists do an update
155
-			$stmt = \OC::$server->getDatabaseConnection()->prepare(
156
-				'UPDATE `*PREFIX*storages` SET `id` = ? WHERE `id` = ?'
157
-			);
158
-			$stmt->execute(array($this->id, $oldId));
159
-		}
160
-		// only the bucket based id may exist, do nothing
161
-	}
162
-
163
-	/**
164
-	 * Remove a file or folder
165
-	 *
166
-	 * @param string $path
167
-	 * @return bool
168
-	 */
169
-	protected function remove($path) {
170
-		// remember fileType to reduce http calls
171
-		$fileType = $this->filetype($path);
172
-		if ($fileType === 'dir') {
173
-			return $this->rmdir($path);
174
-		} else if ($fileType === 'file') {
175
-			return $this->unlink($path);
176
-		} else {
177
-			return false;
178
-		}
179
-	}
180
-
181
-	public function mkdir($path) {
182
-		$path = $this->normalizePath($path);
183
-
184
-		if ($this->is_dir($path)) {
185
-			return false;
186
-		}
187
-
188
-		try {
189
-			$this->getConnection()->putObject(array(
190
-				'Bucket' => $this->bucket,
191
-				'Key' => $path . '/',
192
-				'Body' => '',
193
-				'ContentType' => 'httpd/unix-directory'
194
-			));
195
-			$this->testTimeout();
196
-		} catch (S3Exception $e) {
197
-			\OCP\Util::logException('files_external', $e);
198
-			return false;
199
-		}
200
-
201
-		$this->invalidateCache($path);
202
-
203
-		return true;
204
-	}
205
-
206
-	public function file_exists($path) {
207
-		return $this->filetype($path) !== false;
208
-	}
209
-
210
-
211
-	public function rmdir($path) {
212
-		$path = $this->normalizePath($path);
213
-
214
-		if ($this->isRoot($path)) {
215
-			return $this->clearBucket();
216
-		}
217
-
218
-		if (!$this->file_exists($path)) {
219
-			return false;
220
-		}
221
-
222
-		$this->invalidateCache($path);
223
-		return $this->batchDelete($path);
224
-	}
225
-
226
-	protected function clearBucket() {
227
-		$this->clearCache();
228
-		try {
229
-			$this->getConnection()->clearBucket($this->bucket);
230
-			return true;
231
-			// clearBucket() is not working with Ceph, so if it fails we try the slower approach
232
-		} catch (\Exception $e) {
233
-			return $this->batchDelete();
234
-		}
235
-	}
236
-
237
-	private function batchDelete($path = null) {
238
-		$params = array(
239
-			'Bucket' => $this->bucket
240
-		);
241
-		if ($path !== null) {
242
-			$params['Prefix'] = $path . '/';
243
-		}
244
-		try {
245
-			$connection = $this->getConnection();
246
-			// Since there are no real directories on S3, we need
247
-			// to delete all objects prefixed with the path.
248
-			do {
249
-				// instead of the iterator, manually loop over the list ...
250
-				$objects = $connection->listObjects($params);
251
-				// ... so we can delete the files in batches
252
-				if (isset($objects['Contents'])) {
253
-					$connection->deleteObjects([
254
-						'Bucket' => $this->bucket,
255
-						'Delete' => [
256
-							'Objects' => $objects['Contents']
257
-						]
258
-					]);
259
-					$this->testTimeout();
260
-				}
261
-				// we reached the end when the list is no longer truncated
262
-			} while ($objects['IsTruncated']);
263
-		} catch (S3Exception $e) {
264
-			\OCP\Util::logException('files_external', $e);
265
-			return false;
266
-		}
267
-		return true;
268
-	}
269
-
270
-	public function opendir($path) {
271
-		$path = $this->normalizePath($path);
272
-
273
-		if ($this->isRoot($path)) {
274
-			$path = '';
275
-		} else {
276
-			$path .= '/';
277
-		}
278
-
279
-		try {
280
-			$files = array();
281
-			$results = $this->getConnection()->getPaginator('ListObjects', [
282
-				'Bucket' => $this->bucket,
283
-				'Delimiter' => '/',
284
-				'Prefix' => $path,
285
-			]);
286
-
287
-			foreach ($results as $result) {
288
-				// sub folders
289
-				if (is_array($result['CommonPrefixes'])) {
290
-					foreach ($result['CommonPrefixes'] as $prefix) {
291
-						$files[] = substr(trim($prefix['Prefix'], '/'), strlen($path));
292
-					}
293
-				}
294
-				foreach ($result['Contents'] as $object) {
295
-					if (isset($object['Key']) && $object['Key'] === $path) {
296
-						// it's the directory itself, skip
297
-						continue;
298
-					}
299
-					$file = basename(
300
-						isset($object['Key']) ? $object['Key'] : $object['Prefix']
301
-					);
302
-					$files[] = $file;
303
-				}
304
-			}
305
-
306
-			return IteratorDirectory::wrap($files);
307
-		} catch (S3Exception $e) {
308
-			\OCP\Util::logException('files_external', $e);
309
-			return false;
310
-		}
311
-	}
312
-
313
-	public function stat($path) {
314
-		$path = $this->normalizePath($path);
315
-
316
-		try {
317
-			$stat = array();
318
-			if ($this->is_dir($path)) {
319
-				//folders don't really exist
320
-				$stat['size'] = -1; //unknown
321
-				$stat['mtime'] = time() - $this->rescanDelay * 1000;
322
-			} else {
323
-				$result = $this->headObject($path);
324
-
325
-				$stat['size'] = $result['ContentLength'] ? $result['ContentLength'] : 0;
326
-				if (isset($result['Metadata']['lastmodified'])) {
327
-					$stat['mtime'] = strtotime($result['Metadata']['lastmodified']);
328
-				} else {
329
-					$stat['mtime'] = strtotime($result['LastModified']);
330
-				}
331
-			}
332
-			$stat['atime'] = time();
333
-
334
-			return $stat;
335
-		} catch (S3Exception $e) {
336
-			\OCP\Util::logException('files_external', $e);
337
-			return false;
338
-		}
339
-	}
340
-
341
-	public function is_dir($path) {
342
-		$path = $this->normalizePath($path);
343
-		try {
344
-			return $this->isRoot($path) || $this->headObject($path . '/');
345
-		} catch (S3Exception $e) {
346
-			\OCP\Util::logException('files_external', $e);
347
-			return false;
348
-		}
349
-	}
350
-
351
-	public function filetype($path) {
352
-		$path = $this->normalizePath($path);
353
-
354
-		if ($this->isRoot($path)) {
355
-			return 'dir';
356
-		}
357
-
358
-		try {
359
-			if ($this->headObject($path)) {
360
-				return 'file';
361
-			}
362
-			if ($this->headObject($path . '/')) {
363
-				return 'dir';
364
-			}
365
-		} catch (S3Exception $e) {
366
-			\OCP\Util::logException('files_external', $e);
367
-			return false;
368
-		}
369
-
370
-		return false;
371
-	}
372
-
373
-	public function getPermissions($path) {
374
-		$type = $this->filetype($path);
375
-		if (!$type) {
376
-			return 0;
377
-		}
378
-		return $type === 'dir' ? Constants::PERMISSION_ALL : Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
379
-	}
380
-
381
-	public function unlink($path) {
382
-		$path = $this->normalizePath($path);
383
-
384
-		if ($this->is_dir($path)) {
385
-			return $this->rmdir($path);
386
-		}
387
-
388
-		try {
389
-			$this->deleteObject($path);
390
-			$this->invalidateCache($path);
391
-		} catch (S3Exception $e) {
392
-			\OCP\Util::logException('files_external', $e);
393
-			return false;
394
-		}
395
-
396
-		return true;
397
-	}
398
-
399
-	public function fopen($path, $mode) {
400
-		$path = $this->normalizePath($path);
401
-
402
-		switch ($mode) {
403
-			case 'r':
404
-			case 'rb':
405
-				try {
406
-					return $this->readObject($path);
407
-				} catch (S3Exception $e) {
408
-					\OCP\Util::logException('files_external', $e);
409
-					return false;
410
-				}
411
-			case 'w':
412
-			case 'wb':
413
-				$tmpFile = \OCP\Files::tmpFile();
414
-
415
-				$handle = fopen($tmpFile, 'w');
416
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
417
-					$this->writeBack($tmpFile, $path);
418
-				});
419
-			case 'a':
420
-			case 'ab':
421
-			case 'r+':
422
-			case 'w+':
423
-			case 'wb+':
424
-			case 'a+':
425
-			case 'x':
426
-			case 'x+':
427
-			case 'c':
428
-			case 'c+':
429
-				if (strrpos($path, '.') !== false) {
430
-					$ext = substr($path, strrpos($path, '.'));
431
-				} else {
432
-					$ext = '';
433
-				}
434
-				$tmpFile = \OCP\Files::tmpFile($ext);
435
-				if ($this->file_exists($path)) {
436
-					$source = $this->readObject($path);
437
-					file_put_contents($tmpFile, $source);
438
-				}
439
-
440
-				$handle = fopen($tmpFile, $mode);
441
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
442
-					$this->writeBack($tmpFile, $path);
443
-				});
444
-		}
445
-		return false;
446
-	}
447
-
448
-	public function touch($path, $mtime = null) {
449
-		$path = $this->normalizePath($path);
450
-
451
-		$metadata = array();
452
-		if (is_null($mtime)) {
453
-			$mtime = time();
454
-		}
455
-		$metadata = [
456
-			'lastmodified' => gmdate(\DateTime::RFC1123, $mtime)
457
-		];
458
-
459
-		$fileType = $this->filetype($path);
460
-		try {
461
-			if ($fileType !== false) {
462
-				if ($fileType === 'dir' && !$this->isRoot($path)) {
463
-					$path .= '/';
464
-				}
465
-				$this->getConnection()->copyObject([
466
-					'Bucket' => $this->bucket,
467
-					'Key' => $this->cleanKey($path),
468
-					'Metadata' => $metadata,
469
-					'CopySource' => $this->bucket . '/' . $path,
470
-					'MetadataDirective' => 'REPLACE',
471
-				]);
472
-				$this->testTimeout();
473
-			} else {
474
-				$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
475
-				$this->getConnection()->putObject([
476
-					'Bucket' => $this->bucket,
477
-					'Key' => $this->cleanKey($path),
478
-					'Metadata' => $metadata,
479
-					'Body' => '',
480
-					'ContentType' => $mimeType,
481
-					'MetadataDirective' => 'REPLACE',
482
-				]);
483
-				$this->testTimeout();
484
-			}
485
-		} catch (S3Exception $e) {
486
-			\OCP\Util::logException('files_external', $e);
487
-			return false;
488
-		}
489
-
490
-		$this->invalidateCache($path);
491
-		return true;
492
-	}
493
-
494
-	public function copy($path1, $path2) {
495
-		$path1 = $this->normalizePath($path1);
496
-		$path2 = $this->normalizePath($path2);
497
-
498
-		if ($this->is_file($path1)) {
499
-			try {
500
-				$this->getConnection()->copyObject(array(
501
-					'Bucket' => $this->bucket,
502
-					'Key' => $this->cleanKey($path2),
503
-					'CopySource' => S3Client::encodeKey($this->bucket . '/' . $path1)
504
-				));
505
-				$this->testTimeout();
506
-			} catch (S3Exception $e) {
507
-				\OCP\Util::logException('files_external', $e);
508
-				return false;
509
-			}
510
-		} else {
511
-			$this->remove($path2);
512
-
513
-			try {
514
-				$this->getConnection()->copyObject(array(
515
-					'Bucket' => $this->bucket,
516
-					'Key' => $path2 . '/',
517
-					'CopySource' => S3Client::encodeKey($this->bucket . '/' . $path1 . '/')
518
-				));
519
-				$this->testTimeout();
520
-			} catch (S3Exception $e) {
521
-				\OCP\Util::logException('files_external', $e);
522
-				return false;
523
-			}
524
-
525
-			$dh = $this->opendir($path1);
526
-			if (is_resource($dh)) {
527
-				while (($file = readdir($dh)) !== false) {
528
-					if (\OC\Files\Filesystem::isIgnoredDir($file)) {
529
-						continue;
530
-					}
531
-
532
-					$source = $path1 . '/' . $file;
533
-					$target = $path2 . '/' . $file;
534
-					$this->copy($source, $target);
535
-				}
536
-			}
537
-		}
538
-
539
-		$this->invalidateCache($path2);
540
-
541
-		return true;
542
-	}
543
-
544
-	public function rename($path1, $path2) {
545
-		$path1 = $this->normalizePath($path1);
546
-		$path2 = $this->normalizePath($path2);
547
-
548
-		if ($this->is_file($path1)) {
549
-
550
-			if ($this->copy($path1, $path2) === false) {
551
-				return false;
552
-			}
553
-
554
-			if ($this->unlink($path1) === false) {
555
-				$this->unlink($path2);
556
-				return false;
557
-			}
558
-		} else {
559
-
560
-			if ($this->copy($path1, $path2) === false) {
561
-				return false;
562
-			}
563
-
564
-			if ($this->rmdir($path1) === false) {
565
-				$this->rmdir($path2);
566
-				return false;
567
-			}
568
-		}
569
-
570
-		return true;
571
-	}
572
-
573
-	public function test() {
574
-		$test = $this->getConnection()->getBucketAcl(array(
575
-			'Bucket' => $this->bucket,
576
-		));
577
-		if (isset($test) && !is_null($test->getPath('Owner/ID'))) {
578
-			return true;
579
-		}
580
-		return false;
581
-	}
582
-
583
-	public function getId() {
584
-		return $this->id;
585
-	}
586
-
587
-	public function writeBack($tmpFile, $path) {
588
-		try {
589
-			$source = fopen($tmpFile, 'r');
590
-			$this->writeObject($path, $source);
591
-			$this->invalidateCache($path);
592
-			fclose($source);
593
-
594
-			unlink($tmpFile);
595
-			return true;
596
-		} catch (S3Exception $e) {
597
-			\OCP\Util::logException('files_external', $e);
598
-			return false;
599
-		}
600
-	}
601
-
602
-	/**
603
-	 * check if curl is installed
604
-	 */
605
-	public static function checkDependencies() {
606
-		return true;
607
-	}
49
+    use S3ConnectionTrait;
50
+    use S3ObjectTrait;
51
+
52
+    public function needsPartFile() {
53
+        return false;
54
+    }
55
+
56
+    /**
57
+     * @var int in seconds
58
+     */
59
+    private $rescanDelay = 10;
60
+
61
+    /** @var CappedMemoryCache|Result[] */
62
+    private $objectCache;
63
+
64
+    public function __construct($parameters) {
65
+        parent::__construct($parameters);
66
+        $this->parseParams($parameters);
67
+        $this->objectCache = new CappedMemoryCache();
68
+    }
69
+
70
+    /**
71
+     * @param string $path
72
+     * @return string correctly encoded path
73
+     */
74
+    private function normalizePath($path) {
75
+        $path = trim($path, '/');
76
+
77
+        if (!$path) {
78
+            $path = '.';
79
+        }
80
+
81
+        return $path;
82
+    }
83
+
84
+    private function isRoot($path) {
85
+        return $path === '.';
86
+    }
87
+
88
+    private function cleanKey($path) {
89
+        if ($this->isRoot($path)) {
90
+            return '/';
91
+        }
92
+        return $path;
93
+    }
94
+
95
+    private function clearCache() {
96
+        $this->objectCache = new CappedMemoryCache();
97
+    }
98
+
99
+    private function invalidateCache($key) {
100
+        unset($this->objectCache[$key]);
101
+        $keys = array_keys($this->objectCache->getData());
102
+        $keyLength = strlen($key);
103
+        foreach ($keys as $existingKey) {
104
+            if (substr($existingKey, 0, $keyLength) === $key) {
105
+                unset($this->objectCache[$existingKey]);
106
+            }
107
+        }
108
+    }
109
+
110
+    /**
111
+     * @param $key
112
+     * @return Result|boolean
113
+     */
114
+    private function headObject($key) {
115
+        if (!isset($this->objectCache[$key])) {
116
+            try {
117
+                $this->objectCache[$key] = $this->getConnection()->headObject(array(
118
+                    'Bucket' => $this->bucket,
119
+                    'Key' => $key
120
+                ));
121
+            } catch (S3Exception $e) {
122
+                if ($e->getStatusCode() >= 500) {
123
+                    throw $e;
124
+                }
125
+                $this->objectCache[$key] = false;
126
+            }
127
+        }
128
+
129
+        return $this->objectCache[$key];
130
+    }
131
+
132
+    /**
133
+     * Updates old storage ids (v0.2.1 and older) that are based on key and secret to new ones based on the bucket name.
134
+     * TODO Do this in an update.php. requires iterating over all users and loading the mount.json from their home
135
+     *
136
+     * @param array $params
137
+     */
138
+    public function updateLegacyId(array $params) {
139
+        $oldId = 'amazon::' . $params['key'] . md5($params['secret']);
140
+
141
+        // find by old id or bucket
142
+        $stmt = \OC::$server->getDatabaseConnection()->prepare(
143
+            'SELECT `numeric_id`, `id` FROM `*PREFIX*storages` WHERE `id` IN (?, ?)'
144
+        );
145
+        $stmt->execute(array($oldId, $this->id));
146
+        while ($row = $stmt->fetch()) {
147
+            $storages[$row['id']] = $row['numeric_id'];
148
+        }
149
+
150
+        if (isset($storages[$this->id]) && isset($storages[$oldId])) {
151
+            // if both ids exist, delete the old storage and corresponding filecache entries
152
+            \OC\Files\Cache\Storage::remove($oldId);
153
+        } else if (isset($storages[$oldId])) {
154
+            // if only the old id exists do an update
155
+            $stmt = \OC::$server->getDatabaseConnection()->prepare(
156
+                'UPDATE `*PREFIX*storages` SET `id` = ? WHERE `id` = ?'
157
+            );
158
+            $stmt->execute(array($this->id, $oldId));
159
+        }
160
+        // only the bucket based id may exist, do nothing
161
+    }
162
+
163
+    /**
164
+     * Remove a file or folder
165
+     *
166
+     * @param string $path
167
+     * @return bool
168
+     */
169
+    protected function remove($path) {
170
+        // remember fileType to reduce http calls
171
+        $fileType = $this->filetype($path);
172
+        if ($fileType === 'dir') {
173
+            return $this->rmdir($path);
174
+        } else if ($fileType === 'file') {
175
+            return $this->unlink($path);
176
+        } else {
177
+            return false;
178
+        }
179
+    }
180
+
181
+    public function mkdir($path) {
182
+        $path = $this->normalizePath($path);
183
+
184
+        if ($this->is_dir($path)) {
185
+            return false;
186
+        }
187
+
188
+        try {
189
+            $this->getConnection()->putObject(array(
190
+                'Bucket' => $this->bucket,
191
+                'Key' => $path . '/',
192
+                'Body' => '',
193
+                'ContentType' => 'httpd/unix-directory'
194
+            ));
195
+            $this->testTimeout();
196
+        } catch (S3Exception $e) {
197
+            \OCP\Util::logException('files_external', $e);
198
+            return false;
199
+        }
200
+
201
+        $this->invalidateCache($path);
202
+
203
+        return true;
204
+    }
205
+
206
+    public function file_exists($path) {
207
+        return $this->filetype($path) !== false;
208
+    }
209
+
210
+
211
+    public function rmdir($path) {
212
+        $path = $this->normalizePath($path);
213
+
214
+        if ($this->isRoot($path)) {
215
+            return $this->clearBucket();
216
+        }
217
+
218
+        if (!$this->file_exists($path)) {
219
+            return false;
220
+        }
221
+
222
+        $this->invalidateCache($path);
223
+        return $this->batchDelete($path);
224
+    }
225
+
226
+    protected function clearBucket() {
227
+        $this->clearCache();
228
+        try {
229
+            $this->getConnection()->clearBucket($this->bucket);
230
+            return true;
231
+            // clearBucket() is not working with Ceph, so if it fails we try the slower approach
232
+        } catch (\Exception $e) {
233
+            return $this->batchDelete();
234
+        }
235
+    }
236
+
237
+    private function batchDelete($path = null) {
238
+        $params = array(
239
+            'Bucket' => $this->bucket
240
+        );
241
+        if ($path !== null) {
242
+            $params['Prefix'] = $path . '/';
243
+        }
244
+        try {
245
+            $connection = $this->getConnection();
246
+            // Since there are no real directories on S3, we need
247
+            // to delete all objects prefixed with the path.
248
+            do {
249
+                // instead of the iterator, manually loop over the list ...
250
+                $objects = $connection->listObjects($params);
251
+                // ... so we can delete the files in batches
252
+                if (isset($objects['Contents'])) {
253
+                    $connection->deleteObjects([
254
+                        'Bucket' => $this->bucket,
255
+                        'Delete' => [
256
+                            'Objects' => $objects['Contents']
257
+                        ]
258
+                    ]);
259
+                    $this->testTimeout();
260
+                }
261
+                // we reached the end when the list is no longer truncated
262
+            } while ($objects['IsTruncated']);
263
+        } catch (S3Exception $e) {
264
+            \OCP\Util::logException('files_external', $e);
265
+            return false;
266
+        }
267
+        return true;
268
+    }
269
+
270
+    public function opendir($path) {
271
+        $path = $this->normalizePath($path);
272
+
273
+        if ($this->isRoot($path)) {
274
+            $path = '';
275
+        } else {
276
+            $path .= '/';
277
+        }
278
+
279
+        try {
280
+            $files = array();
281
+            $results = $this->getConnection()->getPaginator('ListObjects', [
282
+                'Bucket' => $this->bucket,
283
+                'Delimiter' => '/',
284
+                'Prefix' => $path,
285
+            ]);
286
+
287
+            foreach ($results as $result) {
288
+                // sub folders
289
+                if (is_array($result['CommonPrefixes'])) {
290
+                    foreach ($result['CommonPrefixes'] as $prefix) {
291
+                        $files[] = substr(trim($prefix['Prefix'], '/'), strlen($path));
292
+                    }
293
+                }
294
+                foreach ($result['Contents'] as $object) {
295
+                    if (isset($object['Key']) && $object['Key'] === $path) {
296
+                        // it's the directory itself, skip
297
+                        continue;
298
+                    }
299
+                    $file = basename(
300
+                        isset($object['Key']) ? $object['Key'] : $object['Prefix']
301
+                    );
302
+                    $files[] = $file;
303
+                }
304
+            }
305
+
306
+            return IteratorDirectory::wrap($files);
307
+        } catch (S3Exception $e) {
308
+            \OCP\Util::logException('files_external', $e);
309
+            return false;
310
+        }
311
+    }
312
+
313
+    public function stat($path) {
314
+        $path = $this->normalizePath($path);
315
+
316
+        try {
317
+            $stat = array();
318
+            if ($this->is_dir($path)) {
319
+                //folders don't really exist
320
+                $stat['size'] = -1; //unknown
321
+                $stat['mtime'] = time() - $this->rescanDelay * 1000;
322
+            } else {
323
+                $result = $this->headObject($path);
324
+
325
+                $stat['size'] = $result['ContentLength'] ? $result['ContentLength'] : 0;
326
+                if (isset($result['Metadata']['lastmodified'])) {
327
+                    $stat['mtime'] = strtotime($result['Metadata']['lastmodified']);
328
+                } else {
329
+                    $stat['mtime'] = strtotime($result['LastModified']);
330
+                }
331
+            }
332
+            $stat['atime'] = time();
333
+
334
+            return $stat;
335
+        } catch (S3Exception $e) {
336
+            \OCP\Util::logException('files_external', $e);
337
+            return false;
338
+        }
339
+    }
340
+
341
+    public function is_dir($path) {
342
+        $path = $this->normalizePath($path);
343
+        try {
344
+            return $this->isRoot($path) || $this->headObject($path . '/');
345
+        } catch (S3Exception $e) {
346
+            \OCP\Util::logException('files_external', $e);
347
+            return false;
348
+        }
349
+    }
350
+
351
+    public function filetype($path) {
352
+        $path = $this->normalizePath($path);
353
+
354
+        if ($this->isRoot($path)) {
355
+            return 'dir';
356
+        }
357
+
358
+        try {
359
+            if ($this->headObject($path)) {
360
+                return 'file';
361
+            }
362
+            if ($this->headObject($path . '/')) {
363
+                return 'dir';
364
+            }
365
+        } catch (S3Exception $e) {
366
+            \OCP\Util::logException('files_external', $e);
367
+            return false;
368
+        }
369
+
370
+        return false;
371
+    }
372
+
373
+    public function getPermissions($path) {
374
+        $type = $this->filetype($path);
375
+        if (!$type) {
376
+            return 0;
377
+        }
378
+        return $type === 'dir' ? Constants::PERMISSION_ALL : Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
379
+    }
380
+
381
+    public function unlink($path) {
382
+        $path = $this->normalizePath($path);
383
+
384
+        if ($this->is_dir($path)) {
385
+            return $this->rmdir($path);
386
+        }
387
+
388
+        try {
389
+            $this->deleteObject($path);
390
+            $this->invalidateCache($path);
391
+        } catch (S3Exception $e) {
392
+            \OCP\Util::logException('files_external', $e);
393
+            return false;
394
+        }
395
+
396
+        return true;
397
+    }
398
+
399
+    public function fopen($path, $mode) {
400
+        $path = $this->normalizePath($path);
401
+
402
+        switch ($mode) {
403
+            case 'r':
404
+            case 'rb':
405
+                try {
406
+                    return $this->readObject($path);
407
+                } catch (S3Exception $e) {
408
+                    \OCP\Util::logException('files_external', $e);
409
+                    return false;
410
+                }
411
+            case 'w':
412
+            case 'wb':
413
+                $tmpFile = \OCP\Files::tmpFile();
414
+
415
+                $handle = fopen($tmpFile, 'w');
416
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
417
+                    $this->writeBack($tmpFile, $path);
418
+                });
419
+            case 'a':
420
+            case 'ab':
421
+            case 'r+':
422
+            case 'w+':
423
+            case 'wb+':
424
+            case 'a+':
425
+            case 'x':
426
+            case 'x+':
427
+            case 'c':
428
+            case 'c+':
429
+                if (strrpos($path, '.') !== false) {
430
+                    $ext = substr($path, strrpos($path, '.'));
431
+                } else {
432
+                    $ext = '';
433
+                }
434
+                $tmpFile = \OCP\Files::tmpFile($ext);
435
+                if ($this->file_exists($path)) {
436
+                    $source = $this->readObject($path);
437
+                    file_put_contents($tmpFile, $source);
438
+                }
439
+
440
+                $handle = fopen($tmpFile, $mode);
441
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
442
+                    $this->writeBack($tmpFile, $path);
443
+                });
444
+        }
445
+        return false;
446
+    }
447
+
448
+    public function touch($path, $mtime = null) {
449
+        $path = $this->normalizePath($path);
450
+
451
+        $metadata = array();
452
+        if (is_null($mtime)) {
453
+            $mtime = time();
454
+        }
455
+        $metadata = [
456
+            'lastmodified' => gmdate(\DateTime::RFC1123, $mtime)
457
+        ];
458
+
459
+        $fileType = $this->filetype($path);
460
+        try {
461
+            if ($fileType !== false) {
462
+                if ($fileType === 'dir' && !$this->isRoot($path)) {
463
+                    $path .= '/';
464
+                }
465
+                $this->getConnection()->copyObject([
466
+                    'Bucket' => $this->bucket,
467
+                    'Key' => $this->cleanKey($path),
468
+                    'Metadata' => $metadata,
469
+                    'CopySource' => $this->bucket . '/' . $path,
470
+                    'MetadataDirective' => 'REPLACE',
471
+                ]);
472
+                $this->testTimeout();
473
+            } else {
474
+                $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
475
+                $this->getConnection()->putObject([
476
+                    'Bucket' => $this->bucket,
477
+                    'Key' => $this->cleanKey($path),
478
+                    'Metadata' => $metadata,
479
+                    'Body' => '',
480
+                    'ContentType' => $mimeType,
481
+                    'MetadataDirective' => 'REPLACE',
482
+                ]);
483
+                $this->testTimeout();
484
+            }
485
+        } catch (S3Exception $e) {
486
+            \OCP\Util::logException('files_external', $e);
487
+            return false;
488
+        }
489
+
490
+        $this->invalidateCache($path);
491
+        return true;
492
+    }
493
+
494
+    public function copy($path1, $path2) {
495
+        $path1 = $this->normalizePath($path1);
496
+        $path2 = $this->normalizePath($path2);
497
+
498
+        if ($this->is_file($path1)) {
499
+            try {
500
+                $this->getConnection()->copyObject(array(
501
+                    'Bucket' => $this->bucket,
502
+                    'Key' => $this->cleanKey($path2),
503
+                    'CopySource' => S3Client::encodeKey($this->bucket . '/' . $path1)
504
+                ));
505
+                $this->testTimeout();
506
+            } catch (S3Exception $e) {
507
+                \OCP\Util::logException('files_external', $e);
508
+                return false;
509
+            }
510
+        } else {
511
+            $this->remove($path2);
512
+
513
+            try {
514
+                $this->getConnection()->copyObject(array(
515
+                    'Bucket' => $this->bucket,
516
+                    'Key' => $path2 . '/',
517
+                    'CopySource' => S3Client::encodeKey($this->bucket . '/' . $path1 . '/')
518
+                ));
519
+                $this->testTimeout();
520
+            } catch (S3Exception $e) {
521
+                \OCP\Util::logException('files_external', $e);
522
+                return false;
523
+            }
524
+
525
+            $dh = $this->opendir($path1);
526
+            if (is_resource($dh)) {
527
+                while (($file = readdir($dh)) !== false) {
528
+                    if (\OC\Files\Filesystem::isIgnoredDir($file)) {
529
+                        continue;
530
+                    }
531
+
532
+                    $source = $path1 . '/' . $file;
533
+                    $target = $path2 . '/' . $file;
534
+                    $this->copy($source, $target);
535
+                }
536
+            }
537
+        }
538
+
539
+        $this->invalidateCache($path2);
540
+
541
+        return true;
542
+    }
543
+
544
+    public function rename($path1, $path2) {
545
+        $path1 = $this->normalizePath($path1);
546
+        $path2 = $this->normalizePath($path2);
547
+
548
+        if ($this->is_file($path1)) {
549
+
550
+            if ($this->copy($path1, $path2) === false) {
551
+                return false;
552
+            }
553
+
554
+            if ($this->unlink($path1) === false) {
555
+                $this->unlink($path2);
556
+                return false;
557
+            }
558
+        } else {
559
+
560
+            if ($this->copy($path1, $path2) === false) {
561
+                return false;
562
+            }
563
+
564
+            if ($this->rmdir($path1) === false) {
565
+                $this->rmdir($path2);
566
+                return false;
567
+            }
568
+        }
569
+
570
+        return true;
571
+    }
572
+
573
+    public function test() {
574
+        $test = $this->getConnection()->getBucketAcl(array(
575
+            'Bucket' => $this->bucket,
576
+        ));
577
+        if (isset($test) && !is_null($test->getPath('Owner/ID'))) {
578
+            return true;
579
+        }
580
+        return false;
581
+    }
582
+
583
+    public function getId() {
584
+        return $this->id;
585
+    }
586
+
587
+    public function writeBack($tmpFile, $path) {
588
+        try {
589
+            $source = fopen($tmpFile, 'r');
590
+            $this->writeObject($path, $source);
591
+            $this->invalidateCache($path);
592
+            fclose($source);
593
+
594
+            unlink($tmpFile);
595
+            return true;
596
+        } catch (S3Exception $e) {
597
+            \OCP\Util::logException('files_external', $e);
598
+            return false;
599
+        }
600
+    }
601
+
602
+    /**
603
+     * check if curl is installed
604
+     */
605
+    public static function checkDependencies() {
606
+        return true;
607
+    }
608 608
 
609 609
 }
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -136,7 +136,7 @@  discard block
 block discarded – undo
136 136
 	 * @param array $params
137 137
 	 */
138 138
 	public function updateLegacyId(array $params) {
139
-		$oldId = 'amazon::' . $params['key'] . md5($params['secret']);
139
+		$oldId = 'amazon::'.$params['key'].md5($params['secret']);
140 140
 
141 141
 		// find by old id or bucket
142 142
 		$stmt = \OC::$server->getDatabaseConnection()->prepare(
@@ -188,7 +188,7 @@  discard block
 block discarded – undo
188 188
 		try {
189 189
 			$this->getConnection()->putObject(array(
190 190
 				'Bucket' => $this->bucket,
191
-				'Key' => $path . '/',
191
+				'Key' => $path.'/',
192 192
 				'Body' => '',
193 193
 				'ContentType' => 'httpd/unix-directory'
194 194
 			));
@@ -239,7 +239,7 @@  discard block
 block discarded – undo
239 239
 			'Bucket' => $this->bucket
240 240
 		);
241 241
 		if ($path !== null) {
242
-			$params['Prefix'] = $path . '/';
242
+			$params['Prefix'] = $path.'/';
243 243
 		}
244 244
 		try {
245 245
 			$connection = $this->getConnection();
@@ -341,7 +341,7 @@  discard block
 block discarded – undo
341 341
 	public function is_dir($path) {
342 342
 		$path = $this->normalizePath($path);
343 343
 		try {
344
-			return $this->isRoot($path) || $this->headObject($path . '/');
344
+			return $this->isRoot($path) || $this->headObject($path.'/');
345 345
 		} catch (S3Exception $e) {
346 346
 			\OCP\Util::logException('files_external', $e);
347 347
 			return false;
@@ -359,7 +359,7 @@  discard block
 block discarded – undo
359 359
 			if ($this->headObject($path)) {
360 360
 				return 'file';
361 361
 			}
362
-			if ($this->headObject($path . '/')) {
362
+			if ($this->headObject($path.'/')) {
363 363
 				return 'dir';
364 364
 			}
365 365
 		} catch (S3Exception $e) {
@@ -413,7 +413,7 @@  discard block
 block discarded – undo
413 413
 				$tmpFile = \OCP\Files::tmpFile();
414 414
 
415 415
 				$handle = fopen($tmpFile, 'w');
416
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
416
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
417 417
 					$this->writeBack($tmpFile, $path);
418 418
 				});
419 419
 			case 'a':
@@ -438,7 +438,7 @@  discard block
 block discarded – undo
438 438
 				}
439 439
 
440 440
 				$handle = fopen($tmpFile, $mode);
441
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
441
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
442 442
 					$this->writeBack($tmpFile, $path);
443 443
 				});
444 444
 		}
@@ -466,7 +466,7 @@  discard block
 block discarded – undo
466 466
 					'Bucket' => $this->bucket,
467 467
 					'Key' => $this->cleanKey($path),
468 468
 					'Metadata' => $metadata,
469
-					'CopySource' => $this->bucket . '/' . $path,
469
+					'CopySource' => $this->bucket.'/'.$path,
470 470
 					'MetadataDirective' => 'REPLACE',
471 471
 				]);
472 472
 				$this->testTimeout();
@@ -500,7 +500,7 @@  discard block
 block discarded – undo
500 500
 				$this->getConnection()->copyObject(array(
501 501
 					'Bucket' => $this->bucket,
502 502
 					'Key' => $this->cleanKey($path2),
503
-					'CopySource' => S3Client::encodeKey($this->bucket . '/' . $path1)
503
+					'CopySource' => S3Client::encodeKey($this->bucket.'/'.$path1)
504 504
 				));
505 505
 				$this->testTimeout();
506 506
 			} catch (S3Exception $e) {
@@ -513,8 +513,8 @@  discard block
 block discarded – undo
513 513
 			try {
514 514
 				$this->getConnection()->copyObject(array(
515 515
 					'Bucket' => $this->bucket,
516
-					'Key' => $path2 . '/',
517
-					'CopySource' => S3Client::encodeKey($this->bucket . '/' . $path1 . '/')
516
+					'Key' => $path2.'/',
517
+					'CopySource' => S3Client::encodeKey($this->bucket.'/'.$path1.'/')
518 518
 				));
519 519
 				$this->testTimeout();
520 520
 			} catch (S3Exception $e) {
@@ -529,8 +529,8 @@  discard block
 block discarded – undo
529 529
 						continue;
530 530
 					}
531 531
 
532
-					$source = $path1 . '/' . $file;
533
-					$target = $path2 . '/' . $file;
532
+					$source = $path1.'/'.$file;
533
+					$target = $path2.'/'.$file;
534 534
 					$this->copy($source, $target);
535 535
 				}
536 536
 			}
Please login to merge, or discard this patch.