diff --git a/apps/files_external/lib/amazons3.php b/apps/files_external/lib/amazons3.php
new file mode 100644
index 0000000000000000000000000000000000000000..e847ef143c3d14bd774753f05f9bc2b1bcec2161
--- /dev/null
+++ b/apps/files_external/lib/amazons3.php
@@ -0,0 +1,236 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Michael Gapczynski
+* @copyright 2012 Michael Gapczynski mtgap@owncloud.com
+*
+* 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/>.
+*/
+
+require_once 'aws-sdk-1.5.5/sdk.class.php';
+
+class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common {
+
+	private $s3;
+	private $bucket;
+	private $objects = array();
+
+	private static $tempFiles = array();
+	
+	// TODO options: storage class, encryption server side, encrypt before upload?
+
+	public function __construct($params) {
+		$this->s3 = new AmazonS3(array('key' => $params['key'], 'secret' => $params['secret']));
+		$this->bucket = $params['bucket'];
+	}
+
+	private function getObject($path) {
+		if (array_key_exists($path, $this->objects)) {
+			return $this->objects[$path];
+		} else {
+			$response = $this->s3->get_object_metadata($this->bucket, $path);
+			if ($response) {
+				$this->objects[$path] = $response;
+				return $response;
+			// This object could be a folder, a '/' must be at the end of the path
+			} else if (substr($path, -1) != '/') {
+				$response = $this->s3->get_object_metadata($this->bucket, $path.'/');
+				if ($response) {
+					$this->objects[$path] = $response;
+					return $response;
+				}
+			}
+		}
+		return false;
+	}
+
+	public function mkdir($path) {
+		// Folders in Amazon S3 are 0 byte objects with a '/' at the end of the name
+		if (substr($path, -1) != '/') {
+			$path .= '/';
+		}
+		$response = $this->s3->create_object($this->bucket, $path, array('body' => ''));
+		return $response->isOK();
+	}
+
+	public function rmdir($path) {
+		if (substr($path, -1) != '/') {
+			$path .= '/';
+		}
+		return $this->unlink($path);
+	}
+
+	public function opendir($path) {
+		if ($path == '' || $path == '/') {
+			// Use the '/' delimiter to only fetch objects inside the folder
+			$opt = array('delimiter' => '/');
+		} else {
+			if (substr($path, -1) != '/') {
+				$path .= '/';
+			}
+			$opt = array('delimiter' => '/', 'prefix' => $path);
+		}
+		$response = $this->s3->list_objects($this->bucket, $opt);
+		if ($response->isOK()) {
+			$files = array();
+			foreach ($response->body->Contents as $object) {
+				// The folder being opened also shows up in the list of objects, don't add it to the files
+				if ($object->Key != $path) {
+					$files[] = basename($object->Key);
+				}
+			}
+			// Sub folders show up as CommonPrefixes
+			foreach ($response->body->CommonPrefixes as $object) {
+				$files[] = basename($object->Prefix);
+			}
+			OC_FakeDirStream::$dirs['amazons3'] = $files;
+			return opendir('fakedir://amazons3');
+		}
+		return false;
+	}
+
+	public function stat($path) {
+		if ($path == '' || $path == '/') {
+			$stat['size'] = $this->s3->get_bucket_filesize($this->bucket);
+			$stat['atime'] = time();
+			$stat['mtime'] = $stat['atime'];
+			$stat['ctime'] = $stat['atime'];
+		} else if ($object = $this->getObject($path)) {
+			$stat['size'] = $object['Size'];
+			$stat['atime'] = time();
+			$stat['mtime'] = strtotime($object['LastModified']);
+			$stat['ctime'] = $stat['mtime'];
+		}
+		if (isset($stat)) {
+			return $stat;
+		}
+		return false;
+	}
+
+	public function filetype($path) {
+		if ($path == '' || $path == '/') {
+			return 'dir';
+		} else if ($object = $this->getObject($path)) {
+			// Amazon S3 doesn't have typical folders, this is an alternative method to detect a folder
+			if (substr($object['Key'], -1) == '/' && $object['Size'] == 0) {
+				return 'dir';
+			} else {
+				return 'file';
+			}
+		}
+		return false;
+	}
+
+	public function is_readable($path) {
+		// TODO Check acl and determine who grantee is
+		return true;
+	}
+
+	public function is_writable($path) {
+		// TODO Check acl and determine who grantee is
+		return true;
+	}
+
+	public function file_exists($path) {
+		if ($this->filetype($path) == 'dir' && substr($path, -1) != '/') {
+			$path .= '/';
+		}
+		return $this->s3->if_object_exists($this->bucket, $path);
+	}
+
+	public function unlink($path) {
+		$response = $this->s3->delete_object($this->bucket, $path);
+		return $response->isOK();
+	}
+
+	public function fopen($path, $mode) {
+		switch ($mode) {
+			case 'r':
+			case 'rb':
+				$tmpFile = OC_Helper::tmpFile();
+				$handle = fopen($tmpFile, 'w');
+				$response = $this->s3->get_object($this->bucket, $path, array('fileDownload' => $handle));
+				if ($response->isOK()) {
+					return fopen($tmpFile, 'r');
+				}
+				break;
+			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 = '';
+				}
+				$tmpFile = OC_Helper::tmpFile($ext);
+				OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack');
+				if ($this->file_exists($path)) {
+					$source = $this->fopen($path, 'r');
+					file_put_contents($tmpFile, $source);
+				}
+				self::$tempFiles[$tmpFile] = $path;
+				return fopen('close://'.$tmpFile, $mode);
+		}
+		return false;
+	}
+
+	public function writeBack($tmpFile) {
+		if (isset(self::$tempFiles[$tmpFile])) {
+			$handle = fopen($tmpFile, 'r');
+			$response = $this->s3->create_object($this->bucket, self::$tempFiles[$tmpFile], array('fileUpload' => $handle));
+			if ($response->isOK()) {
+				unlink($tmpFile);
+			}
+		}
+	}
+
+	public function getMimeType($path) {
+		if ($this->filetype($path) == 'dir') {
+			return 'httpd/unix-directory';
+		} else if ($object = $this->getObject($path)) {
+			return $object['ContentType'];
+		}
+		return false;
+	}
+
+	public function free_space($path) {
+		// Infinite? 
+		return false;
+	}
+
+	public function touch($path, $mtime = null) {
+		if (is_null($mtime)) {
+			$mtime = time();
+		}
+		if ($this->filetype($path) == 'dir' && substr($path, -1) != '/') {
+			$path .= '/';
+		}
+		$response = $this->s3->update_object($this->bucket, $path, array('meta' => array('LastModified' => $mtime)));
+		return $response->isOK();
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/files_external/tests/amazons3.php b/apps/files_external/tests/amazons3.php
new file mode 100644
index 0000000000000000000000000000000000000000..d0084c94afd1c5f5f78f3a907845e23244951b8e
--- /dev/null
+++ b/apps/files_external/tests/amazons3.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Michael Gapczynski
+* @copyright 2012 Michael Gapczynski mtgap@owncloud.com
+*
+* 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/>.
+*/
+
+$config = include('apps/files_external/tests/config.php');
+if (!is_array($config) or !isset($config['amazons3']) or !$config['amazons3']['run']) {
+	abstract class Test_Filestorage_AmazonS3 extends Test_FileStorage{}
+	return;
+} else {
+	class Test_Filestorage_AmazonS3 extends Test_FileStorage {
+
+		private $config;
+		private $id;
+
+		public function setUp() {
+			$id = uniqid();
+			$this->config = include('apps/files_external/tests/config.php');
+			$this->config['amazons3']['bucket'] = $id; // Make sure we have a new empty bucket to work in
+			$this->instance = new OC_Filestorage_AmazonS3($this->config['amazons3']);
+		}
+
+		public function tearDown() {
+			$s3 = new AmazonS3(array('key' => $this->config['amazons3']['key'], 'secret' => $this->config['amazons3']['secret']));
+			if ($s3->delete_all_objects($this->id)) {
+				$s3->delete_bucket($this->id);
+			}
+		}
+	}
+}
+
+?>
+
diff --git a/apps/files_external/tests/config.php b/apps/files_external/tests/config.php
index 35b5fa038631567169a56274013d7ffa890b95e8..970008d642c90eac5732c65601cd6fc4788d9c63 100644
--- a/apps/files_external/tests/config.php
+++ b/apps/files_external/tests/config.php
@@ -36,4 +36,10 @@ return array(
 		'host'=>'localhost',
 		'root'=>'/test',
 	),
+	'amazons3'=>array(
+		'run'=>false,
+		'key'=>'test',
+		'secret'=>'test',
+		'bucket'=>'bucket',
+	),
 );