diff --git a/apps/files/appinfo/filesync.php b/apps/files/appinfo/filesync.php
new file mode 100644
index 0000000000000000000000000000000000000000..707ee2435c083db00e102629b8bc9b89dcae01a6
--- /dev/null
+++ b/apps/files/appinfo/filesync.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+/**
+ * filesync can be called with a PUT method.
+ * PUT takes a stream starting with a 2 byte blocksize,
+ *     followed by binary md5 of the blocks. Everything in big-endian.
+ *     The return is a json encoded with:
+ *       - 'transferid'
+ *       - 'needed' chunks
+ *       - 'last' checked chunk
+ * The URL is made of 3 parts, the service url (remote.php/filesync/), the sync
+ * type and the path in ownCloud.
+ * At the moment the only supported sync type is 'oc_chunked'.
+ * The final URL will look like http://.../remote.php/filesync/oc_chunked/path/to/file
+ */
+
+// only need filesystem apps
+$RUNTIME_APPTYPES=array('filesystem','authentication');
+OC_App::loadApps($RUNTIME_APPTYPES);
+if(!OC_User::isLoggedIn()){
+        if(!isset($_SERVER['PHP_AUTH_USER'])){
+                header('WWW-Authenticate: Basic realm="ownCloud Server"');
+                header('HTTP/1.0 401 Unauthorized');
+                echo 'Valid credentials must be supplied';
+                exit();
+        } else {
+                if(!OC_User::login($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"])){
+                        exit();
+                }
+        }
+}
+
+list($type,$file) = explode('/', substr($path_info,1+strlen($service)+1), 2);
+
+if ($type != 'oc_chunked') {
+	OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND);
+	die;
+}
+
+if (!OC_Filesystem::is_file($file)) {
+	OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND);
+	die;
+}
+
+switch($_SERVER['REQUEST_METHOD']) {
+	case 'PUT':
+		$input = fopen("php://input", "r");
+		$org_file = OC_Filesystem::fopen($file, 'rb');
+		$info = array(
+			'name' => basename($file),
+		);
+		$sync = new OC_FileChunking($info);
+		$result = $sync->signature_split($org_file, $input);
+		echo json_encode($result);
+		break;
+	default:
+		OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND);
+}
diff --git a/apps/files/appinfo/info.xml b/apps/files/appinfo/info.xml
index 105df092ce59c743bff1b9b115226c761ae4f9b7..e58f83c5a01260969ca8a72c3c8e2c891215c2b2 100644
--- a/apps/files/appinfo/info.xml
+++ b/apps/files/appinfo/info.xml
@@ -15,5 +15,6 @@
 	<remote>
 		<files>appinfo/remote.php</files>
 		<webdav>appinfo/remote.php</webdav>
+		<filesync>appinfo/filesync.php</filesync>
 	</remote>
 </info>
diff --git a/apps/files/appinfo/version b/apps/files/appinfo/version
index 1b87bcd0b09c209d6eac67fd268556b35acd1933..e25d8d9f357cfa028ffbf2f7bdc597e28968696b 100644
--- a/apps/files/appinfo/version
+++ b/apps/files/appinfo/version
@@ -1 +1 @@
-1.1.4
\ No newline at end of file
+1.1.5
diff --git a/lib/connector/sabre/directory.php b/lib/connector/sabre/directory.php
index 7f8434c71512256aa911d2d553e0b7a3331f9ea2..09c65f19b80195b08972dc8b8d326c057ba70343 100644
--- a/lib/connector/sabre/directory.php
+++ b/lib/connector/sabre/directory.php
@@ -48,11 +48,23 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa
 	 * @return null|string
 	 */
 	public function createFile($name, $data = null) {
+		if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
+			$info = OC_FileChunking::decodeName($name);
+			$chunk_handler = new OC_FileChunking($info);
+			$chunk_handler->store($info['index'], $data);
+			if ($chunk_handler->isComplete()) {
+				$newPath = $this->path . '/' . $info['name'];
+				$f = OC_Filesystem::fopen($newPath, 'w');
+				$chunk_handler->assemble($f);
+				return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath);
+			}
+		} else {
+			$newPath = $this->path . '/' . $name;
+			OC_Filesystem::file_put_contents($newPath,$data);
+			return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath);
+		}
 
-		$newPath = $this->path . '/' . $name;
-		OC_Filesystem::file_put_contents($newPath,$data);
-
-		return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath);
+		return null;
 	}
 
 	/**
diff --git a/lib/filechunking.php b/lib/filechunking.php
new file mode 100644
index 0000000000000000000000000000000000000000..d03af226d8b9a3ddf908b46277e9788a28cdd4b2
--- /dev/null
+++ b/lib/filechunking.php
@@ -0,0 +1,94 @@
+<?php
+/**
+ * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+
+class OC_FileChunking {
+	protected $info;
+	protected $cache;
+
+	static public function decodeName($name) {
+		preg_match('/(?P<name>.*)-chunking-(?P<transferid>\d+)-(?P<chunkcount>\d+)-(?P<index>\d+)/', $name, $matches);
+		return $matches;
+	}
+
+	public function __construct($info) {
+		$this->info = $info;
+	}
+
+	public function getPrefix() {
+		$name = $this->info['name'];
+		$transferid = $this->info['transferid'];
+
+		return $name.'-chunking-'.$transferid.'-';
+	}
+
+	protected function getCache() {
+		if (!isset($this->cache)) {
+			$this->cache = new OC_Cache_File();
+		}
+		return $this->cache;
+	}
+
+	public function store($index, $data) {
+		$cache = $this->getCache();
+		$name = $this->getPrefix().$index;
+		$cache->set($name, $data);
+	}
+
+	public function isComplete() {
+		$prefix = $this->getPrefix();
+		$parts = 0;
+		$cache = $this->getCache();
+		for($i=0; $i < $this->info['chunkcount']; $i++) {
+			if ($cache->hasKey($prefix.$i)) {
+				$parts ++;
+			}
+		}
+		return $parts == $this->info['chunkcount'];
+	}
+
+	public function assemble($f) {
+		$cache = $this->getCache();
+		$prefix = $this->getPrefix();
+		for($i=0; $i < $this->info['chunkcount']; $i++) {
+			$chunk = $cache->get($prefix.$i);
+			$cache->remove($prefix.$i);
+			fwrite($f,$chunk);
+		}
+		fclose($f);
+	}
+
+	public function signature_split($orgfile, $input) {
+		$info = unpack('n', fread($input, 2));
+		$blocksize = $info[1];
+		$this->info['transferid'] = mt_rand();
+		$count = 0;
+		$needed = array();
+		$cache = $this->getCache();
+		$prefix = $this->getPrefix();
+		while (!feof($orgfile)) {
+			$new_md5 = fread($input, 16);
+			if (feof($input)) {
+				break;
+			}
+			$data = fread($orgfile, $blocksize);
+			$org_md5 = md5($data, true);
+			if ($org_md5 == $new_md5) {
+				$cache->set($prefix.$count, $data);
+			} else {
+				$needed[] = $count;
+			}
+			$count++;
+		}
+		return array(
+			'transferid' => $this->info['transferid'],
+			'needed' => $needed,
+			'count' => $count,
+		);
+	}
+}