1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @author André Gaul <[email protected]> |
4
|
|
|
* @author Arthur Schiwon <[email protected]> |
5
|
|
|
* @author Bart Visscher <[email protected]> |
6
|
|
|
* @author Christian Berendt <[email protected]> |
7
|
|
|
* @author Christopher T. Johnson <[email protected]> |
8
|
|
|
* @author Johan Björk <[email protected]> |
9
|
|
|
* @author Jörn Friedrich Dreyer <[email protected]> |
10
|
|
|
* @author Martin Mattel <[email protected]> |
11
|
|
|
* @author Michael Gapczynski <[email protected]> |
12
|
|
|
* @author Morris Jobke <[email protected]> |
13
|
|
|
* @author Philipp Kapfer <[email protected]> |
14
|
|
|
* @author Robin Appelman <[email protected]> |
15
|
|
|
* @author Robin McCorkell <[email protected]> |
16
|
|
|
* @author Thomas Müller <[email protected]> |
17
|
|
|
* @author Vincent Petry <[email protected]> |
18
|
|
|
* |
19
|
|
|
* @copyright Copyright (c) 2016, ownCloud, Inc. |
20
|
|
|
* @license AGPL-3.0 |
21
|
|
|
* |
22
|
|
|
* This code is free software: you can redistribute it and/or modify |
23
|
|
|
* it under the terms of the GNU Affero General Public License, version 3, |
24
|
|
|
* as published by the Free Software Foundation. |
25
|
|
|
* |
26
|
|
|
* This program is distributed in the hope that it will be useful, |
27
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
28
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
29
|
|
|
* GNU Affero General Public License for more details. |
30
|
|
|
* |
31
|
|
|
* You should have received a copy of the GNU Affero General Public License, version 3, |
32
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/> |
33
|
|
|
* |
34
|
|
|
*/ |
35
|
|
|
|
36
|
|
|
namespace OCA\Files_External\Lib\Storage; |
37
|
|
|
|
38
|
|
|
set_include_path(get_include_path() . PATH_SEPARATOR . |
39
|
|
|
\OC_App::getAppPath('files_external') . '/3rdparty/aws-sdk-php'); |
40
|
|
|
require 'aws-autoloader.php'; |
41
|
|
|
|
42
|
|
|
use Aws\S3\S3Client; |
43
|
|
|
use Aws\S3\Exception\S3Exception; |
44
|
|
|
use Icewind\Streams\IteratorDirectory; |
45
|
|
|
|
46
|
|
|
class AmazonS3 extends \OC\Files\Storage\Common { |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var \Aws\S3\S3Client |
50
|
|
|
*/ |
51
|
|
|
private $connection; |
52
|
|
|
/** |
53
|
|
|
* @var string |
54
|
|
|
*/ |
55
|
|
|
private $bucket; |
56
|
|
|
/** |
57
|
|
|
* @var array |
58
|
|
|
*/ |
59
|
|
|
private static $tmpFiles = array(); |
60
|
|
|
/** |
61
|
|
|
* @var array |
62
|
|
|
*/ |
63
|
|
|
private $params; |
64
|
|
|
/** |
65
|
|
|
* @var bool |
66
|
|
|
*/ |
67
|
|
|
private $test = false; |
68
|
|
|
/** |
69
|
|
|
* @var int |
70
|
|
|
*/ |
71
|
|
|
private $timeout = 15; |
72
|
|
|
/** |
73
|
|
|
* @var int in seconds |
74
|
|
|
*/ |
75
|
|
|
private $rescanDelay = 10; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* @param string $path |
79
|
|
|
* @return string correctly encoded path |
80
|
|
|
*/ |
81
|
|
|
private function normalizePath($path) { |
82
|
|
|
$path = trim($path, '/'); |
83
|
|
|
|
84
|
|
|
if (!$path) { |
85
|
|
|
$path = '.'; |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
return $path; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* when running the tests wait to let the buckets catch up |
93
|
|
|
*/ |
94
|
|
|
private function testTimeout() { |
95
|
|
|
if ($this->test) { |
96
|
|
|
sleep($this->timeout); |
97
|
|
|
} |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
private function isRoot($path) { |
101
|
|
|
return $path === '.'; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
private function cleanKey($path) { |
105
|
|
|
if ($this->isRoot($path)) { |
106
|
|
|
return '/'; |
107
|
|
|
} |
108
|
|
|
return $path; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
public function __construct($params) { |
112
|
|
|
if (empty($params['key']) || empty($params['secret']) || empty($params['bucket'])) { |
113
|
|
|
throw new \Exception("Access Key, Secret and Bucket have to be configured."); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
$this->id = 'amazon::' . $params['bucket']; |
|
|
|
|
117
|
|
|
$this->updateLegacyId($params); |
118
|
|
|
|
119
|
|
|
$this->bucket = $params['bucket']; |
120
|
|
|
$this->test = isset($params['test']); |
121
|
|
|
$this->timeout = (!isset($params['timeout'])) ? 15 : $params['timeout']; |
122
|
|
|
$this->rescanDelay = (!isset($params['rescanDelay'])) ? 10 : $params['rescanDelay']; |
123
|
|
|
$params['region'] = empty($params['region']) ? 'eu-west-1' : $params['region']; |
124
|
|
|
$params['hostname'] = empty($params['hostname']) ? 's3.amazonaws.com' : $params['hostname']; |
125
|
|
|
if (!isset($params['port']) || $params['port'] === '') { |
126
|
|
|
$params['port'] = ($params['use_ssl'] === false) ? 80 : 443; |
127
|
|
|
} |
128
|
|
|
$this->params = $params; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Updates old storage ids (v0.2.1 and older) that are based on key and secret to new ones based on the bucket name. |
133
|
|
|
* TODO Do this in an update.php. requires iterating over all users and loading the mount.json from their home |
134
|
|
|
* |
135
|
|
|
* @param array $params |
136
|
|
|
*/ |
137
|
|
|
public function updateLegacyId (array $params) { |
138
|
|
|
$oldId = 'amazon::' . $params['key'] . md5($params['secret']); |
139
|
|
|
|
140
|
|
|
// find by old id or bucket |
141
|
|
|
$stmt = \OC::$server->getDatabaseConnection()->prepare( |
142
|
|
|
'SELECT `numeric_id`, `id` FROM `*PREFIX*storages` WHERE `id` IN (?, ?)' |
143
|
|
|
); |
144
|
|
|
$stmt->execute(array($oldId, $this->id)); |
145
|
|
|
while ($row = $stmt->fetch()) { |
146
|
|
|
$storages[$row['id']] = $row['numeric_id']; |
|
|
|
|
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
if (isset($storages[$this->id]) && isset($storages[$oldId])) { |
150
|
|
|
// if both ids exist, delete the old storage and corresponding filecache entries |
151
|
|
|
\OC\Files\Cache\Storage::remove($oldId); |
152
|
|
|
} else if (isset($storages[$oldId])) { |
153
|
|
|
// if only the old id exists do an update |
154
|
|
|
$stmt = \OC::$server->getDatabaseConnection()->prepare( |
155
|
|
|
'UPDATE `*PREFIX*storages` SET `id` = ? WHERE `id` = ?' |
156
|
|
|
); |
157
|
|
|
$stmt->execute(array($this->id, $oldId)); |
158
|
|
|
} |
159
|
|
|
// only the bucket based id may exist, do nothing |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* Remove a file or folder |
164
|
|
|
* |
165
|
|
|
* @param string $path |
166
|
|
|
* @return bool |
167
|
|
|
*/ |
168
|
|
|
protected function remove($path) { |
169
|
|
|
// remember fileType to reduce http calls |
170
|
|
|
$fileType = $this->filetype($path); |
171
|
|
|
if ($fileType === 'dir') { |
172
|
|
|
return $this->rmdir($path); |
173
|
|
|
} else if ($fileType === 'file') { |
174
|
|
|
return $this->unlink($path); |
175
|
|
|
} else { |
176
|
|
|
return false; |
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
public function mkdir($path) { |
181
|
|
|
$path = $this->normalizePath($path); |
182
|
|
|
|
183
|
|
|
if ($this->is_dir($path)) { |
184
|
|
|
return false; |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
try { |
188
|
|
|
$this->getConnection()->putObject(array( |
189
|
|
|
'Bucket' => $this->bucket, |
190
|
|
|
'Key' => $path . '/', |
191
|
|
|
'Body' => '', |
192
|
|
|
'ContentType' => 'httpd/unix-directory' |
193
|
|
|
)); |
194
|
|
|
$this->testTimeout(); |
195
|
|
|
} catch (S3Exception $e) { |
|
|
|
|
196
|
|
|
\OCP\Util::logException('files_external', $e); |
197
|
|
|
return false; |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
return true; |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
public function file_exists($path) { |
204
|
|
|
return $this->filetype($path) !== false; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
|
208
|
|
|
public function rmdir($path) { |
209
|
|
|
$path = $this->normalizePath($path); |
210
|
|
|
|
211
|
|
|
if ($this->isRoot($path)) { |
212
|
|
|
return $this->clearBucket(); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
if (!$this->file_exists($path)) { |
216
|
|
|
return false; |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
return $this->batchDelete($path); |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
protected function clearBucket() { |
223
|
|
|
try { |
224
|
|
|
$this->getConnection()->clearBucket($this->bucket); |
225
|
|
|
return true; |
226
|
|
|
// clearBucket() is not working with Ceph, so if it fails we try the slower approach |
227
|
|
|
} catch (\Exception $e) { |
228
|
|
|
return $this->batchDelete(); |
229
|
|
|
} |
230
|
|
|
return false; |
|
|
|
|
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
private function batchDelete ($path = null) { |
234
|
|
|
$params = array( |
235
|
|
|
'Bucket' => $this->bucket |
236
|
|
|
); |
237
|
|
|
if ($path !== null) { |
238
|
|
|
$params['Prefix'] = $path . '/'; |
239
|
|
|
} |
240
|
|
|
try { |
241
|
|
|
// Since there are no real directories on S3, we need |
242
|
|
|
// to delete all objects prefixed with the path. |
243
|
|
|
do { |
244
|
|
|
// instead of the iterator, manually loop over the list ... |
245
|
|
|
$objects = $this->getConnection()->listObjects($params); |
246
|
|
|
// ... so we can delete the files in batches |
247
|
|
|
$this->getConnection()->deleteObjects(array( |
248
|
|
|
'Bucket' => $this->bucket, |
249
|
|
|
'Objects' => $objects['Contents'] |
250
|
|
|
)); |
251
|
|
|
$this->testTimeout(); |
252
|
|
|
// we reached the end when the list is no longer truncated |
253
|
|
|
} while ($objects['IsTruncated']); |
254
|
|
|
} catch (S3Exception $e) { |
|
|
|
|
255
|
|
|
\OCP\Util::logException('files_external', $e); |
256
|
|
|
return false; |
257
|
|
|
} |
258
|
|
|
return true; |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
public function opendir($path) { |
262
|
|
|
$path = $this->normalizePath($path); |
263
|
|
|
|
264
|
|
|
if ($this->isRoot($path)) { |
265
|
|
|
$path = ''; |
266
|
|
|
} else { |
267
|
|
|
$path .= '/'; |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
try { |
271
|
|
|
$files = array(); |
272
|
|
|
$result = $this->getConnection()->getIterator('ListObjects', array( |
273
|
|
|
'Bucket' => $this->bucket, |
274
|
|
|
'Delimiter' => '/', |
275
|
|
|
'Prefix' => $path |
276
|
|
|
), array('return_prefixes' => true)); |
277
|
|
|
|
278
|
|
|
foreach ($result as $object) { |
279
|
|
|
if (isset($object['Key']) && $object['Key'] === $path) { |
280
|
|
|
// it's the directory itself, skip |
281
|
|
|
continue; |
282
|
|
|
} |
283
|
|
|
$file = basename( |
284
|
|
|
isset($object['Key']) ? $object['Key'] : $object['Prefix'] |
285
|
|
|
); |
286
|
|
|
$files[] = $file; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
return IteratorDirectory::wrap($files); |
290
|
|
|
} catch (S3Exception $e) { |
|
|
|
|
291
|
|
|
\OCP\Util::logException('files_external', $e); |
292
|
|
|
return false; |
293
|
|
|
} |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
public function stat($path) { |
297
|
|
|
$path = $this->normalizePath($path); |
298
|
|
|
|
299
|
|
|
try { |
300
|
|
|
$stat = array(); |
301
|
|
|
if ($this->is_dir($path)) { |
302
|
|
|
//folders don't really exist |
303
|
|
|
$stat['size'] = -1; //unknown |
304
|
|
|
$stat['mtime'] = time() - $this->rescanDelay * 1000; |
305
|
|
|
} else { |
306
|
|
|
$result = $this->getConnection()->headObject(array( |
307
|
|
|
'Bucket' => $this->bucket, |
308
|
|
|
'Key' => $path |
309
|
|
|
)); |
310
|
|
|
|
311
|
|
|
$stat['size'] = $result['ContentLength'] ? $result['ContentLength'] : 0; |
312
|
|
|
if ($result['Metadata']['lastmodified']) { |
313
|
|
|
$stat['mtime'] = strtotime($result['Metadata']['lastmodified']); |
314
|
|
|
} else { |
315
|
|
|
$stat['mtime'] = strtotime($result['LastModified']); |
316
|
|
|
} |
317
|
|
|
} |
318
|
|
|
$stat['atime'] = time(); |
319
|
|
|
|
320
|
|
|
return $stat; |
321
|
|
|
} catch(S3Exception $e) { |
|
|
|
|
322
|
|
|
\OCP\Util::logException('files_external', $e); |
323
|
|
|
return false; |
324
|
|
|
} |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
public function filetype($path) { |
328
|
|
|
$path = $this->normalizePath($path); |
329
|
|
|
|
330
|
|
|
if ($this->isRoot($path)) { |
331
|
|
|
return 'dir'; |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
try { |
335
|
|
|
if ($this->getConnection()->doesObjectExist($this->bucket, $path)) { |
336
|
|
|
return 'file'; |
337
|
|
|
} |
338
|
|
|
if ($this->getConnection()->doesObjectExist($this->bucket, $path.'/')) { |
339
|
|
|
return 'dir'; |
340
|
|
|
} |
341
|
|
|
} catch (S3Exception $e) { |
|
|
|
|
342
|
|
|
\OCP\Util::logException('files_external', $e); |
343
|
|
|
return false; |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
return false; |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
public function unlink($path) { |
350
|
|
|
$path = $this->normalizePath($path); |
351
|
|
|
|
352
|
|
|
if ($this->is_dir($path)) { |
353
|
|
|
return $this->rmdir($path); |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
try { |
357
|
|
|
$this->getConnection()->deleteObject(array( |
358
|
|
|
'Bucket' => $this->bucket, |
359
|
|
|
'Key' => $path |
360
|
|
|
)); |
361
|
|
|
$this->testTimeout(); |
362
|
|
|
} catch (S3Exception $e) { |
|
|
|
|
363
|
|
|
\OCP\Util::logException('files_external', $e); |
364
|
|
|
return false; |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
return true; |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
public function fopen($path, $mode) { |
371
|
|
|
$path = $this->normalizePath($path); |
372
|
|
|
|
373
|
|
|
switch ($mode) { |
374
|
|
|
case 'r': |
375
|
|
|
case 'rb': |
376
|
|
|
$tmpFile = \OCP\Files::tmpFile(); |
377
|
|
|
self::$tmpFiles[$tmpFile] = $path; |
378
|
|
|
|
379
|
|
|
try { |
380
|
|
|
$this->getConnection()->getObject(array( |
381
|
|
|
'Bucket' => $this->bucket, |
382
|
|
|
'Key' => $path, |
383
|
|
|
'SaveAs' => $tmpFile |
384
|
|
|
)); |
385
|
|
|
} catch (S3Exception $e) { |
|
|
|
|
386
|
|
|
\OCP\Util::logException('files_external', $e); |
387
|
|
|
return false; |
388
|
|
|
} |
389
|
|
|
|
390
|
|
|
return fopen($tmpFile, 'r'); |
391
|
|
|
case 'w': |
392
|
|
|
case 'wb': |
393
|
|
|
case 'a': |
394
|
|
|
case 'ab': |
395
|
|
|
case 'r+': |
396
|
|
|
case 'w+': |
397
|
|
|
case 'wb+': |
398
|
|
|
case 'a+': |
399
|
|
|
case 'x': |
400
|
|
|
case 'x+': |
401
|
|
|
case 'c': |
402
|
|
View Code Duplication |
case 'c+': |
|
|
|
|
403
|
|
|
if (strrpos($path, '.') !== false) { |
404
|
|
|
$ext = substr($path, strrpos($path, '.')); |
405
|
|
|
} else { |
406
|
|
|
$ext = ''; |
407
|
|
|
} |
408
|
|
|
$tmpFile = \OCP\Files::tmpFile($ext); |
409
|
|
|
\OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); |
410
|
|
|
if ($this->file_exists($path)) { |
411
|
|
|
$source = $this->fopen($path, 'r'); |
412
|
|
|
file_put_contents($tmpFile, $source); |
413
|
|
|
} |
414
|
|
|
self::$tmpFiles[$tmpFile] = $path; |
415
|
|
|
|
416
|
|
|
return fopen('close://' . $tmpFile, $mode); |
417
|
|
|
} |
418
|
|
|
return false; |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
public function touch($path, $mtime = null) { |
422
|
|
|
$path = $this->normalizePath($path); |
423
|
|
|
|
424
|
|
|
$metadata = array(); |
|
|
|
|
425
|
|
|
if (is_null($mtime)) { |
426
|
|
|
$mtime = time(); |
427
|
|
|
} |
428
|
|
|
$metadata = [ |
429
|
|
|
'lastmodified' => gmdate(\Aws\Common\Enum\DateFormat::RFC1123, $mtime) |
430
|
|
|
]; |
431
|
|
|
|
432
|
|
|
$fileType = $this->filetype($path); |
433
|
|
|
try { |
434
|
|
|
if ($fileType !== false) { |
435
|
|
|
if ($fileType === 'dir' && ! $this->isRoot($path)) { |
436
|
|
|
$path .= '/'; |
437
|
|
|
} |
438
|
|
|
$this->getConnection()->copyObject([ |
439
|
|
|
'Bucket' => $this->bucket, |
440
|
|
|
'Key' => $this->cleanKey($path), |
441
|
|
|
'Metadata' => $metadata, |
442
|
|
|
'CopySource' => $this->bucket . '/' . $path, |
443
|
|
|
'MetadataDirective' => 'REPLACE', |
444
|
|
|
]); |
445
|
|
|
$this->testTimeout(); |
446
|
|
|
} else { |
447
|
|
|
$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path); |
448
|
|
|
$this->getConnection()->putObject([ |
449
|
|
|
'Bucket' => $this->bucket, |
450
|
|
|
'Key' => $this->cleanKey($path), |
451
|
|
|
'Metadata' => $metadata, |
452
|
|
|
'Body' => '', |
453
|
|
|
'ContentType' => $mimeType, |
454
|
|
|
'MetadataDirective' => 'REPLACE', |
455
|
|
|
]); |
456
|
|
|
$this->testTimeout(); |
457
|
|
|
} |
458
|
|
|
} catch (S3Exception $e) { |
|
|
|
|
459
|
|
|
\OCP\Util::logException('files_external', $e); |
460
|
|
|
return false; |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
return true; |
464
|
|
|
} |
465
|
|
|
|
466
|
|
|
public function copy($path1, $path2) { |
467
|
|
|
$path1 = $this->normalizePath($path1); |
468
|
|
|
$path2 = $this->normalizePath($path2); |
469
|
|
|
|
470
|
|
|
if ($this->is_file($path1)) { |
471
|
|
|
try { |
472
|
|
|
$this->getConnection()->copyObject(array( |
473
|
|
|
'Bucket' => $this->bucket, |
474
|
|
|
'Key' => $this->cleanKey($path2), |
475
|
|
|
'CopySource' => S3Client::encodeKey($this->bucket . '/' . $path1) |
476
|
|
|
)); |
477
|
|
|
$this->testTimeout(); |
478
|
|
|
} catch (S3Exception $e) { |
|
|
|
|
479
|
|
|
\OCP\Util::logException('files_external', $e); |
480
|
|
|
return false; |
481
|
|
|
} |
482
|
|
|
} else { |
483
|
|
|
$this->remove($path2); |
484
|
|
|
|
485
|
|
|
try { |
486
|
|
|
$this->getConnection()->copyObject(array( |
487
|
|
|
'Bucket' => $this->bucket, |
488
|
|
|
'Key' => $path2 . '/', |
489
|
|
|
'CopySource' => S3Client::encodeKey($this->bucket . '/' . $path1 . '/') |
490
|
|
|
)); |
491
|
|
|
$this->testTimeout(); |
492
|
|
|
} catch (S3Exception $e) { |
|
|
|
|
493
|
|
|
\OCP\Util::logException('files_external', $e); |
494
|
|
|
return false; |
495
|
|
|
} |
496
|
|
|
|
497
|
|
|
$dh = $this->opendir($path1); |
498
|
|
View Code Duplication |
if (is_resource($dh)) { |
|
|
|
|
499
|
|
|
while (($file = readdir($dh)) !== false) { |
500
|
|
|
if (\OC\Files\Filesystem::isIgnoredDir($file)) { |
501
|
|
|
continue; |
502
|
|
|
} |
503
|
|
|
|
504
|
|
|
$source = $path1 . '/' . $file; |
505
|
|
|
$target = $path2 . '/' . $file; |
506
|
|
|
$this->copy($source, $target); |
507
|
|
|
} |
508
|
|
|
} |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
return true; |
512
|
|
|
} |
513
|
|
|
|
514
|
|
|
public function rename($path1, $path2) { |
515
|
|
|
$path1 = $this->normalizePath($path1); |
516
|
|
|
$path2 = $this->normalizePath($path2); |
517
|
|
|
|
518
|
|
|
if ($this->is_file($path1)) { |
519
|
|
|
|
520
|
|
|
if ($this->copy($path1, $path2) === false) { |
521
|
|
|
return false; |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
if ($this->unlink($path1) === false) { |
525
|
|
|
$this->unlink($path2); |
526
|
|
|
return false; |
527
|
|
|
} |
528
|
|
|
} else { |
529
|
|
|
|
530
|
|
|
if ($this->copy($path1, $path2) === false) { |
531
|
|
|
return false; |
532
|
|
|
} |
533
|
|
|
|
534
|
|
|
if ($this->rmdir($path1) === false) { |
535
|
|
|
$this->rmdir($path2); |
536
|
|
|
return false; |
537
|
|
|
} |
538
|
|
|
} |
539
|
|
|
|
540
|
|
|
return true; |
541
|
|
|
} |
542
|
|
|
|
543
|
|
|
public function test() { |
544
|
|
|
$test = $this->getConnection()->getBucketAcl(array( |
545
|
|
|
'Bucket' => $this->bucket, |
546
|
|
|
)); |
547
|
|
|
if (isset($test) && !is_null($test->getPath('Owner/ID'))) { |
548
|
|
|
return true; |
549
|
|
|
} |
550
|
|
|
return false; |
551
|
|
|
} |
552
|
|
|
|
553
|
|
|
public function getId() { |
554
|
|
|
return $this->id; |
555
|
|
|
} |
556
|
|
|
|
557
|
|
|
/** |
558
|
|
|
* Returns the connection |
559
|
|
|
* |
560
|
|
|
* @return S3Client connected client |
561
|
|
|
* @throws \Exception if connection could not be made |
562
|
|
|
*/ |
563
|
|
|
public function getConnection() { |
564
|
|
|
if (!is_null($this->connection)) { |
565
|
|
|
return $this->connection; |
566
|
|
|
} |
567
|
|
|
|
568
|
|
|
$scheme = ($this->params['use_ssl'] === false) ? 'http' : 'https'; |
569
|
|
|
$base_url = $scheme . '://' . $this->params['hostname'] . ':' . $this->params['port'] . '/'; |
570
|
|
|
|
571
|
|
|
$this->connection = S3Client::factory(array( |
572
|
|
|
'key' => $this->params['key'], |
573
|
|
|
'secret' => $this->params['secret'], |
574
|
|
|
'base_url' => $base_url, |
575
|
|
|
'region' => $this->params['region'], |
576
|
|
|
S3Client::COMMAND_PARAMS => [ |
577
|
|
|
'PathStyle' => $this->params['use_path_style'], |
578
|
|
|
], |
579
|
|
|
)); |
580
|
|
|
|
581
|
|
|
if (!$this->connection->isValidBucketName($this->bucket)) { |
582
|
|
|
throw new \Exception("The configured bucket name is invalid."); |
583
|
|
|
} |
584
|
|
|
|
585
|
|
|
if (!$this->connection->doesBucketExist($this->bucket)) { |
586
|
|
|
try { |
587
|
|
|
$this->connection->createBucket(array( |
588
|
|
|
'Bucket' => $this->bucket |
589
|
|
|
)); |
590
|
|
|
$this->connection->waitUntilBucketExists(array( |
591
|
|
|
'Bucket' => $this->bucket, |
592
|
|
|
'waiter.interval' => 1, |
593
|
|
|
'waiter.max_attempts' => 15 |
594
|
|
|
)); |
595
|
|
|
$this->testTimeout(); |
596
|
|
|
} catch (S3Exception $e) { |
|
|
|
|
597
|
|
|
\OCP\Util::logException('files_external', $e); |
598
|
|
|
throw new \Exception('Creation of bucket failed. '.$e->getMessage()); |
599
|
|
|
} |
600
|
|
|
} |
601
|
|
|
|
602
|
|
|
return $this->connection; |
603
|
|
|
} |
604
|
|
|
|
605
|
|
|
public function writeBack($tmpFile) { |
606
|
|
|
if (!isset(self::$tmpFiles[$tmpFile])) { |
607
|
|
|
return false; |
608
|
|
|
} |
609
|
|
|
|
610
|
|
|
try { |
611
|
|
|
$this->getConnection()->putObject(array( |
612
|
|
|
'Bucket' => $this->bucket, |
613
|
|
|
'Key' => $this->cleanKey(self::$tmpFiles[$tmpFile]), |
614
|
|
|
'SourceFile' => $tmpFile, |
615
|
|
|
'ContentType' => \OC::$server->getMimeTypeDetector()->detect($tmpFile), |
616
|
|
|
'ContentLength' => filesize($tmpFile) |
617
|
|
|
)); |
618
|
|
|
$this->testTimeout(); |
619
|
|
|
|
620
|
|
|
unlink($tmpFile); |
621
|
|
|
} catch (S3Exception $e) { |
|
|
|
|
622
|
|
|
\OCP\Util::logException('files_external', $e); |
623
|
|
|
return false; |
624
|
|
|
} |
625
|
|
|
} |
626
|
|
|
|
627
|
|
|
/** |
628
|
|
|
* check if curl is installed |
629
|
|
|
*/ |
630
|
|
|
public static function checkDependencies() { |
631
|
|
|
return true; |
632
|
|
|
} |
633
|
|
|
|
634
|
|
|
} |
635
|
|
|
|
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: