2012-05-17 05:51:45 +04:00
< ? php
/**
2013-01-28 18:34:15 +04:00
* ownCloud
*
* @ author Michael Gapczynski
2013-07-03 20:01:14 +04:00
* @ author Christian Berendt
2013-01-28 18:34:15 +04:00
* @ copyright 2012 Michael Gapczynski mtgap @ owncloud . com
2013-07-03 20:01:14 +04:00
* @ copyright 2013 Christian Berendt berendt @ b1 - systems . de
2013-01-28 18:34:15 +04:00
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation ; either
* version 3 of the License , or any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details .
*
* You should have received a copy of the GNU Affero General Public
* License along with this library . If not , see < http :// www . gnu . org / licenses />.
*/
2012-05-17 05:51:45 +04:00
2012-09-07 20:30:48 +04:00
namespace OC\Files\Storage ;
2012-05-17 05:51:45 +04:00
2013-07-03 20:01:14 +04:00
set_include_path ( get_include_path () . PATH_SEPARATOR .
\OC_App :: getAppPath ( 'files_external' ) . '/3rdparty/aws-sdk-php' );
2013-07-08 13:11:07 +04:00
require 'aws-autoloader.php' ;
2013-07-03 20:01:14 +04:00
use Aws\S3\S3Client ;
use Aws\S3\Exception\S3Exception ;
2012-05-17 05:51:45 +04:00
2012-09-07 20:30:48 +04:00
class AmazonS3 extends \OC\Files\Storage\Common {
2012-05-17 05:51:45 +04:00
2013-08-01 12:05:01 +04:00
/**
* @ var \Aws\S3\S3Client
*/
2013-07-03 20:01:14 +04:00
private $connection ;
2013-08-01 12:05:01 +04:00
/**
* @ var string
*/
2012-05-17 05:51:45 +04:00
private $bucket ;
2013-08-01 12:05:01 +04:00
/**
* @ var array
*/
2013-07-03 20:01:14 +04:00
private static $tmpFiles = array ();
2013-08-01 12:05:01 +04:00
/**
* @ var bool
*/
2013-07-10 09:47:15 +04:00
private $test = false ;
2013-08-01 12:05:01 +04:00
/**
* @ var int
*/
2013-07-10 09:47:15 +04:00
private $timeout = 15 ;
2012-05-17 05:51:45 +04:00
2014-02-06 19:30:58 +04:00
/**
* @ param string $path
*/
2013-07-03 20:01:14 +04:00
private function normalizePath ( $path ) {
2013-07-08 13:14:47 +04:00
$path = trim ( $path , '/' );
2013-07-03 20:01:14 +04:00
2013-07-10 18:12:48 +04:00
if ( ! $path ) {
2013-07-03 20:01:14 +04:00
$path = '.' ;
}
return $path ;
}
2012-05-17 05:51:45 +04:00
2013-07-10 09:47:15 +04:00
private function testTimeout () {
if ( $this -> test ) {
sleep ( $this -> timeout );
}
}
2013-06-01 13:28:02 +04:00
/**
* check if curl is installed
*/
public static function checkDependencies () {
if ( function_exists ( 'curl_init' )) {
return true ;
} else {
$l = new \OC_L10N ( 'files_external' );
return $l -> t ( '<b>Note:</b> The cURL support in PHP is not enabled or installed. Mounting of Amazon S3 is not possible. Please ask your system administrator to install it.' );
}
}
2012-05-17 05:51:45 +04:00
public function __construct ( $params ) {
2013-07-10 18:12:48 +04:00
if ( ! isset ( $params [ 'key' ]) || ! isset ( $params [ 'secret' ]) || ! isset ( $params [ 'bucket' ])) {
2013-07-08 17:10:10 +04:00
throw new \Exception ( " Access Key, Secret and Bucket have to be configured. " );
2012-12-24 22:45:52 +04:00
}
2012-05-17 05:51:45 +04:00
2013-07-03 20:01:14 +04:00
$this -> id = 'amazon::' . $params [ 'key' ] . md5 ( $params [ 'secret' ]);
2013-07-10 09:47:15 +04:00
2013-07-03 20:01:14 +04:00
$this -> bucket = $params [ 'bucket' ];
$scheme = ( $params [ 'use_ssl' ] === 'false' ) ? 'http' : 'https' ;
2013-08-17 14:58:10 +04:00
$this -> test = isset ( $params [ 'test' ]);
2013-07-10 09:47:15 +04:00
$this -> timeout = ( ! isset ( $params [ 'timeout' ])) ? 15 : $params [ 'timeout' ];
2013-12-02 19:55:43 +04:00
$params [ 'region' ] = ( ! isset ( $params [ 'region' ]) || $params [ 'region' ] === '' ) ? 'eu-west-1' : $params [ 'region' ];
$params [ 'hostname' ] = ( ! isset ( $params [ 'hostname' ]) || $params [ 'hostname' ] === '' ) ? 's3.amazonaws.com' : $params [ 'hostname' ];
if ( ! isset ( $params [ 'port' ]) || $params [ 'port' ] === '' ) {
2013-07-10 09:47:15 +04:00
$params [ 'port' ] = ( $params [ 'use_ssl' ] === 'false' ) ? 80 : 443 ;
2012-05-17 05:51:45 +04:00
}
2013-07-10 09:47:15 +04:00
$base_url = $scheme . '://' . $params [ 'hostname' ] . ':' . $params [ 'port' ] . '/' ;
$this -> connection = S3Client :: factory ( array (
'key' => $params [ 'key' ],
'secret' => $params [ 'secret' ],
'base_url' => $base_url ,
'region' => $params [ 'region' ]
));
2012-05-17 05:51:45 +04:00
2013-07-10 18:12:48 +04:00
if ( ! $this -> connection -> isValidBucketName ( $this -> bucket )) {
2013-07-08 17:10:10 +04:00
throw new \Exception ( " The configured bucket name is invalid. " );
2013-07-08 16:59:09 +04:00
}
2013-07-10 18:12:48 +04:00
if ( ! $this -> connection -> doesBucketExist ( $this -> bucket )) {
2013-07-10 09:47:15 +04:00
try {
$result = $this -> connection -> createBucket ( array (
'Bucket' => $this -> bucket
));
$this -> connection -> waitUntilBucketExists ( array (
'Bucket' => $this -> bucket ,
'waiter.interval' => 1 ,
'waiter.max_attempts' => 15
));
$this -> testTimeout ();
} catch ( S3Exception $e ) {
2013-08-01 12:08:41 +04:00
\OCP\Util :: writeLog ( 'files_external' , $e -> getMessage (), \OCP\Util :: ERROR );
2013-07-10 09:47:15 +04:00
throw new \Exception ( " Creation of bucket failed. " );
2013-07-08 18:06:08 +04:00
}
2013-07-03 20:01:14 +04:00
}
2013-07-10 18:12:48 +04:00
if ( ! $this -> file_exists ( '.' )) {
2013-07-03 20:01:14 +04:00
$result = $this -> connection -> putObject ( array (
'Bucket' => $this -> bucket ,
'Key' => '.' ,
'Body' => '' ,
'ContentType' => 'httpd/unix-directory' ,
'ContentLength' => 0
));
2013-07-10 09:47:15 +04:00
$this -> testTimeout ();
2013-07-03 20:01:14 +04:00
}
2012-10-12 01:06:57 +04:00
}
2012-05-17 05:51:45 +04:00
public function mkdir ( $path ) {
2013-07-03 20:01:14 +04:00
$path = $this -> normalizePath ( $path );
2013-07-10 18:12:48 +04:00
if ( $this -> is_dir ( $path )) {
2013-07-03 20:01:14 +04:00
return false ;
}
try {
$result = $this -> connection -> putObject ( array (
'Bucket' => $this -> bucket ,
'Key' => $path . '/' ,
'Body' => '' ,
'ContentType' => 'httpd/unix-directory' ,
'ContentLength' => 0
));
2013-07-10 09:47:15 +04:00
$this -> testTimeout ();
2013-07-03 20:01:14 +04:00
} catch ( S3Exception $e ) {
2013-08-01 12:08:41 +04:00
\OCP\Util :: writeLog ( 'files_external' , $e -> getMessage (), \OCP\Util :: ERROR );
2013-07-03 20:01:14 +04:00
return false ;
}
return true ;
}
public function file_exists ( $path ) {
$path = $this -> normalizePath ( $path );
2013-07-10 18:12:48 +04:00
if ( ! $path ) {
2013-07-03 20:01:14 +04:00
$path = '.' ;
} else if ( $path != '.' && $this -> is_dir ( $path )) {
2012-05-17 05:51:45 +04:00
$path .= '/' ;
}
2013-07-03 20:01:14 +04:00
try {
$result = $this -> connection -> doesObjectExist (
$this -> bucket ,
$path
);
} catch ( S3Exception $e ) {
2013-08-01 12:08:41 +04:00
\OCP\Util :: writeLog ( 'files_external' , $e -> getMessage (), \OCP\Util :: ERROR );
2013-07-03 20:01:14 +04:00
return false ;
}
return $result ;
2012-05-17 05:51:45 +04:00
}
2013-07-03 20:01:14 +04:00
2012-05-17 05:51:45 +04:00
public function rmdir ( $path ) {
2013-07-03 20:01:14 +04:00
$path = $this -> normalizePath ( $path );
2013-07-10 18:12:48 +04:00
if ( ! $this -> file_exists ( $path )) {
2013-07-03 20:01:14 +04:00
return false ;
}
$dh = $this -> opendir ( $path );
2013-09-04 15:06:04 +04:00
if ( is_resource ( $dh )) {
while (( $file = readdir ( $dh )) !== false ) {
if ( $file === '.' || $file === '..' ) {
continue ;
}
if ( $this -> is_dir ( $path . '/' . $file )) {
$this -> rmdir ( $path . '/' . $file );
} else {
$this -> unlink ( $path . '/' . $file );
}
2013-07-03 20:01:14 +04:00
}
2013-09-04 15:06:04 +04:00
}
2013-07-03 20:01:14 +04:00
try {
$result = $this -> connection -> deleteObject ( array (
'Bucket' => $this -> bucket ,
'Key' => $path . '/'
));
2013-07-10 09:47:15 +04:00
$this -> testTimeout ();
2013-07-03 20:01:14 +04:00
} catch ( S3Exception $e ) {
2013-08-01 12:08:41 +04:00
\OCP\Util :: writeLog ( 'files_external' , $e -> getMessage (), \OCP\Util :: ERROR );
2013-07-03 20:01:14 +04:00
return false ;
2012-05-17 05:51:45 +04:00
}
2013-07-03 20:01:14 +04:00
return true ;
2012-05-17 05:51:45 +04:00
}
public function opendir ( $path ) {
2013-07-03 20:01:14 +04:00
$path = $this -> normalizePath ( $path );
2013-07-10 18:09:22 +04:00
if ( $path === '.' ) {
2013-07-03 20:01:14 +04:00
$path = '' ;
} else if ( $path ) {
$path .= '/' ;
2012-05-17 05:51:45 +04:00
}
2013-07-03 20:01:14 +04:00
try {
2012-05-17 05:51:45 +04:00
$files = array ();
2013-07-03 20:01:14 +04:00
$result = $this -> connection -> getIterator ( 'ListObjects' , array (
'Bucket' => $this -> bucket ,
'Delimiter' => '/' ,
'Prefix' => $path
), array ( 'return_prefixes' => true ));
foreach ( $result as $object ) {
$file = basename (
isset ( $object [ 'Key' ]) ? $object [ 'Key' ] : $object [ 'Prefix' ]
);
2013-07-10 18:12:48 +04:00
if ( $file != basename ( $path )) {
2013-07-03 20:01:14 +04:00
$files [] = $file ;
2012-05-17 05:51:45 +04:00
}
}
2013-07-03 20:01:14 +04:00
2013-01-28 18:34:15 +04:00
\OC\Files\Stream\Dir :: register ( 'amazons3' . $path , $files );
2013-07-03 20:01:14 +04:00
2013-01-28 18:34:15 +04:00
return opendir ( 'fakedir://amazons3' . $path );
2013-07-03 20:01:14 +04:00
} catch ( S3Exception $e ) {
2013-08-01 12:08:41 +04:00
\OCP\Util :: writeLog ( 'files_external' , $e -> getMessage (), \OCP\Util :: ERROR );
2013-07-03 20:01:14 +04:00
return false ;
2012-05-17 05:51:45 +04:00
}
}
public function stat ( $path ) {
2013-07-03 20:01:14 +04:00
$path = $this -> normalizePath ( $path );
try {
if ( $this -> is_dir ( $path ) && $path != '.' ) {
$path .= '/' ;
}
$result = $this -> connection -> headObject ( array (
'Bucket' => $this -> bucket ,
'Key' => $path
));
$stat = array ();
$stat [ 'size' ] = $result [ 'ContentLength' ] ? $result [ 'ContentLength' ] : 0 ;
if ( $result [ 'Metadata' ][ 'lastmodified' ]) {
$stat [ 'mtime' ] = strtotime ( $result [ 'Metadata' ][ 'lastmodified' ]);
} else {
$stat [ 'mtime' ] = strtotime ( $result [ 'LastModified' ]);
}
2012-05-17 05:51:45 +04:00
$stat [ 'atime' ] = time ();
2013-07-03 20:01:14 +04:00
2012-05-17 05:51:45 +04:00
return $stat ;
2013-07-03 20:01:14 +04:00
} catch ( S3Exception $e ) {
2013-08-01 12:08:41 +04:00
\OCP\Util :: writeLog ( 'files_external' , $e -> getMessage (), \OCP\Util :: ERROR );
2013-07-03 20:01:14 +04:00
return false ;
2012-05-17 05:51:45 +04:00
}
}
public function filetype ( $path ) {
2013-07-03 20:01:14 +04:00
$path = $this -> normalizePath ( $path );
try {
if ( $path != '.' && $this -> connection -> doesObjectExist ( $this -> bucket , $path )) {
return 'file' ;
}
if ( $path != '.' ) {
$path .= '/' ;
2012-05-17 05:51:45 +04:00
}
2013-07-03 20:01:14 +04:00
if ( $this -> connection -> doesObjectExist ( $this -> bucket , $path )) {
return 'dir' ;
}
} catch ( S3Exception $e ) {
2013-08-01 12:08:41 +04:00
\OCP\Util :: writeLog ( 'files_external' , $e -> getMessage (), \OCP\Util :: ERROR );
2013-07-03 20:01:14 +04:00
return false ;
2012-05-17 05:51:45 +04:00
}
2013-07-03 20:01:14 +04:00
2012-05-17 05:51:45 +04:00
return false ;
}
2013-07-03 20:01:14 +04:00
public function unlink ( $path ) {
$path = $this -> normalizePath ( $path );
try {
$result = $this -> connection -> deleteObject ( array (
'Bucket' => $this -> bucket ,
'Key' => $path
));
2013-07-10 09:47:15 +04:00
$this -> testTimeout ();
2013-07-03 20:01:14 +04:00
} catch ( S3Exception $e ) {
2013-08-01 12:08:41 +04:00
\OCP\Util :: writeLog ( 'files_external' , $e -> getMessage (), \OCP\Util :: ERROR );
2013-07-03 20:01:14 +04:00
return false ;
}
return true ;
2012-05-17 05:51:45 +04:00
}
public function fopen ( $path , $mode ) {
2013-07-03 20:01:14 +04:00
$path = $this -> normalizePath ( $path );
2012-05-17 05:51:45 +04:00
switch ( $mode ) {
case 'r' :
case 'rb' :
2012-09-07 20:30:48 +04:00
$tmpFile = \OC_Helper :: tmpFile ();
2013-07-03 20:01:14 +04:00
self :: $tmpFiles [ $tmpFile ] = $path ;
try {
$result = $this -> connection -> getObject ( array (
'Bucket' => $this -> bucket ,
'Key' => $path ,
'SaveAs' => $tmpFile
));
} catch ( S3Exception $e ) {
2013-08-01 12:08:41 +04:00
\OCP\Util :: writeLog ( 'files_external' , $e -> getMessage (), \OCP\Util :: ERROR );
2013-07-03 20:01:14 +04:00
return false ;
2012-05-17 05:51:45 +04:00
}
2013-07-03 20:01:14 +04:00
return fopen ( $tmpFile , 'r' );
2012-05-17 05:51:45 +04:00
case 'w' :
case 'wb' :
case 'a' :
case 'ab' :
case 'r+' :
case 'w+' :
case 'wb+' :
case 'a+' :
case 'x' :
case 'x+' :
case 'c' :
case 'c+' :
if ( strrpos ( $path , '.' ) !== false ) {
$ext = substr ( $path , strrpos ( $path , '.' ));
} else {
$ext = '' ;
}
2012-09-07 20:30:48 +04:00
$tmpFile = \OC_Helper :: tmpFile ( $ext );
2013-01-28 18:34:15 +04:00
\OC\Files\Stream\Close :: registerCallback ( $tmpFile , array ( $this , 'writeBack' ));
2012-05-17 05:51:45 +04:00
if ( $this -> file_exists ( $path )) {
$source = $this -> fopen ( $path , 'r' );
file_put_contents ( $tmpFile , $source );
}
2013-07-03 20:01:14 +04:00
self :: $tmpFiles [ $tmpFile ] = $path ;
2013-01-28 18:34:15 +04:00
return fopen ( 'close://' . $tmpFile , $mode );
2012-05-17 05:51:45 +04:00
}
return false ;
}
public function getMimeType ( $path ) {
2013-07-03 20:01:14 +04:00
$path = $this -> normalizePath ( $path );
if ( $this -> is_dir ( $path )) {
2012-05-17 05:51:45 +04:00
return 'httpd/unix-directory' ;
2013-07-03 20:01:14 +04:00
} else if ( $this -> file_exists ( $path )) {
try {
$result = $this -> connection -> headObject ( array (
'Bucket' => $this -> bucket ,
'Key' => $path
));
} catch ( S3Exception $e ) {
2013-08-01 12:08:41 +04:00
\OCP\Util :: writeLog ( 'files_external' , $e -> getMessage (), \OCP\Util :: ERROR );
2013-07-03 20:01:14 +04:00
return false ;
2012-11-30 19:27:11 +04:00
}
2013-07-03 20:01:14 +04:00
return $result [ 'ContentType' ];
2012-05-17 05:51:45 +04:00
}
return false ;
}
public function touch ( $path , $mtime = null ) {
2013-07-03 20:01:14 +04:00
$path = $this -> normalizePath ( $path );
$metadata = array ();
2013-07-10 18:12:48 +04:00
if ( ! is_null ( $mtime )) {
2013-07-03 20:01:14 +04:00
$metadata = array ( 'lastmodified' => $mtime );
2012-05-17 05:51:45 +04:00
}
2013-07-03 20:01:14 +04:00
try {
if ( $this -> file_exists ( $path )) {
if ( $this -> is_dir ( $path ) && $path != '.' ) {
$path .= '/' ;
}
$result = $this -> connection -> copyObject ( array (
'Bucket' => $this -> bucket ,
'Key' => $path ,
'Metadata' => $metadata ,
'CopySource' => $this -> bucket . '/' . $path
));
2013-07-10 09:47:15 +04:00
$this -> testTimeout ();
2013-07-03 20:01:14 +04:00
} else {
$result = $this -> connection -> putObject ( array (
'Bucket' => $this -> bucket ,
'Key' => $path ,
'Metadata' => $metadata
));
2013-07-10 09:47:15 +04:00
$this -> testTimeout ();
2013-07-03 20:01:14 +04:00
}
} catch ( S3Exception $e ) {
2013-08-01 12:08:41 +04:00
\OCP\Util :: writeLog ( 'files_external' , $e -> getMessage (), \OCP\Util :: ERROR );
2013-07-03 20:01:14 +04:00
return false ;
}
return true ;
}
public function copy ( $path1 , $path2 ) {
$path1 = $this -> normalizePath ( $path1 );
$path2 = $this -> normalizePath ( $path2 );
if ( $this -> is_file ( $path1 )) {
try {
$result = $this -> connection -> copyObject ( array (
'Bucket' => $this -> bucket ,
'Key' => $path2 ,
'CopySource' => $this -> bucket . '/' . $path1
));
2013-07-10 09:47:15 +04:00
$this -> testTimeout ();
2013-07-03 20:01:14 +04:00
} catch ( S3Exception $e ) {
2013-08-01 12:08:41 +04:00
\OCP\Util :: writeLog ( 'files_external' , $e -> getMessage (), \OCP\Util :: ERROR );
2013-07-03 20:01:14 +04:00
return false ;
}
} else {
if ( $this -> file_exists ( $path2 )) {
return false ;
}
try {
$result = $this -> connection -> copyObject ( array (
'Bucket' => $this -> bucket ,
'Key' => $path2 . '/' ,
'CopySource' => $this -> bucket . '/' . $path1 . '/'
));
2013-07-10 09:47:15 +04:00
$this -> testTimeout ();
2013-07-03 20:01:14 +04:00
} catch ( S3Exception $e ) {
2013-08-01 12:08:41 +04:00
\OCP\Util :: writeLog ( 'files_external' , $e -> getMessage (), \OCP\Util :: ERROR );
2013-07-03 20:01:14 +04:00
return false ;
}
$dh = $this -> opendir ( $path1 );
2013-09-04 15:06:04 +04:00
if ( is_resource ( $dh )) {
while (( $file = readdir ( $dh )) !== false ) {
if ( $file === '.' || $file === '..' ) {
continue ;
}
$source = $path1 . '/' . $file ;
$target = $path2 . '/' . $file ;
$this -> copy ( $source , $target );
2013-07-03 20:01:14 +04:00
}
2013-09-04 15:06:04 +04:00
}
2013-07-03 20:01:14 +04:00
}
return true ;
}
public function rename ( $path1 , $path2 ) {
$path1 = $this -> normalizePath ( $path1 );
$path2 = $this -> normalizePath ( $path2 );
if ( $this -> is_file ( $path1 )) {
2013-07-10 18:09:22 +04:00
if ( $this -> copy ( $path1 , $path2 ) === false ) {
2013-07-03 20:01:14 +04:00
return false ;
}
2013-07-10 18:09:22 +04:00
if ( $this -> unlink ( $path1 ) === false ) {
2013-07-03 20:01:14 +04:00
$this -> unlink ( $path2 );
return false ;
}
} else {
if ( $this -> file_exists ( $path2 )) {
return false ;
}
2013-07-10 18:09:22 +04:00
if ( $this -> copy ( $path1 , $path2 ) === false ) {
2013-07-03 20:01:14 +04:00
return false ;
}
2013-07-10 18:12:48 +04:00
if ( $this -> rmdir ( $path1 ) === false ) {
2013-07-03 20:01:14 +04:00
$this -> rmdir ( $path2 );
return false ;
}
2012-05-17 05:51:45 +04:00
}
2013-07-03 20:01:14 +04:00
return true ;
2012-05-17 05:51:45 +04:00
}
2012-12-28 21:00:48 +04:00
public function test () {
2013-12-02 21:32:32 +04:00
$test = $this -> connection -> getBucketAcl ( array (
'Bucket' => $this -> bucket ,
));
if ( isset ( $test ) && ! is_null ( $test -> getPath ( 'Owner/ID' ))) {
2012-12-28 21:00:48 +04:00
return true ;
}
return false ;
}
2013-07-03 20:01:14 +04:00
public function getId () {
return $this -> id ;
}
public function getConnection () {
return $this -> connection ;
}
public function writeBack ( $tmpFile ) {
2013-07-10 18:12:48 +04:00
if ( ! isset ( self :: $tmpFiles [ $tmpFile ])) {
2013-07-03 20:01:14 +04:00
return false ;
}
try {
$result = $this -> connection -> putObject ( array (
'Bucket' => $this -> bucket ,
'Key' => self :: $tmpFiles [ $tmpFile ],
'SourceFile' => $tmpFile ,
'ContentType' => \OC_Helper :: getMimeType ( $tmpFile ),
'ContentLength' => filesize ( $tmpFile )
));
2013-07-10 09:47:15 +04:00
$this -> testTimeout ();
2013-07-03 20:01:14 +04:00
unlink ( $tmpFile );
} catch ( S3Exception $e ) {
2013-08-01 12:08:41 +04:00
\OCP\Util :: writeLog ( 'files_external' , $e -> getMessage (), \OCP\Util :: ERROR );
2013-07-03 20:01:14 +04:00
return false ;
}
}
2012-05-17 05:51:45 +04:00
}